diff --git a/tools/isobuild/builder.js b/tools/isobuild/builder.js index 04a23919ec..797dca477c 100644 --- a/tools/isobuild/builder.js +++ b/tools/isobuild/builder.js @@ -1,7 +1,7 @@ import assert from "assert"; import {WatchSet, readAndWatchFile, sha1} from '../fs/watch'; import files, { - symlinkWithOverwrite, + symlinkWithOverwrite, realpath, } from '../fs/files'; import NpmDiscards from './npm-discards.js'; import {Profile} from '../tool-env/profile'; @@ -540,7 +540,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` // as well as node_modules/meteor and the parent directories of any // scoped npm packages. this._ensureAllNonPackageDirectories( - files.realpath(options.from), + realpath(options.from), options.to ); } @@ -637,7 +637,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` }); } - const rootDir = files.realpath(from); + const rootDir = realpath(from); const walk = (absFrom, relTo) => { if (symlink && ! (relTo in this.usedAsFile)) { @@ -661,7 +661,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` return; } - // Returns files.realpath(thisAbsFrom), iff it is external to + // Returns files.realpath(thisAbsFrom), if it is external to // rootDir, using caching because this function might be called // more than once. let cachedExternalPath; @@ -671,7 +671,7 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}` } try { - var real = files.realpath(thisAbsFrom); + var real = realpath(thisAbsFrom); } catch (e) { if (e.code !== "ENOENT" && e.code !== "ELOOP") { diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index cee56599cd..de60cc3aca 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -173,6 +173,7 @@ import { loadIsopackage } from '../tool-env/isopackets.js'; import { CORDOVA_PLATFORM_VERSIONS } from '../cordova'; import { gzipSync } from "zlib"; import { PackageRegistry } from "../../packages/meteor/define-package.js"; +import { optimisticLStatOrNull } from '../fs/optimistic'; const SOURCE_URL_PREFIX = "meteor://\u{1f4bb}app"; @@ -486,12 +487,11 @@ export class NodeModulesDirectory { return true; } - const real = files.realpathOrNull(path); - if (typeof real === "string" && - real !== path) { + const fileStatus = optimisticLStatOrNull(path); + if (fileStatus && fileStatus.isSymbolicLink()) { // If node_modules/.bin/command is a symlink, determine the // answer by calling isWithinProdPackage(real). - return isWithinProdPackage(real); + return isWithinProdPackage(files.realpathOrNull(path)); } // If node_modules/.bin/command is not a symlink, then it's hard diff --git a/tools/isobuild/package-source.js b/tools/isobuild/package-source.js index aa2ccdfbd7..253c5fda18 100644 --- a/tools/isobuild/package-source.js +++ b/tools/isobuild/package-source.js @@ -216,14 +216,44 @@ var getExcerptFromReadme = function (text) { class SymlinkLoopChecker { constructor(sourceRoot) { this.sourceRoot = sourceRoot; + this._realSourceRoot = files.realpath(sourceRoot); this._seenPaths = {}; + this._cache = new Map(); } + // Avoids running realpath unless necessary + // since it is relatively slow on windows + _realpath = Profile('_realpath', function (relDir) { + const absPath = files.pathJoin(this._realSourceRoot, relDir); + + if (files.lstat(absPath).isSymbolicLink()) { + const result = files.realpath(absPath); + this._cache.set(relDir, result); + + return result; + } + + let result; + const parentDir = files.pathDirname(relDir); + const parentEntry = this._cache.get(parentDir); + if (parentDir === '.') { + result = absPath; + } else if (parentEntry) { + result = files.pathJoin(parentEntry, files.pathBasename(relDir)); + } else { + // The parent dir was never checked, which prevents us from + // skipping realpath + result = files.realpath(absPath); + } + + this._cache.set(relDir, result); + return result; + }) + check(relDir, quietly = true) { - const absPath = files.pathJoin(this.sourceRoot, relDir); try { - var realPath = files.realpath(absPath); + var realPath = this._realpath(relDir); } catch (e) { if (!e || e.code !== 'ELOOP') { throw e;