@emeryld/manager 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/manager-cli.js +1 -3
- package/dist/git.js +68 -0
- package/dist/helper-cli.js +299 -0
- package/dist/menu.js +130 -0
- package/dist/packages.js +281 -0
- package/dist/preflight.js +26 -0
- package/dist/prompts.js +85 -0
- package/dist/publish.js +142 -0
- package/dist/release.js +292 -0
- package/dist/semver.js +17 -0
- package/dist/utils/colors.js +11 -0
- package/dist/utils/log.js +19 -0
- package/dist/utils/run.js +21 -0
- package/dist/workspace.js +246 -0
- package/package.json +5 -5
- package/tsconfig.base.json +0 -1
- package/src/git.ts +0 -74
- package/src/helper-cli.ts +0 -405
- package/src/menu.ts +0 -142
- package/src/packages.ts +0 -305
- package/src/preflight.ts +0 -26
- package/src/prompts.ts +0 -93
- package/src/publish.ts +0 -183
- package/src/release.ts +0 -410
- package/src/semver.ts +0 -27
- package/src/sync-version.mjs +0 -213
- package/src/utils/colors.ts +0 -11
- package/src/utils/log.ts +0 -42
- package/src/utils/run.ts +0 -30
- package/src/workspace.ts +0 -290
package/src/utils/run.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// src/utils/run.js
|
|
2
|
-
import { spawn, type SpawnOptions } from 'node:child_process'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
7
|
-
export const rootDir = path.resolve(path.dirname(__filename), '..', '..')
|
|
8
|
-
|
|
9
|
-
export function run(
|
|
10
|
-
command: string,
|
|
11
|
-
args: string[],
|
|
12
|
-
options: SpawnOptions = {},
|
|
13
|
-
) {
|
|
14
|
-
return new Promise<void>((resolve, reject) => {
|
|
15
|
-
const child = spawn(command, args, {
|
|
16
|
-
cwd: rootDir,
|
|
17
|
-
stdio: 'inherit',
|
|
18
|
-
...options,
|
|
19
|
-
})
|
|
20
|
-
child.on('close', (code) => {
|
|
21
|
-
if (code === 0) resolve()
|
|
22
|
-
else
|
|
23
|
-
reject(
|
|
24
|
-
new Error(
|
|
25
|
-
`Command "${command} ${args.join(' ')}" exited with ${code}`,
|
|
26
|
-
),
|
|
27
|
-
)
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
}
|
package/src/workspace.ts
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
// src/workspace.js
|
|
2
|
-
import { spawnSync } from 'node:child_process'
|
|
3
|
-
import { run, rootDir } from './utils/run.js'
|
|
4
|
-
import type { LoadedPackage } from './utils/log.js'
|
|
5
|
-
import { logGlobal, logPkg, colors } from './utils/log.js'
|
|
6
|
-
import { collectGitStatus, gitAdd, gitCommit } from './git.js'
|
|
7
|
-
import { askLine, promptSingleKey } from './prompts.js'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const dependencyFiles = new Set([
|
|
11
|
-
'package.json',
|
|
12
|
-
'pnpm-lock.yaml',
|
|
13
|
-
'package-lock.json',
|
|
14
|
-
'npm-shrinkwrap.json',
|
|
15
|
-
'pnpm-workspace.yaml',
|
|
16
|
-
])
|
|
17
|
-
|
|
18
|
-
function extractPathFromStatus(line: string) {
|
|
19
|
-
const match = line.match(/^[AMDR\? ][\S\?]\s+(.*)$/)
|
|
20
|
-
if (!match) return undefined
|
|
21
|
-
const rawPath = match[1].trim().replace(/"/g, '')
|
|
22
|
-
const normalized = rawPath.includes(' -> ')
|
|
23
|
-
? rawPath.split(' -> ').pop()
|
|
24
|
-
: rawPath
|
|
25
|
-
return normalized
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function dependencyPathsFromStatus(status: string[]) {
|
|
29
|
-
return status
|
|
30
|
-
.map(extractPathFromStatus)
|
|
31
|
-
.filter((p): p is string =>
|
|
32
|
-
Boolean(p && dependencyFiles.has(p.split('/').pop() ?? '')),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function formatPkgLabel(pkg: LoadedPackage) {
|
|
37
|
-
return pkg.substitute ?? pkg.name ?? pkg.dirName
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function logDependencyChanges(paths: string[], targets: LoadedPackage[]) {
|
|
41
|
-
if (paths.length === 0) return
|
|
42
|
-
const byDir = new Map(targets.map((t) => [t.dirName, formatPkgLabel(t)]))
|
|
43
|
-
|
|
44
|
-
logGlobal('Dependency file changes detected:', colors.cyan)
|
|
45
|
-
paths.forEach((p) => {
|
|
46
|
-
const parts = p.split('/')
|
|
47
|
-
const file = parts[parts.length - 1]
|
|
48
|
-
const pkgIndex = parts.indexOf('packages')
|
|
49
|
-
if (pkgIndex !== -1 && parts[pkgIndex + 1]) {
|
|
50
|
-
const dir = parts[pkgIndex + 1]
|
|
51
|
-
const fileLabel = parts.slice(pkgIndex + 2).join('/') || 'package.json'
|
|
52
|
-
console.log(
|
|
53
|
-
` • ${colors.dim(`${byDir.get(dir) ?? dir} (${fileLabel})`)}`,
|
|
54
|
-
)
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
if (
|
|
58
|
-
file === 'pnpm-lock.yaml' ||
|
|
59
|
-
file === 'package-lock.json' ||
|
|
60
|
-
file === 'npm-shrinkwrap.json'
|
|
61
|
-
) {
|
|
62
|
-
console.log(` • ${colors.dim(`workspace lockfile (${file})`)}`)
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
if (file === 'pnpm-workspace.yaml') {
|
|
66
|
-
console.log(` • ${colors.dim('workspace pnpm-workspace.yaml')}`)
|
|
67
|
-
return
|
|
68
|
-
}
|
|
69
|
-
if (file === 'package.json') {
|
|
70
|
-
console.log(` • ${colors.dim('workspace package.json')}`)
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
console.log(` • ${colors.dim(p)}`)
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
type VersionChange = { dep: string; from: string; to: string; label: string }
|
|
78
|
-
|
|
79
|
-
function readDiff(path: string) {
|
|
80
|
-
const res = spawnSync('git', ['diff', '--unified=0', '--', path], {
|
|
81
|
-
cwd: rootDir,
|
|
82
|
-
encoding: 'utf8',
|
|
83
|
-
})
|
|
84
|
-
if (typeof res.stdout === 'string') return res.stdout
|
|
85
|
-
return ''
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function parseVersionChanges(path: string, label: string): VersionChange[] {
|
|
89
|
-
const diff = readDiff(path)
|
|
90
|
-
if (!diff) return []
|
|
91
|
-
|
|
92
|
-
const changes = new Map<string, { from?: string; to?: string }>()
|
|
93
|
-
const lineRe = /^[\-\+]\s+"([^"]+)":\s*"([^"]+)"/
|
|
94
|
-
|
|
95
|
-
diff.split('\n').forEach((line) => {
|
|
96
|
-
if (line.startsWith('+++') || line.startsWith('---')) return
|
|
97
|
-
const match = line.match(lineRe)
|
|
98
|
-
if (!match) return
|
|
99
|
-
const [, dep, version] = match
|
|
100
|
-
const entry = changes.get(dep) ?? {}
|
|
101
|
-
if (line.startsWith('-')) entry.from = version
|
|
102
|
-
if (line.startsWith('+')) entry.to = version
|
|
103
|
-
changes.set(dep, entry)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
return [...changes.entries()]
|
|
107
|
-
.filter(([, v]) => v.from && v.to && v.from !== v.to)
|
|
108
|
-
.map(([dep, v]) => ({
|
|
109
|
-
dep,
|
|
110
|
-
from: v.from as string,
|
|
111
|
-
to: v.to as string,
|
|
112
|
-
label,
|
|
113
|
-
}))
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function summarizeVersionChanges(paths: string[], targets: LoadedPackage[]) {
|
|
117
|
-
const byDir = new Map(targets.map((t) => [t.dirName, formatPkgLabel(t)]))
|
|
118
|
-
const changes: VersionChange[] = []
|
|
119
|
-
|
|
120
|
-
paths
|
|
121
|
-
.filter((p) => p.endsWith('package.json'))
|
|
122
|
-
.forEach((p) => {
|
|
123
|
-
const parts = p.split('/')
|
|
124
|
-
const pkgIndex = parts.indexOf('packages')
|
|
125
|
-
const label =
|
|
126
|
-
pkgIndex !== -1 && parts[pkgIndex + 1]
|
|
127
|
-
? (byDir.get(parts[pkgIndex + 1]) ?? parts[pkgIndex + 1])
|
|
128
|
-
: 'workspace'
|
|
129
|
-
changes.push(...parseVersionChanges(p, label))
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
if (changes.length === 0) return ''
|
|
133
|
-
|
|
134
|
-
const grouped = new Map<string, VersionChange[]>()
|
|
135
|
-
changes.forEach((c) => {
|
|
136
|
-
const list = grouped.get(c.label) ?? []
|
|
137
|
-
list.push(c)
|
|
138
|
-
grouped.set(c.label, list)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
const summaries: string[] = []
|
|
142
|
-
grouped.forEach((list, label) => {
|
|
143
|
-
const slice = list.slice(0, 3).map((c) => `${c.dep} ${c.from}→${c.to}`)
|
|
144
|
-
const extra =
|
|
145
|
-
list.length > slice.length ? ` (+${list.length - slice.length} more)` : ''
|
|
146
|
-
summaries.push(`${label}: ${slice.join(', ')}${extra}`)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
return summaries.join('; ')
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function buildUpdateCommitMessage(paths: string[], targets: LoadedPackage[]) {
|
|
153
|
-
const changeSummary = summarizeVersionChanges(paths, targets)
|
|
154
|
-
if (changeSummary) return `chore(deps): ${changeSummary}`
|
|
155
|
-
|
|
156
|
-
const labels = new Set<string>()
|
|
157
|
-
const byDir = new Map(targets.map((t) => [t.dirName, formatPkgLabel(t)]))
|
|
158
|
-
paths.forEach((p) => {
|
|
159
|
-
const parts = p.split('/')
|
|
160
|
-
const pkgIndex = parts.indexOf('packages')
|
|
161
|
-
if (pkgIndex !== -1 && parts[pkgIndex + 1]) {
|
|
162
|
-
const dir = parts[pkgIndex + 1]
|
|
163
|
-
labels.add(byDir.get(dir) ?? dir)
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
if (parts[parts.length - 1] === 'package.json') {
|
|
167
|
-
labels.add('workspace deps')
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
if (labels.size === 0 && targets.length === 1) {
|
|
172
|
-
labels.add(formatPkgLabel(targets[0]))
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const labelList = [...labels]
|
|
176
|
-
if (labelList.length === 0) return 'chore(deps): update dependencies'
|
|
177
|
-
if (labelList.length === 1) return `chore(deps): update ${labelList[0]}`
|
|
178
|
-
return `chore(deps): update ${labelList.join(', ')}`
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async function promptCommitMessage(proposed: string) {
|
|
182
|
-
const confirm = await promptSingleKey<'yes' | 'no'>(
|
|
183
|
-
`Use commit message "${proposed}"? (y/n): `,
|
|
184
|
-
(key) => {
|
|
185
|
-
if (key === 'y') return 'yes'
|
|
186
|
-
if (key === 'n') return 'no'
|
|
187
|
-
return undefined
|
|
188
|
-
},
|
|
189
|
-
)
|
|
190
|
-
if (confirm === 'yes') return proposed
|
|
191
|
-
|
|
192
|
-
let custom = ''
|
|
193
|
-
while (!custom.trim()) {
|
|
194
|
-
// eslint-disable-next-line no-await-in-loop
|
|
195
|
-
custom = await askLine('Enter commit message: ')
|
|
196
|
-
if (!custom.trim())
|
|
197
|
-
console.log(colors.red('Commit message cannot be empty.'))
|
|
198
|
-
}
|
|
199
|
-
return custom.trim()
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export async function runCleanInstall() {
|
|
203
|
-
logGlobal('Cleaning workspace…', colors.cyan)
|
|
204
|
-
await run('pnpm', ['run', 'clean'])
|
|
205
|
-
logGlobal('Reinstalling dependencies…', colors.cyan)
|
|
206
|
-
await run('pnpm', ['install'])
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export async function updateDependencies(targets: LoadedPackage[]) {
|
|
210
|
-
const preStatus = await collectGitStatus()
|
|
211
|
-
|
|
212
|
-
if (targets.length === 1) {
|
|
213
|
-
const filterArg = targets[0].name ?? `./packages/${targets[0].dirName}`
|
|
214
|
-
logPkg(targets[0], `Updating dependencies…`)
|
|
215
|
-
await run('pnpm', ['-r', '--filter', filterArg, 'update'])
|
|
216
|
-
} else {
|
|
217
|
-
logGlobal('Updating dependencies across the workspace…', colors.cyan)
|
|
218
|
-
await run('pnpm', ['-r', 'update'])
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const postStatus = await collectGitStatus()
|
|
222
|
-
const depPaths = dependencyPathsFromStatus(postStatus)
|
|
223
|
-
const uniqueDepPaths = [...new Set(depPaths)]
|
|
224
|
-
|
|
225
|
-
if (uniqueDepPaths.length === 0) {
|
|
226
|
-
logGlobal(
|
|
227
|
-
'No dependency file changes detected; skipping commit.',
|
|
228
|
-
colors.dim,
|
|
229
|
-
)
|
|
230
|
-
return
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (preStatus.length) {
|
|
234
|
-
logGlobal(
|
|
235
|
-
'Working tree had changes before update; will only stage dependency files for the commit.',
|
|
236
|
-
colors.yellow,
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
logDependencyChanges(uniqueDepPaths, targets)
|
|
241
|
-
|
|
242
|
-
const proposed = buildUpdateCommitMessage(uniqueDepPaths, targets)
|
|
243
|
-
const message = await promptCommitMessage(proposed)
|
|
244
|
-
|
|
245
|
-
logGlobal('Staging dependency changes…', colors.cyan)
|
|
246
|
-
await gitAdd(uniqueDepPaths)
|
|
247
|
-
logGlobal('Creating commit…', colors.cyan)
|
|
248
|
-
await gitCommit(message)
|
|
249
|
-
logGlobal(`Commit created: ${message}`, colors.green)
|
|
250
|
-
logGlobal('Pushing to origin…', colors.cyan)
|
|
251
|
-
await run('git', ['push'])
|
|
252
|
-
logGlobal('Push complete.', colors.green)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export async function typecheckAll() {
|
|
256
|
-
logGlobal('Running typecheck for all packages…', colors.cyan)
|
|
257
|
-
await run('pnpm', ['typecheck'])
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export async function typecheckSingle(pkg: LoadedPackage) {
|
|
261
|
-
const filterArg = pkg.name ?? `./packages/${pkg.dirName}`
|
|
262
|
-
logPkg(pkg, `Running typecheck…`)
|
|
263
|
-
await run('pnpm', ['run', '--filter', filterArg, 'typecheck'])
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export async function buildAll() {
|
|
267
|
-
logGlobal('Running build for all packages…', colors.cyan)
|
|
268
|
-
await run('pnpm', ['build'])
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export async function buildSingle(pkg: LoadedPackage) {
|
|
272
|
-
const filterArg = pkg.name ?? `./packages/${pkg.dirName}`
|
|
273
|
-
logPkg(pkg, `Running build…`)
|
|
274
|
-
await run('pnpm', ['run', '--filter', filterArg, 'build'])
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export async function buildPackageLocally(pkg: LoadedPackage) {
|
|
278
|
-
logPkg(pkg, 'Building local dist before publish…')
|
|
279
|
-
await run('pnpm', ['run', 'build'], { cwd: pkg.path })
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export async function testAll() {
|
|
283
|
-
logGlobal('Running tests for all packages…', colors.cyan)
|
|
284
|
-
await run('pnpm', ['test'])
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export async function testSingle(pkg: LoadedPackage) {
|
|
288
|
-
logPkg(pkg, `Running tests…`)
|
|
289
|
-
await run('pnpm', ['test', '--', `packages/${pkg.dirName}`])
|
|
290
|
-
}
|