diff --git a/.changeset/kind-walls-cover.md b/.changeset/kind-walls-cover.md new file mode 100644 index 0000000000..26af492c4f --- /dev/null +++ b/.changeset/kind-walls-cover.md @@ -0,0 +1,5 @@ +--- +"@pnpm/resolve-dependencies": major +--- + +Don't skip a dependency that is named the same way as the package, if it has a different version. diff --git a/.changeset/nice-dragons-end.md b/.changeset/nice-dragons-end.md new file mode 100644 index 0000000000..2209eb0011 --- /dev/null +++ b/.changeset/nice-dragons-end.md @@ -0,0 +1,7 @@ +--- +"@pnpm/core": patch +"@pnpm/headless": patch +"pnpm": patch +--- + +A package should be able to be a dependency of itself. diff --git a/packages/core/src/install/link.ts b/packages/core/src/install/link.ts index edac190782..a1a094ec34 100644 --- a/packages/core/src/install/link.ts +++ b/packages/core/src/install/link.ts @@ -423,6 +423,14 @@ async function linkAllPkgs ( }) } depNode.isBuilt = isBuilt + + const selfDep = depNode.children[depNode.name] + if (selfDep) { + const pkg = opts.depGraph[selfDep] + if (!pkg || !pkg.installable && pkg.optional) return + const targetModulesDir = path.join(depNode.modules, depNode.name, 'node_modules') + await limitLinking(async () => symlinkDependency(pkg.dir, targetModulesDir, depNode.name)) + } }) ) } @@ -449,21 +457,14 @@ async function linkAllModules ( }, {}) await Promise.all( - Object.keys(childrenToLink) - .map(async (childAlias) => { - if (childrenToLink[childAlias].startsWith('link:')) { - await limitLinking(async () => symlinkDependency(path.resolve(opts.lockfileDir, childrenToLink[childAlias].substr(5)), modules, childAlias)) - return - } - const pkg = depGraph[childrenToLink[childAlias]] - if (!pkg || !pkg.installable && pkg.optional) return - if (childAlias === name) { - logger.warn({ - message: `Cannot link dependency with name ${childAlias} to ${modules}. Dependency's name should differ from the parent's name.`, - prefix: opts.lockfileDir, - }) + Object.entries(childrenToLink) + .map(async ([childAlias, childDepPath]) => { + if (childDepPath.startsWith('link:')) { + await limitLinking(async () => symlinkDependency(path.resolve(opts.lockfileDir, childDepPath.substr(5)), modules, childAlias)) return } + const pkg = depGraph[childDepPath] + if (!pkg || !pkg.installable && pkg.optional || childAlias === name) return await limitLinking(async () => symlinkDependency(pkg.dir, modules, childAlias)) }) ) diff --git a/packages/core/test/install/misc.ts b/packages/core/test/install/misc.ts index aa44651b3c..31033c8591 100644 --- a/packages/core/test/install/misc.ts +++ b/packages/core/test/install/misc.ts @@ -1320,3 +1320,23 @@ test('installing a package with broken bin', async () => { await project.has('broken-bin') }) + +test('a package should be able to be a dependency of itself', async () => { + const project = prepareEmpty() + + const manifest = await addDependenciesToPackage({}, ['@paul-soporan/test-package-self-require-trap@2.0.0'], await testDefaults()) + + const subpkg = '.pnpm/@paul-soporan+test-package-self-require-trap@2.0.0/node_modules/@paul-soporan/test-package-self-require-trap/node_modules/@paul-soporan/test-package-self-require-trap/package.json' + { + const pkg = project.requireModule(subpkg) + expect(pkg.version).toBe('1.0.0') + } + + await rimraf('node_modules') + await install(manifest, await testDefaults({ frozenLockfile: true })) + + { + const pkg = project.requireModule(subpkg) + expect(pkg.version).toBe('1.0.0') + } +}) diff --git a/packages/headless/src/index.ts b/packages/headless/src/index.ts index 9addf5ad25..949d0830a9 100644 --- a/packages/headless/src/index.ts +++ b/packages/headless/src/index.ts @@ -690,6 +690,14 @@ async function linkAllPkgs ( }) } depNode.isBuilt = isBuilt + + const selfDep = depNode.children[depNode.name] + if (selfDep) { + const pkg = opts.depGraph[selfDep] + if (!pkg) return + const targetModulesDir = path.join(depNode.modules, depNode.name, 'node_modules') + await limitLinking(async () => symlinkDependency(pkg.dir, targetModulesDir, depNode.name)) + } }) ) } @@ -763,17 +771,13 @@ async function linkAllModules ( }, {}) await Promise.all( - Object.keys(childrenToLink) - .map(async (alias) => { + Object.entries(childrenToLink) + .map(async ([alias, pkgDir]) => { // if (!pkg.installable && pkg.optional) return if (alias === depNode.name) { - logger.warn({ - message: `Cannot link dependency with name ${alias} to ${depNode.modules}. Dependency's name should differ from the parent's name.`, - prefix: opts.lockfileDir, - }) return } - await limitLinking(async () => symlinkDependency(childrenToLink[alias], depNode.modules, alias)) + await limitLinking(async () => symlinkDependency(pkgDir, depNode.modules, alias)) }) ) }) diff --git a/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts b/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts index bdcf376300..fb6e3df45b 100644 --- a/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts +++ b/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts @@ -11,7 +11,6 @@ export interface WantedDependency { export default function getNonDevWantedDependencies (pkg: DependencyManifest) { const bd = pkg.bundleDependencies ?? pkg.bundleDependencies const bundledDeps = new Set(Array.isArray(bd) ? bd : []) - bundledDeps.add(pkg.name) const filterDeps = getNotBundledDeps.bind(null, bundledDeps) return getWantedDependenciesFromGivenSet( filterDeps({ ...pkg.optionalDependencies, ...pkg.dependencies }), diff --git a/packages/resolve-dependencies/src/resolveDependencies.ts b/packages/resolve-dependencies/src/resolveDependencies.ts index 735f5c05c6..561b762761 100644 --- a/packages/resolve-dependencies/src/resolveDependencies.ts +++ b/packages/resolve-dependencies/src/resolveDependencies.ts @@ -713,7 +713,7 @@ async function resolveDependency ( options.parentPkg.nodeId, options.parentPkg.depPath, depPath - ) + ) || depPath === options.parentPkg.depPath ) { return null }