@guiho/mirror 3.0.0-alpha.4 → 3.0.0-alpha.9

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.
@@ -1,501 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import { afterEach, describe, expect, test } from 'bun:test'
6
- import { mkdir, mkdtemp, rm } from 'node:fs/promises'
7
- import { tmpdir } from 'node:os'
8
- import { join } from 'node:path'
9
- import {
10
- applyVersionPlan,
11
- buildVersionPlan,
12
- loadMirrorConfig,
13
- parseMirrorCliOptions,
14
- readJsrName,
15
- readJsrVersion,
16
- readPackageName,
17
- readPackageVersion,
18
- renderGitTag,
19
- resolveNextVersion,
20
- validateMirrorConfig,
21
- versionFromTag,
22
- writeJsrVersion,
23
- writePackageVersion,
24
- } from './guiho-mirror'
25
-
26
- const temporaryDirectories: string[] = []
27
-
28
- afterEach(async () => {
29
- await Promise.all(temporaryDirectories.splice(0).map((path) => rm(path, { recursive: true, force: true })))
30
- })
31
-
32
- describe('Mirror v3', () => {
33
- test('parses operational and override flags', () => {
34
- const options = parseMirrorCliOptions([
35
- '--source',
36
- 'package.json',
37
- '--output',
38
- 'package.json',
39
- '--output=jsr.json,git',
40
- '--package-file=custom-package.json',
41
- '--jsr-file',
42
- 'custom-jsr.json',
43
- '--preid',
44
- 'alpha',
45
- '--dry-run',
46
- '--commit',
47
- '--push',
48
- '--allow-dirty',
49
- '--yes',
50
- ])
51
-
52
- expect(options).toMatchObject({
53
- source: 'package.json',
54
- output: ['package.json', 'jsr.json', 'git'],
55
- packageFile: 'custom-package.json',
56
- jsrFile: 'custom-jsr.json',
57
- preid: 'alpha',
58
- dryRun: true,
59
- commit: true,
60
- push: true,
61
- allowDirty: true,
62
- yes: true,
63
- })
64
- })
65
-
66
- test('expands short flag aliases -dy and -y', () => {
67
- const options = parseMirrorCliOptions(['-dy', '-y'])
68
-
69
- expect(options.dryRun).toBe(true)
70
- expect(options.yes).toBe(true)
71
- })
72
-
73
- test('discovers explicit, root, and nested configs with root precedence', async () => {
74
- const cwd = await createTempDir()
75
- await mkdir(join(cwd, 'config'), { recursive: true })
76
- await writeText(join(cwd, 'explicit.toml'), packageConfig({ output: ['jsr.json'], source: 'jsr.json' }))
77
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'] }))
78
- await writeText(join(cwd, 'config', 'mirror.config.toml'), gitConfig())
79
-
80
- const explicit = await loadMirrorConfig({ cwd, config: 'explicit.toml' })
81
- const root = await loadMirrorConfig({ cwd })
82
-
83
- await rm(join(cwd, 'mirror.config.toml'))
84
- const nested = await loadMirrorConfig({ cwd })
85
-
86
- expect(explicit.configPath).toBe(join(cwd, 'explicit.toml'))
87
- expect(explicit.version.source).toBe('jsr.json')
88
- expect(root.configPath).toBe(join(cwd, 'mirror.config.toml'))
89
- expect(root.version.source).toBe('package.json')
90
- expect(nested.configPath).toBe(join(cwd, 'config', 'mirror.config.toml'))
91
- expect(nested.version.source).toBe('git')
92
- })
93
-
94
- test('validates schema, adapter names, project name sources, and tag templates', async () => {
95
- const cwd = await createPackageAndJsrFixture()
96
-
97
- await writeText(join(cwd, 'mirror.config.toml'), 'schema = 2\n')
98
- await expect(loadMirrorConfig({ cwd })).rejects.toThrow('schema')
99
-
100
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['npm'] }))
101
- await expect(loadMirrorConfig({ cwd })).rejects.toThrow('version.output')
102
-
103
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'], nameSource: 'git' }))
104
- await expect(loadMirrorConfig({ cwd })).rejects.toThrow('project.name_source')
105
-
106
- await initializeGitRepository(cwd)
107
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['git'], tagTemplate: 'release-{version}' }))
108
- await expect(validateMirrorConfig({ cwd })).rejects.toThrow('Unsupported Git tag template')
109
- })
110
-
111
- test('merges CLI overrides over config values', async () => {
112
- const cwd = await createPackageAndJsrFixture()
113
- await writeText(join(cwd, 'custom-package.json'), JSON.stringify({ name: 'custom-package', version: '2.0.0' }, null, 2))
114
- await writeText(join(cwd, 'custom-jsr.json'), JSON.stringify({ name: 'custom-jsr', version: '2.3.0' }, null, 2))
115
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'], preid: 'beta' }))
116
-
117
- const config = await loadMirrorConfig({
118
- cwd,
119
- source: 'jsr.json',
120
- output: ['jsr.json', 'git'],
121
- packageFile: 'custom-package.json',
122
- jsrFile: 'custom-jsr.json',
123
- preid: 'alpha',
124
- push: true,
125
- allowDirty: true,
126
- })
127
-
128
- expect(config.version.source).toBe('jsr.json')
129
- expect(config.version.output).toEqual(['jsr.json', 'git'])
130
- expect(config.package.path).toBe('custom-package.json')
131
- expect(config.jsr.path).toBe('custom-jsr.json')
132
- expect(config.version.prereleaseId).toBe('alpha')
133
- expect(config.git.commit).toBe(true)
134
- expect(config.git.push).toBe(true)
135
- expect(config.git.allowDirty).toBe(true)
136
- })
137
-
138
- test('reads and writes package and JSR names and versions', async () => {
139
- const cwd = await createPackageAndJsrFixture()
140
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'jsr.json'] }))
141
- const config = await loadMirrorConfig({ cwd })
142
-
143
- expect(await readPackageName(config)).toBe('@guiho/mirror')
144
- expect(await readJsrName(config)).toBe('@guiho/mirror')
145
- expect(await readPackageVersion(config)).toBe('1.0.0')
146
- expect(await readJsrVersion(config)).toBe('1.0.0')
147
-
148
- await writePackageVersion(config, '1.0.1')
149
- await writeJsrVersion(config, '1.0.1')
150
-
151
- expect(await readPackageVersion(config)).toBe('1.0.1')
152
- expect(await readJsrVersion(config)).toBe('1.0.1')
153
- })
154
-
155
- test('resolves semantic version targets and prerelease identifiers', () => {
156
- expect(resolveNextVersion('1.0.0', 'patch')).toBe('1.0.1')
157
- expect(resolveNextVersion('1.0.0', 'prepatch')).toBe('1.0.1-0')
158
- expect(resolveNextVersion('1.0.0', 'prepatch', 'alpha')).toBe('1.0.1-alpha.0')
159
- expect(resolveNextVersion('1.0.0', '2.3.4')).toBe('2.3.4')
160
- })
161
-
162
- test('extracts and renders versions with supported Git tag templates', () => {
163
- expect(versionFromTag('v{version}', 'v1.2.3')).toBe('1.2.3')
164
- expect(versionFromTag('{name}@{version}', '@guiho/mirror@1.2.3', '@guiho/mirror')).toBe('1.2.3')
165
- expect(versionFromTag('v{version}', 'not-a-version')).toBeUndefined()
166
- expect(renderGitTag('v{version}', '1.2.3')).toBe('v1.2.3')
167
- expect(renderGitTag('{name}@{version}', '1.2.3', '@guiho/mirror')).toBe('@guiho/mirror@1.2.3')
168
- })
169
-
170
- test('plans package and JSR file outputs', async () => {
171
- const cwd = await createPackageAndJsrFixture()
172
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'jsr.json'] }))
173
-
174
- const plan = await buildVersionPlan('patch', { cwd })
175
-
176
- expect(plan.currentVersion).toBe('1.0.0')
177
- expect(plan.nextVersion).toBe('1.0.1')
178
- expect(plan.actions.map((action) => action.type)).toEqual(['write-file', 'write-file'])
179
- })
180
-
181
- test('applies package and JSR file outputs outside Git', async () => {
182
- const cwd = await createPackageAndJsrFixture()
183
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'jsr.json'] }))
184
-
185
- const result = await applyVersionPlan('minor', { cwd, yes: true })
186
-
187
- expect(result.applied).toBe(true)
188
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.1.0')
189
- expect(await readJsrVersion(await loadMirrorConfig({ cwd }))).toBe('1.1.0')
190
- })
191
-
192
- test('dry-run apply does not mutate files and does not require confirmation', async () => {
193
- const cwd = await createPackageAndJsrFixture()
194
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'] }))
195
-
196
- const result = await applyVersionPlan('patch', { cwd, dryRun: true })
197
-
198
- expect(result.applied).toBe(false)
199
- expect(result.dryRun).toBe(true)
200
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.0.0')
201
- })
202
-
203
- test('reads the current version from matching Git tags', async () => {
204
- const cwd = await createGitFixture()
205
- await git(cwd, 'tag', 'v1.0.0')
206
- await git(cwd, 'tag', 'v1.2.0')
207
- await writeText(join(cwd, 'mirror.config.toml'), gitConfig())
208
-
209
- const plan = await buildVersionPlan('patch', { cwd })
210
-
211
- expect(plan.currentVersion).toBe('1.2.0')
212
- expect(plan.nextVersion).toBe('1.2.1')
213
- expect(plan.gitTag).toBe('v1.2.1')
214
- })
215
-
216
- test('requires commit or push when file outputs and Git tag output are combined', async () => {
217
- const cwd = await createPackageAndJsrFixture()
218
- await initializeGitRepository(cwd)
219
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'git'] }))
220
-
221
- await expect(buildVersionPlan('patch', { cwd })).rejects.toThrow('requires --commit or --push')
222
-
223
- const packageJson = await Bun.file(join(cwd, 'package.json')).json()
224
- expect(packageJson.version).toBe('1.0.0')
225
- })
226
-
227
- test('fails on dirty Git worktrees unless allow-dirty is set', async () => {
228
- const cwd = await createPackageAndJsrFixture()
229
- await initializeGitRepository(cwd)
230
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'] }))
231
- await commitAll(cwd, 'Add Mirror config')
232
- await writeText(join(cwd, 'README.md'), '# dirty fixture\n')
233
-
234
- await expect(applyVersionPlan('patch', { cwd, yes: true })).rejects.toThrow('dirty')
235
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.0.0')
236
-
237
- const result = await applyVersionPlan('patch', { cwd, yes: true, allowDirty: true })
238
- expect(result.applied).toBe(true)
239
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.0.1')
240
- })
241
-
242
- test('applies file output, release commit, and Git tag with --commit', async () => {
243
- const cwd = await createPackageAndJsrFixture()
244
- await initializeGitRepository(cwd)
245
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'git'] }))
246
- await commitAll(cwd, 'Add Mirror config')
247
-
248
- const result = await applyVersionPlan('patch', { cwd, commit: true, yes: true })
249
-
250
- expect(result.applied).toBe(true)
251
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.0.1')
252
- expect((await gitText(cwd, 'tag', '--list')).trim()).toBe('@guiho/mirror@1.0.1')
253
- expect((await gitText(cwd, 'status', '--porcelain')).trim()).toBe('')
254
- })
255
-
256
- test('push implies commit and pushes the release tag', async () => {
257
- const remote = await createBareGitRepository()
258
- const cwd = await createPackageAndJsrFixture()
259
- await initializeGitRepository(cwd)
260
- await git(cwd, 'remote', 'add', 'origin', remote)
261
- await git(cwd, 'push', '-u', 'origin', 'HEAD')
262
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json', 'git'] }))
263
- await commitAll(cwd, 'Add Mirror config')
264
-
265
- const result = await applyVersionPlan('patch', { cwd, push: true, yes: true })
266
-
267
- expect(result.plan.commitEnabled).toBe(true)
268
- expect(result.plan.pushEnabled).toBe(true)
269
- expect((await gitText(remote, 'tag', '--list')).trim()).toBe('@guiho/mirror@1.0.1')
270
- })
271
-
272
- test('git-only releases with --commit create tags without empty commits', async () => {
273
- const cwd = await createGitFixture()
274
- await git(cwd, 'tag', 'v1.0.0')
275
- await writeText(join(cwd, 'mirror.config.toml'), gitConfig())
276
- await commitAll(cwd, 'Add Mirror config')
277
- const commitsBefore = (await gitText(cwd, 'rev-list', '--count', 'HEAD')).trim()
278
-
279
- const result = await applyVersionPlan('patch', { cwd, commit: true, yes: true })
280
-
281
- expect(result.applied).toBe(true)
282
- expect((await gitText(cwd, 'rev-list', '--count', 'HEAD')).trim()).toBe(commitsBefore)
283
- expect((await gitText(cwd, 'tag', '--list')).trim().split(/\r?\n/).sort()).toEqual(['v1.0.0', 'v1.0.1'])
284
- })
285
-
286
- test('runs CLI config show and config check', async () => {
287
- const cwd = await createGitFixture()
288
- await writeText(join(cwd, 'mirror.config.toml'), gitConfig())
289
-
290
- const show = await runMirrorCli('config', 'show', '--cwd', cwd)
291
- const check = await runMirrorCli('config', 'check', '--cwd', cwd)
292
-
293
- expect(show.exitCode).toBe(0)
294
- expect(show.stdout).toContain('source: git')
295
- expect(check.exitCode).toBe(0)
296
- expect(check.stdout.trim()).toBe('ok')
297
- })
298
-
299
- test('runs the top-level CLI as successful help output', async () => {
300
- const result = await runMirrorCli()
301
-
302
- expect(result.exitCode).toBe(0)
303
- expect(result.stdout).toMatch(/mirror v\d+\.\d+\.\d+/)
304
- expect(result.stdout).toContain('USAGE')
305
- })
306
-
307
- test('runs CLI help without ANSI colors when no-color is set', async () => {
308
- const result = await runMirrorCli('--no-color', '--help')
309
-
310
- expect(result.exitCode).toBe(0)
311
- expect(result.stdout).toContain('USAGE')
312
- expect(result.stdout).not.toContain('\u001B[')
313
- })
314
-
315
- test('runs CLI version current, next, plan, and apply', async () => {
316
- const cwd = await createPackageAndJsrFixture()
317
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'] }))
318
-
319
- const current = await runMirrorCli('version', 'current', '--cwd', cwd)
320
- const next = await runMirrorCli('version', 'next', 'patch', '--cwd', cwd)
321
- const plan = await runMirrorCli('version', 'plan', 'patch', '--cwd', cwd)
322
- const apply = await runMirrorCli('version', 'apply', 'patch', '--cwd', cwd, '--yes')
323
-
324
- expect(current.exitCode).toBe(0)
325
- expect(current.stdout.trim()).toBe('1.0.0')
326
- expect(next.exitCode).toBe(0)
327
- expect(next.stdout.trim()).toBe('1.0.1')
328
- expect(plan.exitCode).toBe(0)
329
- expect(plan.stdout).toContain('next: 1.0.1')
330
- expect(apply.exitCode).toBe(0)
331
- expect(apply.stdout).toContain('next: 1.0.1')
332
- expect(apply.stdout).toContain('applied: true')
333
- expect(await readPackageVersion(await loadMirrorConfig({ cwd }))).toBe('1.0.1')
334
- })
335
-
336
- test('runs CLI source and repeated output overrides', async () => {
337
- const cwd = await createPackageAndJsrFixture()
338
- await writeText(join(cwd, 'mirror.config.toml'), packageConfig({ output: ['package.json'] }))
339
-
340
- const result = await runMirrorCli(
341
- 'version',
342
- 'plan',
343
- 'patch',
344
- '--cwd',
345
- cwd,
346
- '--source',
347
- 'package.json',
348
- '--output',
349
- 'package.json',
350
- '--output',
351
- 'jsr.json',
352
- )
353
-
354
- expect(result.exitCode).toBe(0)
355
- expect(result.stdout).toContain('output: package.json, jsr.json')
356
- expect(result.stdout).toContain('next: 1.0.1')
357
- })
358
- })
359
-
360
- const createTempDir = async () => {
361
- const path = await mkdtemp(join(tmpdir(), 'guiho-mirror-'))
362
- temporaryDirectories.push(path)
363
- return path
364
- }
365
-
366
- const createPackageAndJsrFixture = async () => {
367
- const cwd = await createTempDir()
368
- await writeJson(join(cwd, 'package.json'), {
369
- name: '@guiho/mirror',
370
- version: '1.0.0',
371
- })
372
- await writeJson(join(cwd, 'jsr.json'), {
373
- name: '@guiho/mirror',
374
- version: '1.0.0',
375
- exports: './source/guiho-mirror.ts',
376
- })
377
- return cwd
378
- }
379
-
380
- const createGitFixture = async () => {
381
- const cwd = await createTempDir()
382
- await initializeGitRepository(cwd)
383
- return cwd
384
- }
385
-
386
- const createBareGitRepository = async () => {
387
- const cwd = await createTempDir()
388
- await git(cwd, 'init', '--bare')
389
- return cwd
390
- }
391
-
392
- const initializeGitRepository = async (cwd: string) => {
393
- await git(cwd, 'init')
394
- await git(cwd, 'config', 'user.email', 'mirror@example.com')
395
- await git(cwd, 'config', 'user.name', 'Mirror Test')
396
- await writeText(join(cwd, 'README.md'), '# fixture\n')
397
- await git(cwd, 'add', '.')
398
- await git(cwd, 'commit', '-m', 'Initial commit')
399
- }
400
-
401
- const commitAll = async (cwd: string, message: string) => {
402
- await git(cwd, 'add', '.')
403
- await git(cwd, 'commit', '-m', message)
404
- }
405
-
406
- const writeText = async (path: string, content: string) => {
407
- await Bun.write(path, content)
408
- }
409
-
410
- const writeJson = async (path: string, object: Record<string, unknown>) => {
411
- await writeText(path, `${JSON.stringify(object, null, 2)}\n`)
412
- }
413
-
414
- const packageConfig = ({
415
- output,
416
- source = 'package.json',
417
- nameSource = 'package.json',
418
- tagTemplate = '{name}@{version}',
419
- preid = '',
420
- }: {
421
- output: string[]
422
- source?: string
423
- nameSource?: string
424
- tagTemplate?: string
425
- preid?: string
426
- }) => `schema = 1
427
-
428
- [project]
429
- name_source = "${nameSource}"
430
-
431
- [version]
432
- scheme = "semver"
433
- source = "${source}"
434
- output = [${output.map((value) => `"${value}"`).join(', ')}]
435
- prerelease_id = "${preid}"
436
-
437
- [package]
438
- path = "package.json"
439
-
440
- [jsr]
441
- path = "jsr.json"
442
-
443
- [git]
444
- tag_template = "${tagTemplate}"
445
- commit = false
446
- push = false
447
- allow_dirty = false
448
- `
449
-
450
- const gitConfig = () => `schema = 1
451
-
452
- [project]
453
- name = "fixture"
454
-
455
- [version]
456
- scheme = "semver"
457
- source = "git"
458
- output = ["git"]
459
- prerelease_id = ""
460
-
461
- [git]
462
- tag_template = "v{version}"
463
- commit = false
464
- push = false
465
- allow_dirty = false
466
- `
467
-
468
- const git = async (cwd: string, ...args: string[]) => {
469
- const result = Bun.spawn(['git', ...args], { cwd, stdout: 'pipe', stderr: 'pipe' })
470
- const exitCode = await result.exited
471
-
472
- if (exitCode !== 0) {
473
- // @ts-expect-error
474
- throw new Error(`git ${args.join(' ')} failed: ${await result.stderr.text()}`)
475
- }
476
- }
477
-
478
- const gitText = async (cwd: string, ...args: string[]) => {
479
- const result = Bun.spawn(['git', ...args], { cwd, stdout: 'pipe', stderr: 'pipe' })
480
- const exitCode = await result.exited
481
- // @ts-expect-error
482
- const stdout = await result.stdout.text()
483
-
484
- if (exitCode !== 0) {
485
- // @ts-expect-error
486
- throw new Error(`git ${args.join(' ')} failed: ${await result.stderr.text()}`)
487
- }
488
-
489
- return stdout
490
- }
491
-
492
- const runMirrorCli = async (...args: string[]) => {
493
- const result = Bun.spawn(['bun', join(import.meta.dir, 'guiho-mirror-bin.ts'), ...args], {
494
- stdout: 'pipe',
495
- stderr: 'pipe',
496
- })
497
- // @ts-expect-error
498
- const [exitCode, stdout, stderr] = await Promise.all([result.exited, result.stdout.text(), result.stderr.text()])
499
-
500
- return { exitCode, stdout, stderr }
501
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- export type {
6
- MirrorAdapterName,
7
- MirrorCliOptions,
8
- MirrorConfig,
9
- MirrorExecutionResult,
10
- MirrorFormat,
11
- MirrorRawConfig,
12
- MirrorVersionPlan,
13
- MirrorVersionPlanAction,
14
- MirrorVersionTarget,
15
- } from './types'
16
-
17
- export { MirrorError, invariant } from './errors'
18
- export { parseMirrorCliOptions } from './flags'
19
- export { createInitConfig, discoverMirrorConfig, loadMirrorConfig, normalizeMirrorConfig, writeInitConfig } from './config'
20
- export { assertValidSemver, isMirrorReleaseTarget, mirrorReleaseTargets, resolveNextVersion, sortSemverDescending } from './version'
21
- export {
22
- createGitCommit,
23
- createGitTag,
24
- ensureAdapterFiles,
25
- assertSupportedGitTagTemplate,
26
- isGitDirty,
27
- isGitRepository,
28
- readCurrentVersion,
29
- readGitVersion,
30
- readJsrName,
31
- readJsrVersion,
32
- readPackageName,
33
- readPackageVersion,
34
- renderGitTag,
35
- resolveProjectName,
36
- supportedGitTagTemplates,
37
- versionFromTag,
38
- writeJsrVersion,
39
- writePackageVersion,
40
- } from './adapters'
41
- export { buildVersionPlan, releaseLabel, resolveFileOutputPaths, validateMirrorConfig } from './plan'
42
- export { applyVersionPlan, executeVersionPlan } from './executor'
43
- export { mirrorBanner, reportConfig, reportConfigSchema, reportExecution, reportExecutionSummary, reportPlan, reportValue } from './reporter'
44
- export { createMirrorCommand, runMirrorCli } from './cli'
package/source/plan.ts DELETED
@@ -1,98 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import { relative } from 'node:path'
6
- import type { MirrorCliOptions, MirrorConfig, MirrorVersionPlan, MirrorVersionPlanAction } from './types'
7
- import { MirrorError } from './errors'
8
- import { loadMirrorConfig, relativeFromCwd, resolveMirrorPath } from './config'
9
- import { ensureAdapterFiles, readCurrentVersion, renderGitTag, resolveProjectName } from './adapters'
10
- import { resolveNextVersion } from './version'
11
-
12
- export const validateMirrorConfig = async (options: MirrorCliOptions = {}): Promise<MirrorConfig> => {
13
- const config = await loadMirrorConfig(options)
14
- await ensureAdapterFiles(config)
15
-
16
- const projectName = await resolveProjectName(config)
17
-
18
- if (config.version.source === 'git' || config.version.output.includes('git')) {
19
- renderGitTag(config.git.tagTemplate, '0.0.0', projectName)
20
- }
21
-
22
- return config
23
- }
24
-
25
- export const buildVersionPlan = async (target: string, options: MirrorCliOptions = {}): Promise<MirrorVersionPlan> => {
26
- const config = await validateMirrorConfig(options)
27
-
28
- const projectName = await resolveProjectName(config)
29
- const currentVersion = await readCurrentVersion(config, projectName)
30
- const nextVersion = resolveNextVersion(currentVersion, target, config.version.prereleaseId)
31
- const fileOutputPaths = resolveFileOutputPaths(config)
32
- const commitEnabled = config.git.commit
33
- const pushEnabled = config.git.push
34
- const actions: MirrorVersionPlanAction[] = []
35
-
36
- for (const path of fileOutputPaths) {
37
- actions.push({
38
- type: 'write-file',
39
- adapter: path.endsWith(config.package.path) ? 'package.json' : 'jsr.json',
40
- path,
41
- currentVersion,
42
- nextVersion,
43
- })
44
- }
45
-
46
- const gitTag = config.version.output.includes('git') ? renderGitTag(config.git.tagTemplate, nextVersion, projectName) : undefined
47
-
48
- if (fileOutputPaths.length > 0 && gitTag && !commitEnabled && !pushEnabled) {
49
- throw new MirrorError('Git tag output with file outputs requires --commit or --push so the tag points at the version commit.')
50
- }
51
-
52
- if (commitEnabled && fileOutputPaths.length > 0) {
53
- actions.push({
54
- type: 'git-commit',
55
- message: releaseLabel(nextVersion, projectName),
56
- paths: fileOutputPaths.map((path) => relative(config.cwd, path)),
57
- })
58
- }
59
-
60
- if (gitTag) actions.push({ type: 'git-tag', tag: gitTag })
61
-
62
- if (pushEnabled) {
63
- actions.push({
64
- type: 'git-push',
65
- includeCommit: fileOutputPaths.length > 0,
66
- includeTags: Boolean(gitTag),
67
- })
68
- }
69
-
70
- return {
71
- cwd: config.cwd,
72
- configPath: config.configPath,
73
- source: config.version.source,
74
- output: config.version.output,
75
- currentVersion,
76
- nextVersion,
77
- project: { name: projectName },
78
- commitEnabled,
79
- pushEnabled,
80
- allowDirty: config.git.allowDirty,
81
- fileOutputPaths,
82
- gitTag,
83
- actions,
84
- }
85
- }
86
-
87
- export const resolveFileOutputPaths = (config: MirrorConfig) => {
88
- const paths: string[] = []
89
-
90
- if (config.version.output.includes('package.json')) paths.push(resolveMirrorPath(config.cwd, config.package.path))
91
- if (config.version.output.includes('jsr.json')) paths.push(resolveMirrorPath(config.cwd, config.jsr.path))
92
-
93
- return paths
94
- }
95
-
96
- export const releaseLabel = (version: string, projectName?: string) => (projectName ? `${projectName}@${version}` : `v${version}`)
97
-
98
- export const planPathForDisplay = (plan: MirrorVersionPlan, path: string) => relativeFromCwd(plan.cwd, path)