Skip to content

Commit

Permalink
feat: store all latest version packages to .store/node_modules (#443)
Browse files Browse the repository at this point in the history
hard to fixed all hoist dependencies by default.
can disable fallback store by --disable-fallback-store

closes #442
  • Loading branch information
fengmk2 committed Jan 14, 2023
1 parent d70c0a7 commit 71fd1e6
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 91 deletions.
27 changes: 3 additions & 24 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <root>/node_modules/.store/node_modules
'disable-fallback-store',
'save-dependencies-tree',
'force-link-latest',
'workspaces',
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -400,38 +398,19 @@ 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']) {
const envConfig = pkg.config.npminstall['env:development'];
if (envConfig.prune === true) {
config.prune = true;
}
if (envConfig.disableDedupe === true) {
config.disableDedupe = true;
}
}
}
}
Expand Down
31 changes: 14 additions & 17 deletions lib/local_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <root>/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);
Expand Down Expand Up @@ -313,38 +308,39 @@ 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 `<root>/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) {
if (options.publicHoistLatestVersions.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 `<root>/node_modules`
await linkLatestVersion({
name,
version,
}, options.storeDir, 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) {
Expand Down Expand Up @@ -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: <root>/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',
Expand Down
14 changes: 7 additions & 7 deletions test/bundleDependencies.test.js
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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'));
});
});
11 changes: 0 additions & 11 deletions test/fixtures/disable-dedupe-config/package.json

This file was deleted.

13 changes: 0 additions & 13 deletions test/fixtures/disable-dedupe-development-config/package.json

This file was deleted.

13 changes: 0 additions & 13 deletions test/fixtures/disable-dedupe-production-config/package.json

This file was deleted.

6 changes: 0 additions & 6 deletions test/fixtures/disable-dedupe/package.json

This file was deleted.

6 changes: 6 additions & 0 deletions test/fixtures/install-disable-fallback-store/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "install-disable-fallback-store",
"dependencies": {
"urllib": "3"
}
}
33 changes: 33 additions & 0 deletions test/install-disable-fallback-store.test.js
Original file line number Diff line number Diff line change
@@ -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'));
});
});

0 comments on commit 71fd1e6

Please sign in to comment.