Skip to content

Commit

Permalink
feat: add force-link-latest mode (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
XadillaX authored and dead-horse committed Oct 10, 2019
1 parent 576145e commit 4c918ac
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ test/fixtures/css-loader-example2/webpack*.json
test/fixtures/high-speed-store/tmp
.nyc_output
.npminstall_tarball

.editorconfig
3 changes: 3 additions & 0 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const argv = parseArgs(orignalArgv, {
// please don't use on frontend project
'disable-dedupe',
'save-dependencies-tree',
'force-link-latest',
],
default: {
optional: true,
Expand Down Expand Up @@ -124,6 +125,7 @@ Options:
--prune: prune unnecessary files from ./node_modules, such as markdown, typescript source files, and so on.
--high-speed-store: specify high speed store script to cache tgz files, and so on. Should export '* getStream(url)' function.
--dependencies-tree: install with dependencies tree to restore the last install.
--force-link-latest: force link latest version package to module root path.
`
);
process.exit(0);
Expand Down Expand Up @@ -241,6 +243,7 @@ debug('argv: %j, env: %j', argv, env);
config.ignoreScripts = argv['ignore-scripts'] || getIgnoreScripts();
config.ignoreOptionalDependencies = !argv.optional;
config.detail = argv.detail;
config.forceLinkLatest = !!argv['force-link-latest'];
config.trace = argv.trace;
config.engineStrict = argv['engine-strict'];
config.registryOnly = argv['registry-only'];
Expand Down
2 changes: 1 addition & 1 deletion lib/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function get(url, options, globalOptions) {
const registryUrl = cnpmConfig.get('registry');
const registryUri = registryUrl && registryUrl.replace(urlParser.parse(registryUrl).protocol, '') || '';
const authed = registryUri && url.indexOf(registryUri) !== -1;
const hasUserSettings = typeof cnpmConfig.get(registryUri + ':username') === 'string' && typeof cnpmConfig.get(registryUri + ':_password') === 'string';
const hasUserSettings = typeof cnpmConfig.get(registryUri + ':username') === 'string' && typeof cnpmConfig.get(registryUri + ':_password') === 'string';
if (hasUserSettings && (authed || cnpmConfig.get(registryUri + ':always-auth') || cnpmConfig.get('always-auth'))) {
const authToken = (`${cnpmConfig.get(registryUri + ':username')}:${Buffer.from(cnpmConfig.get(registryUri + ':_password'), 'base64').toString()}`);
options.headers.Authorization = `Basic ${Buffer.from(authToken).toString('base64')}`;
Expand Down
37 changes: 36 additions & 1 deletion lib/local_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,50 @@ async function linkAllLatestVersion(rootPkgsMap, options) {
options.spinner && options.spinner.succeed(`Linked ${options.latestVersions.size} latest versions`);
}

async function shouldOverrideLink(pkg, linkDir, options) {
const { forceLinkLatest, rootPkgDependencies } = options;
if (!forceLinkLatest) return false;

let rootPkgs;
if (options.client) {
rootPkgs = rootPkgDependencies.client;
} else if (options.production) {
rootPkgs = rootPkgDependencies.prod;
} else {
rootPkgs = rootPkgDependencies.all;
}

// if root packages include this package, then do not link
for (const rootPkg of rootPkgs) {
if (pkg.name === rootPkg.name) {
return false;
}
}

const pkgJSONPath = path.join(linkDir, 'package.json');
try {
const pkgJSON = await utils.readJSON(pkgJSONPath);
if (semver.gt(pkg.version, pkgJSON.version)) {
// pkg is the newer version
return true;
}

return false;
} catch (e) {
return true;
}
}

async function linkLatestVersion(pkg, storeDir, options) {
const linkDir = path.join(storeDir, pkg.name);
if (await fs.exists(linkDir)) {
if (await fs.exists(linkDir) && !(await shouldOverrideLink(pkg, linkDir, options))) {
options.progresses.finishedLinkTasks++;
return debug('[%s/%s] %s already exists',
options.progresses.finishedLinkTasks,
options.progresses.linkTasks,
linkDir);
}
await utils.rimraf(linkDir); // make sure to delete linkDir
await utils.mkdirp(path.dirname(linkDir));
const realDir = utils.getPackageStorePath(storeDir, pkg);
const relative = await utils.forceSymlink(realDir, linkDir);
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/optional-dep-postinstall/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"httpsync": "*"
},
"dependencies": {
"pinyin": "^2.2.1"
"shameimaru": "^1.0.2"
}
}
59 changes: 59 additions & 0 deletions test/linkLatestVersion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,73 @@ describe('test/linkLatestVersion.test.js', () => {
afterEach(cleanup);

it('should install latest version to node_modules', async () => {
const names = [ 'debug', 'ms', 'iconv-lite', 'utility' ];
await npminstall({
root,
});
const pkg = await helper.readJSON(path.join(root, 'node_modules', 'urllib', 'package.json'));
assert.equal(pkg.version, '2.7.1');

const versions = {};
for (const name of names) {
const pkg = await helper.readJSON(path.join(root, 'node_modules', name, 'package.json'));
versions[pkg.name] = pkg.version;
}

const pkg2 = await helper.readJSON(path.join(root,
'node_modules', 'iconv-lite', 'package.json'));
assert.equal(pkg2.name, 'iconv-lite');

await npminstall({
root,
pkgs: [{ name: 'toshihiko', version: '1.0.0-alpha.10' }],
});

for (const name of names) {
const pkg = await helper.readJSON(path.join(root, 'node_modules', name, 'package.json'));
assert.strictEqual(pkg.version, versions[pkg.name]);
}
});

it('should force link latest version to node_modules', async () => {
const names = [ 'debug', 'ms', 'iconv-lite', 'utility' ];
await npminstall({
root,
forceLinkLatest: true,
});
const pkg = await helper.readJSON(path.join(root, 'node_modules', 'urllib', 'package.json'));
assert.equal(pkg.version, '2.7.1');

const versions = {};
for (const name of names) {
const pkg = await helper.readJSON(path.join(root, 'node_modules', name, 'package.json'));
versions[pkg.name] = pkg.version;
}

const pkg2 = await helper.readJSON(path.join(root,
'node_modules', 'iconv-lite', 'package.json'));
assert.equal(pkg2.name, 'iconv-lite');

await npminstall({
root,
pkgs: [{ name: 'toshihiko', version: '1.0.0-alpha.10' }],
forceLinkLatest: true,
});

for (const name of names) {
const pkg = await helper.readJSON(path.join(root, 'node_modules', name, 'package.json'));
switch (name) {
case 'debug':
case 'iconv-lite':
assert.strictEqual(pkg.version, versions[pkg.name]);
break;

case 'ms':
case 'utility':
default:
assert(pkg.version !== versions[pkg.name]);
break;
}
}
});
});
2 changes: 1 addition & 1 deletion test/postInstallError.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('test/postInstallError.test.js', () => {
await coffee.fork(helper.npminstall, [ '--production' ], { cwd })
.debug()
.expect('code', 0)
.expect('stderr', /httpsync@\* optional error: Error: Run ".*?build\.sh" error/)
.expect('stderr', /httpsync@\* optional error: .*Error: Run ".*?build\.sh" error/)
.expect('stderr', /scripts.install httpsync@\* run "sh build.sh"/)
.expect('stdout', /All packages installed/)
.end();
Expand Down

0 comments on commit 4c918ac

Please sign in to comment.