Skip to content

Commit

Permalink
feat: support dependencies tree (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
dead-horse committed Dec 24, 2018
1 parent a31b19c commit d029138
Show file tree
Hide file tree
Showing 8 changed files with 2,313 additions and 9 deletions.
16 changes: 16 additions & 0 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const argv = parseArgs(orignalArgv, {
'proxy',
// --high-speed-store=filepath
'high-speed-store',
'dependencies-tree',
],
boolean: [
'version',
Expand Down Expand Up @@ -59,6 +60,7 @@ const argv = parseArgs(orignalArgv, {
// disable dedupe mode https://docs.npmjs.com/cli/dedupe, back to npm@2 mode
// please don't use on frontend project
'disable-dedupe',
'save-dependencies-tree',
],
default: {
optional: true,
Expand Down Expand Up @@ -123,6 +125,7 @@ Options:
--fix-bug-versions: auto fix bug version of package.
--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.
`
);
process.exit(0);
Expand Down Expand Up @@ -268,6 +271,19 @@ co(function* () {
};
}

const dependenciesTree = argv['dependencies-tree'];
if (dependenciesTree) {
try {
const content = fs.readFileSync(dependenciesTree);
config.dependenciesTree = JSON.parse(content);
} catch (err) {
console.warn(chalk.yellow('npminstall WARN load dependencies tree %s error: %s'), dependenciesTree, err.message);
}
}
if (argv['save-dependencies-tree']) {
config.saveDependenciesTree = true;
}

if (argv['high-speed-store']) {
config.highSpeedStore = require(argv['high-speed-store']);
}
Expand Down
16 changes: 8 additions & 8 deletions lib/download/npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ module.exports = function* (pkg, options) {
module.exports.resolve = resolve;

function* resolve(pkg, options) {
const dependenciesTree = options.cache.dependenciesTree;
// check cache first
if (dependenciesTree[pkg.raw]) {
debug('resolve hit dependencies cache: %s', pkg.raw);
return dependenciesTree[pkg.raw];
}

const packageMetaKey = `npm:resolve:package:${pkg.name}`;
let packageMeta = options.cache[packageMetaKey];
if (!packageMeta) {
Expand Down Expand Up @@ -58,13 +65,6 @@ function* resolve(pkg, options) {
if (err) throw err;
}

// check cache first
const cacheKey = `npm:resolve:version:${pkg.raw}`;
if (options.cache[cacheKey]) {
debug('resolve hit cache: %s', cacheKey);
return options.cache[cacheKey];
}

let spec = pkg.spec;
if (spec === '*') {
spec = 'latest';
Expand Down Expand Up @@ -105,7 +105,7 @@ function* resolve(pkg, options) {
pkg.name, pkg.rawSpec, pkg.spec, realPkg.version, distTags);

// cache resolve result
options.cache[cacheKey] = realPkg;
dependenciesTree[pkg.raw] = realPkg;
return realPkg;
}

Expand Down
10 changes: 9 additions & 1 deletion lib/format_install_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ module.exports = function formatInstallOptions(options) {
options.latestVersions = new Map();
// store latest packages
options.latestPackages = new Map();
options.cache = {};
options.cache = {
dependenciesTree: {},
};

// use depsTree
if (options.dependenciesTree) {
Object.assign(options.cache.dependenciesTree, options.dependenciesTree);
}

assert(options.root && typeof options.root === 'string', 'options.root required and must be string');
options.registry = options.registry || 'https://registry.npmjs.com';
if (!options.targetDir) {
Expand Down
2 changes: 2 additions & 0 deletions lib/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ function* _install(parentDir, pkg, ancestors, options) {
if (c) {
const realPkg = c.package;
if (semver.satisfies(realPkg.version, p.spec)) {
// add to cache.dependenciesTree, keep resolve version data complete
options.cache.dependenciesTree[p.raw] = realPkg;
const realPkgDir = c.dir;
yield linkModule(pkg, parentDir, realPkg, realPkgDir, options);
return {
Expand Down
42 changes: 42 additions & 0 deletions lib/local_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ function* _install(options) {
// record all installed packages' versions
recordPackageVersions(options);

// record dependencies tree resolved from npm
recordDependenciesTree(options);

// print install finished
finishInstall(options);
}
Expand Down Expand Up @@ -481,6 +484,17 @@ function recordPackageVersions(options) {
fs.writeFileSync(packageVersionsFile, JSON.stringify(versions, null, 2));
}

function recordDependenciesTree(options) {
if (!options.saveDependenciesTree) return;

const tree = {};
for (const key in options.cache.dependenciesTree) {
tree[key] = omitPackage(options.cache.dependenciesTree[key]);
}
const installCacheFile = path.join(options.storeDir, '.dependencies_tree.json');
fs.writeFileSync(installCacheFile, JSON.stringify(tree, null, 2));
}

function finishInstall(options) {
const totalUse = Date.now() - options.start;
const downloadUse = options.downloadFinished - options.start;
Expand All @@ -506,3 +520,31 @@ function finishInstall(options) {
options.console.info.apply(console, logArguments);
}
}

function omitPackage(pkg) {
const keys = [
'name',
'version',
'dependencies',
'devDependencies',
'optionalDependencies',
'clientDependencies',
'buildDependencies',
'isomorphicDependencies',
'peerDependencies',
'publish_time',
'deprecate',
'license',
'os',
'engines',
'dist',
'scripts',
'_id',
'__fixDependencies',
];
const res = {};
for (const key of keys) {
if (pkg[key]) res[key] = pkg[key];
}
return res;
}
42 changes: 42 additions & 0 deletions test/dependencies-tree.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const path = require('path');
const rimraf = require('rimraf');
const coffee = require('coffee');
const fs = require('fs');
const assert = require('assert');

const npminstall = path.join(__dirname, '..', 'bin', 'install.js');

describe('test/dependencies-tree.test.js', () => {

const cwd = path.join(__dirname, 'fixtures', 'dependencies-tree');

function cleanup() {
rimraf.sync(path.join(cwd, 'node_modules'));
}

beforeEach(cleanup);
afterEach(cleanup);

it('should install save dependencies tree', function* () {
yield coffee.fork(npminstall, [ '-c', '--save-dependencies-tree' ], { cwd })
.debug()
.expect('code', 0)
.end();
const file = path.join(cwd, 'node_modules/.dependencies_tree.json');
const tree = JSON.parse(fs.readFileSync(file, 'utf8'));
assert(tree['koa@^2.0.0']);
assert(tree['mocha@3']);
});

it('should install from dependencies tree work', function* () {
yield coffee.fork(npminstall, [ '-c', '--dependencies-tree=.dependencies_tree.json' ], { cwd })
.debug()
.expect('code', 0)
.expect('stderr', /json 0\(0B\)/)
.end();
const file = path.join(cwd, 'node_modules/.dependencies_tree.json');
assert(!fs.existsSync(file));
});
});

0 comments on commit d029138

Please sign in to comment.