UNPKG

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