diff --git a/bin/install.js b/bin/install.js index 2a8f5df9..22274876 100755 --- a/bin/install.js +++ b/bin/install.js @@ -65,9 +65,8 @@ Object.assign(argv, parseArgs(originalArgv, { 'cache-strict', 'fix-bug-versions', 'prune', - // disable dedupe mode https://docs.npmjs.com/cli/dedupe, back to npm@2 mode - // please don't use on frontend project - 'disable-dedupe', + // don't link latest version to /node_modules/.store/node_modules + 'disable-fallback-store', 'save-dependencies-tree', 'force-link-latest', 'workspaces', @@ -288,8 +287,7 @@ debug('argv: %j, env: %j', argv, env); flatten, proxy, prune, - // FIXME: ignored by following process, see lib/local_install.js - disableDedupe: argv['disable-dedupe'], + disableFallbackStore: argv['disable-fallback-store'], workspacesMap, // don't enable workspace on global install enableWorkspace, @@ -400,28 +398,12 @@ debug('argv: %j, env: %j', argv, env); if (pkg.config.npminstall.prune === true) { config.prune = true; } - if (pkg.config.npminstall.disableDedupe === true) { - config.disableDedupe = true; - } - // env config - // { - // "config": { - // "npminstall": { - // "env:production": { - // "disableDedupe": true - // } - // } - // } - // } // production if (config.production && pkg.config.npminstall['env:production']) { const envConfig = pkg.config.npminstall['env:production']; if (envConfig.prune === true) { config.prune = true; } - if (envConfig.disableDedupe === true) { - config.disableDedupe = true; - } } // development if (!config.production && pkg.config.npminstall['env:development']) { @@ -429,9 +411,6 @@ debug('argv: %j, env: %j', argv, env); if (envConfig.prune === true) { config.prune = true; } - if (envConfig.disableDedupe === true) { - config.disableDedupe = true; - } } } } diff --git a/lib/local_install.js b/lib/local_install.js index e048468f..50f6c1b6 100644 --- a/lib/local_install.js +++ b/lib/local_install.js @@ -45,14 +45,11 @@ const Context = require('./context'); * - {Array} [forbiddenLicenses] - forbit install packages which used these licenses * - {Boolean} [trace] - show memory and cpu usages traces of installation * - {Boolean} [flatten] - flatten dependencies by matching ancestors' dependencies - * - {Boolean} [disableDedupe] - disable dedupe mode, will back to npm@2 node_modules tree + * - {Boolean} [disableFallbackStore] - disable fallback store, default is `false` * @param {Object} context - install context */ module.exports = async (options, context = new Context()) => { options = formatInstallOptions(options); - // set disableDedupe to true, don't link latest version to root node_modules - // FIXME: should follow user config and set default value before this function. - options.disableDedupe = true; options.spinner && options.spinner.start(); let traceTimer; let showTrace; @@ -156,11 +153,9 @@ async function _install(options, context) { options.downloadFinished = Date.now(); options.spinner && options.spinner.succeed(`Installed ${pkgs.length} packages on ${options.root}`); - if (!options.disableDedupe) { - // dedupe mode https://docs.npmjs.com/cli/dedupe - // link every packages' latest version to target directory - // won't override exists target directory - await linkAllLatestVersion(rootPkgsMap, options); + if (!options.disableFallbackStore) { + // link every packages' latest version to /node_modules/.store/node_modules, fallback for peerDeps + await linkAllLatestVersionToFallbackDir(rootPkgsMap, options); } // https://pnpm.io/zh/next/npmrc#public-hoist-pattern await linkPublicHoistPackagesToRoot(rootPkgsMap, options); @@ -313,21 +308,22 @@ async function validatePeerDependencies(params, options) { } } -async function linkAllLatestVersion(rootPkgsMap, options) { +async function linkAllLatestVersionToFallbackDir(rootPkgsMap, options) { if (options.latestVersions.size > 0) { const mapper = async ([ name, version ]) => { if (!rootPkgsMap.has(name)) { options.progresses.linkTasks++; - // link latest package to `storeDir/node_modules` + // link latest package to `/node_modules/.store/node_modules` await linkLatestVersion({ name, version, - }, options.storeDir, options); + }, options.storeDir, options, true); } }; await pMap(options.latestVersions, mapper, 20); + const fallbackStoreDir = path.join(options.storeDir, '.store/node_modules'); + options.spinner?.succeed(`Linked ${options.latestVersions.size} latest versions fallback to ${fallbackStoreDir}`); } - options.spinner && options.spinner.succeed(`Linked ${options.latestVersions.size} latest versions`); } async function linkPublicHoistPackagesToRoot(rootPkgsMap, options) { @@ -335,7 +331,7 @@ async function linkPublicHoistPackagesToRoot(rootPkgsMap, options) { const mapper = async ([ name, version ]) => { if (!rootPkgsMap.has(name)) { options.progresses.linkTasks++; - // link latest package to `storeDir/node_modules` + // link latest package to `/node_modules` await linkLatestVersion({ name, version, @@ -343,8 +339,8 @@ async function linkPublicHoistPackagesToRoot(rootPkgsMap, options) { } }; await pMap(options.publicHoistLatestVersions, mapper, 20); + options.spinner?.succeed(`Linked ${options.publicHoistLatestVersions.size} public hoist packages to ${options.storeDir}`); } - options.spinner && options.spinner.succeed(`Linked ${options.publicHoistLatestVersions.size} public hoist packages`); } async function shouldOverrideLink(pkg, linkDir, options) { @@ -381,8 +377,9 @@ async function shouldOverrideLink(pkg, linkDir, options) { } } -async function linkLatestVersion(pkg, storeDir, options) { - const linkDir = path.join(storeDir, pkg.name); +async function linkLatestVersion(pkg, storeDir, options, isFallback = false) { + // storeDir: /node_modules + const linkDir = isFallback ? path.join(storeDir, '.store/node_modules', pkg.name) : path.join(storeDir, pkg.name); if (await utils.exists(linkDir) && !(await shouldOverrideLink(pkg, linkDir, options))) { options.progresses.finishedLinkTasks++; return debug('[%s/%s] %s already exists', diff --git a/test/bundleDependencies.test.js b/test/bundleDependencies.test.js index 32993551..7b7d3268 100644 --- a/test/bundleDependencies.test.js +++ b/test/bundleDependencies.test.js @@ -1,9 +1,9 @@ const assert = require('assert'); const path = require('path'); const fs = require('fs/promises'); +const assertFile = require('assert-file'); const npminstall = require('./npminstall'); const helper = require('./helper'); -const { exists } = require('../lib/utils'); describe('test/bundleDependencies.test.js', () => { const [ tmp, cleanup ] = helper.tmp(); @@ -34,11 +34,11 @@ describe('test/bundleDependencies.test.js', () => { { name: 'nyc', version: '6.4.2' }, ], }); - let e = await exists(path.join(tmp, 'node_modules/nyc')); - assert(e); - e = await exists(path.join(tmp, 'node_modules/.store/nyc@6.4.2/node_modules/nyc')); - assert(e); - e = await exists(path.join(tmp, 'node_modules/.store/nyc@6.4.2/node_modules/foreground-child')); - assert(e); + assertFile(path.join(tmp, 'node_modules/nyc')); + assertFile.fail(path.join(tmp, 'node_modules/.store/node_modules/nyc')); + assertFile(path.join(tmp, 'node_modules/.store/nyc@6.4.2/node_modules/nyc')); + assertFile(path.join(tmp, 'node_modules/.store/nyc@6.4.2/node_modules/foreground-child')); + assertFile(path.join(tmp, 'node_modules/.store/node_modules')); + assertFile(path.join(tmp, 'node_modules/.store/node_modules/glob')); }); }); diff --git a/test/fixtures/disable-dedupe-config/package.json b/test/fixtures/disable-dedupe-config/package.json deleted file mode 100644 index 36236169..00000000 --- a/test/fixtures/disable-dedupe-config/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "disable-dedupe-demo", - "dependencies": { - "koa": "2" - }, - "config": { - "npminstall": { - "disableDedupe": true - } - } -} diff --git a/test/fixtures/disable-dedupe-development-config/package.json b/test/fixtures/disable-dedupe-development-config/package.json deleted file mode 100644 index cf1ec822..00000000 --- a/test/fixtures/disable-dedupe-development-config/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "disable-dedupe-demo", - "dependencies": { - "koa": "2" - }, - "config": { - "npminstall": { - "env:development": { - "disableDedupe": true - } - } - } -} diff --git a/test/fixtures/disable-dedupe-production-config/package.json b/test/fixtures/disable-dedupe-production-config/package.json deleted file mode 100644 index 0c04aa35..00000000 --- a/test/fixtures/disable-dedupe-production-config/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "disable-dedupe-demo", - "dependencies": { - "koa": "2" - }, - "config": { - "npminstall": { - "env:production": { - "disableDedupe": true - } - } - } -} diff --git a/test/fixtures/disable-dedupe/package.json b/test/fixtures/disable-dedupe/package.json deleted file mode 100644 index 21b20948..00000000 --- a/test/fixtures/disable-dedupe/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "disable-dedupe-demo", - "dependencies": { - "koa": "2" - } -} diff --git a/test/fixtures/install-disable-fallback-store/package.json b/test/fixtures/install-disable-fallback-store/package.json new file mode 100644 index 00000000..150db454 --- /dev/null +++ b/test/fixtures/install-disable-fallback-store/package.json @@ -0,0 +1,6 @@ +{ + "name": "install-disable-fallback-store", + "dependencies": { + "urllib": "3" + } +} diff --git a/test/install-disable-fallback-store.test.js b/test/install-disable-fallback-store.test.js new file mode 100644 index 00000000..86e3f827 --- /dev/null +++ b/test/install-disable-fallback-store.test.js @@ -0,0 +1,33 @@ +const path = require('path'); +const coffee = require('coffee'); +const assertFile = require('assert-file'); +const helper = require('./helper'); + +describe('test/install-disable-fallback-store.test.js', () => { + const cwd = helper.fixtures('install-disable-fallback-store'); + const cleanup = helper.cleanup(cwd); + + beforeEach(cleanup); + + it('should install --disable-fallback-store', async () => { + await coffee.fork(helper.npminstall, [ '--disable-fallback-store' ], { cwd }) + .debug() + .expect('code', 0) + .end(); + assertFile(path.join(cwd, 'node_modules/urllib/package.json')); + assertFile.fail(path.join(cwd, 'node_modules/.store/node_modules')); + assertFile.fail(path.join(cwd, 'node_modules/.store/node_modules/undici')); + assertFile.fail(path.join(cwd, 'node_modules/.store/node_modules/urllib')); + }); + + it('should install --disable-fallback-store=false', async () => { + await coffee.fork(helper.npminstall, [], { cwd }) + .debug() + .expect('code', 0) + .end(); + assertFile(path.join(cwd, 'node_modules/urllib/package.json')); + assertFile(path.join(cwd, 'node_modules/.store/node_modules')); + assertFile(path.join(cwd, 'node_modules/.store/node_modules/undici')); + assertFile.fail(path.join(cwd, 'node_modules/.store/node_modules/urllib')); + }); +});