UNPKG

10.8 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("@phosphor/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 (let 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 let 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 let text = JSON.stringify(sortPackageJson(data), null, 2) + '\n';
61 let 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 let 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 let indentRe = acc.match(/([^\S\r\n]*).*$/);
147 let 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 let 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 let filePath = path.resolve(path.join('.', 'dev_mode', 'package.json'));
211 let data = readJSONFile(filePath);
212 data.jupyterlab.version = curr;
213 writeJSONFile(filePath, data);
214}
215exports.postbump = postbump;
216/**
217 * Run a command with terminal output.
218 *
219 * @param cmd - The command to run.
220 */
221function run(cmd, options = {}, quiet) {
222 options = options || {};
223 options['stdio'] = options.stdio || 'inherit';
224 if (!quiet) {
225 console.log('>', cmd);
226 }
227 const value = childProcess.execSync(cmd, options);
228 if (value === null) {
229 return '';
230 }
231 return value
232 .toString()
233 .replace(/(\r\n|\n)$/, '')
234 .trim();
235}
236exports.run = run;
237/**
238 * Get a graph that has all of the package data for the local packages and their
239 * first order dependencies.
240 */
241function getPackageGraph() {
242 // Pick up all the package versions.
243 const paths = getLernaPaths();
244 const locals = {};
245 // These two are not part of the workspaces but should be
246 // considered part of the dependency graph.
247 paths.push('./jupyterlab/tests/mock_packages/extension');
248 paths.push('./jupyterlab/tests/mock_packages/mimeextension');
249 // Gather all of our package data.
250 paths.forEach(pkgPath => {
251 // Read in the package.json.
252 let data;
253 try {
254 data = readJSONFile(path.join(pkgPath, 'package.json'));
255 }
256 catch (e) {
257 console.error(e);
258 return;
259 }
260 locals[data.name] = data;
261 });
262 // Build up a dependency graph from all our local packages and
263 // their first order dependencies.
264 const graph = new dependency_graph_1.DepGraph();
265 Object.keys(locals).forEach(name => {
266 const data = locals[name];
267 graph.addNode(name, data);
268 const deps = data.dependencies || {};
269 Object.keys(deps).forEach(depName => {
270 if (!graph.hasNode(depName)) {
271 let depData;
272 // get data from locals if available, otherwise from
273 // third party library.
274 if (depName in locals) {
275 depData = locals[depName];
276 }
277 else {
278 depData = requirePackage(name, depName);
279 }
280 graph.addNode(depName, depData);
281 }
282 graph.addDependency(data.name, depName);
283 });
284 });
285 return graph;
286}
287exports.getPackageGraph = getPackageGraph;
288/**
289 * Resolve a `package.json` in the `module` starting at resolution from the `parentModule`.
290 *
291 * We could just use "require(`${depName}/package.json`)", however this won't work for modules
292 * that are not hoisted to the top level.
293 */
294function requirePackage(parentModule, module) {
295 const packagePath = `${module}/package.json`;
296 let parentModulePath;
297 // This will fail when the parent module cannot be loaded, like `@jupyterlab/test-root`
298 try {
299 parentModulePath = require.resolve(parentModule);
300 }
301 catch (_a) {
302 return require(packagePath);
303 }
304 const requirePath = require.resolve(packagePath, {
305 paths: [parentModulePath]
306 });
307 return require(requirePath);
308}
309/**
310 * Ensure the given path uses '/' as path separator.
311 */
312function ensureUnixPathSep(source) {
313 if (path.sep === '/') {
314 return source;
315 }
316 return source.replace(backSlash, '/');
317}
318exports.ensureUnixPathSep = ensureUnixPathSep;
319/**
320 * Get the last portion of a path, without its extension (if any).
321 *
322 * @param path - The file path.
323 *
324 * @returns the last part of the path, sans extension.
325 */
326function stem(path) {
327 return path
328 .split('\\')
329 .pop()
330 .split('/')
331 .pop()
332 .split('.')
333 .shift();
334}
335exports.stem = stem;
336/**
337 * Given a 'snake-case', 'snake_case', or 'snake case' string,
338 * will return the camel case version: 'snakeCase'.
339 *
340 * @param str: the snake-case input string.
341 *
342 * @param upper: default = false. If true, the first letter of the
343 * returned string will be capitalized.
344 *
345 * @returns the camel case version of the input string.
346 */
347function camelCase(str, upper = false) {
348 return str.replace(/(?:^\w|[A-Z]|\b\w|\s+|-+|_+)/g, function (match, index) {
349 if (+match === 0 || match[0] === '-') {
350 return '';
351 }
352 else if (index === 0 && !upper) {
353 return match.toLowerCase();
354 }
355 else {
356 return match.toUpperCase();
357 }
358 });
359}
360exports.camelCase = camelCase;
361//# sourceMappingURL=utils.js.map
\No newline at end of file