@yarnpkg/pnpify
Advanced tools
Comparing version 2.0.0-rc.12 to 2.0.0-rc.13
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
process.env.NODE_OPTIONS += ` --require ${require.resolve(`@yarnpkg/monorepo/scripts/setup-ts-execution`)}`; | ||
@@ -5,2 +7,2 @@ | ||
require(`./cli`); | ||
require(`./cli`); |
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
process.env.NODE_OPTIONS += ` --require ${require.resolve(`@yarnpkg/monorepo/scripts/setup-ts-execution`)}`; | ||
@@ -5,2 +7,2 @@ | ||
module.exports = require(`./index`); | ||
module.exports = require(`./index`); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const hoist_1 = require("./hoist"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.buildNodeModulesTree = exports.LinkType = void 0; | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _hoist = require("./hoist"); | ||
// Babel doesn't support const enums, thats why we use non-const enum for LinkType in @yarnpkg/pnp | ||
// But because of this TypeScript requires @yarnpkg/pnp during runtime | ||
// To prevent this we redeclare LinkType enum here, to not depend on @yarnpkg/pnp during runtime | ||
var LinkType; | ||
let LinkType; | ||
exports.LinkType = LinkType; | ||
(function (LinkType) { | ||
LinkType["HARD"] = "HARD"; | ||
LinkType["SOFT"] = "SOFT"; | ||
})(LinkType = exports.LinkType || (exports.LinkType = {})); | ||
LinkType["HARD"] = "HARD"; | ||
LinkType["SOFT"] = "SOFT"; | ||
})(LinkType || (exports.LinkType = LinkType = {})); | ||
; | ||
/** | ||
* Node modules tree - a map of every folder within the node_modules, along with their | ||
* directory listing and whether they are a symlink and their location. | ||
* | ||
* Sample contents: | ||
* /home/user/project/node_modules -> {dirList: ['foo', 'bar']} | ||
* /home/user/project/node_modules/foo -> {target: '/home/user/project/.yarn/.cache/foo.zip/node_modules/foo', linkType: 'HARD'} | ||
* /home/user/project/node_modules/bar -> {target: '/home/user/project/packages/bar', linkType: 'SOFT'} | ||
*/ | ||
/** node_modules path segment */ | ||
const NODE_MODULES = fslib_1.toFilename(`node_modules`); | ||
const NODE_MODULES = (0, _fslib.toFilename)(`node_modules`); | ||
/** Package locator key for usage inside maps */ | ||
/** | ||
@@ -23,5 +45,3 @@ * Returns path to archive, if package location is inside the archive. | ||
*/ | ||
const getArchivePath = (packagePath) => packagePath.indexOf(`.zip/${NODE_MODULES}/`) >= 0 ? | ||
fslib_1.npath.toPortablePath(packagePath.split(`/${NODE_MODULES}/`)[0]) : | ||
null; | ||
const getArchivePath = packagePath => packagePath.indexOf(`.zip/${NODE_MODULES}/`) >= 0 ? _fslib.npath.toPortablePath(packagePath.split(`/${NODE_MODULES}/`)[0]) : null; | ||
/** | ||
@@ -35,9 +55,11 @@ * Determines package's weight relative to other packages for hoisting purposes. | ||
*/ | ||
const getPackageWeight = (packagePath, { optimizeSizeOnDisk }) => { | ||
if (!optimizeSizeOnDisk) | ||
return 1; | ||
const archivePath = getArchivePath(packagePath); | ||
if (archivePath === null) | ||
return 1; | ||
return fslib_1.xfs.statSync(archivePath).size; | ||
const getPackageWeight = (packagePath, { | ||
optimizeSizeOnDisk | ||
}) => { | ||
if (!optimizeSizeOnDisk) return 1; | ||
const archivePath = getArchivePath(packagePath); | ||
if (archivePath === null) return 1; | ||
return _fslib.xfs.statSync(archivePath).size; | ||
}; | ||
@@ -52,6 +74,12 @@ /** | ||
*/ | ||
exports.buildNodeModulesTree = (pnp, options) => { | ||
const { packageTree, packages, locators } = buildPackageTree(pnp, options); | ||
const hoistedTree = hoist_1.hoist(packageTree, packages); | ||
return populateNodeModulesTree(pnp, hoistedTree, locators, options); | ||
const buildNodeModulesTree = (pnp, options) => { | ||
const { | ||
packageTree, | ||
packages, | ||
locators | ||
} = buildPackageTree(pnp, options); | ||
const hoistedTree = (0, _hoist.hoist)(packageTree, packages); | ||
return populateNodeModulesTree(pnp, hoistedTree, locators, options); | ||
}; | ||
@@ -65,63 +93,87 @@ /** | ||
*/ | ||
exports.buildNodeModulesTree = buildNodeModulesTree; | ||
const buildPackageTree = (pnp, options) => { | ||
const packageTree = []; | ||
const packages = []; | ||
const locators = []; | ||
const locatorToPackageMap = new Map(); | ||
const packageInfos = []; | ||
const pnpRoots = pnp.getDependencyTreeRoots(); | ||
let lastPkgId = 0; | ||
const getLocatorKey = (locator) => `${locator.name}:${locator.reference}`; | ||
const assignPackageId = (locator, pkg) => { | ||
const locatorKey = getLocatorKey(locator); | ||
const pkgId = locatorToPackageMap.get(locatorKey); | ||
if (typeof pkgId !== 'undefined') | ||
return pkgId; | ||
const newPkgId = lastPkgId++; | ||
const packagePath = fslib_1.npath.toPortablePath(pkg.packageLocation); | ||
const weight = getPackageWeight(packagePath, options); | ||
locatorToPackageMap.set(locatorKey, newPkgId); | ||
locators.push(locator); | ||
packages.push({ name: locator.name, weight }); | ||
packageInfos.push(pkg); | ||
return newPkgId; | ||
const packageTree = []; | ||
const packages = []; | ||
const locators = []; | ||
const locatorToPackageMap = new Map(); | ||
const packageInfos = []; | ||
const pnpRoots = pnp.getDependencyTreeRoots(); | ||
let lastPkgId = 0; | ||
const getLocatorKey = locator => `${locator.name}:${locator.reference}`; | ||
const assignPackageId = (locator, pkg) => { | ||
const locatorKey = getLocatorKey(locator); | ||
const pkgId = locatorToPackageMap.get(locatorKey); | ||
if (typeof pkgId !== 'undefined') return pkgId; | ||
const newPkgId = lastPkgId++; | ||
const packagePath = _fslib.npath.toPortablePath(pkg.packageLocation); | ||
const weight = getPackageWeight(packagePath, options); | ||
locatorToPackageMap.set(locatorKey, newPkgId); | ||
locators.push(locator); | ||
packages.push({ | ||
name: locator.name, | ||
weight | ||
}); | ||
packageInfos.push(pkg); | ||
return newPkgId; | ||
}; | ||
const addedIds = new Set(); | ||
const addPackageToTree = (pkg, pkgId, parentDepIds) => { | ||
if (addedIds.has(pkgId)) return; | ||
addedIds.add(pkgId); | ||
const deps = new Set(); | ||
const peerDeps = new Set(); | ||
packageTree[pkgId] = { | ||
deps, | ||
peerDeps | ||
}; | ||
const addedIds = new Set(); | ||
const addPackageToTree = (pkg, pkgId, parentDepIds) => { | ||
if (addedIds.has(pkgId)) | ||
return; | ||
addedIds.add(pkgId); | ||
const deps = new Set(); | ||
const peerDeps = new Set(); | ||
packageTree[pkgId] = { deps, peerDeps }; | ||
for (const [name, referencish] of pkg.packageDependencies) { | ||
if (referencish !== null) { | ||
const locator = pnp.getLocator(name, referencish); | ||
const depPkg = pnp.getPackageInformation(locator); | ||
const depPkgId = assignPackageId(locator, depPkg); | ||
if (pkg.packagePeers.has(name)) { | ||
peerDeps.add(depPkgId); | ||
} | ||
else { | ||
deps.add(depPkgId); | ||
} | ||
} | ||
for (const [name, referencish] of pkg.packageDependencies) { | ||
if (referencish !== null) { | ||
const locator = pnp.getLocator(name, referencish); | ||
const depPkg = pnp.getPackageInformation(locator); | ||
const depPkgId = assignPackageId(locator, depPkg); | ||
if (pkg.packagePeers.has(name)) { | ||
peerDeps.add(depPkgId); | ||
} else { | ||
deps.add(depPkgId); | ||
} | ||
const allDepIds = new Set([...deps, ...peerDeps]); | ||
for (const depId of allDepIds) { | ||
const depPkg = packageInfos[depId]; | ||
addPackageToTree(depPkg, depId, allDepIds); | ||
} | ||
}; | ||
const pkg = pnp.getPackageInformation(pnp.topLevel); | ||
const topLocator = pnp.findPackageLocator(pkg.packageLocation); | ||
const topLocatorKey = getLocatorKey(topLocator); | ||
for (const locator of pnpRoots) { | ||
if (getLocatorKey(locator) !== topLocatorKey) { | ||
pkg.packageDependencies.set(locator.name, locator.reference); | ||
} | ||
} | ||
} | ||
const pkgId = assignPackageId(topLocator, pkg); | ||
addPackageToTree(pkg, pkgId, new Set()); | ||
return { packageTree, packages, locators }; | ||
const allDepIds = new Set([...deps, ...peerDeps]); | ||
for (const depId of allDepIds) { | ||
const depPkg = packageInfos[depId]; | ||
addPackageToTree(depPkg, depId, allDepIds); | ||
} | ||
}; | ||
const pkg = pnp.getPackageInformation(pnp.topLevel); | ||
const topLocator = pnp.findPackageLocator(pkg.packageLocation); | ||
const topLocatorKey = getLocatorKey(topLocator); | ||
for (const locator of pnpRoots) { | ||
if (getLocatorKey(locator) !== topLocatorKey) { | ||
pkg.packageDependencies.set(locator.name, locator.reference); | ||
} | ||
} | ||
const pkgId = assignPackageId(topLocator, pkg); | ||
addPackageToTree(pkg, pkgId, new Set()); | ||
return { | ||
packageTree, | ||
packages, | ||
locators | ||
}; | ||
}; | ||
@@ -137,63 +189,93 @@ /** | ||
*/ | ||
const populateNodeModulesTree = (pnp, hoistedTree, locators, options) => { | ||
const tree = new Map(); | ||
const makeLeafNode = (locator) => { | ||
const info = pnp.getPackageInformation(locator); | ||
if (options.pnpifyFs) { | ||
return { target: fslib_1.npath.toPortablePath(info.packageLocation), linkType: LinkType.SOFT }; | ||
} | ||
else { | ||
const truePath = pnp.resolveVirtual && locator.reference && locator.reference.startsWith('virtual:') ? pnp.resolveVirtual(info.packageLocation) : info.packageLocation; | ||
return { target: fslib_1.npath.toPortablePath(truePath || info.packageLocation), linkType: info.linkType }; | ||
} | ||
const tree = new Map(); | ||
const makeLeafNode = locator => { | ||
const info = pnp.getPackageInformation(locator); | ||
if (options.pnpifyFs) { | ||
return { | ||
target: _fslib.npath.toPortablePath(info.packageLocation), | ||
linkType: LinkType.SOFT | ||
}; | ||
} else { | ||
const truePath = pnp.resolveVirtual && locator.reference && locator.reference.startsWith('virtual:') ? pnp.resolveVirtual(info.packageLocation) : info.packageLocation; | ||
return { | ||
target: _fslib.npath.toPortablePath(truePath || info.packageLocation), | ||
linkType: info.linkType | ||
}; | ||
} | ||
}; | ||
const getPackageName = locator => { | ||
const [nameOrScope, name] = locator.name.split('/'); | ||
return name ? { | ||
scope: (0, _fslib.toFilename)(nameOrScope), | ||
name: (0, _fslib.toFilename)(name) | ||
} : { | ||
scope: null, | ||
name: (0, _fslib.toFilename)(nameOrScope) | ||
}; | ||
const getPackageName = (locator) => { | ||
const [nameOrScope, name] = locator.name.split('/'); | ||
return name ? { scope: fslib_1.toFilename(nameOrScope), name: fslib_1.toFilename(name) } : { scope: null, name: fslib_1.toFilename(nameOrScope) }; | ||
}; | ||
const seenPkgIds = new Set(); | ||
const buildTree = (nodeId, locationPrefix) => { | ||
if (seenPkgIds.has(nodeId)) | ||
return; | ||
seenPkgIds.add(nodeId); | ||
for (const depId of hoistedTree[nodeId]) { | ||
const locator = locators[depId]; | ||
const { name, scope } = getPackageName(locator); | ||
const packageNameParts = scope ? [scope, name] : [name]; | ||
const nodeModulesDirPath = fslib_1.ppath.join(locationPrefix, NODE_MODULES); | ||
const nodeModulesLocation = fslib_1.ppath.join(nodeModulesDirPath, ...packageNameParts); | ||
const leafNode = makeLeafNode(locator); | ||
tree.set(nodeModulesLocation, leafNode); | ||
const segments = nodeModulesLocation.split('/'); | ||
const nodeModulesIdx = segments.indexOf(NODE_MODULES); | ||
let segCount = segments.length - 1; | ||
while (nodeModulesIdx >= 0 && segCount > nodeModulesIdx) { | ||
const dirPath = fslib_1.npath.toPortablePath(segments.slice(0, segCount).join(fslib_1.ppath.sep)); | ||
const targetDir = fslib_1.toFilename(segments[segCount]); | ||
const subdirs = tree.get(dirPath); | ||
if (!subdirs) { | ||
tree.set(dirPath, { dirList: new Set([targetDir]) }); | ||
} | ||
else if (subdirs.dirList) { | ||
if (subdirs.dirList.has(targetDir)) { | ||
break; | ||
} | ||
else { | ||
subdirs.dirList.add(targetDir); | ||
} | ||
} | ||
segCount--; | ||
} | ||
// In case of pnpifyFs we represent modules as symlinks to archives in NodeModulesFS | ||
// `/home/user/project/foo` is a symlink to `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo` | ||
// To make this fs layout work with legacy tools we make | ||
// `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo/node_modules` (which normally does not exist inside archive) a symlink to: | ||
// `/home/user/project/node_modules/foo/node_modules`, so that the tools were able to access it | ||
buildTree(depId, options.pnpifyFs ? leafNode.target : nodeModulesLocation); | ||
}; | ||
const seenPkgIds = new Set(); | ||
const buildTree = (nodeId, locationPrefix) => { | ||
if (seenPkgIds.has(nodeId)) return; | ||
seenPkgIds.add(nodeId); | ||
for (const depId of hoistedTree[nodeId]) { | ||
const locator = locators[depId]; | ||
const { | ||
name, | ||
scope | ||
} = getPackageName(locator); | ||
const packageNameParts = scope ? [scope, name] : [name]; | ||
const nodeModulesDirPath = _fslib.ppath.join(locationPrefix, NODE_MODULES); | ||
const nodeModulesLocation = _fslib.ppath.join(nodeModulesDirPath, ...packageNameParts); | ||
const leafNode = makeLeafNode(locator); | ||
tree.set(nodeModulesLocation, leafNode); | ||
const segments = nodeModulesLocation.split('/'); | ||
const nodeModulesIdx = segments.indexOf(NODE_MODULES); | ||
let segCount = segments.length - 1; | ||
while (nodeModulesIdx >= 0 && segCount > nodeModulesIdx) { | ||
const dirPath = _fslib.npath.toPortablePath(segments.slice(0, segCount).join(_fslib.ppath.sep)); | ||
const targetDir = (0, _fslib.toFilename)(segments[segCount]); | ||
const subdirs = tree.get(dirPath); | ||
if (!subdirs) { | ||
tree.set(dirPath, { | ||
dirList: new Set([targetDir]) | ||
}); | ||
} else if (subdirs.dirList) { | ||
if (subdirs.dirList.has(targetDir)) { | ||
break; | ||
} else { | ||
subdirs.dirList.add(targetDir); | ||
} | ||
} | ||
}; | ||
const rootNode = makeLeafNode(locators[0]); | ||
const rootPath = rootNode.target && rootNode.target; | ||
buildTree(0, rootPath); | ||
return tree; | ||
segCount--; | ||
} // In case of pnpifyFs we represent modules as symlinks to archives in NodeModulesFS | ||
// `/home/user/project/foo` is a symlink to `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo` | ||
// To make this fs layout work with legacy tools we make | ||
// `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo/node_modules` (which normally does not exist inside archive) a symlink to: | ||
// `/home/user/project/node_modules/foo/node_modules`, so that the tools were able to access it | ||
buildTree(depId, options.pnpifyFs ? leafNode.target : nodeModulesLocation); | ||
} | ||
}; | ||
const rootNode = makeLeafNode(locators[0]); | ||
const rootPath = rootNode.target && rootNode.target; | ||
buildTree(0, rootPath); | ||
return tree; | ||
}; | ||
@@ -211,9 +293,12 @@ /** | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const benchmarkRawHoisting = (packageTree, packages) => { | ||
const iterCount = 100; | ||
const startTime = Date.now(); | ||
for (let iter = 0; iter < iterCount; iter++) | ||
hoist_1.hoist(packageTree, packages); | ||
const endTime = Date.now(); | ||
return (endTime - startTime) / iterCount; | ||
const iterCount = 100; | ||
const startTime = Date.now(); | ||
for (let iter = 0; iter < iterCount; iter++) (0, _hoist.hoist)(packageTree, packages); | ||
const endTime = Date.now(); | ||
return (endTime - startTime) / iterCount; | ||
}; | ||
@@ -231,12 +316,20 @@ /** | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const benchmarkBuildTree = (pnp, options) => { | ||
const iterCount = 100; | ||
const startTime = Date.now(); | ||
for (let iter = 0; iter < iterCount; iter++) { | ||
const { packageTree, packages, locators } = buildPackageTree(pnp, options); | ||
const hoistedTree = hoist_1.hoist(packageTree, packages); | ||
populateNodeModulesTree(pnp, hoistedTree, locators, options); | ||
} | ||
const endTime = Date.now(); | ||
return (endTime - startTime) / iterCount; | ||
const iterCount = 100; | ||
const startTime = Date.now(); | ||
for (let iter = 0; iter < iterCount; iter++) { | ||
const { | ||
packageTree, | ||
packages, | ||
locators | ||
} = buildPackageTree(pnp, options); | ||
const hoistedTree = (0, _hoist.hoist)(packageTree, packages); | ||
populateNodeModulesTree(pnp, hoistedTree, locators, options); | ||
} | ||
const endTime = Date.now(); | ||
return (endTime - startTime) / iterCount; | ||
}; | ||
@@ -254,38 +347,52 @@ /** | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const dumpNodeModulesTree = (tree, rootPath) => { | ||
const sortedTree = new Map(); | ||
const keys = Array.from(tree.keys()).sort(); | ||
for (const key of keys) { | ||
const val = tree.get(key); | ||
sortedTree.set(key, val.dirList ? { dirList: new Set(Array.from(val.dirList).sort()) } : val); | ||
const sortedTree = new Map(); | ||
const keys = Array.from(tree.keys()).sort(); | ||
for (const key of keys) { | ||
const val = tree.get(key); | ||
sortedTree.set(key, val.dirList ? { | ||
dirList: new Set(Array.from(val.dirList).sort()) | ||
} : val); | ||
} | ||
const seenPaths = new Set(); | ||
const dumpTree = (nodePath, prefix = '', dirPrefix = '') => { | ||
const node = sortedTree.get(nodePath); | ||
if (!node) return ''; | ||
seenPaths.add(nodePath); | ||
let str = ''; | ||
if (node.dirList) { | ||
const dirs = Array.from(node.dirList); | ||
for (let idx = 0; idx < dirs.length; idx++) { | ||
const dir = dirs[idx]; | ||
str += `${prefix}${idx < dirs.length - 1 ? '├─' : '└─'}${dirPrefix}${dir}\n`; | ||
str += dumpTree(_fslib.ppath.join(nodePath, dir), `${prefix}${idx < dirs.length - 1 ? '│ ' : ' '}`); | ||
} | ||
} else { | ||
const { | ||
target, | ||
linkType | ||
} = node; | ||
str += dumpTree(_fslib.ppath.join(nodePath, NODE_MODULES), `${prefix}│ `, `${NODE_MODULES}/`); | ||
str += `${prefix}└─${linkType === LinkType.SOFT ? 's>' : '>'}${target}\n`; | ||
} | ||
const seenPaths = new Set(); | ||
const dumpTree = (nodePath, prefix = '', dirPrefix = '') => { | ||
const node = sortedTree.get(nodePath); | ||
if (!node) | ||
return ''; | ||
seenPaths.add(nodePath); | ||
let str = ''; | ||
if (node.dirList) { | ||
const dirs = Array.from(node.dirList); | ||
for (let idx = 0; idx < dirs.length; idx++) { | ||
const dir = dirs[idx]; | ||
str += `${prefix}${idx < dirs.length - 1 ? '├─' : '└─'}${dirPrefix}${dir}\n`; | ||
str += dumpTree(fslib_1.ppath.join(nodePath, dir), `${prefix}${idx < dirs.length - 1 ? '│ ' : ' '}`); | ||
} | ||
} | ||
else { | ||
const { target, linkType } = node; | ||
str += dumpTree(fslib_1.ppath.join(nodePath, NODE_MODULES), `${prefix}│ `, `${NODE_MODULES}/`); | ||
str += `${prefix}└─${linkType === LinkType.SOFT ? 's>' : '>'}${target}\n`; | ||
} | ||
return str; | ||
}; | ||
let str = dumpTree(fslib_1.ppath.join(rootPath, NODE_MODULES)); | ||
for (const key of sortedTree.keys()) { | ||
if (!seenPaths.has(key)) { | ||
str += `${key.replace(rootPath, '')}\n${dumpTree(key)}`; | ||
} | ||
return str; | ||
}; | ||
let str = dumpTree(_fslib.ppath.join(rootPath, NODE_MODULES)); | ||
for (const key of sortedTree.keys()) { | ||
if (!seenPaths.has(key)) { | ||
str += `${key.replace(rootPath, '')}\n${dumpTree(key)}`; | ||
} | ||
return str; | ||
} | ||
return str; | ||
}; | ||
@@ -302,44 +409,50 @@ /** | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const dumpDepTree = (tree, locators, nodeId = 0, prefix = '', seenIds = new Set()) => { | ||
if (seenIds.has(nodeId)) | ||
return ''; | ||
seenIds.add(nodeId); | ||
const dumpLocator = (locator) => { | ||
if (locator.reference === 'workspace:.') { | ||
return '.'; | ||
} | ||
else if (!locator.reference) { | ||
return `${locator.name}@${locator.reference}`; | ||
} | ||
else { | ||
const version = (locator.reference.indexOf('#') > 0 ? locator.reference.split('#')[1] : locator.reference).replace('npm:', ''); | ||
if (locator.reference.startsWith('virtual')) { | ||
return `v:${locator.name}@${version}`; | ||
} | ||
else { | ||
return `${locator.name}@${version}`; | ||
} | ||
} | ||
}; | ||
const deps = Array.from((tree[nodeId].deps || tree[nodeId])); | ||
const traverseIds = new Set(); | ||
for (const depId of deps) { | ||
if (!seenIds.has(depId)) { | ||
traverseIds.add(depId); | ||
seenIds.add(depId); | ||
} | ||
if (seenIds.has(nodeId)) return ''; | ||
seenIds.add(nodeId); | ||
const dumpLocator = locator => { | ||
if (locator.reference === 'workspace:.') { | ||
return '.'; | ||
} else if (!locator.reference) { | ||
return `${locator.name}@${locator.reference}`; | ||
} else { | ||
const version = (locator.reference.indexOf('#') > 0 ? locator.reference.split('#')[1] : locator.reference).replace('npm:', ''); | ||
if (locator.reference.startsWith('virtual')) { | ||
return `v:${locator.name}@${version}`; | ||
} else { | ||
return `${locator.name}@${version}`; | ||
} | ||
} | ||
let str = ''; | ||
for (let idx = 0; idx < deps.length; idx++) { | ||
const depId = deps[idx]; | ||
str += `${prefix}${idx < deps.length - 1 ? '├─' : '└─'}${(traverseIds.has(depId) ? '>' : '') + dumpLocator(locators[depId])}\n`; | ||
if (traverseIds.has(depId)) { | ||
seenIds.delete(depId); | ||
str += dumpDepTree(tree, locators, depId, `${prefix}${idx < deps.length - 1 ? '│ ' : ' '}`, seenIds); | ||
seenIds.add(depId); | ||
} | ||
}; | ||
const deps = Array.from(tree[nodeId].deps || tree[nodeId]); | ||
const traverseIds = new Set(); | ||
for (const depId of deps) { | ||
if (!seenIds.has(depId)) { | ||
traverseIds.add(depId); | ||
seenIds.add(depId); | ||
} | ||
for (const depId of traverseIds) | ||
seenIds.delete(depId); | ||
return str; | ||
}; | ||
} | ||
let str = ''; | ||
for (let idx = 0; idx < deps.length; idx++) { | ||
const depId = deps[idx]; | ||
str += `${prefix}${idx < deps.length - 1 ? '├─' : '└─'}${(traverseIds.has(depId) ? '>' : '') + dumpLocator(locators[depId])}\n`; | ||
if (traverseIds.has(depId)) { | ||
seenIds.delete(depId); | ||
str += dumpDepTree(tree, locators, depId, `${prefix}${idx < deps.length - 1 ? '│ ' : ' '}`, seenIds); | ||
seenIds.add(depId); | ||
} | ||
} | ||
for (const depId of traverseIds) seenIds.delete(depId); | ||
return str; | ||
}; |
#!/usr/bin/env node | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const cross_spawn_1 = __importDefault(require("cross-spawn")); | ||
const dynamicRequire_1 = require("./dynamicRequire"); | ||
const generateSdk_1 = require("./generateSdk"); | ||
const [, , name, ...rest] = process.argv; | ||
if (name === `--help` || name === `-h`) | ||
help(false); | ||
else if (name === `--sdk` && rest.length === 0) | ||
sdk(); | ||
else if (typeof name !== `undefined` && name[0] !== `-`) | ||
run(name, rest); | ||
else | ||
help(true); | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _crossSpawn = _interopRequireDefault(require("cross-spawn")); | ||
var _dynamicRequire = require("./dynamicRequire"); | ||
var _generateSdk = require("./generateSdk"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const [,, name, ...rest] = process.argv; | ||
if (name === `--help` || name === `-h`) help(false);else if (name === `--sdk` && rest.length === 0) sdk();else if (typeof name !== `undefined` && name[0] !== `-`) run(name, rest);else help(true); | ||
function help(error) { | ||
const logFn = error ? console.error : console.log; | ||
process.exitCode = error ? 1 : 0; | ||
logFn(`Usage: yarn pnpify --sdk`); | ||
logFn(`Usage: yarn pnpify <program> [...argv]`); | ||
logFn(); | ||
logFn(`Setups a TypeScript sdk for use within your VSCode editor instance`); | ||
logFn(`More info at https://next.yarnpkg.com/advanced/pnpify`); | ||
const logFn = error ? console.error : console.log; | ||
process.exitCode = error ? 1 : 0; | ||
logFn(`Usage: yarn pnpify --sdk`); | ||
logFn(`Usage: yarn pnpify <program> [...argv]`); | ||
logFn(); | ||
logFn(`Setups a TypeScript sdk for use within your VSCode editor instance`); | ||
logFn(`More info at https://next.yarnpkg.com/advanced/pnpify`); | ||
} | ||
function sdk() { | ||
const { getPackageInformation, topLevel } = dynamicRequire_1.dynamicRequire(`pnpapi`); | ||
const { packageLocation } = getPackageInformation(topLevel); | ||
const projectRoot = fslib_1.npath.toPortablePath(packageLocation); | ||
generateSdk_1.generateSdk(projectRoot).catch(error => { | ||
console.error(error.stack); | ||
process.exitCode = 1; | ||
}); | ||
const { | ||
getPackageInformation, | ||
topLevel | ||
} = (0, _dynamicRequire.dynamicRequire)(`pnpapi`); | ||
const { | ||
packageLocation | ||
} = getPackageInformation(topLevel); | ||
const projectRoot = _fslib.npath.toPortablePath(packageLocation); | ||
(0, _generateSdk.generateSdk)(projectRoot).catch(error => { | ||
console.error(error.stack); | ||
process.exitCode = 1; | ||
}); | ||
} | ||
function run(name, argv) { | ||
let { NODE_OPTIONS } = process.env; | ||
NODE_OPTIONS = `${NODE_OPTIONS || ``} --require ${dynamicRequire_1.dynamicRequire.resolve(`@yarnpkg/pnpify`)}`.trim(); | ||
const child = cross_spawn_1.default(name, argv, { | ||
env: Object.assign(Object.assign({}, process.env), { NODE_OPTIONS }), | ||
stdio: `inherit`, | ||
}); | ||
child.on(`exit`, code => { | ||
process.exitCode = code !== null ? code : 1; | ||
}); | ||
} | ||
let { | ||
NODE_OPTIONS | ||
} = process.env; | ||
NODE_OPTIONS = `${NODE_OPTIONS || ``} --require ${_dynamicRequire.dynamicRequire.resolve(`@yarnpkg/pnpify`)}`.trim(); | ||
const child = (0, _crossSpawn.default)(name, argv, { | ||
env: { ...process.env, | ||
NODE_OPTIONS | ||
}, | ||
stdio: `inherit` | ||
}); | ||
child.on(`exit`, code => { | ||
process.exitCode = code !== null ? code : 1; | ||
}); | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const dynamicRequire = typeof __non_webpack_require__ !== `undefined` | ||
? __non_webpack_require__ | ||
: require; | ||
exports.dynamicRequire = dynamicRequire; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.dynamicRequire = void 0; | ||
const dynamicRequire = typeof __non_webpack_require__ !== `undefined` ? __non_webpack_require__ : require; | ||
exports.dynamicRequire = dynamicRequire; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const comment_json_1 = __importDefault(require("comment-json")); | ||
const dynamicRequire_1 = require("./dynamicRequire"); | ||
const TEMPLATE = (relPnpApiPath, module, { usePnpify }) => [ | ||
`const relPnpApiPath = ${JSON.stringify(fslib_1.npath.toPortablePath(relPnpApiPath))};\n`, | ||
`const absPnpApiPath = require(\`path\`).resolve(__dirname, relPnpApiPath);\n`, | ||
`\n`, | ||
`// Setup the environment to be able to require ${module}\n`, | ||
`require(absPnpApiPath).setup();\n`, | ||
`\n`, | ||
`// Prepare the environment (to be ready in case of child_process.spawn etc)\n`, | ||
`process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || \`\`;\n`, | ||
`process.env.NODE_OPTIONS += \` -r \${absPnpApiPath}\`;\n`, | ||
...(usePnpify ? [ | ||
`process.env.NODE_OPTIONS += \` -r \${require.resolve(\`@yarnpkg/pnpify\`)}\`;\n`, | ||
`\n`, | ||
`// Apply PnPify to the current process\n`, | ||
`require(\`@yarnpkg/pnpify\`).patchFs();\n`, | ||
] : []), | ||
`\n`, | ||
`// Defer to the real ${module} your application uses\n`, | ||
`module.exports = require(\`${module}\`);\n`, | ||
].join(``); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.generateSdk = exports.generateEslintWrapper = void 0; | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _commentJson = _interopRequireDefault(require("comment-json")); | ||
var _dynamicRequire = require("./dynamicRequire"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const TEMPLATE = (relPnpApiPath, module, { | ||
usePnpify | ||
}) => [`const {dirname, resolve} = require(\`path\`);\n`, `\n`, `const relPnpApiPath = ${JSON.stringify(_fslib.npath.toPortablePath(relPnpApiPath))};\n`, `\n`, `const absPnpApiPath = resolve(__dirname, relPnpApiPath);\n`, `const absRootPath = dirname(absPnpApiPath);\n`, `\n`, `// Setup the environment to be able to require ${module}\n`, `require(absPnpApiPath).setup();\n`, `\n`, ...(usePnpify ? [`const pnpifyResolution = require.resolve(\`@yarnpkg/pnpify\`, {paths: [absRootPath]});\n`] : []), `const moduleResolution = require.resolve(\`${module}\`, {paths: [absRootPath]});\n`, `\n`, `// Prepare the environment (to be ready in case of child_process.spawn etc)\n`, `process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || \`\`;\n`, `process.env.NODE_OPTIONS += \` -r \${absPnpApiPath}\`;\n`, ...(usePnpify ? [`process.env.NODE_OPTIONS += \` -r \${pnpifyResolution}\`;\n`, `\n`, `// Apply PnPify to the current process\n`, `require(pnpifyResolution).patchFs();\n`] : []), `\n`, `// Defer to the real ${module} your application uses\n`, `module.exports = require(moduleResolution);\n`].join(``); | ||
const addVSCodeWorkspaceSettings = async (projectRoot, settings) => { | ||
const settingsPath = fslib_1.ppath.join(projectRoot, `.vscode/settings.json`); | ||
const content = await fslib_1.xfs.existsPromise(settingsPath) ? await fslib_1.xfs.readFilePromise(settingsPath, `utf8`) : `{}`; | ||
const data = comment_json_1.default.parse(content); | ||
const patched = `${comment_json_1.default.stringify(Object.assign(Object.assign({}, data), settings), null, 2)}\n`; | ||
await fslib_1.xfs.mkdirpPromise(fslib_1.ppath.dirname(settingsPath)); | ||
await fslib_1.xfs.changeFilePromise(settingsPath, patched); | ||
const settingsPath = _fslib.ppath.join(projectRoot, `.vscode/settings.json`); | ||
const content = (await _fslib.xfs.existsPromise(settingsPath)) ? await _fslib.xfs.readFilePromise(settingsPath, `utf8`) : `{}`; | ||
const data = _commentJson.default.parse(content); | ||
const patched = `${_commentJson.default.stringify({ ...data, | ||
...settings | ||
}, null, 2)}\n`; | ||
await _fslib.xfs.mkdirpPromise(_fslib.ppath.dirname(settingsPath)); | ||
await _fslib.xfs.changeFilePromise(settingsPath, patched); | ||
}; | ||
const generateTypescriptWrapper = async (projectRoot, target) => { | ||
const typescript = fslib_1.ppath.join(target, `typescript`); | ||
const manifest = fslib_1.ppath.join(typescript, `package.json`); | ||
const tsserver = fslib_1.ppath.join(typescript, `lib/tsserver.js`); | ||
const relPnpApiPath = fslib_1.ppath.relative(fslib_1.ppath.dirname(tsserver), fslib_1.ppath.join(projectRoot, `.pnp.js`)); | ||
await fslib_1.xfs.mkdirpPromise(fslib_1.ppath.dirname(tsserver)); | ||
await fslib_1.xfs.writeFilePromise(manifest, JSON.stringify({ name: 'typescript', version: `${dynamicRequire_1.dynamicRequire('typescript/package.json').version}-pnpify` }, null, 2)); | ||
await fslib_1.xfs.writeFilePromise(tsserver, TEMPLATE(relPnpApiPath, "typescript/lib/tsserver", { usePnpify: true })); | ||
await addVSCodeWorkspaceSettings(projectRoot, { 'typescript.tsdk': fslib_1.npath.fromPortablePath(fslib_1.ppath.relative(projectRoot, fslib_1.ppath.dirname(tsserver))) }); | ||
const typescript = _fslib.ppath.join(target, `typescript`); | ||
const manifest = _fslib.ppath.join(typescript, `package.json`); | ||
const tsserver = _fslib.ppath.join(typescript, `lib/tsserver.js`); | ||
const relPnpApiPath = _fslib.ppath.relative(_fslib.ppath.dirname(tsserver), _fslib.ppath.join(projectRoot, `.pnp.js`)); | ||
await _fslib.xfs.mkdirpPromise(_fslib.ppath.dirname(tsserver)); | ||
await _fslib.xfs.writeFilePromise(manifest, JSON.stringify({ | ||
name: 'typescript', | ||
version: `${(0, _dynamicRequire.dynamicRequire)('typescript/package.json').version}-pnpify` | ||
}, null, 2)); | ||
await _fslib.xfs.writeFilePromise(tsserver, TEMPLATE(relPnpApiPath, "typescript/lib/tsserver", { | ||
usePnpify: true | ||
})); | ||
await addVSCodeWorkspaceSettings(projectRoot, { | ||
'typescript.tsdk': _fslib.npath.fromPortablePath(_fslib.ppath.relative(projectRoot, _fslib.ppath.dirname(tsserver))) | ||
}); | ||
}; | ||
exports.generateEslintWrapper = async (projectRoot, target) => { | ||
const eslint = fslib_1.ppath.join(target, `eslint`); | ||
const manifest = fslib_1.ppath.join(eslint, `package.json`); | ||
const api = fslib_1.ppath.join(eslint, `lib/api.js`); | ||
const relPnpApiPath = fslib_1.ppath.relative(fslib_1.ppath.dirname(api), fslib_1.ppath.join(projectRoot, `.pnp.js`)); | ||
await fslib_1.xfs.mkdirpPromise(fslib_1.ppath.dirname(api)); | ||
await fslib_1.xfs.writeFilePromise(manifest, JSON.stringify({ name: 'eslint', version: `${dynamicRequire_1.dynamicRequire('eslint/package.json').version}-pnpify`, main: 'lib/api.js' }, null, 2)); | ||
await fslib_1.xfs.writeFilePromise(api, TEMPLATE(relPnpApiPath, "eslint", { usePnpify: false })); | ||
await addVSCodeWorkspaceSettings(projectRoot, { 'eslint.nodePath': fslib_1.npath.fromPortablePath(fslib_1.ppath.relative(projectRoot, fslib_1.ppath.dirname(eslint))) }); | ||
const generateEslintWrapper = async (projectRoot, target) => { | ||
const eslint = _fslib.ppath.join(target, `eslint`); | ||
const manifest = _fslib.ppath.join(eslint, `package.json`); | ||
const api = _fslib.ppath.join(eslint, `lib/api.js`); | ||
const relPnpApiPath = _fslib.ppath.relative(_fslib.ppath.dirname(api), _fslib.ppath.join(projectRoot, `.pnp.js`)); | ||
await _fslib.xfs.mkdirpPromise(_fslib.ppath.dirname(api)); | ||
await _fslib.xfs.writeFilePromise(manifest, JSON.stringify({ | ||
name: 'eslint', | ||
version: `${(0, _dynamicRequire.dynamicRequire)('eslint/package.json').version}-pnpify`, | ||
main: 'lib/api.js' | ||
}, null, 2)); | ||
await _fslib.xfs.writeFilePromise(api, TEMPLATE(relPnpApiPath, "eslint", { | ||
usePnpify: false | ||
})); | ||
await addVSCodeWorkspaceSettings(projectRoot, { | ||
'eslint.nodePath': _fslib.npath.fromPortablePath(_fslib.ppath.relative(projectRoot, _fslib.ppath.dirname(eslint))) | ||
}); | ||
}; | ||
const isPackageInstalled = (name) => { | ||
try { | ||
dynamicRequire_1.dynamicRequire.resolve(name); | ||
return true; | ||
exports.generateEslintWrapper = generateEslintWrapper; | ||
const isPackageInstalled = name => { | ||
try { | ||
_dynamicRequire.dynamicRequire.resolve(name); | ||
return true; | ||
} catch (e) { | ||
if (e.code && e.code === 'MODULE_NOT_FOUND') { | ||
return false; | ||
} else { | ||
throw e; | ||
} | ||
catch (e) { | ||
if (e.code && e.code === 'MODULE_NOT_FOUND') { | ||
return false; | ||
} | ||
else { | ||
throw e; | ||
} | ||
} | ||
} | ||
}; | ||
exports.generateSdk = async (projectRoot) => { | ||
const hasTypescript = isPackageInstalled('typescript'); | ||
const hasEslint = isPackageInstalled('eslint'); | ||
const targetFolder = fslib_1.ppath.join(projectRoot, `.vscode/pnpify`); | ||
if (!hasTypescript && !hasEslint) | ||
console.warn(`Neither 'typescript' nor 'eslint' are installed. Nothing to do.`); | ||
else | ||
await fslib_1.xfs.removePromise(targetFolder); | ||
if (hasTypescript) | ||
await generateTypescriptWrapper(projectRoot, targetFolder); | ||
if (hasEslint) { | ||
await exports.generateEslintWrapper(projectRoot, targetFolder); | ||
} | ||
const generateSdk = async projectRoot => { | ||
const hasTypescript = isPackageInstalled('typescript'); | ||
const hasEslint = isPackageInstalled('eslint'); | ||
const targetFolder = _fslib.ppath.join(projectRoot, `.vscode/pnpify`); | ||
if (!hasTypescript && !hasEslint) console.warn(`Neither 'typescript' nor 'eslint' are installed. Nothing to do.`);else await _fslib.xfs.removePromise(targetFolder); | ||
if (hasTypescript) await generateTypescriptWrapper(projectRoot, targetFolder); | ||
if (hasEslint) { | ||
await generateEslintWrapper(projectRoot, targetFolder); | ||
} | ||
}; | ||
exports.generateSdk = generateSdk; |
482
lib/hoist.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.hoist = void 0; | ||
/** | ||
* Package id - a number that uniquiely identifies both package and its dependencies. | ||
* There must be package with id 0 - which contains all the other packages. | ||
*/ | ||
/** | ||
* Package name - a string that denotes the fact that two packages with the same name | ||
* cannot be dependencies of the same parent package. The package with the same name | ||
* can have multiple package ids associated with the packages, either because of the | ||
* different package versions, or because of the different dependency sets, | ||
* as in peer dependent package. | ||
*/ | ||
/** | ||
* Package weight - a number that somehow signifies which package is heavier and should have | ||
* a priority to be hoisted. The packages having the biggest weight with all their transitive | ||
* dependencies are hoisted first. | ||
*/ | ||
/** | ||
* Package tree - is simply an array, with index being package id and the value - the dependencies | ||
* of that package. Package tree can contain cycles. | ||
* | ||
* Hoisted package tree has the same type, but values should be treated as not necesseraly | ||
* dependencies, but rather hoisted packages. | ||
*/ | ||
/** | ||
* Initial information about the package. | ||
*/ | ||
/** | ||
* The results of weighting each package with its transitive dependencies in some subtree. | ||
*/ | ||
/** | ||
* Mapping which packages depend on a given package. It is used to determine hoisting weight, | ||
* e.g. which one among the group of packages with the same name should be hoisted. | ||
* The package having the biggest self-weight (including all transitive dependencies) multiplied | ||
* by the number of ancestors using this package will be hoisted. | ||
*/ | ||
/** | ||
* Hoists package tree, by applying hoisting algorithm to each tree node independently. | ||
@@ -20,19 +67,30 @@ * We first try to hoist all the packages from anywhere in the whole tree to the root package. | ||
*/ | ||
exports.hoist = (tree, packageInfos, nohoist = new Set()) => { | ||
// Make tree copy, which will be mutated by hoisting algorithm | ||
const treeCopy = tree.map(({ deps, peerDeps }) => ({ deps: new Set(deps), peerDeps: new Set(peerDeps), origDeps: new Set(deps), origPeerDeps: new Set(peerDeps) })); | ||
const seenIds = new Set(); | ||
const hoistSubTree = (nodeId, ancestorMap) => { | ||
if (seenIds.has(nodeId)) | ||
return; | ||
seenIds.add(nodeId); | ||
// Apply mutating hoisting algorithm on each tree node starting from the root | ||
hoistInplace(treeCopy, nodeId, packageInfos, ancestorMap, nohoist); | ||
for (const depId of treeCopy[nodeId].deps) { | ||
hoistSubTree(depId, ancestorMap); | ||
} | ||
}; | ||
if (treeCopy.length > 0 && treeCopy[0].deps.size > 0) | ||
hoistSubTree(0, buildAncestorMap(tree)); | ||
return treeCopy.map(({ deps }) => deps); | ||
const hoist = (tree, packageInfos, nohoist = new Set()) => { | ||
// Make tree copy, which will be mutated by hoisting algorithm | ||
const treeCopy = tree.map(({ | ||
deps, | ||
peerDeps | ||
}) => ({ | ||
deps: new Set(deps), | ||
peerDeps: new Set(peerDeps), | ||
origDeps: new Set(deps), | ||
origPeerDeps: new Set(peerDeps) | ||
})); | ||
const seenIds = new Set(); | ||
const hoistSubTree = (nodeId, ancestorMap) => { | ||
if (seenIds.has(nodeId)) return; | ||
seenIds.add(nodeId); // Apply mutating hoisting algorithm on each tree node starting from the root | ||
hoistInplace(treeCopy, nodeId, packageInfos, ancestorMap, nohoist); | ||
for (const depId of treeCopy[nodeId].deps) { | ||
hoistSubTree(depId, ancestorMap); | ||
} | ||
}; | ||
if (treeCopy.length > 0 && treeCopy[0].deps.size > 0) hoistSubTree(0, buildAncestorMap(tree)); | ||
return treeCopy.map(({ | ||
deps | ||
}) => deps); | ||
}; | ||
@@ -47,20 +105,23 @@ /** | ||
*/ | ||
const buildAncestorMap = (tree) => { | ||
const ancestorMap = []; | ||
const seenIds = new Set(); | ||
const addAncestor = (nodeId) => { | ||
if (seenIds.has(nodeId)) | ||
return; | ||
seenIds.add(nodeId); | ||
for (const depId of tree[nodeId].deps) { | ||
const ancestors = ancestorMap[depId]; | ||
if (!ancestors) | ||
ancestorMap[depId] = new Set([nodeId]); | ||
else | ||
ancestors.add(nodeId); | ||
addAncestor(depId); | ||
} | ||
}; | ||
addAncestor(0); | ||
return ancestorMap; | ||
exports.hoist = hoist; | ||
const buildAncestorMap = tree => { | ||
const ancestorMap = []; | ||
const seenIds = new Set(); | ||
const addAncestor = nodeId => { | ||
if (seenIds.has(nodeId)) return; | ||
seenIds.add(nodeId); | ||
for (const depId of tree[nodeId].deps) { | ||
const ancestors = ancestorMap[depId]; | ||
if (!ancestors) ancestorMap[depId] = new Set([nodeId]);else ancestors.add(nodeId); | ||
addAncestor(depId); | ||
} | ||
}; | ||
addAncestor(0); | ||
return ancestorMap; | ||
}; | ||
@@ -77,31 +138,35 @@ /** | ||
*/ | ||
const hoistInplace = (tree, rootId, packages, ancestorMap, nohoist) => { | ||
// Get the list of package ids that can and should be hoisted to the subtree root | ||
const hoistedDepIds = computeHoistCandidates(tree, rootId, packages, ancestorMap, nohoist); | ||
const seenIds = new Set(); | ||
const removeHoistedDeps = (nodeId) => { | ||
if (seenIds.has(nodeId)) | ||
return; | ||
seenIds.add(nodeId); | ||
// No need to traverse past nohoist node | ||
if (nohoist.has(nodeId)) | ||
return; | ||
const depIds = tree[nodeId].deps; | ||
for (const depId of depIds) { | ||
// First traverse to deeper levels | ||
removeHoistedDeps(depId); | ||
// Then remove hoisted deps from current node | ||
if (hoistedDepIds.has(depId)) { | ||
depIds.delete(depId); | ||
// Remove hoisted dependency from the ancestor map | ||
ancestorMap[depId].delete(nodeId); | ||
} | ||
} | ||
}; | ||
removeHoistedDeps(rootId); | ||
const nodeDepIds = tree[rootId].deps; | ||
for (const depId of hoistedDepIds) { | ||
// Add hoisted packages to the subtree root | ||
nodeDepIds.add(depId); | ||
// Get the list of package ids that can and should be hoisted to the subtree root | ||
const hoistedDepIds = computeHoistCandidates(tree, rootId, packages, ancestorMap, nohoist); | ||
const seenIds = new Set(); | ||
const removeHoistedDeps = nodeId => { | ||
if (seenIds.has(nodeId)) return; | ||
seenIds.add(nodeId); // No need to traverse past nohoist node | ||
if (nohoist.has(nodeId)) return; | ||
const depIds = tree[nodeId].deps; | ||
for (const depId of depIds) { | ||
// First traverse to deeper levels | ||
removeHoistedDeps(depId); // Then remove hoisted deps from current node | ||
if (hoistedDepIds.has(depId)) { | ||
depIds.delete(depId); // Remove hoisted dependency from the ancestor map | ||
ancestorMap[depId].delete(nodeId); | ||
} | ||
} | ||
}; | ||
removeHoistedDeps(rootId); | ||
const nodeDepIds = tree[rootId].deps; | ||
for (const depId of hoistedDepIds) { | ||
// Add hoisted packages to the subtree root | ||
nodeDepIds.add(depId); | ||
} | ||
}; | ||
@@ -120,25 +185,32 @@ /** | ||
*/ | ||
const weighPackages = (weighCanidates, tree, packages, ancestorMap, nohoist) => { | ||
const nodeWeights = new Map(); | ||
const weighNode = (nodeId) => { | ||
const storedWeight = nodeWeights.get(nodeId); | ||
if (typeof storedWeight !== 'undefined') | ||
return storedWeight; | ||
let totalWeigth = 0; | ||
// First add the zero entry to the weights map, so that we know we have "seen" this node | ||
nodeWeights.set(nodeId, totalWeigth); | ||
if (!nohoist.has(nodeId)) { | ||
totalWeigth = packages[nodeId].weight; | ||
for (const depId of tree[nodeId].deps) | ||
totalWeigth += weighNode(depId); | ||
nodeWeights.set(nodeId, totalWeigth); | ||
} | ||
return totalWeigth; | ||
}; | ||
const weights = new Map(); | ||
for (const nodeId of weighCanidates) | ||
// Total weight for hoisting purposes is a weight of the package with all transitive dependencies | ||
// multipled by the number of package ancestors, which use the package | ||
weights.set(nodeId, weighNode(nodeId) * ancestorMap[nodeId].size); | ||
return weights; | ||
const nodeWeights = new Map(); | ||
const weighNode = nodeId => { | ||
const storedWeight = nodeWeights.get(nodeId); | ||
if (typeof storedWeight !== 'undefined') return storedWeight; | ||
let totalWeigth = 0; // First add the zero entry to the weights map, so that we know we have "seen" this node | ||
nodeWeights.set(nodeId, totalWeigth); | ||
if (!nohoist.has(nodeId)) { | ||
totalWeigth = packages[nodeId].weight; | ||
for (const depId of tree[nodeId].deps) totalWeigth += weighNode(depId); | ||
nodeWeights.set(nodeId, totalWeigth); | ||
} | ||
return totalWeigth; | ||
}; | ||
const weights = new Map(); | ||
for (const nodeId of weighCanidates) // Total weight for hoisting purposes is a weight of the package with all transitive dependencies | ||
// multipled by the number of package ancestors, which use the package | ||
weights.set(nodeId, weighNode(nodeId) * ancestorMap[nodeId].size); | ||
return weights; | ||
}; | ||
@@ -153,20 +225,30 @@ /** | ||
*/ | ||
const getHeaviestPackages = (weights, packages) => { | ||
const heaviestPackages = new Map(); | ||
for (const [pkgId, weight] of weights) { | ||
const pkgName = packages[pkgId].name; | ||
let heaviestPkg = heaviestPackages.get(pkgName); | ||
if (!heaviestPkg) { | ||
heaviestPkg = { weight, pkgId }; | ||
heaviestPackages.set(pkgName, heaviestPkg); | ||
} | ||
else if (weight > heaviestPkg.weight) { | ||
heaviestPkg.weight = weight; | ||
heaviestPkg.pkgId = pkgId; | ||
} | ||
const heaviestPackages = new Map(); | ||
for (const [pkgId, weight] of weights) { | ||
const pkgName = packages[pkgId].name; | ||
let heaviestPkg = heaviestPackages.get(pkgName); | ||
if (!heaviestPkg) { | ||
heaviestPkg = { | ||
weight, | ||
pkgId | ||
}; | ||
heaviestPackages.set(pkgName, heaviestPkg); | ||
} else if (weight > heaviestPkg.weight) { | ||
heaviestPkg.weight = weight; | ||
heaviestPkg.pkgId = pkgId; | ||
} | ||
const heavyPackageIds = new Set(); | ||
for (const { pkgId } of heaviestPackages.values()) | ||
heavyPackageIds.add(pkgId); | ||
return heavyPackageIds; | ||
} | ||
const heavyPackageIds = new Set(); | ||
for (const { | ||
pkgId | ||
} of heaviestPackages.values()) heavyPackageIds.add(pkgId); | ||
return heavyPackageIds; | ||
}; | ||
@@ -199,102 +281,106 @@ /** | ||
*/ | ||
const computeHoistCandidates = (tree, rootId, packages, ancestorMap, nohoist) => { | ||
// Packages that should be hoisted for sure (they have only 1 version) | ||
const packagesToHoist = new Set(); | ||
// Names of the packages of hoist candidates | ||
const packagesToHoistNames = new Map(); | ||
// Hoist candidates that has no peer deps or that has all peer deps already hoisted | ||
const pureHoistCandidates = new Set(); | ||
// Hoist candidates with peer deps | ||
const hoistCandidatesWithPeerDeps = new Set(); | ||
const seenDepNames = new Map(); | ||
for (const depId of tree[rootId].origDeps) | ||
seenDepNames.set(packages[depId].name, depId); | ||
for (const depId of tree[rootId].origPeerDeps) | ||
seenDepNames.set(packages[depId].name, depId); | ||
const seenIds = new Set(); | ||
const findHoistCandidates = (nodeId) => { | ||
if (seenIds.has(nodeId)) | ||
return; | ||
seenIds.add(nodeId); | ||
const name = packages[nodeId].name; | ||
const seenPkgId = seenDepNames.get(name); | ||
const pkg = tree[nodeId]; | ||
// Check rule 1 | ||
if (!tree[rootId].origPeerDeps.has(nodeId) && (!seenPkgId || seenPkgId === nodeId)) { | ||
if (pkg.peerDeps.size > 0) { | ||
hoistCandidatesWithPeerDeps.add(nodeId); | ||
} | ||
else { | ||
const pkgId = packagesToHoistNames.get(name); | ||
if (pkgId) { | ||
packagesToHoist.delete(pkgId); | ||
pureHoistCandidates.add(pkgId); | ||
pureHoistCandidates.add(nodeId); | ||
} | ||
else { | ||
packagesToHoist.add(nodeId); | ||
packagesToHoistNames.set(name, nodeId); | ||
} | ||
} | ||
// Packages that should be hoisted for sure (they have only 1 version) | ||
const packagesToHoist = new Set(); // Names of the packages of hoist candidates | ||
const packagesToHoistNames = new Map(); // Hoist candidates that has no peer deps or that has all peer deps already hoisted | ||
const pureHoistCandidates = new Set(); // Hoist candidates with peer deps | ||
const hoistCandidatesWithPeerDeps = new Set(); | ||
const seenDepNames = new Map(); | ||
for (const depId of tree[rootId].origDeps) seenDepNames.set(packages[depId].name, depId); | ||
for (const depId of tree[rootId].origPeerDeps) seenDepNames.set(packages[depId].name, depId); | ||
const seenIds = new Set(); | ||
const findHoistCandidates = nodeId => { | ||
if (seenIds.has(nodeId)) return; | ||
seenIds.add(nodeId); | ||
const name = packages[nodeId].name; | ||
const seenPkgId = seenDepNames.get(name); | ||
const pkg = tree[nodeId]; // Check rule 1 | ||
if (!tree[rootId].origPeerDeps.has(nodeId) && (!seenPkgId || seenPkgId === nodeId)) { | ||
if (pkg.peerDeps.size > 0) { | ||
hoistCandidatesWithPeerDeps.add(nodeId); | ||
} else { | ||
const pkgId = packagesToHoistNames.get(name); | ||
if (pkgId) { | ||
packagesToHoist.delete(pkgId); | ||
pureHoistCandidates.add(pkgId); | ||
pureHoistCandidates.add(nodeId); | ||
} else { | ||
packagesToHoist.add(nodeId); | ||
packagesToHoistNames.set(name, nodeId); | ||
} | ||
if (!seenPkgId) | ||
seenDepNames.set(name, nodeId); | ||
for (const depId of pkg.deps) | ||
findHoistCandidates(depId); | ||
if (!seenPkgId) { | ||
seenDepNames.delete(name); | ||
} | ||
} | ||
if (!seenPkgId) seenDepNames.set(name, nodeId); | ||
for (const depId of pkg.deps) findHoistCandidates(depId); | ||
if (!seenPkgId) { | ||
seenDepNames.delete(name); | ||
} | ||
}; // Find packages names that are candidates for hoisting | ||
for (const depId of tree[rootId].deps) findHoistCandidates(depId); | ||
const pureHoistCandidatesWeights = weighPackages(pureHoistCandidates, tree, packages, ancestorMap, nohoist); // Among all pure hoist candidates choose the heaviest and add them to packages to hoist list | ||
getHeaviestPackages(pureHoistCandidatesWeights, packages).forEach(pkgId => { | ||
packagesToHoistNames.set(packages[pkgId].name, pkgId); | ||
packagesToHoist.add(pkgId); | ||
}); | ||
let newHoistCandidates = packagesToHoist; // Loop until new hoist candidates appear | ||
while (newHoistCandidates.size > 0) { | ||
let nextHoistCandidates = new Set(); | ||
for (const peerDepCandId of hoistCandidatesWithPeerDeps) { | ||
// Peer dependencies that are going to be hoisted to the top, or were hoisted above the top | ||
const nonHoistedPeerDeps = tree[peerDepCandId].peerDeps; | ||
/* eslint-disable arca/curly */ | ||
if (nonHoistedPeerDeps.size < newHoistCandidates.size) { | ||
for (const peerDepId of nonHoistedPeerDeps) if (newHoistCandidates.has(peerDepId)) // Remove all the packages that are going to be hoisted from current peer deps | ||
nonHoistedPeerDeps.delete(peerDepId); | ||
} else { | ||
for (const candidateId of newHoistCandidates) if (nonHoistedPeerDeps.has(candidateId)) // Remove all the packages that are going to be hoisted from current peer deps | ||
nonHoistedPeerDeps.delete(candidateId); | ||
} | ||
/* eslint-enable arca/curly */ | ||
if (nonHoistedPeerDeps.size === 0) { | ||
// Check that we don't already have the package with the same name but different version | ||
// among hoist candidates | ||
const name = packages[peerDepCandId].name; | ||
const hoistedPkgId = packagesToHoistNames.get(name); // Recheck rule 1 for the peer dependent package that is going to be hoisted | ||
if (!hoistedPkgId || hoistedPkgId === peerDepCandId) { | ||
// Peer dependent package can be hoisted if all of its peer deps are going to be hoisted | ||
nextHoistCandidates.add(peerDepCandId); | ||
packagesToHoist.add(peerDepCandId); | ||
packagesToHoistNames.set(name, peerDepCandId); | ||
hoistCandidatesWithPeerDeps.delete(peerDepCandId); | ||
} else { | ||
// We cannot hoist this package without breaking rule 1, stop trying | ||
hoistCandidatesWithPeerDeps.delete(peerDepCandId); | ||
} | ||
}; | ||
// Find packages names that are candidates for hoisting | ||
for (const depId of tree[rootId].deps) | ||
findHoistCandidates(depId); | ||
const pureHoistCandidatesWeights = weighPackages(pureHoistCandidates, tree, packages, ancestorMap, nohoist); | ||
// Among all pure hoist candidates choose the heaviest and add them to packages to hoist list | ||
getHeaviestPackages(pureHoistCandidatesWeights, packages).forEach(pkgId => { | ||
packagesToHoistNames.set(packages[pkgId].name, pkgId); | ||
packagesToHoist.add(pkgId); | ||
}); | ||
let newHoistCandidates = packagesToHoist; | ||
// Loop until new hoist candidates appear | ||
while (newHoistCandidates.size > 0) { | ||
let nextHoistCandidates = new Set(); | ||
for (const peerDepCandId of hoistCandidatesWithPeerDeps) { | ||
// Peer dependencies that are going to be hoisted to the top, or were hoisted above the top | ||
const nonHoistedPeerDeps = tree[peerDepCandId].peerDeps; | ||
/* eslint-disable arca/curly */ | ||
if (nonHoistedPeerDeps.size < newHoistCandidates.size) { | ||
for (const peerDepId of nonHoistedPeerDeps) | ||
if (newHoistCandidates.has(peerDepId)) | ||
// Remove all the packages that are going to be hoisted from current peer deps | ||
nonHoistedPeerDeps.delete(peerDepId); | ||
} | ||
else { | ||
for (const candidateId of newHoistCandidates) | ||
if (nonHoistedPeerDeps.has(candidateId)) | ||
// Remove all the packages that are going to be hoisted from current peer deps | ||
nonHoistedPeerDeps.delete(candidateId); | ||
} | ||
/* eslint-enable arca/curly */ | ||
if (nonHoistedPeerDeps.size === 0) { | ||
// Check that we don't already have the package with the same name but different version | ||
// among hoist candidates | ||
const name = packages[peerDepCandId].name; | ||
const hoistedPkgId = packagesToHoistNames.get(name); | ||
// Recheck rule 1 for the peer dependent package that is going to be hoisted | ||
if (!hoistedPkgId || hoistedPkgId === peerDepCandId) { | ||
// Peer dependent package can be hoisted if all of its peer deps are going to be hoisted | ||
nextHoistCandidates.add(peerDepCandId); | ||
packagesToHoist.add(peerDepCandId); | ||
packagesToHoistNames.set(name, peerDepCandId); | ||
hoistCandidatesWithPeerDeps.delete(peerDepCandId); | ||
} | ||
else { | ||
// We cannot hoist this package without breaking rule 1, stop trying | ||
hoistCandidatesWithPeerDeps.delete(peerDepCandId); | ||
} | ||
} | ||
} | ||
newHoistCandidates = nextHoistCandidates; | ||
} | ||
} | ||
return packagesToHoist; | ||
}; | ||
newHoistCandidates = nextHoistCandidates; | ||
} | ||
return packagesToHoist; | ||
}; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const fs_1 = __importDefault(require("fs")); | ||
const NodeModulesFS_1 = require("./NodeModulesFS"); | ||
exports.NodeModulesFS = NodeModulesFS_1.NodeModulesFS; | ||
exports.PortableNodeModulesFs = NodeModulesFS_1.PortableNodeModulesFs; | ||
const dynamicRequire_1 = require("./dynamicRequire"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "NodeModulesFS", { | ||
enumerable: true, | ||
get: function () { | ||
return _NodeModulesFS.NodeModulesFS; | ||
} | ||
}); | ||
Object.defineProperty(exports, "PortableNodeModulesFs", { | ||
enumerable: true, | ||
get: function () { | ||
return _NodeModulesFS.PortableNodeModulesFs; | ||
} | ||
}); | ||
exports.patchFs = void 0; | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _fs = _interopRequireDefault(require("fs")); | ||
var _NodeModulesFS = require("./NodeModulesFS"); | ||
var _dynamicRequire = require("./dynamicRequire"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
let fsPatched = false; | ||
let pnp; | ||
try { | ||
pnp = dynamicRequire_1.dynamicRequire('pnpapi'); | ||
} | ||
catch (e) { | ||
} | ||
exports.patchFs = () => { | ||
if (pnp && !fsPatched) { | ||
const realFs = Object.assign({}, fs_1.default); | ||
const nodeModulesFS = new NodeModulesFS_1.NodeModulesFS(pnp, { realFs }); | ||
fslib_1.patchFs(fs_1.default, nodeModulesFS); | ||
fsPatched = true; | ||
} | ||
pnp = (0, _dynamicRequire.dynamicRequire)('pnpapi'); | ||
} catch (e) {} | ||
const patchFs = () => { | ||
if (pnp && !fsPatched) { | ||
const realFs = { ..._fs.default | ||
}; | ||
const nodeModulesFS = new _NodeModulesFS.NodeModulesFS(pnp, { | ||
realFs | ||
}); | ||
(0, _fslib.patchFs)(_fs.default, nodeModulesFS); | ||
fsPatched = true; | ||
} | ||
}; | ||
if (!process.mainModule) | ||
exports.patchFs(); | ||
exports.patchFs = patchFs; | ||
if (!process.mainModule) patchFs(); |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const fslib_2 = require("@yarnpkg/fslib"); | ||
const fslib_3 = require("@yarnpkg/fslib"); | ||
const fs_1 = __importDefault(require("fs")); | ||
const WatchManager_1 = require("./WatchManager"); | ||
const buildNodeModulesTree_1 = require("./buildNodeModulesTree"); | ||
const resolveNodeModulesPath_1 = require("./resolveNodeModulesPath"); | ||
class NodeModulesFS extends fslib_3.ProxiedFS { | ||
constructor(pnp, { realFs = fs_1.default, pnpifyFs = true } = {}) { | ||
super(fslib_1.npath); | ||
this.baseFs = new PortableNodeModulesFs(pnp, { baseFs: new fslib_3.NodeFS(realFs), pnpifyFs }); | ||
} | ||
mapFromBase(path) { | ||
return fslib_1.npath.fromPortablePath(path); | ||
} | ||
mapToBase(path) { | ||
return fslib_1.npath.toPortablePath(path); | ||
} | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.PortableNodeModulesFs = exports.NodeModulesFS = void 0; | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _fs = _interopRequireDefault(require("fs")); | ||
var _WatchManager = require("./WatchManager"); | ||
var _buildNodeModulesTree = require("./buildNodeModulesTree"); | ||
var _resolveNodeModulesPath = require("./resolveNodeModulesPath"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
class NodeModulesFS extends _fslib.ProxiedFS { | ||
constructor(pnp, { | ||
realFs = _fs.default, | ||
pnpifyFs = true | ||
} = {}) { | ||
super(_fslib.npath); | ||
this.baseFs = void 0; | ||
this.baseFs = new PortableNodeModulesFs(pnp, { | ||
baseFs: new _fslib.NodeFS(realFs), | ||
pnpifyFs | ||
}); | ||
} | ||
mapFromBase(path) { | ||
return _fslib.npath.fromPortablePath(path); | ||
} | ||
mapToBase(path) { | ||
return _fslib.npath.toPortablePath(path); | ||
} | ||
} | ||
exports.NodeModulesFS = NodeModulesFS; | ||
; | ||
class PortableNodeModulesFs extends fslib_3.FakeFS { | ||
constructor(pnp, { baseFs = new fslib_3.NodeFS(), pnpifyFs = true, optimizeSizeOnDisk = false } = {}) { | ||
super(fslib_1.ppath); | ||
if (!pnp.getDependencyTreeRoots) | ||
throw new Error('NodeModulesFS supports PnP API versions 3+, please upgrade your PnP API provider'); | ||
this.options = { baseFs, pnpifyFs, optimizeSizeOnDisk }; | ||
this.baseFs = baseFs; | ||
this.nodeModulesTree = buildNodeModulesTree_1.buildNodeModulesTree(pnp, this.options); | ||
this.watchManager = new WatchManager_1.WatchManager(); | ||
const pnpRootPath = fslib_1.npath.toPortablePath(pnp.getPackageInformation(pnp.topLevel).packageLocation); | ||
this.watchPnpFile(pnpRootPath); | ||
class PortableNodeModulesFs extends _fslib.FakeFS { | ||
constructor(pnp, { | ||
baseFs = new _fslib.NodeFS(), | ||
pnpifyFs = true, | ||
optimizeSizeOnDisk = false | ||
} = {}) { | ||
super(_fslib.ppath); | ||
this.baseFs = void 0; | ||
this.options = void 0; | ||
this.watchManager = void 0; | ||
this.nodeModulesTree = void 0; | ||
if (!pnp.getDependencyTreeRoots) throw new Error('NodeModulesFS supports PnP API versions 3+, please upgrade your PnP API provider'); | ||
this.options = { | ||
baseFs, | ||
pnpifyFs, | ||
optimizeSizeOnDisk | ||
}; | ||
this.baseFs = baseFs; | ||
this.nodeModulesTree = (0, _buildNodeModulesTree.buildNodeModulesTree)(pnp, this.options); | ||
this.watchManager = new _WatchManager.WatchManager(); | ||
const pnpRootPath = _fslib.npath.toPortablePath(pnp.getPackageInformation(pnp.topLevel).packageLocation); | ||
this.watchPnpFile(pnpRootPath); | ||
} | ||
watchPnpFile(pnpRootPath) { | ||
const pnpFilePath = _fslib.ppath.join(pnpRootPath, (0, _fslib.toFilename)('.pnp.js')); | ||
this.baseFs.watch(pnpRootPath, { | ||
persistent: false | ||
}, (_, filename) => { | ||
if (filename === '.pnp.js') { | ||
delete require.cache[pnpFilePath]; | ||
const pnp = require(pnpFilePath); | ||
this.nodeModulesTree = (0, _buildNodeModulesTree.buildNodeModulesTree)(pnp, this.options); | ||
this.watchManager.notifyWatchers(nodePath => (0, _resolveNodeModulesPath.resolveNodeModulesPath)(nodePath, this.nodeModulesTree)); | ||
} | ||
}); | ||
} | ||
persistPath(dir) { | ||
const pathStack = []; | ||
let curPath = dir; | ||
while (!this.baseFs.existsSync(curPath)) { | ||
pathStack.push(curPath); | ||
curPath = _fslib.ppath.dirname(curPath); | ||
} | ||
watchPnpFile(pnpRootPath) { | ||
const pnpFilePath = fslib_1.ppath.join(pnpRootPath, fslib_2.toFilename('.pnp.js')); | ||
this.baseFs.watch(pnpRootPath, { persistent: false }, (_, filename) => { | ||
if (filename === '.pnp.js') { | ||
delete require.cache[pnpFilePath]; | ||
const pnp = require(pnpFilePath); | ||
this.nodeModulesTree = buildNodeModulesTree_1.buildNodeModulesTree(pnp, this.options); | ||
this.watchManager.notifyWatchers((nodePath) => resolveNodeModulesPath_1.resolveNodeModulesPath(nodePath, this.nodeModulesTree)); | ||
} | ||
}); | ||
for (const fullPath of pathStack.reverse()) { | ||
this.baseFs.mkdirSync(fullPath); | ||
} | ||
persistPath(dir) { | ||
const pathStack = []; | ||
let curPath = dir; | ||
while (!this.baseFs.existsSync(curPath)) { | ||
pathStack.push(curPath); | ||
curPath = fslib_1.ppath.dirname(curPath); | ||
} | ||
for (const fullPath of pathStack.reverse()) { | ||
this.baseFs.mkdirSync(fullPath); | ||
} | ||
} | ||
resolve(path) { | ||
return this.baseFs.resolve(this.resolvePath(path).resolvedPath); | ||
} | ||
getBaseFs() { | ||
return this.baseFs; | ||
} | ||
resolvePath(p) { | ||
if (typeof p === `number`) { | ||
return { | ||
resolvedPath: p, | ||
fullOriginalPath: p | ||
}; | ||
} else { | ||
const fullOriginalPath = this.pathUtils.resolve(p); | ||
return { ...(0, _resolveNodeModulesPath.resolveNodeModulesPath)(fullOriginalPath, this.nodeModulesTree), | ||
fullOriginalPath | ||
}; | ||
} | ||
resolve(path) { | ||
return this.baseFs.resolve(this.resolvePath(path).resolvedPath); | ||
} | ||
resolveFilePath(p) { | ||
if (typeof p === `number`) return p; | ||
let pnpPath = this.resolvePath(p); | ||
return pnpPath.resolvedPath; | ||
} | ||
resolveDirOrFilePath(p) { | ||
if (typeof p === `number`) return p; | ||
let pnpPath = this.resolvePath(p); | ||
return pnpPath.forwardedDirPath || pnpPath.resolvedPath; | ||
} | ||
resolveLink(p, op, onSymlink, onRealPath) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.isSymlink) { | ||
let stat; | ||
try { | ||
stat = this.baseFs.lstatSync(pnpPath.resolvedPath); | ||
} catch (e) {} | ||
if (stat) { | ||
return onSymlink(stat, this.pathUtils.relative(this.pathUtils.dirname(pnpPath.fullOriginalPath), pnpPath.resolvedPath)); | ||
} | ||
} | ||
getBaseFs() { | ||
return this.baseFs; | ||
return onRealPath(pnpPath.forwardedDirPath || pnpPath.resolvedPath); | ||
} | ||
static makeSymlinkStats(stats) { | ||
return Object.assign(stats, { | ||
isFile: () => false, | ||
isDirectory: () => false, | ||
isSymbolicLink: () => true | ||
}); | ||
} | ||
static createFsError(code, message) { | ||
return Object.assign(new Error(`${code}: ${message}`), { | ||
code | ||
}); | ||
} | ||
getRealPath() { | ||
return this.baseFs.getRealPath(); | ||
} | ||
async openPromise(p, flags, mode) { | ||
return await this.baseFs.openPromise(this.resolveFilePath(p), flags, mode); | ||
} | ||
openSync(p, flags, mode) { | ||
return this.baseFs.openSync(this.resolveFilePath(p), flags, mode); | ||
} | ||
async readPromise(fd, buffer, offset, length, position) { | ||
return await this.baseFs.readPromise(fd, buffer, offset, length, position); | ||
} | ||
readSync(fd, buffer, offset, length, position) { | ||
return this.baseFs.readSync(fd, buffer, offset, length, position); | ||
} | ||
async writePromise(fd, buffer, offset, length, position) { | ||
if (typeof buffer === `string`) { | ||
return await this.baseFs.writePromise(fd, buffer, offset); | ||
} else { | ||
return await this.baseFs.writePromise(fd, buffer, offset, length, position); | ||
} | ||
resolvePath(p) { | ||
if (typeof p === `number`) { | ||
return { resolvedPath: p, fullOriginalPath: p }; | ||
} | ||
else { | ||
const fullOriginalPath = this.pathUtils.resolve(p); | ||
return Object.assign(Object.assign({}, resolveNodeModulesPath_1.resolveNodeModulesPath(fullOriginalPath, this.nodeModulesTree)), { fullOriginalPath }); | ||
} | ||
} | ||
writeSync(fd, buffer, offset, length, position) { | ||
if (typeof buffer === `string`) { | ||
return this.baseFs.writeSync(fd, buffer, offset); | ||
} else { | ||
return this.baseFs.writeSync(fd, buffer, offset, length, position); | ||
} | ||
resolveFilePath(p) { | ||
if (typeof p === `number`) | ||
return p; | ||
let pnpPath = this.resolvePath(p); | ||
return pnpPath.resolvedPath; | ||
} | ||
async closePromise(fd) { | ||
await this.baseFs.closePromise(fd); | ||
} | ||
closeSync(fd) { | ||
this.baseFs.closeSync(fd); | ||
} | ||
createReadStream(p, opts) { | ||
return this.baseFs.createReadStream(p !== null ? this.resolveFilePath(p) : p, opts); | ||
} | ||
createWriteStream(p, opts) { | ||
return this.baseFs.createWriteStream(p !== null ? this.resolveDirOrFilePath(p) : p, opts); | ||
} | ||
async realpathPromise(p) { | ||
const pnpPath = this.resolvePath(p); | ||
return pnpPath.dirList ? pnpPath.resolvedPath : this.baseFs.realpathPromise(pnpPath.resolvedPath); | ||
} | ||
realpathSync(p) { | ||
const pnpPath = this.resolvePath(p); | ||
return pnpPath.dirList ? pnpPath.resolvedPath : this.baseFs.realpathSync(pnpPath.resolvedPath); | ||
} | ||
async existsPromise(p) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList) { | ||
return true; | ||
} else { | ||
return await this.baseFs.existsPromise(pnpPath.resolvedPath); | ||
} | ||
resolveDirOrFilePath(p) { | ||
if (typeof p === `number`) | ||
return p; | ||
let pnpPath = this.resolvePath(p); | ||
return pnpPath.forwardedDirPath || pnpPath.resolvedPath; | ||
} | ||
existsSync(p) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList) { | ||
return true; | ||
} else { | ||
return this.baseFs.existsSync(pnpPath.resolvedPath); | ||
} | ||
resolveLink(p, op, onSymlink, onRealPath) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.isSymlink) { | ||
let stat; | ||
try { | ||
stat = this.baseFs.lstatSync(pnpPath.resolvedPath); | ||
} | ||
catch (e) { } | ||
if (stat) { | ||
return onSymlink(stat, this.pathUtils.relative(this.pathUtils.dirname(pnpPath.fullOriginalPath), pnpPath.resolvedPath)); | ||
} | ||
} | ||
return onRealPath(pnpPath.forwardedDirPath || pnpPath.resolvedPath); | ||
} | ||
async accessPromise(p, mode) { | ||
return await this.baseFs.accessPromise(this.resolveDirOrFilePath(p), mode); | ||
} | ||
accessSync(p, mode) { | ||
return this.baseFs.accessSync(this.resolveDirOrFilePath(p), mode); | ||
} | ||
async statPromise(p) { | ||
return await this.baseFs.statPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
statSync(p) { | ||
return this.baseFs.statSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async lstatPromise(p) { | ||
return this.resolveLink(p, 'lstat', stats => PortableNodeModulesFs.makeSymlinkStats(stats), async resolvedPath => await this.baseFs.lstatPromise(resolvedPath)); | ||
} | ||
lstatSync(p) { | ||
return this.resolveLink(p, 'lstat', stats => PortableNodeModulesFs.makeSymlinkStats(stats), resolvedPath => this.baseFs.lstatSync(this.resolveDirOrFilePath(resolvedPath))); | ||
} | ||
async chmodPromise(p, mask) { | ||
return await this.baseFs.chmodPromise(this.resolveDirOrFilePath(p), mask); | ||
} | ||
chmodSync(p, mask) { | ||
return this.baseFs.chmodSync(this.resolveDirOrFilePath(p), mask); | ||
} | ||
async renamePromise(oldP, newP) { | ||
return await this.baseFs.renamePromise(this.resolveDirOrFilePath(oldP), this.resolveDirOrFilePath(newP)); | ||
} | ||
renameSync(oldP, newP) { | ||
return this.baseFs.renameSync(this.resolveDirOrFilePath(oldP), this.resolveDirOrFilePath(newP)); | ||
} | ||
async copyFilePromise(sourceP, destP, flags) { | ||
return await this.baseFs.copyFilePromise(this.resolveFilePath(sourceP), this.resolveDirOrFilePath(destP), flags); | ||
} | ||
copyFileSync(sourceP, destP, flags) { | ||
return this.baseFs.copyFileSync(this.resolveFilePath(sourceP), this.resolveDirOrFilePath(destP), flags); | ||
} | ||
async appendFilePromise(p, content, opts) { | ||
return await this.baseFs.appendFilePromise(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
appendFileSync(p, content, opts) { | ||
return this.baseFs.appendFileSync(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
async writeFilePromise(p, content, opts) { | ||
return await this.baseFs.writeFilePromise(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
writeFileSync(p, content, opts) { | ||
return this.baseFs.writeFileSync(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
async unlinkPromise(p) { | ||
return await this.baseFs.unlinkPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
unlinkSync(p) { | ||
return this.baseFs.unlinkSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async utimesPromise(p, atime, mtime) { | ||
return await this.baseFs.utimesPromise(this.resolveDirOrFilePath(p), atime, mtime); | ||
} | ||
utimesSync(p, atime, mtime) { | ||
return this.baseFs.utimesSync(this.resolveDirOrFilePath(p), atime, mtime); | ||
} | ||
async mkdirPromise(p, opts) { | ||
const pnpPath = this.resolvePath(p); | ||
const parentPath = this.resolvePath(_fslib.ppath.dirname(p)); | ||
if (parentPath.dirList) this.persistPath(parentPath.resolvedPath); | ||
return this.baseFs.mkdirPromise(pnpPath.resolvedPath, opts); | ||
} | ||
mkdirSync(p, opts) { | ||
const pnpPath = this.resolvePath(p); | ||
const parentPath = this.resolvePath(_fslib.ppath.dirname(p)); | ||
if (parentPath.dirList) this.persistPath(parentPath.resolvedPath); | ||
return this.baseFs.mkdirSync(pnpPath.resolvedPath, opts); | ||
} | ||
async rmdirPromise(p) { | ||
return await this.baseFs.rmdirPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
rmdirSync(p) { | ||
return this.baseFs.rmdirSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async symlinkPromise(target, p) { | ||
return await this.baseFs.symlinkPromise(this.resolveDirOrFilePath(target), this.resolveDirOrFilePath(p)); | ||
} | ||
symlinkSync(target, p) { | ||
return this.baseFs.symlinkSync(this.resolveDirOrFilePath(target), this.resolveDirOrFilePath(p)); | ||
} | ||
async readFilePromise(p, encoding) { | ||
// This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) | ||
switch (encoding) { | ||
case `utf8`: | ||
return await this.baseFs.readFilePromise(this.resolveFilePath(p), encoding); | ||
default: | ||
return await this.baseFs.readFilePromise(this.resolveFilePath(p), encoding); | ||
} | ||
static makeSymlinkStats(stats) { | ||
return Object.assign(stats, { | ||
isFile: () => false, | ||
isDirectory: () => false, | ||
isSymbolicLink: () => true, | ||
} | ||
readFileSync(p, encoding) { | ||
// This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) | ||
switch (encoding) { | ||
case `utf8`: | ||
return this.baseFs.readFileSync(this.resolveFilePath(p), encoding); | ||
default: | ||
return this.baseFs.readFileSync(this.resolveFilePath(p), encoding); | ||
} | ||
} | ||
async readdirPromise(p, { | ||
withFileTypes | ||
} = {}) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList || this.resolvePath(_fslib.ppath.join(p, (0, _fslib.toFilename)('node_modules'))).dirList) { | ||
let fsDirList = []; | ||
try { | ||
fsDirList = await this.baseFs.readdirPromise(pnpPath.resolvedPath); | ||
} catch (e) {// Ignore errors | ||
} | ||
const entries = Array.from(pnpPath.dirList || [(0, _fslib.toFilename)('node_modules')]).concat(fsDirList).sort(); | ||
if (!withFileTypes) return entries; | ||
return entries.map(name => { | ||
return Object.assign(this.lstatSync(_fslib.ppath.join(p, name)), { | ||
name | ||
}); | ||
}); | ||
} else { | ||
return await this.baseFs.readdirPromise(pnpPath.resolvedPath, { | ||
withFileTypes: withFileTypes | ||
}); | ||
} | ||
static createFsError(code, message) { | ||
return Object.assign(new Error(`${code}: ${message}`), { code }); | ||
} | ||
readdirSync(p, { | ||
withFileTypes | ||
} = {}) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList || this.resolvePath(_fslib.ppath.join(p, (0, _fslib.toFilename)('node_modules'))).dirList) { | ||
let fsDirList = []; | ||
try { | ||
fsDirList = this.baseFs.readdirSync(pnpPath.resolvedPath); | ||
} catch (e) {// Ignore errors | ||
} | ||
const entries = Array.from(pnpPath.dirList || [(0, _fslib.toFilename)('node_modules')]).concat(fsDirList).sort(); | ||
if (!withFileTypes) return entries; | ||
return entries.map(name => { | ||
return Object.assign(this.lstatSync(_fslib.ppath.join(p, name)), { | ||
name | ||
}); | ||
}); | ||
} else { | ||
return this.baseFs.readdirSync(pnpPath.resolvedPath, { | ||
withFileTypes: withFileTypes | ||
}); | ||
} | ||
getRealPath() { | ||
return this.baseFs.getRealPath(); | ||
} | ||
async readlinkPromise(p) { | ||
return this.resolveLink(p, 'readlink', (_stats, targetPath) => targetPath, async targetPath => await this.baseFs.readlinkPromise(this.resolveDirOrFilePath(targetPath))); | ||
} | ||
readlinkSync(p) { | ||
return this.resolveLink(p, 'readlink', (_stats, targetPath) => targetPath, targetPath => this.baseFs.readlinkSync(this.resolveDirOrFilePath(targetPath))); | ||
} | ||
watch(p, a, b) { | ||
const pnpPath = this.resolvePath(p); | ||
const watchPath = pnpPath.resolvedPath; | ||
if (watchPath && pnpPath.dirList) { | ||
const callback = typeof a === 'function' ? a : typeof b === 'function' ? b : () => {}; | ||
return this.watchManager.registerWatcher(watchPath, pnpPath.dirList, callback); | ||
} else { | ||
return this.baseFs.watch(p, // @ts-ignore | ||
a, b); | ||
} | ||
async openPromise(p, flags, mode) { | ||
return await this.baseFs.openPromise(this.resolveFilePath(p), flags, mode); | ||
} | ||
openSync(p, flags, mode) { | ||
return this.baseFs.openSync(this.resolveFilePath(p), flags, mode); | ||
} | ||
async readPromise(fd, buffer, offset, length, position) { | ||
return await this.baseFs.readPromise(fd, buffer, offset, length, position); | ||
} | ||
readSync(fd, buffer, offset, length, position) { | ||
return this.baseFs.readSync(fd, buffer, offset, length, position); | ||
} | ||
async writePromise(fd, buffer, offset, length, position) { | ||
if (typeof buffer === `string`) { | ||
return await this.baseFs.writePromise(fd, buffer, offset); | ||
} | ||
else { | ||
return await this.baseFs.writePromise(fd, buffer, offset, length, position); | ||
} | ||
} | ||
writeSync(fd, buffer, offset, length, position) { | ||
if (typeof buffer === `string`) { | ||
return this.baseFs.writeSync(fd, buffer, offset); | ||
} | ||
else { | ||
return this.baseFs.writeSync(fd, buffer, offset, length, position); | ||
} | ||
} | ||
async closePromise(fd) { | ||
await this.baseFs.closePromise(fd); | ||
} | ||
closeSync(fd) { | ||
this.baseFs.closeSync(fd); | ||
} | ||
createReadStream(p, opts) { | ||
return this.baseFs.createReadStream(p !== null ? this.resolveFilePath(p) : p, opts); | ||
} | ||
createWriteStream(p, opts) { | ||
return this.baseFs.createWriteStream(p !== null ? this.resolveDirOrFilePath(p) : p, opts); | ||
} | ||
async realpathPromise(p) { | ||
const pnpPath = this.resolvePath(p); | ||
return pnpPath.dirList ? pnpPath.resolvedPath : this.baseFs.realpathPromise(pnpPath.resolvedPath); | ||
} | ||
realpathSync(p) { | ||
const pnpPath = this.resolvePath(p); | ||
return pnpPath.dirList ? pnpPath.resolvedPath : this.baseFs.realpathSync(pnpPath.resolvedPath); | ||
} | ||
async existsPromise(p) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList) { | ||
return true; | ||
} | ||
else { | ||
return await this.baseFs.existsPromise(pnpPath.resolvedPath); | ||
} | ||
} | ||
existsSync(p) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList) { | ||
return true; | ||
} | ||
else { | ||
return this.baseFs.existsSync(pnpPath.resolvedPath); | ||
} | ||
} | ||
async accessPromise(p, mode) { | ||
return await this.baseFs.accessPromise(this.resolveDirOrFilePath(p), mode); | ||
} | ||
accessSync(p, mode) { | ||
return this.baseFs.accessSync(this.resolveDirOrFilePath(p), mode); | ||
} | ||
async statPromise(p) { | ||
return await this.baseFs.statPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
statSync(p) { | ||
return this.baseFs.statSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async lstatPromise(p) { | ||
return this.resolveLink(p, 'lstat', (stats) => PortableNodeModulesFs.makeSymlinkStats(stats), async (resolvedPath) => await this.baseFs.lstatPromise(resolvedPath)); | ||
} | ||
lstatSync(p) { | ||
return this.resolveLink(p, 'lstat', (stats) => PortableNodeModulesFs.makeSymlinkStats(stats), (resolvedPath) => this.baseFs.lstatSync(this.resolveDirOrFilePath(resolvedPath))); | ||
} | ||
async chmodPromise(p, mask) { | ||
return await this.baseFs.chmodPromise(this.resolveDirOrFilePath(p), mask); | ||
} | ||
chmodSync(p, mask) { | ||
return this.baseFs.chmodSync(this.resolveDirOrFilePath(p), mask); | ||
} | ||
async renamePromise(oldP, newP) { | ||
return await this.baseFs.renamePromise(this.resolveDirOrFilePath(oldP), this.resolveDirOrFilePath(newP)); | ||
} | ||
renameSync(oldP, newP) { | ||
return this.baseFs.renameSync(this.resolveDirOrFilePath(oldP), this.resolveDirOrFilePath(newP)); | ||
} | ||
async copyFilePromise(sourceP, destP, flags) { | ||
return await this.baseFs.copyFilePromise(this.resolveFilePath(sourceP), this.resolveDirOrFilePath(destP), flags); | ||
} | ||
copyFileSync(sourceP, destP, flags) { | ||
return this.baseFs.copyFileSync(this.resolveFilePath(sourceP), this.resolveDirOrFilePath(destP), flags); | ||
} | ||
async appendFilePromise(p, content, opts) { | ||
return await this.baseFs.appendFilePromise(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
appendFileSync(p, content, opts) { | ||
return this.baseFs.appendFileSync(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
async writeFilePromise(p, content, opts) { | ||
return await this.baseFs.writeFilePromise(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
writeFileSync(p, content, opts) { | ||
return this.baseFs.writeFileSync(this.resolveDirOrFilePath(p), content, opts); | ||
} | ||
async unlinkPromise(p) { | ||
return await this.baseFs.unlinkPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
unlinkSync(p) { | ||
return this.baseFs.unlinkSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async utimesPromise(p, atime, mtime) { | ||
return await this.baseFs.utimesPromise(this.resolveDirOrFilePath(p), atime, mtime); | ||
} | ||
utimesSync(p, atime, mtime) { | ||
return this.baseFs.utimesSync(this.resolveDirOrFilePath(p), atime, mtime); | ||
} | ||
async mkdirPromise(p, opts) { | ||
const pnpPath = this.resolvePath(p); | ||
const parentPath = this.resolvePath(fslib_1.ppath.dirname(p)); | ||
if (parentPath.dirList) | ||
this.persistPath(parentPath.resolvedPath); | ||
return this.baseFs.mkdirPromise(pnpPath.resolvedPath, opts); | ||
} | ||
mkdirSync(p, opts) { | ||
const pnpPath = this.resolvePath(p); | ||
const parentPath = this.resolvePath(fslib_1.ppath.dirname(p)); | ||
if (parentPath.dirList) | ||
this.persistPath(parentPath.resolvedPath); | ||
return this.baseFs.mkdirSync(pnpPath.resolvedPath, opts); | ||
} | ||
async rmdirPromise(p) { | ||
return await this.baseFs.rmdirPromise(this.resolveDirOrFilePath(p)); | ||
} | ||
rmdirSync(p) { | ||
return this.baseFs.rmdirSync(this.resolveDirOrFilePath(p)); | ||
} | ||
async symlinkPromise(target, p) { | ||
return await this.baseFs.symlinkPromise(this.resolveDirOrFilePath(target), this.resolveDirOrFilePath(p)); | ||
} | ||
symlinkSync(target, p) { | ||
return this.baseFs.symlinkSync(this.resolveDirOrFilePath(target), this.resolveDirOrFilePath(p)); | ||
} | ||
async readFilePromise(p, encoding) { | ||
// This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) | ||
switch (encoding) { | ||
case `utf8`: | ||
return await this.baseFs.readFilePromise(this.resolveFilePath(p), encoding); | ||
default: | ||
return await this.baseFs.readFilePromise(this.resolveFilePath(p), encoding); | ||
} | ||
} | ||
readFileSync(p, encoding) { | ||
// This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered) | ||
switch (encoding) { | ||
case `utf8`: | ||
return this.baseFs.readFileSync(this.resolveFilePath(p), encoding); | ||
default: | ||
return this.baseFs.readFileSync(this.resolveFilePath(p), encoding); | ||
} | ||
} | ||
async readdirPromise(p, { withFileTypes } = {}) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList || this.resolvePath(fslib_1.ppath.join(p, fslib_2.toFilename('node_modules'))).dirList) { | ||
let fsDirList = []; | ||
try { | ||
fsDirList = await this.baseFs.readdirPromise(pnpPath.resolvedPath); | ||
} | ||
catch (e) { | ||
// Ignore errors | ||
} | ||
const entries = Array.from(pnpPath.dirList || [fslib_2.toFilename('node_modules')]).concat(fsDirList).sort(); | ||
if (!withFileTypes) | ||
return entries; | ||
return entries.map(name => { | ||
return Object.assign(this.lstatSync(fslib_1.ppath.join(p, name)), { | ||
name, | ||
}); | ||
}); | ||
} | ||
else { | ||
return await this.baseFs.readdirPromise(pnpPath.resolvedPath, { withFileTypes: withFileTypes }); | ||
} | ||
} | ||
readdirSync(p, { withFileTypes } = {}) { | ||
const pnpPath = this.resolvePath(p); | ||
if (pnpPath.dirList || this.resolvePath(fslib_1.ppath.join(p, fslib_2.toFilename('node_modules'))).dirList) { | ||
let fsDirList = []; | ||
try { | ||
fsDirList = this.baseFs.readdirSync(pnpPath.resolvedPath); | ||
} | ||
catch (e) { | ||
// Ignore errors | ||
} | ||
const entries = Array.from(pnpPath.dirList || [fslib_2.toFilename('node_modules')]).concat(fsDirList).sort(); | ||
if (!withFileTypes) | ||
return entries; | ||
return entries.map(name => { | ||
return Object.assign(this.lstatSync(fslib_1.ppath.join(p, name)), { | ||
name, | ||
}); | ||
}); | ||
} | ||
else { | ||
return this.baseFs.readdirSync(pnpPath.resolvedPath, { withFileTypes: withFileTypes }); | ||
} | ||
} | ||
async readlinkPromise(p) { | ||
return this.resolveLink(p, 'readlink', (_stats, targetPath) => targetPath, async (targetPath) => await this.baseFs.readlinkPromise(this.resolveDirOrFilePath(targetPath))); | ||
} | ||
readlinkSync(p) { | ||
return this.resolveLink(p, 'readlink', (_stats, targetPath) => targetPath, (targetPath) => this.baseFs.readlinkSync(this.resolveDirOrFilePath(targetPath))); | ||
} | ||
watch(p, a, b) { | ||
const pnpPath = this.resolvePath(p); | ||
const watchPath = pnpPath.resolvedPath; | ||
if (watchPath && pnpPath.dirList) { | ||
const callback = typeof a === 'function' ? a : typeof b === 'function' ? b : () => { }; | ||
return this.watchManager.registerWatcher(watchPath, pnpPath.dirList, callback); | ||
} | ||
else { | ||
return this.baseFs.watch(p, | ||
// @ts-ignore | ||
a, b); | ||
} | ||
} | ||
} | ||
} | ||
exports.PortableNodeModulesFs = PortableNodeModulesFs; | ||
exports.PortableNodeModulesFs = PortableNodeModulesFs; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fslib_1 = require("@yarnpkg/fslib"); | ||
const fslib_2 = require("@yarnpkg/fslib"); | ||
const buildNodeModulesTree_1 = require("./buildNodeModulesTree"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.resolveNodeModulesPath = void 0; | ||
var _fslib = require("@yarnpkg/fslib"); | ||
var _buildNodeModulesTree = require("./buildNodeModulesTree"); | ||
const NODE_MODULES = 'node_modules'; | ||
/** | ||
* Resolved `/node_modules` path inside PnP project info. | ||
* | ||
* Dirs ending with '/node_modules/foo/node_modules' or '.../node_modules/foo/node_modules/@scope' | ||
* do not physically exist, but we must pretend they do exist if package `foo` has dependencies | ||
* and there is some package `@scope/bar` inside these dependencies. We need two things to emulate | ||
* these dirs existence: | ||
* | ||
* 1. List of entries in these dirs. We retrieve them by calling PnP API and getting dependencies | ||
* for the issuer `.../foo/` and store into `dirList` field | ||
* 2. And we need either fake stats or we can forward underlying fs to stat the issuer dir. | ||
* The issuer dir exists on fs. We store issuer dir into `statPath` field | ||
*/ | ||
/** | ||
* Resolves paths containing `/node_modules` inside PnP projects. If path is outside PnP | ||
@@ -15,53 +35,58 @@ * project it is not changed. | ||
*/ | ||
exports.resolveNodeModulesPath = (inputPath, nodeModulesTree) => { | ||
const result = { resolvedPath: inputPath }; | ||
const segments = inputPath.split(fslib_2.ppath.sep); | ||
const firstIdx = segments.indexOf(NODE_MODULES); | ||
if (firstIdx < 0) | ||
return result; | ||
let lastIdx = segments.lastIndexOf(NODE_MODULES); | ||
if (typeof segments[lastIdx + 1] !== 'undefined') | ||
// We have the situation .../node_modules/{something or @something} | ||
lastIdx++; | ||
if (segments[lastIdx][0] === '@' && typeof segments[lastIdx + 1] !== 'undefined') | ||
// We have the situation .../node_modules/@something/{foo} | ||
lastIdx++; | ||
// We lookup all the path substrings that end on [firstIdx..lastIdx] in the node_modules tree | ||
// and follow them if they are symlinks | ||
let locationCandidate = fslib_2.npath.toPortablePath(segments.slice(0, firstIdx).join(fslib_2.ppath.sep)); | ||
let node, lastNode, lastNodeLocation; | ||
let curIdx = firstIdx; | ||
let request = fslib_1.PortablePath.dot; | ||
while (curIdx <= lastIdx) { | ||
const curSegment = fslib_2.toFilename(segments[curIdx]); | ||
locationCandidate = fslib_2.ppath.join(locationCandidate, curSegment); | ||
node = nodeModulesTree.get(locationCandidate); | ||
if (node) { | ||
if (node.linkType === buildNodeModulesTree_1.LinkType.SOFT) | ||
locationCandidate = node.target; | ||
lastNode = node; | ||
request = fslib_1.PortablePath.dot; | ||
lastNodeLocation = locationCandidate; | ||
} | ||
else { | ||
request = fslib_2.ppath.join(request, curSegment); | ||
} | ||
curIdx++; | ||
const resolveNodeModulesPath = (inputPath, nodeModulesTree) => { | ||
const result = { | ||
resolvedPath: inputPath | ||
}; | ||
const segments = inputPath.split(_fslib.ppath.sep); | ||
const firstIdx = segments.indexOf(NODE_MODULES); | ||
if (firstIdx < 0) return result; | ||
let lastIdx = segments.lastIndexOf(NODE_MODULES); | ||
if (typeof segments[lastIdx + 1] !== 'undefined') // We have the situation .../node_modules/{something or @something} | ||
lastIdx++; | ||
if (segments[lastIdx][0] === '@' && typeof segments[lastIdx + 1] !== 'undefined') // We have the situation .../node_modules/@something/{foo} | ||
lastIdx++; // We lookup all the path substrings that end on [firstIdx..lastIdx] in the node_modules tree | ||
// and follow them if they are symlinks | ||
let locationCandidate = _fslib.npath.toPortablePath(segments.slice(0, firstIdx).join(_fslib.ppath.sep)); | ||
let node, lastNode, lastNodeLocation; | ||
let curIdx = firstIdx; | ||
let request = _fslib.PortablePath.dot; | ||
while (curIdx <= lastIdx) { | ||
const curSegment = (0, _fslib.toFilename)(segments[curIdx]); | ||
locationCandidate = _fslib.ppath.join(locationCandidate, curSegment); | ||
node = nodeModulesTree.get(locationCandidate); | ||
if (node) { | ||
if (node.linkType === _buildNodeModulesTree.LinkType.SOFT) locationCandidate = node.target; | ||
lastNode = node; | ||
request = _fslib.PortablePath.dot; | ||
lastNodeLocation = locationCandidate; | ||
} else { | ||
request = _fslib.ppath.join(request, curSegment); | ||
} | ||
request = fslib_2.ppath.join(request, ...segments.slice(lastIdx + 1).map(x => fslib_2.toFilename(x))); | ||
if (lastNode) { | ||
if (!lastNode.dirList || request !== fslib_1.PortablePath.dot) { | ||
result.resolvedPath = fslib_2.ppath.join(lastNodeLocation, request); | ||
result.isSymlink = lastNode && lastNode.linkType === buildNodeModulesTree_1.LinkType.SOFT && request === fslib_1.PortablePath.dot; | ||
} | ||
else if (request === fslib_1.PortablePath.dot) { | ||
result.dirList = lastNode.dirList; | ||
result.forwardedDirPath = fslib_2.npath.toPortablePath(segments.slice(0, firstIdx).join(fslib_2.ppath.sep)); | ||
// If node_modules is inside .zip archive, we use parent folder as a statPath instead | ||
if (result.forwardedDirPath.endsWith('.zip')) { | ||
result.forwardedDirPath = fslib_2.ppath.dirname(result.forwardedDirPath); | ||
} | ||
} | ||
curIdx++; | ||
} | ||
request = _fslib.ppath.join(request, ...segments.slice(lastIdx + 1).map(x => (0, _fslib.toFilename)(x))); | ||
if (lastNode) { | ||
if (!lastNode.dirList || request !== _fslib.PortablePath.dot) { | ||
result.resolvedPath = _fslib.ppath.join(lastNodeLocation, request); | ||
result.isSymlink = lastNode && lastNode.linkType === _buildNodeModulesTree.LinkType.SOFT && request === _fslib.PortablePath.dot; | ||
} else if (request === _fslib.PortablePath.dot) { | ||
result.dirList = lastNode.dirList; | ||
result.forwardedDirPath = _fslib.npath.toPortablePath(segments.slice(0, firstIdx).join(_fslib.ppath.sep)); // If node_modules is inside .zip archive, we use parent folder as a statPath instead | ||
if (result.forwardedDirPath.endsWith('.zip')) { | ||
result.forwardedDirPath = _fslib.ppath.dirname(result.forwardedDirPath); | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
}; | ||
exports.resolveNodeModulesPath = resolveNodeModulesPath; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const events_1 = require("events"); | ||
class WatchEventEmitter extends events_1.EventEmitter { | ||
constructor(dirWatchers, watchPath, watcherId) { | ||
super(); | ||
this.dirWatchers = dirWatchers; | ||
this.watchPath = watchPath; | ||
this.watcherId = watcherId; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.WatchManager = void 0; | ||
var _events = require("events"); | ||
class WatchEventEmitter extends _events.EventEmitter { | ||
constructor(dirWatchers, watchPath, watcherId) { | ||
super(); | ||
this.dirWatchers = void 0; | ||
this.watchPath = void 0; | ||
this.watcherId = void 0; | ||
this.dirWatchers = dirWatchers; | ||
this.watchPath = watchPath; | ||
this.watcherId = watcherId; | ||
} | ||
close() { | ||
const dirWatcher = this.dirWatchers.get(this.watchPath); | ||
dirWatcher.eventEmitters.delete(this.watcherId); | ||
if (dirWatcher.eventEmitters.size === 0) { | ||
this.dirWatchers.delete(this.watchPath); | ||
} | ||
close() { | ||
const dirWatcher = this.dirWatchers.get(this.watchPath); | ||
dirWatcher.eventEmitters.delete(this.watcherId); | ||
if (dirWatcher.eventEmitters.size === 0) { | ||
this.dirWatchers.delete(this.watchPath); | ||
} | ||
} | ||
} | ||
} | ||
class WatchManager extends events_1.EventEmitter { | ||
constructor() { | ||
super(...arguments); | ||
this.dirWatchers = new Map(); | ||
this.lastWatcherId = 0; | ||
class WatchManager extends _events.EventEmitter { | ||
constructor(...args) { | ||
super(...args); | ||
this.dirWatchers = new Map(); | ||
this.lastWatcherId = 0; | ||
} | ||
registerWatcher(watchPath, dirList, callback) { | ||
let dirWatcher = this.dirWatchers.get(watchPath); | ||
if (!dirWatcher) { | ||
dirWatcher = { | ||
eventEmitters: new Map(), | ||
dirEntries: dirList | ||
}; | ||
this.dirWatchers.set(watchPath, dirWatcher); | ||
} | ||
registerWatcher(watchPath, dirList, callback) { | ||
let dirWatcher = this.dirWatchers.get(watchPath); | ||
if (!dirWatcher) { | ||
dirWatcher = { eventEmitters: new Map(), dirEntries: dirList }; | ||
this.dirWatchers.set(watchPath, dirWatcher); | ||
const watcherId = this.lastWatcherId++; | ||
const watchEventEmitter = new WatchEventEmitter(this.dirWatchers, watchPath, watcherId); | ||
dirWatcher.eventEmitters.set(watcherId, watchEventEmitter); | ||
watchEventEmitter.on('rename', filename => callback('rename', filename)); | ||
return watchEventEmitter; | ||
} | ||
notifyWatchers(resolvePath) { | ||
for (const [watchPath, dirWatcher] of this.dirWatchers) { | ||
const newDirEntries = resolvePath(watchPath).dirList || new Set(); // Difference between new and old directory contents | ||
const dirEntryDiff = new Set(); | ||
for (const entry of newDirEntries) { | ||
if (!dirWatcher.dirEntries.has(entry)) { | ||
dirEntryDiff.add(entry); | ||
} | ||
const watcherId = this.lastWatcherId++; | ||
const watchEventEmitter = new WatchEventEmitter(this.dirWatchers, watchPath, watcherId); | ||
dirWatcher.eventEmitters.set(watcherId, watchEventEmitter); | ||
watchEventEmitter.on('rename', (filename) => callback('rename', filename)); | ||
return watchEventEmitter; | ||
} | ||
notifyWatchers(resolvePath) { | ||
for (const [watchPath, dirWatcher] of this.dirWatchers) { | ||
const newDirEntries = resolvePath(watchPath).dirList || new Set(); | ||
// Difference between new and old directory contents | ||
const dirEntryDiff = new Set(); | ||
for (const entry of newDirEntries) { | ||
if (!dirWatcher.dirEntries.has(entry)) { | ||
dirEntryDiff.add(entry); | ||
} | ||
} | ||
for (const entry of dirWatcher.dirEntries) { | ||
if (!newDirEntries.has(entry)) { | ||
dirEntryDiff.add(entry); | ||
} | ||
} | ||
for (const entry of dirEntryDiff) { | ||
for (const watchEventEmitter of dirWatcher.eventEmitters.values()) { | ||
watchEventEmitter.emit('rename', entry); | ||
} | ||
} | ||
dirWatcher.dirEntries = newDirEntries; | ||
} | ||
for (const entry of dirWatcher.dirEntries) { | ||
if (!newDirEntries.has(entry)) { | ||
dirEntryDiff.add(entry); | ||
} | ||
} | ||
for (const entry of dirEntryDiff) { | ||
for (const watchEventEmitter of dirWatcher.eventEmitters.values()) { | ||
watchEventEmitter.emit('rename', entry); | ||
} | ||
} | ||
dirWatcher.dirEntries = newDirEntries; | ||
} | ||
} | ||
} | ||
exports.WatchManager = WatchManager; | ||
exports.WatchManager = WatchManager; |
{ | ||
"name": "@yarnpkg/pnpify", | ||
"version": "2.0.0-rc.12", | ||
"version": "2.0.0-rc.13", | ||
"main": "./lib/index.js", | ||
@@ -9,3 +9,3 @@ "bin": "./lib/cli.js", | ||
"dependencies": { | ||
"@yarnpkg/fslib": "2.0.0-rc.12", | ||
"@yarnpkg/fslib": "^2.0.0-rc.12", | ||
"comment-json": "^2.2.0", | ||
@@ -17,7 +17,6 @@ "cross-spawn": "^6.0.5" | ||
"@types/cross-spawn": "6.0.0", | ||
"@types/node": "^12.12.8", | ||
"@yarnpkg/monorepo": "0.0.0", | ||
"@yarnpkg/pnp": "2.0.0-rc.12", | ||
"@yarnpkg/pnp": "^2.0.0-rc.12", | ||
"eslint": "^5.16.0", | ||
"typescript": "next" | ||
"typescript": "^3.7.4" | ||
}, | ||
@@ -24,0 +23,0 @@ "peerDependencies": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
6
0
55566
12
1366
+ Added@types/emscripten@1.40.0(transitive)
+ Added@yarnpkg/fslib@2.10.4(transitive)
+ Added@yarnpkg/libzip@2.3.0(transitive)
+ Addedtslib@1.14.1(transitive)
- Removed@yarnpkg/fslib@2.0.0-rc.12(transitive)
- Removed@yarnpkg/libzip@2.0.0-rc.5(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedrimraf@2.7.1(transitive)
- Removedtmp@0.1.0(transitive)
- Removedwrappy@1.0.2(transitive)
Updated@yarnpkg/fslib@^2.0.0-rc.12