1 | import { promisify } from 'util';
|
2 | import { valid, coerce, compare } from 'semver';
|
3 | import { map, flatten } from '@ctx-core/array';
|
4 | import { _h__param } from '@ctx-core/cli-args';
|
5 | import { _queue } from '@ctx-core/queue';
|
6 | import fs from 'fs';
|
7 | import child_process from 'child_process';
|
8 | const exec = promisify(child_process.exec);
|
9 | const globby = require('globby');
|
10 | const readFile = promisify(fs.readFile);
|
11 | const writeFile = promisify(fs.writeFile);
|
12 | export async function _workspaces() {
|
13 | const txt__workspaces = (await exec('yarn workspaces info')).stdout;
|
14 | const a1__txt__workspaces = txt__workspaces.split('\n');
|
15 | const line__start__json__workspaces = a1__txt__workspaces.indexOf('{');
|
16 | const line__end__json__workspaces = a1__txt__workspaces.indexOf('}');
|
17 | const json__workspaces = a1__txt__workspaces.slice(line__start__json__workspaces, line__end__json__workspaces + 1).join('\n');
|
18 | return JSON.parse(json__workspaces);
|
19 | }
|
20 | export async function each__package__json(txt__glob, fn) {
|
21 | const a1__package__json = await globby(txt__glob);
|
22 | const a1__promise = map(a1__package__json, fn);
|
23 | await Promise.all(a1__promise);
|
24 | }
|
25 | export async function cli__npm_check_updates__monorepo() {
|
26 | const h__param = _h__param(process.argv.slice(2), {
|
27 | threads: '-t, --threads',
|
28 | workspace_name: '-w, --workspace-name'
|
29 | }, {
|
30 | threads: 20,
|
31 | });
|
32 | const h1__name__workspace__h0__stdout = await npm_check_updates__monorepo(h__param);
|
33 | for (let name__workspace in h1__name__workspace__h0__stdout) {
|
34 | console.info(name__workspace);
|
35 | console.info(h1__name__workspace__h0__stdout[name__workspace]);
|
36 | }
|
37 | }
|
38 | export async function npm_check_updates__monorepo(opts = {}) {
|
39 | const package_name__x__latest_version = {};
|
40 | const queue = _queue(opts.threads || 20);
|
41 | const workspaces = await _workspaces();
|
42 | const a1__workspace_name = opts.workspace_name
|
43 | ? flatten([opts.workspace_name])
|
44 | : Object.keys(workspaces);
|
45 | const a1__promise = _a1__promise(a1__workspace_name, _promise__workspace);
|
46 | if (!opts.workspace_name) {
|
47 | a1__workspace_name.push('.');
|
48 | a1__promise.push(_promise('.'));
|
49 | }
|
50 | const a1__stdout = await Promise.all(a1__promise);
|
51 | return _h1__stdout__h0__name__workspace(a1__workspace_name, a1__stdout);
|
52 | async function _promise(location = '.') {
|
53 | const path__package__json = `${location}/package.json`;
|
54 | const pkg = JSON.parse((await readFile(path__package__json)).toString());
|
55 | const { dependencies, peerDependencies, devDependencies, noUpdate } = pkg;
|
56 | const update_a2 = [];
|
57 | update_a2.push(await update__dependencies(dependencies, noUpdate));
|
58 | update_a2.push(await update__dependencies(devDependencies, noUpdate));
|
59 | update_a2.push(await update__dependencies(peerDependencies, noUpdate));
|
60 | const update_a1 = flatten(update_a2);
|
61 | if (update_a1.length) {
|
62 | await writeFile(path__package__json, JSON.stringify(pkg, null, '\t'));
|
63 | }
|
64 | return update_a1.join('\n');
|
65 | }
|
66 | async function _promise__workspace(name__workspace) {
|
67 | const workspace = workspaces[name__workspace];
|
68 | const { location } = workspace;
|
69 | return _promise(location);
|
70 | }
|
71 | async function update__dependencies(dependencies, noUpdate = []) {
|
72 | noUpdate = noUpdate || [];
|
73 | const update_a1 = [];
|
74 | for (let package_name in dependencies) {
|
75 | if (~noUpdate.indexOf(package_name))
|
76 | continue;
|
77 | const dependency_workspace = workspaces[package_name];
|
78 | const version = dependencies[package_name];
|
79 | const has_carrot = version.slice(0, 1) === '^';
|
80 | if (dependency_workspace) {
|
81 | const { location } = dependency_workspace;
|
82 | const pkg = JSON.parse((await readFile(`${location}/package.json`)).toString());
|
83 | const latest_version = `${version.slice(0, 1) === '^' ? '^' : ''}${pkg.version}`;
|
84 | package_name__x__latest_version[package_name] = pkg.version;
|
85 | if (compare(coerce(latest_version), coerce(version)) > 0) {
|
86 | push__update_a1(update_a1, package_name, version, latest_version);
|
87 | dependencies[package_name] = latest_version;
|
88 | }
|
89 | }
|
90 | else {
|
91 | if (!valid(coerce(dependencies[package_name])))
|
92 | continue;
|
93 | if (!package_name__x__latest_version[package_name]) {
|
94 | const promise = queue.add(async () => (await exec(`npm show ${package_name}@latest | grep latest | grep \\: | cut -f2 -d: | xargs echo`)).stdout.trim());
|
95 | package_name__x__latest_version[package_name] = promise;
|
96 | }
|
97 | if (package_name__x__latest_version[package_name].then) {
|
98 | package_name__x__latest_version[package_name] =
|
99 | await package_name__x__latest_version[package_name];
|
100 | }
|
101 | const latest_stripped_version = package_name__x__latest_version[package_name];
|
102 | if (!latest_stripped_version) {
|
103 | console.warn(`WARN: Unable to parse ${package_name} from npm registry`);
|
104 | }
|
105 | if (latest_stripped_version
|
106 | && compare(coerce(latest_stripped_version), coerce(version)) > 0) {
|
107 | const latest_version = `${has_carrot ? '^' : ''}${latest_stripped_version}`;
|
108 | push__update_a1(update_a1, package_name, version, latest_version);
|
109 | dependencies[package_name] = latest_version;
|
110 | }
|
111 | }
|
112 | }
|
113 | return update_a1;
|
114 | }
|
115 | function push__update_a1(update_a1, package_name, version, latest_version) {
|
116 | update_a1.push(`${package_name}: ${version} -> ${latest_version}`);
|
117 | }
|
118 | }
|
119 | export async function run_parallel__workspaces(cmd_a1, opts = {}) {
|
120 | const queue = _queue(opts.threads || 20);
|
121 | const workspaces = await _workspaces();
|
122 | const cmd = cmd_a1.join(' ');
|
123 | const name_a1__workspace = Object.keys(workspaces);
|
124 | const promise_a1 = _a1__promise(name_a1__workspace, _promise);
|
125 | const stdout_a1 = await Promise.all(promise_a1);
|
126 | return _h1__stdout__h0__name__workspace(name_a1__workspace, stdout_a1);
|
127 | async function _promise(name__workspace) {
|
128 | const workspace = workspaces[name__workspace];
|
129 | const { location } = workspace;
|
130 | return (queue.add(async () => (await exec(`cd ${location}; ${cmd}`)).stdout.trim()));
|
131 | }
|
132 | }
|
133 | function _a1__promise(a1__workspace, _promise) {
|
134 | const a1__promise = [];
|
135 | for (let i = 0; i < a1__workspace.length; i++) {
|
136 | const name__workspace = a1__workspace[i];
|
137 | a1__promise.push(_promise(name__workspace));
|
138 | }
|
139 | return a1__promise;
|
140 | }
|
141 | function _h1__stdout__h0__name__workspace(a1__name__workspace, stdout_a1) {
|
142 | const stdout__name__workspace = {};
|
143 | for (let i = 0; i < a1__name__workspace.length; i++) {
|
144 | const name__workspace = a1__name__workspace[i];
|
145 | stdout__name__workspace[name__workspace] = stdout_a1[i];
|
146 | }
|
147 | return stdout__name__workspace;
|
148 | }
|