Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

utils.js 12 KB

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