From 170e5d3f6dff130d3bfb9cd4ddaf8e4ced06e758 Mon Sep 17 00:00:00 2001 From: Matheus Castro Date: Thu, 22 Dec 2022 14:09:27 -0300 Subject: [PATCH] Remove Fibers from meteor-tools: - Adapt forkJoin and other files functions to correctly be awaited. --- tools/cli/commands-packages.js | 4 +- tools/cli/main.js | 1 + tools/fs/files.ts | 38 ++++++----- tools/isobuild/builder.js | 78 +++++++++++----------- tools/isobuild/bundler.js | 24 +++---- tools/isobuild/import-scanner.ts | 2 +- tools/isobuild/isopack.js | 2 +- tools/meteor-services/deploy.js | 2 +- tools/packaging/package-client.js | 4 +- tools/packaging/tropohouse.js | 4 +- tools/project-context.js | 103 +++++++++++++++++------------- tools/tool-env/cleanup.js | 15 ++--- tools/utils/buildmessage.js | 62 ++++++++---------- 13 files changed, 168 insertions(+), 171 deletions(-) diff --git a/tools/cli/commands-packages.js b/tools/cli/commands-packages.js index 9bad495e74..362de3dfd5 100644 --- a/tools/cli/commands-packages.js +++ b/tools/cli/commands-packages.js @@ -1725,7 +1725,7 @@ var maybeUpdateRelease = async function (options) { await projectContext.prepareProjectForBuild(); }); - projectContext.writeReleaseFileAndDevBundleLink(releaseName); + await projectContext.writeReleaseFileAndDevBundleLink(releaseName); projectContext.packageMapDelta.displayOnConsole({ title: ("Changes to your project's package version selections from " + @@ -2734,7 +2734,7 @@ main.registerCommand({ throw Error("missing tool for " + osArch); } - tmpTropo.linkToLatestMeteor(files.pathJoin( + await tmpTropo.linkToLatestMeteor(files.pathJoin( tmpTropo.packagePath(toolPackage, toolVersion, true), toolRecord.path, 'meteor')); diff --git a/tools/cli/main.js b/tools/cli/main.js index 5d70966800..87bc648971 100644 --- a/tools/cli/main.js +++ b/tools/cli/main.js @@ -928,6 +928,7 @@ makeGlobalAsyncLocalStorage().run({}, async function () { appReleaseFile = new projectContextModule.ReleaseFile({ projectDir: appDir }); + await appReleaseFile.init(); // This is what happens if the file exists and is empty. This really // shouldn't happen unless the user did it manually. if (appReleaseFile.noReleaseSpecified()) { diff --git a/tools/fs/files.ts b/tools/fs/files.ts index 12ab83ea93..fc932c4f76 100644 --- a/tools/fs/files.ts +++ b/tools/fs/files.ts @@ -487,7 +487,7 @@ function pathIsDirectory(path: string) { // If options.ignore is present, it should be a list of regexps. Any // file whose basename matches one of the regexps, before // transformation, will be skipped. -export function cp_r(from: string, to: string, options: { +export async function cp_r(from: string, to: string, options: { preserveSymlinks?: boolean; ignore?: RegExp[]; transformFilename?: (f: string) => string; @@ -531,7 +531,7 @@ export function cp_r(from: string, to: string, options: { mkdir_p(pathDirname(to)); if (stat.isSymbolicLink()) { - symlinkWithOverwrite(readlink(from), to); + await symlinkWithOverwrite(readlink(from), to); } else if (options.transformContents) { writeFile(to, options.transformContents( @@ -557,7 +557,7 @@ export function cp_r(from: string, to: string, options: { // create a symlink, overwriting the target link, file, or directory // if it exists export const symlinkWithOverwrite = -Profile("files.symlinkWithOverwrite", function symlinkWithOverwrite( +Profile("files.symlinkWithOverwrite", async function symlinkWithOverwrite( source: string, target: string, ) { @@ -586,7 +586,7 @@ Profile("files.symlinkWithOverwrite", function symlinkWithOverwrite( return; } // overwrite existing link, file, or directory - rm_recursive(target); + await rm_recursive(target); symlink(...args); } else { throw e; @@ -746,10 +746,10 @@ export function changeTempDirStatus(dir: string, status: boolean) { if (! process.env.METEOR_SAVE_TMPDIRS) { cleanup.onExit(function () { - Object.entries(tempDirs).filter(([_, isTmp]) => !!isTmp).map(([dir]) => dir).forEach(dir => { + return Object.entries(tempDirs).filter(([_, isTmp]) => !!isTmp).map(([dir]) => dir).map(async dir => { delete tempDirs[dir]; try { - rm_recursive(dir); + await rm_recursive(dir); } catch (err) { // Don't crash and print a stack trace because we failed to delete // a temp directory. This happens sometimes on Windows and seems @@ -768,7 +768,7 @@ type TarOptions = { // into a destination directory. destPath should not exist yet, and // the archive should contain a single top-level directory, which will // be renamed atomically to destPath. -export function extractTarGz( +export async function extractTarGz( buffer: Buffer, destPath: string, options: TarOptions = {}, @@ -784,9 +784,7 @@ export function extractTarGz( const startTime = +new Date; // standardize only one way of extracting, as native ones can be tricky - const promise = tryExtractWithNpmTar(buffer, tempDir, options) - - promise.await(); + await tryExtractWithNpmTar(buffer, tempDir, options); // succeed! const topLevelOfArchive = readdir(tempDir) @@ -800,8 +798,8 @@ export function extractTarGz( } const extractDir = pathJoin(tempDir, topLevelOfArchive[0]); - rename(extractDir, destPath); - rm_recursive(tempDir); + await rename(extractDir, destPath); + await rm_recursive(tempDir); if (options.verbose) { console.log("Finished extracting in", Date.now() - startTime, "ms"); @@ -978,7 +976,7 @@ Profile("files.renameDirAlmostAtomically", async (fromDir: string, toDir: string }); export const writeFileAtomically = -Profile("files.writeFileAtomically", function (filename: string, contents: string | Buffer) { +Profile("files.writeFileAtomically", async function (filename: string, contents: string | Buffer) { const parentDir = pathDirname(filename); mkdir_p(parentDir); @@ -988,19 +986,19 @@ Profile("files.writeFileAtomically", function (filename: string, contents: strin ); writeFile(tmpFile, contents); - rename(tmpFile, filename); + await rename(tmpFile, filename); }); // Like fs.symlinkSync, but creates a temporary link and renames it over the // file; this means it works even if the file already exists. // Do not use this function on Windows, it won't work. -export function symlinkOverSync(linkText: string, file: string) { +export async function symlinkOverSync(linkText: string, file: string) { file = pathResolve(file); const tmpSymlink = pathJoin( pathDirname(file), "." + pathBasename(file) + ".tmp" + utils.randomToken()); symlink(linkText, tmpSymlink); - rename(tmpSymlink, file); + await rename(tmpSymlink, file); } // Return the result of evaluating `code` using @@ -1375,7 +1373,7 @@ export function _getLocationFromScriptLinkToMeteorScript(script: string | Buffer return convertToPosixPath(scriptLocation, ! isAbsolute); } -export function linkToMeteorScript( +export async function linkToMeteorScript( scriptLocation: string, linkLocation: string, platform: string, @@ -1390,7 +1388,7 @@ export function linkToMeteorScript( writeFile(linkLocation, script, { encoding: "ascii" }); } else { // Symlink meteor tool - symlinkOverSync(scriptLocation, linkLocation); + await symlinkOverSync(scriptLocation, linkLocation); } } @@ -1615,11 +1613,11 @@ export const rename = isWindowsLikeFilesystem() ? function (from: string, to: st } } attempt(); - }).catch((error: any) => { + }).catch(async (error: any) => { if (error.code === 'EPERM' || error.code === 'EACCESS') { cp_r(from, to, { preserveSymlinks: true }); - rm_recursive(from); + await rm_recursive(from); } else { throw error; } diff --git a/tools/isobuild/builder.js b/tools/isobuild/builder.js index 5fb3f0858d..6732c9a54e 100644 --- a/tools/isobuild/builder.js +++ b/tools/isobuild/builder.js @@ -312,7 +312,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` const absPath = files.pathJoin(this.buildPath, relPath); if (symlink) { - symlinkWithOverwrite(symlink, absPath); + await symlinkWithOverwrite(symlink, absPath); } else { hash = hash || sha1(getData()); @@ -604,7 +604,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` return this._copyDirectory(options); } - _copyDirectory({ + async _copyDirectory({ from, to, ignore, specificFiles, @@ -642,12 +642,12 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` const rootDir = realpath(from); - const walk = (absFrom, relTo) => { + const walk = async (absFrom, relTo) => { if (symlink && ! (relTo in this.usedAsFile)) { this._ensureDirectory(files.pathDirname(relTo)); const absTo = files.pathResolve(this.buildPath, relTo); if (this.previousCreatedSymlinks[absFrom] !== relTo) { - symlinkWithOverwrite(absFrom, absTo); + await symlinkWithOverwrite(absFrom, absTo); } this.usedAsFile[relTo] = false; this.createdSymlinks[absFrom] = relTo; @@ -656,12 +656,12 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` this._ensureDirectory(relTo); - optimisticReaddir(absFrom).forEach(item => { + for (const item of optimisticReaddir(absFrom)) { let thisAbsFrom = files.pathResolve(absFrom, item); const thisRelTo = files.pathJoin(relTo, item); if (specificPaths && !(thisRelTo in specificPaths)) { - return; + continue; } // Returns files.realpath(thisAbsFrom), if it is external to @@ -684,7 +684,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` } const isExternal = - files.pathRelative(rootDir, real).startsWith(".."); + files.pathRelative(rootDir, real).startsWith(".."); // Now cachedExternalPath is either a string or false. return cachedExternalPath = isExternal && real; @@ -709,7 +709,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` if (! fileStatus) { // If the file did not exist, skip it. - return; + continue; } let itemForMatch = item; @@ -720,22 +720,22 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` // skip excluded files if (ignore.some(pattern => itemForMatch.match(pattern))) { - return; + continue; } if (typeof filter === "function" && ! filter(thisAbsFrom, isDirectory)) { - return; + continue; } if (npmDiscards instanceof NpmDiscards && npmDiscards.shouldDiscard(thisAbsFrom, isDirectory)) { - return; + continue; } if (isDirectory) { - walk(thisAbsFrom, thisRelTo); - return; + await walk(thisAbsFrom, thisRelTo); + continue; } if (fileStatus.isSymbolicLink()) { @@ -743,16 +743,16 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` // portable than absolute links, so getExternalPath() is // preferred if it returns a path. const linkSource = getExternalPath() || - files.readlink(thisAbsFrom); + files.readlink(thisAbsFrom); const linkTarget = - files.pathResolve(this.buildPath, thisRelTo); + files.pathResolve(this.buildPath, thisRelTo); - if (symlinkIfPossible(linkSource, linkTarget)) { + if (await symlinkIfPossible(linkSource, linkTarget)) { // A symlink counts as a file, as far as "can you put // something under it" goes. this.usedAsFile[thisRelTo] = true; - return; + continue; } } @@ -767,28 +767,28 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` const content = optimisticReadFile(thisAbsFrom); files.writeFile( - files.pathResolve(this.buildPath, thisRelTo), - // The reason we call files.writeFile here instead of - // files.copyFile is so that we can read the file using - // optimisticReadFile instead of files.createReadStream. - content, - // Logic borrowed from files.copyFile: "Create the file as - // readable and writable by everyone, and executable by everyone - // if the original file is executably by owner. (This mode will be - // modified by umask.) We don't copy the mode *directly* because - // this function is used by 'meteor create' which is copying from - // the read-only tools tree into a writable app." - { mode: (fileStatus.mode & 0o100) ? 0o777 : 0o666 }, + files.pathResolve(this.buildPath, thisRelTo), + // The reason we call files.writeFile here instead of + // files.copyFile is so that we can read the file using + // optimisticReadFile instead of files.createReadStream. + content, + // Logic borrowed from files.copyFile: "Create the file as + // readable and writable by everyone, and executable by everyone + // if the original file is executably by owner. (This mode will be + // modified by umask.) We don't copy the mode *directly* because + // this function is used by 'meteor create' which is copying from + // the read-only tools tree into a writable app." + { mode: (fileStatus.mode & 0o100) ? 0o777 : 0o666 }, ); } this.writtenHashes[thisRelTo] = hash; this.usedAsFile[thisRelTo] = true; } - }); + } }; - walk(rootDir, to); + await walk(rootDir, to); } // Returns a new Builder-compatible object that works just like a @@ -801,7 +801,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` // // TODO(benjamn) This nonsense should be ripped out by any means // necessary... whenever someone has the time. - enter(relPath) { + async enter(relPath) { const subBuilder = {}; const relPathWithSep = relPath + files.pathSep; const methods = [ @@ -814,8 +814,8 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` "enter", ]; - methods.forEach(method => { - subBuilder[method] = (...args) => { + for (const method of methods) { + subBuilder[method] = async (...args) => { if (method === "copyDirectory" || method === "copyNodeModulesDirectory") { // The copy methods take their relative paths via options.to. @@ -825,7 +825,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` args[0] = files.pathJoin(relPath, args[0]); } - let ret = this[method](...args); + let ret = await this[method](...args); if (method === "generateFilename") { // fix up the returned path to be relative to the @@ -835,14 +835,14 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` } if (ret.substr(0, relPathWithSep.length) !== relPathWithSep) { throw new Error("generateFilename returned path outside of " + - "sub-bundle?"); + "sub-bundle?"); } ret = ret.substr(relPathWithSep.length); } return ret; }; - }); + } // Methods that don't have to fix up arguments or return values, because // they are implemented purely in terms of other methods which do. @@ -943,9 +943,9 @@ async function atomicallyRewriteFile(path, data, options) { } } -function symlinkIfPossible(source, target) { +async function symlinkIfPossible(source, target) { try { - symlinkWithOverwrite(source, target); + await symlinkWithOverwrite(source, target); return true; } catch (e) { return false; diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index 7459d6e0d0..be63c2049a 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -2503,7 +2503,7 @@ class JsImage { // them, and 'meteor run' symlinks them. If these contain // arch-specific code then the target will end up having an // appropriately specific arch. - _.each(nodeModulesDirectories, function (nmd) { + for (const nmd of Object.values(nodeModulesDirectories)) { assert.strictEqual(typeof nmd.preferredBundlePath, "string"); // Skip calculating isPortable in 'meteor run' since the @@ -2522,15 +2522,15 @@ class JsImage { }; const prodPackagePredicate = - // This condition essentially means we don't strip devDependencies - // when running tests, which is important for use cases like the one - // described in #7953. Note that devDependencies can still be used - // when buildMode === "development" because the app has access to - // the original node_modules. - (buildMode === "production" || - buildMode === "development") && - nmd.local && // Only filter local node_modules directories. - nmd.getProdPackagePredicate(); + // This condition essentially means we don't strip devDependencies + // when running tests, which is important for use cases like the one + // described in #7953. Note that devDependencies can still be used + // when buildMode === "development" because the app has access to + // the original node_modules. + (buildMode === "production" || + buildMode === "development") && + nmd.local && // Only filter local node_modules directories. + nmd.getProdPackagePredicate(); if (prodPackagePredicate) { // When copying a local node_modules directory, ignore any npm @@ -2546,9 +2546,9 @@ class JsImage { copyOptions.filter = prodPackagePredicate; } - builder.copyNodeModulesDirectory(copyOptions); + await builder.copyNodeModulesDirectory(copyOptions); } - }); + } // This JSON file will be read by npm-rebuild.js, which is executed to // trigger rebuilds for all non-portable npm packages. diff --git a/tools/isobuild/import-scanner.ts b/tools/isobuild/import-scanner.ts index 34fecf889a..03ac55cc05 100644 --- a/tools/isobuild/import-scanner.ts +++ b/tools/isobuild/import-scanner.ts @@ -97,7 +97,7 @@ const reifyCompileWithCache = Profile("reifyCompileWithCache", wrap(function ( if (cacheFilePath) { Promise.resolve().then( - () => writeFileAtomically(cacheFilePath, result), + async () => await writeFileAtomically(cacheFilePath, result), ); } diff --git a/tools/isobuild/isopack.js b/tools/isobuild/isopack.js index ab19e7a80b..77cb537e14 100644 --- a/tools/isobuild/isopack.js +++ b/tools/isobuild/isopack.js @@ -1163,7 +1163,7 @@ Object.assign(Isopack.prototype, { var pluginDir = builder.generateFilename( 'plugin.' + colonConverter.convert(name) + '.' + plugin.arch, { directory: true }); - var pluginBuild = await plugin.write(builder.enter(pluginDir)); + var pluginBuild = await plugin.write(await builder.enter(pluginDir)); var pluginEntry = { name: name, arch: plugin.arch, diff --git a/tools/meteor-services/deploy.js b/tools/meteor-services/deploy.js index 1a413b87d7..8d2f519dbf 100644 --- a/tools/meteor-services/deploy.js +++ b/tools/meteor-services/deploy.js @@ -580,7 +580,7 @@ export async function bundleAndDeploy(options) { if (options.isCacheBuildEnabled) { Console.info('Saving build in cache (--cache-build)...'); - options.projectContext.saveBuildCache({ + await options.projectContext.saveBuildCache({ buildDir, bundlePath, gitCommitHash diff --git a/tools/packaging/package-client.js b/tools/packaging/package-client.js index 44d8ab2c57..ff6b29de2a 100644 --- a/tools/packaging/package-client.js +++ b/tools/packaging/package-client.js @@ -39,10 +39,10 @@ var generateBlankReadme = function () { }; // Save a readme file to a temporary path. -var saveReadmeToTmp = function (readmeInfo) { +var saveReadmeToTmp = async function (readmeInfo) { var tempReadmeDir = files.mkdtemp('readme'); var readmePath = files.pathJoin(tempReadmeDir, "Readme.md"); - files.writeFileAtomically(readmePath, readmeInfo.contents); + await files.writeFileAtomically(readmePath, readmeInfo.contents); return readmePath; }; diff --git a/tools/packaging/tropohouse.js b/tools/packaging/tropohouse.js index 320a0990fc..c8819c8578 100644 --- a/tools/packaging/tropohouse.js +++ b/tools/packaging/tropohouse.js @@ -618,10 +618,10 @@ Object.assign(exports.Tropohouse.prototype, { return files.readLinkToMeteorScript(linkPath, self.platform); }, - linkToLatestMeteor: function (scriptLocation) { + linkToLatestMeteor: async function (scriptLocation) { var self = this; var linkPath = files.pathJoin(self.root, 'meteor'); - files.linkToMeteorScript(scriptLocation, linkPath, self.platform); + await files.linkToMeteorScript(scriptLocation, linkPath, self.platform); }, _getPlatform: function () { diff --git a/tools/project-context.js b/tools/project-context.js index 539f72df91..bcf40378c1 100644 --- a/tools/project-context.js +++ b/tools/project-context.js @@ -391,9 +391,9 @@ Object.assign(ProjectContext.prototype, { var self = this; buildmessage.assertInCapture(); - await buildmessage.enterJob('reading project metadata', function () { + await buildmessage.enterJob('reading project metadata', async function () { // Ensure this is actually a project directory. - self._ensureProjectDir(); + await self._ensureProjectDir(); if (buildmessage.jobHasMessages()) return; @@ -402,6 +402,7 @@ Object.assign(ProjectContext.prototype, { projectDir: self.projectDir, catalog: self._officialCatalog, }); + await self.releaseFile.init(); if (buildmessage.jobHasMessages()) return; @@ -424,6 +425,7 @@ Object.assign(ProjectContext.prototype, { self.cordovaPluginsFile = new exports.CordovaPluginsFile({ projectDir: self.projectDir }); + await self.cordovaPluginsFile.init(); if (buildmessage.jobHasMessages()) return; @@ -431,11 +433,13 @@ Object.assign(ProjectContext.prototype, { self.platformList = new exports.PlatformList({ projectDir: self.projectDir }); + await self.platformList._init(); + if (buildmessage.jobHasMessages()) return; // Read .meteor/.id, creating it if necessary. - self._ensureAppIdentifier(); + await self._ensureAppIdentifier(); if (buildmessage.jobHasMessages()) return; @@ -460,12 +464,12 @@ Object.assign(ProjectContext.prototype, { // Write the new release to .meteor/release and create a // .meteor/dev_bundle symlink to the corresponding dev_bundle. - writeReleaseFileAndDevBundleLink(releaseName) { + async writeReleaseFileAndDevBundleLink(releaseName) { assert.strictEqual(files.inCheckout(), false); - this.releaseFile.write(releaseName); + await this.releaseFile.write(releaseName); }, - _ensureProjectDir: function () { + _ensureProjectDir: async function () { var self = this; files.mkdir_p(files.pathJoin(self.projectDir, '.meteor')); @@ -473,13 +477,13 @@ Object.assign(ProjectContext.prototype, { // so let's make sure it exists! var constraintFilePath = files.pathJoin(self.projectDir, '.meteor', 'packages'); if (! files.exists(constraintFilePath)) { - files.writeFileAtomically(constraintFilePath, ''); + await files.writeFileAtomically(constraintFilePath, ''); } // Let's also make sure we have a minimal gitignore. var gitignorePath = files.pathJoin(self.projectDir, '.meteor', '.gitignore'); if (! files.exists(gitignorePath)) { - files.writeFileAtomically(gitignorePath, 'local\n'); + await files.writeFileAtomically(gitignorePath, 'local\n'); } }, @@ -526,7 +530,7 @@ Object.assign(ProjectContext.prototype, { return self.isopackCache.getLintingMessagesForLocalPackages(); }, - _ensureAppIdentifier: function () { + _ensureAppIdentifier: async function () { var self = this; var identifierFile = files.pathJoin(self.projectDir, '.meteor', '.id'); @@ -551,7 +555,7 @@ Object.assign(ProjectContext.prototype, { "# - ensuring you don't accidentally deploy one app on top of another\n" + "# - providing package authors with aggregated statistics\n" + "\n"); - files.writeFileAtomically(identifierFile, comment + appId + '\n'); + await files.writeFileAtomically(identifierFile, comment + appId + '\n'); } self.appIdentifier = appId; @@ -660,7 +664,7 @@ Object.assign(ProjectContext.prototype, { await self.packageMapDelta.init(); - self._saveResolverResultCache(); + await self._saveResolverResultCache(); self._completedStage = STAGE.RESOLVE_CONSTRAINTS; }); @@ -684,8 +688,8 @@ Object.assign(ProjectContext.prototype, { return this._resolverResultCache; }, - _saveResolverResultCache() { - files.writeFileAtomically( + async _saveResolverResultCache() { + await files.writeFileAtomically( files.pathJoin( this.projectLocalDir, "resolver-result-cache.json" @@ -705,8 +709,8 @@ Object.assign(ProjectContext.prototype, { } }, - saveBuildCache(buildCache) { - files.writeFileAtomically( + async saveBuildCache(buildCache) { + await files.writeFileAtomically( files.pathJoin( this.projectLocalDir, "build-cache.json" @@ -968,12 +972,12 @@ Object.assign(ProjectContext.prototype, { self._completedStage = STAGE.BUILD_LOCAL_PACKAGES; }), - _saveChangedMetadata: Profile('_saveChangedMetadata', function () { + _saveChangedMetadata: Profile('_saveChangedMetadata', async function () { var self = this; // Save any changes to .meteor/packages. if (! self._neverWriteProjectConstraintsFile) - self.projectConstraintsFile.writeIfModified(); + await self.projectConstraintsFile.writeIfModified(); // Write .meteor/versions if the command always wants to (create/update), // or if the release of the app matches the release of the process. @@ -983,7 +987,7 @@ Object.assign(ProjectContext.prototype, { (! release.current.isCheckout() && release.current.name === self.releaseFile.fullReleaseName))) { - self.packageMapFile.write(self.packageMap); + await self.packageMapFile.write(self.packageMap); } self._completedStage = STAGE.SAVE_CHANGED_METADATA; @@ -1098,12 +1102,12 @@ Object.assign(exports.ProjectConstraintsFile.prototype, { }); }, - writeIfModified: function () { + writeIfModified: async function () { var self = this; - self._modified && self._write(); + self._modified && (await self._write()); }, - _write: function () { + _write: async function () { var self = this; var lines = _.map(self._constraintLines, function (lineRecord) { // Don't write packages that were not loaded from .meteor/packages @@ -1119,7 +1123,7 @@ Object.assign(exports.ProjectConstraintsFile.prototype, { lineParts.push(lineRecord.trailingSpaceAndComment, '\n'); return lineParts.join(''); }); - files.writeFileAtomically(self.filename, lines.join('')); + await files.writeFileAtomically(self.filename, lines.join('')); var messages = buildmessage.capture( { title: 're-reading .meteor/packages' }, function () { @@ -1299,7 +1303,7 @@ Object.assign(exports.PackageMapFile.prototype, { return _.clone(self._versions); }, - write: function (packageMap) { + write: async function (packageMap) { var self = this; var newVersions = packageMap.toVersionMap(); @@ -1316,7 +1320,7 @@ Object.assign(exports.PackageMapFile.prototype, { lines.push(packageName + "@" + self._versions[packageName] + "\n"); }); var fileContents = Buffer.from(lines.join('')); - files.writeFileAtomically(self.filename, fileContents); + await files.writeFileAtomically(self.filename, fileContents); // Replace our watchSet with one for the new contents of the file. var hash = watch.sha1(fileContents); @@ -1335,15 +1339,17 @@ exports.PlatformList = function (options) { self.filename = files.pathJoin(options.projectDir, '.meteor', 'platforms'); self.watchSet = null; self._platforms = null; - - self._readFile(); }; // These platforms are always present and can be neither added or removed exports.PlatformList.DEFAULT_PLATFORMS = ['browser', 'server']; Object.assign(exports.PlatformList.prototype, { - _readFile: function () { + _init: async function() { + const self = this; + await self._readFile(); + }, + _readFile: async function () { var self = this; // Reset the WatchSet. @@ -1363,7 +1369,7 @@ Object.assign(exports.PlatformList.prototype, { // Write the platforms to disk (automatically adding DEFAULT_PLATFORMS and // sorting), which automatically calls this function recursively to // re-reads them. - self.write(platforms); + await self.write(platforms); return; } @@ -1372,14 +1378,14 @@ Object.assign(exports.PlatformList.prototype, { // Replaces the current platform file with the given list and resets this // object (and its WatchSet) to track the new value. - write: function (platforms) { + write: async function (platforms) { var self = this; self._platforms = null; platforms = _.uniq( platforms.concat(exports.PlatformList.DEFAULT_PLATFORMS)); platforms.sort(); - files.writeFileAtomically(self.filename, platforms.join('\n') + '\n'); - self._readFile(); + await files.writeFileAtomically(self.filename, platforms.join('\n') + '\n'); + await self._readFile(); }, getPlatforms: function () { @@ -1426,11 +1432,13 @@ exports.CordovaPluginsFile = function (options) { self.watchSet = null; // Map from plugin name to version. self._plugins = null; - - self._readFile(); }; Object.assign(exports.CordovaPluginsFile.prototype, { + init: async function() { + const self = this; + await self._readFile(); + }, _readFile: function () { var self = this; buildmessage.assertInCapture(); @@ -1476,18 +1484,18 @@ Object.assign(exports.CordovaPluginsFile.prototype, { return _.clone(self._plugins); }, - write: function (plugins) { + write: async function (plugins) { var self = this; var pluginNames = Object.keys(plugins); pluginNames.sort(); var lines = _.map(pluginNames, function (pluginName) { return pluginName + '@' + plugins[pluginName] + '\n'; }); - files.writeFileAtomically(self.filename, lines.join('')); - var messages = buildmessage.capture( + await files.writeFileAtomically(self.filename, lines.join('')); + var messages = await buildmessage.capture( { title: 're-reading .meteor/cordova-plugins' }, - function () { - self._readFile(); + async function () { + await self._readFile(); }); // We shouldn't choke on something we just wrote! if (messages.hasMessages()) @@ -1516,10 +1524,13 @@ exports.ReleaseFile = function (options) { // Just the track. self.releaseTrack = null; self.releaseVersion = null; - self._readFile(); }; Object.assign(exports.ReleaseFile.prototype, { + init: async function() { + const self = this; + await self._readFile(); + }, fileMissing: function () { var self = this; return self.unnormalizedReleaseName === null; @@ -1538,7 +1549,7 @@ Object.assign(exports.ReleaseFile.prototype, { || self.isCheckout()); }, - _readFile: function () { + _readFile: async function () { var self = this; // Start a new watchSet, in case we just overwrote this. @@ -1566,7 +1577,7 @@ Object.assign(exports.ReleaseFile.prototype, { self.releaseTrack = parts[0]; self.releaseVersion = parts[1]; - self.ensureDevBundleLink(); + await self.ensureDevBundleLink(); }, // Returns an absolute path to the dev_bundle appropriate for the @@ -1598,7 +1609,7 @@ Object.assign(exports.ReleaseFile.prototype, { }, // Make a symlink from .meteor/local/dev_bundle to the actual dev_bundle. - ensureDevBundleLink() { + async ensureDevBundleLink() { import { makeLink, readLink } from "./cli/dev-bundle-links.js"; const dotMeteorDir = files.pathDirname(this.filename); @@ -1608,7 +1619,7 @@ Object.assign(exports.ReleaseFile.prototype, { if (this.isCheckout()) { // Only create .meteor/local/dev_bundle if .meteor/release refers to // an actual release, and remove it otherwise. - files.rm_recursive(devBundleLink); + await files.rm_recursive(devBundleLink); return; } @@ -1643,10 +1654,10 @@ Object.assign(exports.ReleaseFile.prototype, { } }, - write: function (releaseName) { + write: async function (releaseName) { var self = this; - files.writeFileAtomically(self.filename, releaseName + '\n'); - self._readFile(); + await files.writeFileAtomically(self.filename, releaseName + '\n'); + await self._readFile(); } }); diff --git a/tools/tool-env/cleanup.js b/tools/tool-env/cleanup.js index 2017c6cd7f..4c1b6cd347 100644 --- a/tools/tool-env/cleanup.js +++ b/tools/tool-env/cleanup.js @@ -1,26 +1,19 @@ // A simple interface to register functions to be called when the process exits. -import { noYieldsAllowed } from "../utils/fiber-helpers.js"; - const exitHandlers = []; export function onExit(func) { exitHandlers.push(func); } -function runHandlers() { - noYieldsAllowed(() => { - // Empty and execute all queued exit handlers. - exitHandlers.splice(0).forEach((f) => { - f(); - }); - }); +async function runHandlers() { + await Promise.all(exitHandlers.splice(0).map(f => f())); } process.on('exit', runHandlers); ['SIGINT', 'SIGHUP', 'SIGTERM'].forEach((sig) => { - process.once(sig, () => { - runHandlers(); + process.once(sig, async () => { + await runHandlers(); process.kill(process.pid, sig); }); }); diff --git a/tools/utils/buildmessage.js b/tools/utils/buildmessage.js index bbbc194272..dbcc93dda0 100644 --- a/tools/utils/buildmessage.js +++ b/tools/utils/buildmessage.js @@ -583,7 +583,7 @@ var mergeMessagesIntoCurrentJob = function (innerMessages) { }; // Like _.each, but runs each operation in a separate job -var forkJoin = function (options, iterable, fn) { +var forkJoin = async function (options, iterable, fn) { if (!_.isFunction(fn)) { fn = iterable; iterable = options; @@ -602,44 +602,38 @@ var forkJoin = function (options, iterable, fn) { const parallel = (options.parallel !== undefined) ? options.parallel : true; - return enterJobAsync(options).then(() => { - const errors = []; - let results = _.map(iterable, (...args) => { - const promise = enterJobAsync({ + await enterJobAsync(options); + + const errors = []; + let results = []; + const mappedPromises = iterable.map(async (...args) => { + try { + await enterJobAsync({ title: (options.title || "") + " child" - }).then(() => fn(...args)) - // Collect any errors thrown (and later re-throw the first one), - // but don't stop processing remaining jobs. - .catch(error => (errors.push(error), null)); - - if (parallel) { - // If the jobs are intended to run in parallel, return each - // promise without awaiting it, so that Promise.all can wait for - // them all to be fulfilled. - return promise; - } - - // By awaiting the promise during each iteration, we effectively - // serialize the execution of the jobs. - return promise.await(); - }); - - if (parallel) { - // If the jobs ran in parallel, then results is an array of Promise - // objects that still need to be resolved. - results = Promise.all(results).await(); + }); + await fn(...args); + } catch (e) { + errors.push(e); } + }); - if (errors.length > 0) { - // If any errors were thrown, re-throw the first one. Note that this - // allows jobs to complete successfully (and have whatever - // side-effects they should have) after the first error is thrown, - // though the final results will not be returned below. - throw errors[0]; + if (parallel) { + results = await Promise.all(mappedPromises); + } else { + for (const mappedPromise of mappedPromises) { + results.push(await mappedPromise); } + } - return results; - }).await(); + if (errors.length > 0) { + // If any errors were thrown, re-throw the first one. Note that this + // allows jobs to complete successfully (and have whatever + // side-effects they should have) after the first error is thrown, + // though the final results will not be returned below. + throw errors[0]; + } + + return results; };