UNPKG

6.9 kBJavaScriptView Raw
1#!/usr/bin/env node
2"use strict";
3/*-----------------------------------------------------------------------------
4| Copyright (c) Jupyter Development Team.
5| Distributed under the terms of the Modified BSD License.
6|----------------------------------------------------------------------------*/
7var __importStar = (this && this.__importStar) || function (mod) {
8 if (mod && mod.__esModule) return mod;
9 var result = {};
10 if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
11 result["default"] = mod;
12 return result;
13};
14var __importDefault = (this && this.__importDefault) || function (mod) {
15 return (mod && mod.__esModule) ? mod : { "default": mod };
16};
17Object.defineProperty(exports, "__esModule", { value: true });
18const path = __importStar(require("path"));
19const utils = __importStar(require("./utils"));
20const package_json_1 = __importDefault(require("package-json"));
21const commander_1 = __importDefault(require("commander"));
22const semver_1 = __importDefault(require("semver"));
23let versionCache = new Map();
24const tags = /^([~^]?)([\w.]*)$/;
25async function getVersion(pkg, specifier) {
26 let key = JSON.stringify([pkg, specifier]);
27 if (versionCache.has(key)) {
28 return versionCache.get(key);
29 }
30 if (semver_1.default.validRange(specifier) === null) {
31 // We have a tag, with possibly a range specifier, such as ^latest
32 let match = specifier.match(tags);
33 if (match === null) {
34 throw Error(`Invalid version specifier: ${specifier}`);
35 }
36 // Look up the actual version corresponding to the tag
37 let { version } = await package_json_1.default(pkg, { version: match[2] });
38 specifier = match[1] + version;
39 }
40 versionCache.set(key, specifier);
41 return specifier;
42}
43/**
44 * A very simple subset comparator
45 *
46 * @returns true if we can determine if range1 is a subset of range2, otherwise false
47 *
48 * #### Notes
49 * This will not be able to determine if range1 is a subset of range2 in many cases.
50 */
51function subset(range1, range2) {
52 try {
53 const [, r1, version1] = range1.match(tags);
54 const [, r2] = range2.match(tags);
55 return (['', '~', '^'].indexOf(r1) >= 0 &&
56 r1 === r2 &&
57 semver_1.default.valid(version1) &&
58 semver_1.default.satisfies(version1, range2));
59 }
60 catch (e) {
61 return false;
62 }
63}
64async function handleDependency(dependencies, dep, specifier, minimal) {
65 let log = [];
66 let updated = false;
67 let newRange = await getVersion(dep, specifier);
68 let oldRange = dependencies[dep];
69 if (minimal && subset(newRange, oldRange)) {
70 log.push(`SKIPPING ${dep} ${oldRange} -> ${newRange}`);
71 }
72 else {
73 log.push(`${dep} ${oldRange} -> ${newRange}`);
74 dependencies[dep] = newRange;
75 updated = true;
76 }
77 return { updated, log };
78}
79/**
80 * Handle an individual package on the path - update the dependency.
81 */
82async function handlePackage(name, specifier, packagePath, dryRun = false, minimal = false) {
83 let fileUpdated = false;
84 let fileLog = [];
85 // Read in the package.json.
86 packagePath = path.join(packagePath, 'package.json');
87 let data;
88 try {
89 data = utils.readJSONFile(packagePath);
90 }
91 catch (e) {
92 console.log('Skipping package ' + packagePath);
93 return;
94 }
95 // Update dependencies as appropriate.
96 for (let dtype of ['dependencies', 'devDependencies']) {
97 let deps = data[dtype] || {};
98 if (typeof name === 'string') {
99 let dep = name;
100 if (dep in deps) {
101 let { updated, log } = await handleDependency(deps, dep, specifier, minimal);
102 if (updated) {
103 fileUpdated = true;
104 }
105 fileLog.push(...log);
106 }
107 }
108 else {
109 let keys = Object.keys(deps);
110 keys.sort();
111 for (let dep of keys) {
112 if (dep.match(name)) {
113 let { updated, log } = await handleDependency(deps, dep, specifier, minimal);
114 if (updated) {
115 fileUpdated = true;
116 }
117 fileLog.push(...log);
118 }
119 }
120 }
121 }
122 if (fileLog.length > 0) {
123 console.log(packagePath);
124 console.log(fileLog.join('\n'));
125 console.log();
126 }
127 // Write the file back to disk.
128 if (!dryRun && fileUpdated) {
129 utils.writePackageData(packagePath, data);
130 }
131}
132commander_1.default
133 .description('Update dependency versions')
134 .usage('[options] <package> [versionspec], versionspec defaults to ^latest')
135 .option('--dry-run', 'Do not perform actions, just print output')
136 .option('--regex', 'Package is a regular expression')
137 .option('--lerna', 'Update dependencies in all lerna packages')
138 .option('--path <path>', 'Path to package or monorepo to update')
139 .option('--minimal', 'only update if the change is substantial')
140 .arguments('<package> [versionspec]')
141 .action(async (name, version = '^latest', args) => {
142 let basePath = path.resolve(args.path || '.');
143 let pkg = args.regex ? new RegExp(name) : name;
144 if (args.lerna) {
145 let paths = utils.getLernaPaths(basePath).sort();
146 // We use a loop instead of Promise.all so that the output is in
147 // alphabetical order.
148 for (let pkgPath of paths) {
149 await handlePackage(pkg, version, pkgPath, args.dryRun, args.minimal);
150 }
151 }
152 await handlePackage(pkg, version, basePath, args.dryRun, args.minimal);
153});
154commander_1.default.on('--help', function () {
155 console.log(`
156Examples
157--------
158
159 Update the package 'webpack' to a specific version range:
160
161 update-dependency webpack ^4.0.0
162
163 Update all packages to the latest version, with a caret.
164 Only update if the update is substantial:
165
166 update-dependency --minimal --regex '.*' ^latest
167
168 Print the log of the above without actually making any changes.
169
170 update-dependency --dry-run --minimal --regex '.*' ^latest
171
172 Update all packages starting with '@jupyterlab/' to the version
173 the 'latest' tag currently points to, with a caret range:
174
175 update-dependency --regex '^@jupyterlab/' ^latest
176
177 Update all packages starting with '@jupyterlab/' in all lerna
178 workspaces and the root package.json to whatever version the 'next'
179 tag for each package currently points to (with a caret tag).
180 Update the version range only if the change is substantial.
181
182 update-dependency --lerna --regex --minimal '^@jupyterlab/' ^next
183`);
184});
185commander_1.default.parse(process.argv);
186// If no arguments supplied
187if (!process.argv.slice(2).length) {
188 commander_1.default.outputHelp();
189 process.exit(1);
190}
191//# sourceMappingURL=update-dependency.js.map
\No newline at end of file