UNPKG

10.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const path = require("path");
4const glob = require("glob");
5const fs = require("fs-extra");
6const childProcess = require("child_process");
7const dependency_graph_1 = require("dependency-graph");
8const sortPackageJson = require("sort-package-json");
9const coreutils = require("@lumino/coreutils");
10const backSlash = /\\/g;
11/**
12 * Get all of the lerna package paths.
13 */
14function getLernaPaths(basePath = '.') {
15 basePath = path.resolve(basePath);
16 let packages;
17 try {
18 let baseConfig = require(path.join(basePath, 'package.json'));
19 if (baseConfig.workspaces) {
20 packages = baseConfig.workspaces.packages || baseConfig.workspaces;
21 }
22 else {
23 baseConfig = require(path.join(basePath, 'lerna.json'));
24 packages = baseConfig.packages;
25 }
26 }
27 catch (e) {
28 if (e.code === 'MODULE_NOT_FOUND') {
29 throw new Error(`No yarn workspace / lerna package list found in ${basePath}`);
30 }
31 throw e;
32 }
33 let paths = [];
34 for (const config of packages) {
35 paths = paths.concat(glob.sync(path.join(basePath, config)));
36 }
37 return paths.filter(pkgPath => {
38 return fs.existsSync(path.join(pkgPath, 'package.json'));
39 });
40}
41exports.getLernaPaths = getLernaPaths;
42/**
43 * Get all of the core package paths.
44 */
45function getCorePaths() {
46 const spec = path.resolve(path.join('.', 'packages', '*'));
47 return glob.sync(spec);
48}
49exports.getCorePaths = getCorePaths;
50/**
51 * Write a package.json if necessary.
52 *
53 * @param data - The package data.
54 *
55 * @oaram pkgJsonPath - The path to the package.json file.
56 *
57 * @returns Whether the file has changed.
58 */
59function writePackageData(pkgJsonPath, data) {
60 const text = JSON.stringify(sortPackageJson(data), null, 2) + '\n';
61 const orig = fs
62 .readFileSync(pkgJsonPath, 'utf8')
63 .split('\r\n')
64 .join('\n');
65 if (text !== orig) {
66 fs.writeFileSync(pkgJsonPath, text, 'utf8');
67 return true;
68 }
69 return false;
70}
71exports.writePackageData = writePackageData;
72/**
73 * Read a json file.
74 */
75function readJSONFile(filePath) {
76 try {
77 return JSON.parse(fs.readFileSync(filePath, 'utf8'));
78 }
79 catch (e) {
80 throw `Cannot read JSON for path ${filePath}: ${e}`;
81 }
82}
83exports.readJSONFile = readJSONFile;
84/**
85 * Write a json file.
86 */
87function writeJSONFile(filePath, data) {
88 function sortObjByKey(value) {
89 // https://stackoverflow.com/a/35810961
90 return typeof value === 'object'
91 ? Array.isArray(value)
92 ? value.map(sortObjByKey)
93 : Object.keys(value)
94 .sort()
95 .reduce((o, key) => {
96 const v = value[key];
97 o[key] = sortObjByKey(v);
98 return o;
99 }, {})
100 : value;
101 }
102 const text = JSON.stringify(data, sortObjByKey(data), 2) + '\n';
103 let orig = {};
104 try {
105 orig = readJSONFile(filePath);
106 }
107 catch (e) {
108 // no-op
109 }
110 if (!coreutils.JSONExt.deepEqual(data, orig)) {
111 fs.writeFileSync(filePath, text, 'utf8');
112 return true;
113 }
114 return false;
115}
116exports.writeJSONFile = writeJSONFile;
117/**
118 * Simple template substitution for template vars of the form {{name}}
119 *
120 * @param templ: the template string.
121 * Ex: `This header generated by {{funcName}}`
122 *
123 * @param subs: an object in which the parameter keys are the template
124 * variables and the parameter values are the substitutions.
125 *
126 * @param options: function options.
127 *
128 * @param options.autoindent: default = true. If true, will try to match
129 * indentation level of {{var}} in substituted template.
130 *
131 * @param options.end: default = '\n'. Inserted at the end of
132 * a template post-substitution and post-trim.
133 *
134 * @returns the input template with all {{vars}} substituted, then `.trim`-ed.
135 */
136function fromTemplate(templ, subs, options = {}) {
137 // default options values
138 const autoindent = options.autoindent === undefined ? true : options.autoindent;
139 const end = options.end === undefined ? '\n' : options.end;
140 Object.keys(subs).forEach(key => {
141 const val = subs[key];
142 if (autoindent) {
143 // try to match the indentation level of the {{var}} in the input template.
144 templ = templ.split(`{{${key}}}`).reduce((acc, cur) => {
145 // Regex: 0 or more non-newline whitespaces followed by end of string
146 const indentRe = acc.match(/([^\S\r\n]*).*$/);
147 const indent = indentRe ? indentRe[1] : '';
148 return acc + val.split('\n').join('\n' + indent) + cur;
149 });
150 }
151 else {
152 templ = templ.split(`{{${key}}}`).join(val);
153 }
154 });
155 return templ.trim() + end;
156}
157exports.fromTemplate = fromTemplate;
158/**
159 *
160 * Call a command, checking its status.
161 */
162function checkStatus(cmd) {
163 const data = childProcess.spawnSync(cmd, { shell: true });
164 return data.status;
165}
166exports.checkStatus = checkStatus;
167/**
168 * Get the current version of JupyterLab
169 */
170function getPythonVersion() {
171 const cmd = 'python setup.py --version';
172 return run(cmd, { stdio: 'pipe' }, true);
173}
174exports.getPythonVersion = getPythonVersion;
175/**
176 * Get the current version of a package
177 */
178function getJSVersion(pkg) {
179 const filePath = path.resolve(path.join('.', 'packages', pkg, 'package.json'));
180 const data = readJSONFile(filePath);
181 return data.version;
182}
183exports.getJSVersion = getJSVersion;
184/**
185 * Pre-bump.
186 */
187function prebump() {
188 // Ensure bump2version is installed (active fork of bumpversion)
189 run('python -m pip install bump2version');
190 // Make sure we start in a clean git state.
191 const status = run('git status --porcelain', {
192 stdio: 'pipe',
193 encoding: 'utf8'
194 });
195 if (status.length > 0) {
196 throw new Error(`Must be in a clean git state with no untracked files.
197Run "git status" to see the issues.
198
199${status}`);
200 }
201}
202exports.prebump = prebump;
203/**
204 * Post-bump.
205 */
206function postbump() {
207 // Get the current version.
208 const curr = getPythonVersion();
209 // Update the dev mode version.
210 const filePath = path.resolve(path.join('.', 'dev_mode', 'package.json'));
211 const data = readJSONFile(filePath);
212 data.jupyterlab.version = curr;
213 writeJSONFile(filePath, data);
214 // Commit changes.
215 run('git commit -am "bump version"');
216}
217exports.postbump = postbump;
218/**
219 * Run a command with terminal output.
220 *
221 * @param cmd - The command to run.
222 */
223function run(cmd, options = {}, quiet) {
224 options = options || {};
225 options['stdio'] = options.stdio || 'inherit';
226 if (!quiet) {
227 console.debug('>', cmd);
228 }
229 const value = childProcess.execSync(cmd, options);
230 if (value === null) {
231 return '';
232 }
233 return value
234 .toString()
235 .replace(/(\r\n|\n)$/, '')
236 .trim();
237}
238exports.run = run;
239/**
240 * Get a graph that has all of the package data for the local packages and their
241 * first order dependencies.
242 */
243function getPackageGraph() {
244 // Pick up all the package versions.
245 const paths = getLernaPaths();
246 const locals = {};
247 // These two are not part of the workspaces but should be
248 // considered part of the dependency graph.
249 paths.push('./jupyterlab/tests/mock_packages/extension');
250 paths.push('./jupyterlab/tests/mock_packages/mimeextension');
251 // Gather all of our package data.
252 paths.forEach(pkgPath => {
253 // Read in the package.json.
254 let data;
255 try {
256 data = readJSONFile(path.join(pkgPath, 'package.json'));
257 }
258 catch (e) {
259 console.error(e);
260 return;
261 }
262 locals[data.name] = data;
263 });
264 // Build up a dependency graph from all our local packages and
265 // their first order dependencies.
266 const graph = new dependency_graph_1.DepGraph();
267 Object.keys(locals).forEach(name => {
268 const data = locals[name];
269 graph.addNode(name, data);
270 const deps = data.dependencies || {};
271 Object.keys(deps).forEach(depName => {
272 if (!graph.hasNode(depName)) {
273 let depData;
274 // get data from locals if available, otherwise from
275 // third party library.
276 if (depName in locals) {
277 depData = locals[depName];
278 }
279 else {
280 depData = requirePackage(name, depName);
281 }
282 graph.addNode(depName, depData);
283 }
284 graph.addDependency(data.name, depName);
285 });
286 });
287 return graph;
288}
289exports.getPackageGraph = getPackageGraph;
290/**
291 * Resolve a `package.json` in the `module` starting at resolution from the `parentModule`.
292 *
293 * We could just use "require(`${depName}/package.json`)", however this won't work for modules
294 * that are not hoisted to the top level.
295 */
296function requirePackage(parentModule, module) {
297 const packagePath = `${module}/package.json`;
298 let parentModulePath;
299 // This will fail when the parent module cannot be loaded, like `@jupyterlab/test-root`
300 try {
301 parentModulePath = require.resolve(parentModule);
302 }
303 catch (_a) {
304 return require(packagePath);
305 }
306 const requirePath = require.resolve(packagePath, {
307 paths: [parentModulePath]
308 });
309 return require(requirePath);
310}
311/**
312 * Ensure the given path uses '/' as path separator.
313 */
314function ensureUnixPathSep(source) {
315 if (path.sep === '/') {
316 return source;
317 }
318 return source.replace(backSlash, '/');
319}
320exports.ensureUnixPathSep = ensureUnixPathSep;
321/**
322 * Get the last portion of a path, without its extension (if any).
323 *
324 * @param pathArg - The file path.
325 *
326 * @returns the last part of the path, sans extension.
327 */
328function stem(pathArg) {
329 return path
330 .basename(pathArg)
331 .split('.')
332 .shift();
333}
334exports.stem = stem;
335/**
336 * Given a 'snake-case', 'snake_case', or 'snake case' string,
337 * will return the camel case version: 'snakeCase'.
338 *
339 * @param str: the snake-case input string.
340 *
341 * @param upper: default = false. If true, the first letter of the
342 * returned string will be capitalized.
343 *
344 * @returns the camel case version of the input string.
345 */
346function camelCase(str, upper = false) {
347 return str.replace(/(?:^\w|[A-Z]|\b\w|\s+|-+|_+)/g, function (match, index) {
348 if (+match === 0 || match[0] === '-') {
349 return '';
350 }
351 else if (index === 0 && !upper) {
352 return match.toLowerCase();
353 }
354 else {
355 return match.toUpperCase();
356 }
357 });
358}
359exports.camelCase = camelCase;
360//# sourceMappingURL=utils.js.map
\No newline at end of file