UNPKG

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