@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.
package/source/cli.ts DELETED
@@ -1,285 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import { defineCommand, runMain } from 'citty'
6
- import type { ArgsDef } from 'citty'
7
- import { readFileSync } from 'node:fs'
8
- import { resolve } from 'node:path'
9
- import { MirrorError } from './errors'
10
- import { readCurrentVersion, resolveProjectName } from './adapters'
11
- import { configPathForDisplay, discoverMirrorConfig, loadMirrorConfig, relativeFromCwd, writeInitConfig } from './config'
12
- import { executeVersionPlan } from './executor'
13
- import { parseMirrorCliOptions } from './flags'
14
- import { buildVersionPlan, validateMirrorConfig } from './plan'
15
- import { mirrorBanner, reportConfig, reportConfigSchema, reportExecution, reportExecutionSummary, reportPlan, reportValue } from './reporter'
16
- import type { MirrorAdapterName, MirrorCliOptions } from './types'
17
- import { resolveNextVersion } from './version'
18
-
19
- const mirrorVersion = readInstalledVersion()
20
-
21
- const globalArgs = {
22
- config: { type: 'string', description: 'Path to mirror.config.toml' },
23
- cwd: { type: 'string', description: 'Run as if Mirror started in this directory' },
24
- format: { type: 'enum', options: ['text', 'json'], default: 'text', description: 'Output format' },
25
- 'no-color': { type: 'boolean', description: 'Disable color output' },
26
- } satisfies ArgsDef
27
-
28
- const overrideArgs = {
29
- ...globalArgs,
30
- source: { type: 'enum', options: ['package.json', 'jsr.json', 'git'], description: 'Override version source' },
31
- output: { type: 'string', description: 'Override version output. Repeat or comma-separate values.' },
32
- 'package-file': { type: 'string', description: 'Override package.json path' },
33
- 'jsr-file': { type: 'string', description: 'Override jsr.json path' },
34
- preid: { type: 'string', description: 'Override prerelease identifier' },
35
- } satisfies ArgsDef
36
-
37
- const applyArgs = {
38
- ...overrideArgs,
39
- 'dry-run': { type: 'boolean', alias: 'dy', description: 'Build and print the plan without applying it' },
40
- commit: { type: 'boolean', description: 'Create a release commit when file outputs changed' },
41
- push: { type: 'boolean', description: 'Create the release commit when needed, then push release refs' },
42
- 'allow-dirty': { type: 'boolean', description: 'Allow release in a dirty Git worktree' },
43
- yes: { type: 'boolean', alias: 'y', description: 'Apply without interactive confirmation' },
44
- } satisfies ArgsDef
45
-
46
- const targetArg = {
47
- target: { type: 'positional', description: 'Release target or exact semantic version', required: true },
48
- } satisfies ArgsDef
49
-
50
- export const createMirrorCommand = () =>
51
- defineCommand({
52
- meta: {
53
- name: 'mirror',
54
- version: mirrorVersion,
55
- description: 'Open source project versioning for Bun, npm, JSR, and Git.',
56
- },
57
- args: globalArgs,
58
- subCommands: {
59
- init: createInitCommand(),
60
- config: createConfigCommand(),
61
- version: createVersionCommand(),
62
- },
63
- })
64
-
65
- export const runMirrorCli = async (rawArgs = process.argv.slice(2)) => {
66
- const effectiveArgs = rawArgs.length === 0 ? ['--help'] : rawArgs
67
- const restoreColorOutput = effectiveArgs.includes('--no-color') ? stripColorFromProcessOutput() : () => {}
68
-
69
- try {
70
- if (effectiveArgs.includes('--no-color')) process.env['NO_COLOR'] = '1'
71
-
72
- if (effectiveArgs.includes('--help')) {
73
- const parsed = parseMirrorCliOptions(effectiveArgs)
74
- const cwd = resolve(parsed.cwd ?? process.cwd())
75
- const discovery = await discoverMirrorConfig(cwd, parsed.config)
76
- const configDisplay = discovery.path ? relativeFromCwd(cwd, discovery.path) : ''
77
- process.stdout.write(mirrorBanner(configDisplay))
78
- }
79
-
80
- if (rawArgs.length === 0) {
81
- process.on('exit', () => {
82
- process.stdout.write([
83
- 'EXAMPLES',
84
- '',
85
- ' mirror version current # Print the current version',
86
- ' mirror version plan patch # Preview a patch release plan',
87
- ' mirror version apply minor --commit # Apply a minor release with commit',
88
- ' mirror version plan patch --output=package.json,jsr.json,git # Plan with package, jsr, and git',
89
- ' mirror config schema # Print the configuration file reference',
90
- '',
91
- ].join('\n') + '\n')
92
- })
93
- }
94
-
95
- await runMain(createMirrorCommand(), { rawArgs: effectiveArgs })
96
- } catch (error) {
97
- if (error instanceof MirrorError) {
98
- console.error(error.message)
99
- process.exit(error.exitCode)
100
- }
101
-
102
- throw error
103
- } finally {
104
- restoreColorOutput()
105
- }
106
- }
107
-
108
- const createInitCommand = () =>
109
- defineCommand({
110
- meta: { name: 'init', description: 'Create a Mirror configuration file.' },
111
- subCommands: {
112
- 'package.json': createInitKindCommand('package.json'),
113
- 'jsr.json': createInitKindCommand('jsr.json'),
114
- git: createInitKindCommand('git'),
115
- },
116
- })
117
-
118
- const createInitKindCommand = (kind: MirrorAdapterName) =>
119
- defineCommand({
120
- meta: { name: kind, description: `Create ${kind} project configuration.` },
121
- args: {
122
- ...globalArgs,
123
- yes: { type: 'boolean', description: 'Overwrite existing mirror.config.toml' },
124
- },
125
- async run(context) {
126
- const options = cliOptions(context.rawArgs, context.args)
127
- const path = await writeInitConfig(kind, options.cwd ?? process.cwd(), Boolean(options.yes))
128
- process.stdout.write(reportValue(`created ${path}`, options.format))
129
- },
130
- })
131
-
132
- const createConfigCommand = () =>
133
- defineCommand({
134
- meta: { name: 'config', description: 'Inspect and validate Mirror configuration.' },
135
- subCommands: {
136
- show: defineCommand({
137
- meta: { name: 'show', description: 'Print the resolved configuration.' },
138
- args: overrideArgs,
139
- async run(context) {
140
- const options = cliOptions(context.rawArgs, context.args)
141
- const config = await loadMirrorConfig(options)
142
- if (options.format !== 'json') process.stdout.write(mirrorBanner(configPathForDisplay(config)))
143
- process.stdout.write(reportConfig(config, options.format))
144
- },
145
- }),
146
- check: defineCommand({
147
- meta: { name: 'check', description: 'Validate the resolved configuration.' },
148
- args: overrideArgs,
149
- async run(context) {
150
- const options = cliOptions(context.rawArgs, context.args)
151
- await validateMirrorConfig(options)
152
- process.stdout.write(reportValue('ok', options.format))
153
- },
154
- }),
155
- schema: defineCommand({
156
- meta: { name: 'schema', description: 'Print the configuration file reference.' },
157
- args: globalArgs,
158
- run(context) {
159
- const options = cliOptions(context.rawArgs, context.args)
160
- if (options.format !== 'json') process.stdout.write(mirrorBanner())
161
- process.stdout.write(reportConfigSchema(options.format))
162
- },
163
- }),
164
- },
165
- })
166
-
167
- const createVersionCommand = () =>
168
- defineCommand({
169
- meta: { name: 'version', description: 'Read, plan, and apply version changes.' },
170
- subCommands: {
171
- current: defineCommand({
172
- meta: { name: 'current', description: 'Print the current project version.' },
173
- args: overrideArgs,
174
- async run(context) {
175
- const options = cliOptions(context.rawArgs, context.args)
176
- const config = await loadMirrorConfig(options)
177
- const projectName = await resolveProjectName(config)
178
- process.stdout.write(reportValue(await readCurrentVersion(config, projectName), options.format))
179
- },
180
- }),
181
- next: defineCommand({
182
- meta: { name: 'next', description: 'Print the next version without checking outputs.' },
183
- args: { ...overrideArgs, ...targetArg },
184
- async run(context) {
185
- const options = cliOptions(context.rawArgs, context.args)
186
- const config = await loadMirrorConfig(options)
187
- const projectName = await resolveProjectName(config)
188
- const currentVersion = await readCurrentVersion(config, projectName)
189
- process.stdout.write(reportValue(resolveNextVersion(currentVersion, String(context.args['target']), config.version.prereleaseId), options.format))
190
- },
191
- }),
192
- plan: defineCommand({
193
- meta: { name: 'plan', description: 'Print the release plan without writing anything.' },
194
- args: { ...overrideArgs, ...targetArg },
195
- async run(context) {
196
- const options = cliOptions(context.rawArgs, context.args)
197
- const plan = await buildVersionPlan(String(context.args['target']), options)
198
- if (options.format !== 'json') process.stdout.write(mirrorBanner(plan.configPath ? plan.configPath : ''))
199
- process.stdout.write(reportPlan(plan, options.format))
200
- },
201
- }),
202
- apply: defineCommand({
203
- meta: { name: 'apply', description: 'Apply the release plan.' },
204
- args: { ...applyArgs, ...targetArg },
205
- async run(context) {
206
- const options = cliOptions(context.rawArgs, context.args)
207
- const plan = await buildVersionPlan(String(context.args['target']), options)
208
-
209
- if (options.format !== 'json') process.stdout.write(mirrorBanner(plan.configPath ? plan.configPath : ''))
210
- if (options.format !== 'json') process.stdout.write(reportPlan(plan, options.format))
211
-
212
- const result = await executeVersionPlan(plan, options)
213
- process.stdout.write(options.format === 'json' ? reportExecution(result, options.format) : reportExecutionSummary(result, options.format))
214
- },
215
- }),
216
- },
217
- })
218
-
219
- const cliOptions = (rawArgs: string[], args: Record<string, unknown>): MirrorCliOptions => {
220
- const parsed = parseMirrorCliOptions(rawArgs)
221
-
222
- return {
223
- ...parsed,
224
- config: parsed.config ?? stringArg(args['config']),
225
- cwd: parsed.cwd ?? stringArg(args['cwd']),
226
- format: parsed.format ?? (args['format'] === 'json' ? 'json' : 'text'),
227
- source: parsed.source ?? adapterArg(args['source']),
228
- output: parsed.output ?? outputArg(args['output']),
229
- packageFile: parsed.packageFile ?? stringArg(args['packageFile']),
230
- jsrFile: parsed.jsrFile ?? stringArg(args['jsrFile']),
231
- preid: parsed.preid ?? stringArg(args['preid']),
232
- dryRun: parsed.dryRun || args['dryRun'] === true,
233
- commit: parsed.commit || args['commit'] === true,
234
- push: parsed.push || args['push'] === true,
235
- allowDirty: parsed.allowDirty || args['allowDirty'] === true,
236
- yes: parsed.yes || args['yes'] === true,
237
- }
238
- }
239
-
240
- const stringArg = (value: unknown) => (typeof value === 'string' ? value : undefined)
241
-
242
- const adapterArg = (value: unknown) => {
243
- if (value === 'package.json' || value === 'jsr.json' || value === 'git') return value
244
- return undefined
245
- }
246
-
247
- const outputArg = (value: unknown): MirrorCliOptions['output'] => {
248
- if (typeof value !== 'string') return undefined
249
-
250
- const values = value.split(',').map((item) => item.trim()).filter(Boolean)
251
-
252
- if (values.length === 0) return undefined
253
-
254
- return values.map((item) => {
255
- const adapter = adapterArg(item)
256
- if (!adapter) throw new MirrorError(`Invalid --output value: ${item}`)
257
- return adapter
258
- })
259
- }
260
-
261
- function readInstalledVersion() {
262
- try {
263
- const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as Record<string, unknown>
264
- return typeof packageJson['version'] === 'string' ? packageJson['version'] : '0.0.0'
265
- } catch {
266
- return '0.0.0'
267
- }
268
- }
269
-
270
- const stripAnsi = (value: string) => value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '')
271
-
272
- const stripColorFromProcessOutput = () => {
273
- const originalLog = console.log
274
- const originalError = console.error
275
-
276
- console.log = (...values: unknown[]) => originalLog(...values.map(stripAnsiValue))
277
- console.error = (...values: unknown[]) => originalError(...values.map(stripAnsiValue))
278
-
279
- return () => {
280
- console.log = originalLog
281
- console.error = originalError
282
- }
283
- }
284
-
285
- const stripAnsiValue = (value: unknown) => (typeof value === 'string' ? stripAnsi(value) : value)
package/source/config.ts DELETED
@@ -1,224 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import { existsSync } from 'node:fs'
6
- import { basename, isAbsolute, join, relative, resolve } from 'node:path'
7
- import type {
8
- MirrorAdapterName,
9
- MirrorCliOptions,
10
- MirrorConfig,
11
- MirrorConfigDiscovery,
12
- MirrorProjectNameSource,
13
- MirrorRawConfig,
14
- } from './types'
15
- import { MirrorError } from './errors'
16
-
17
- const adapters = new Set(['package.json', 'jsr.json', 'git'])
18
- const projectNameSources = new Set(['package.json', 'jsr.json'])
19
-
20
- export const resolveMirrorPath = (cwd: string, path: string) => (isAbsolute(path) ? path : resolve(cwd, path))
21
-
22
- export const relativeFromCwd = (cwd: string, path: string) => {
23
- const relativePath = relative(cwd, resolveMirrorPath(cwd, path))
24
- return relativePath || '.'
25
- }
26
-
27
- export const discoverMirrorConfig = async (cwd: string, explicitPath?: string): Promise<MirrorConfigDiscovery> => {
28
- if (explicitPath) {
29
- const configPath = resolveMirrorPath(cwd, explicitPath)
30
- return { path: configPath, raw: await readConfigFile(configPath) }
31
- }
32
-
33
- const rootConfigPath = resolve(cwd, 'mirror.config.toml')
34
- if (existsSync(rootConfigPath)) return { path: rootConfigPath, raw: await readConfigFile(rootConfigPath) }
35
-
36
- const nestedConfigPath = resolve(cwd, 'config', 'mirror.config.toml')
37
- if (existsSync(nestedConfigPath)) return { path: nestedConfigPath, raw: await readConfigFile(nestedConfigPath) }
38
-
39
- return {}
40
- }
41
-
42
- export const readConfigFile = async (path: string): Promise<MirrorRawConfig> => {
43
- if (!existsSync(path)) throw new MirrorError(`Configuration file not found: ${path}`)
44
-
45
- const parsed = Bun.TOML.parse(await Bun.file(path).text())
46
-
47
- if (!isRecord(parsed)) throw new MirrorError(`Configuration file must contain a TOML object: ${path}`)
48
-
49
- return parsed as MirrorRawConfig
50
- }
51
-
52
- export const loadMirrorConfig = async (options: MirrorCliOptions = {}): Promise<MirrorConfig> => {
53
- const cwd = resolve(options.cwd ?? process.cwd())
54
- const discovered = await discoverMirrorConfig(cwd, options.config)
55
-
56
- if (!discovered.raw) throw new MirrorError('Mirror configuration not found. Run `mirror init package`, `mirror init jsr`, or `mirror init git`.')
57
-
58
- return normalizeMirrorConfig(discovered.raw, cwd, discovered.path, options)
59
- }
60
-
61
- export const normalizeMirrorConfig = (
62
- raw: MirrorRawConfig,
63
- cwd: string,
64
- configPath: string | undefined,
65
- options: MirrorCliOptions = {},
66
- ): MirrorConfig => {
67
- if (raw.schema !== 1) throw new MirrorError('Unsupported or missing configuration schema. Expected `schema = 1`.')
68
- if (raw.version?.scheme !== undefined && raw.version.scheme !== 'semver') throw new MirrorError('Only `version.scheme = "semver"` is supported.')
69
-
70
- const source = options.source ?? assertAdapter(raw.version?.source, 'version.source')
71
- const output = dedupeAdapters(options.output ?? assertOutput(raw.version?.output))
72
- const nameSource: MirrorProjectNameSource | undefined = raw.project?.name_source
73
- ? assertProjectNameSource(raw.project.name_source, 'project.name_source')
74
- : undefined
75
- const projectName = optionalString(raw.project?.name, 'project.name')
76
- const prereleaseId = options.preid ?? optionalString(raw.version?.prerelease_id, 'version.prerelease_id') ?? ''
77
- const packagePath = options.packageFile ?? optionalString(raw.package?.path, 'package.path') ?? 'package.json'
78
- const jsrPath = options.jsrFile ?? optionalString(raw.jsr?.path, 'jsr.path') ?? 'jsr.json'
79
- const tagTemplate = optionalString(raw.git?.tag_template, 'git.tag_template') ?? 'v{version}'
80
- const gitCommit = optionalBoolean(raw.git?.commit, 'git.commit') === true
81
- const gitPush = optionalBoolean(raw.git?.push, 'git.push') === true
82
- const gitAllowDirty = optionalBoolean(raw.git?.allow_dirty, 'git.allow_dirty') === true
83
-
84
- return {
85
- schema: 1,
86
- cwd,
87
- configPath,
88
- project: {
89
- name: projectName,
90
- nameSource,
91
- },
92
- version: {
93
- scheme: 'semver',
94
- source,
95
- output,
96
- prereleaseId,
97
- },
98
- package: {
99
- path: packagePath,
100
- },
101
- jsr: {
102
- path: jsrPath,
103
- },
104
- git: {
105
- tagTemplate,
106
- commit: options.commit === true || options.push === true || gitCommit || gitPush,
107
- push: options.push === true || gitPush,
108
- allowDirty: options.allowDirty === true || gitAllowDirty,
109
- },
110
- }
111
- }
112
-
113
- export const createInitConfig = (kind: MirrorAdapterName, cwd: string) => {
114
- const projectName = basename(cwd)
115
-
116
- if (kind === 'package.json') {
117
- return `schema = 1
118
-
119
- [project]
120
- name_source = "package.json"
121
-
122
- [version]
123
- scheme = "semver"
124
- source = "package.json"
125
- output = ["package.json"]
126
- prerelease_id = ""
127
-
128
- [package]
129
- path = "package.json"
130
-
131
- [jsr]
132
- path = "jsr.json"
133
-
134
- [git]
135
- tag_template = "{name}@{version}"
136
- commit = false
137
- push = false
138
- allow_dirty = false
139
- `
140
- }
141
-
142
- if (kind === 'jsr.json') {
143
- return `schema = 1
144
-
145
- [project]
146
- name_source = "jsr.json"
147
-
148
- [version]
149
- scheme = "semver"
150
- source = "jsr.json"
151
- output = ["jsr.json"]
152
- prerelease_id = ""
153
-
154
- [jsr]
155
- path = "jsr.json"
156
-
157
- [git]
158
- tag_template = "{name}@{version}"
159
- commit = false
160
- push = false
161
- allow_dirty = false
162
- `
163
- }
164
-
165
- return `schema = 1
166
-
167
- [project]
168
- name = "${projectName}"
169
-
170
- [version]
171
- scheme = "semver"
172
- source = "git"
173
- output = ["git"]
174
- prerelease_id = ""
175
-
176
- [git]
177
- tag_template = "v{version}"
178
- commit = false
179
- push = false
180
- allow_dirty = false
181
- `
182
- }
183
-
184
- export const writeInitConfig = async (kind: MirrorAdapterName, cwd: string, overwrite = false) => {
185
- const path = join(cwd, 'mirror.config.toml')
186
-
187
- if (existsSync(path) && !overwrite) throw new MirrorError(`Configuration already exists: ${path}`)
188
-
189
- await Bun.write(path, createInitConfig(kind, cwd))
190
- return path
191
- }
192
-
193
- export const configPathForDisplay = (config: MirrorConfig) => (config.configPath ? relativeFromCwd(config.cwd, config.configPath) : '(none)')
194
-
195
- const assertAdapter = (value: unknown, key: string): MirrorAdapterName => {
196
- if (typeof value !== 'string' || !adapters.has(value)) throw new MirrorError(`Invalid or missing ${key}. Expected package.json, jsr.json, or git.`)
197
- return value as MirrorAdapterName
198
- }
199
-
200
- const assertProjectNameSource = (value: unknown, key: string): MirrorProjectNameSource => {
201
- if (typeof value !== 'string' || !projectNameSources.has(value)) throw new MirrorError(`Invalid ${key}. Expected package.json or jsr.json.`)
202
- return value as MirrorProjectNameSource
203
- }
204
-
205
- const assertOutput = (value: unknown): MirrorAdapterName[] => {
206
- if (!Array.isArray(value) || value.length === 0) throw new MirrorError('Invalid or missing version.output. Expected at least one output adapter.')
207
- return value.map((item) => assertAdapter(item, 'version.output'))
208
- }
209
-
210
- const dedupeAdapters = (value: MirrorAdapterName[]) => [...new Set(value)]
211
-
212
- const optionalString = (value: unknown, key: string) => {
213
- if (value === undefined) return undefined
214
- if (typeof value !== 'string') throw new MirrorError(`Invalid ${key}. Expected a string.`)
215
- return value
216
- }
217
-
218
- const optionalBoolean = (value: unknown, key: string) => {
219
- if (value === undefined) return undefined
220
- if (typeof value !== 'boolean') throw new MirrorError(`Invalid ${key}. Expected true or false.`)
221
- return value
222
- }
223
-
224
- const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)
package/source/errors.ts DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- export class MirrorError extends Error {
6
- readonly exitCode: number
7
-
8
- constructor(message: string, exitCode = 1) {
9
- super(message)
10
- this.name = 'MirrorError'
11
- this.exitCode = exitCode
12
- }
13
- }
14
-
15
- export const invariant = (condition: unknown, message: string): asserts condition => {
16
- if (!condition) throw new MirrorError(message)
17
- }
@@ -1,39 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import type { MirrorCliOptions, MirrorExecutionResult } from './types'
6
- import { MirrorError } from './errors'
7
- import { createGitCommit, createGitTag, isGitDirty, isGitRepository, pushGitRefs, writeJsrVersion, writePackageVersion } from './adapters'
8
- import { loadMirrorConfig } from './config'
9
- import { buildVersionPlan } from './plan'
10
-
11
- export const applyVersionPlan = async (target: string, options: MirrorCliOptions = {}): Promise<MirrorExecutionResult> => {
12
- const plan = await buildVersionPlan(target, options)
13
-
14
- return executeVersionPlan(plan, options)
15
- }
16
-
17
- export const executeVersionPlan = async (
18
- plan: MirrorExecutionResult['plan'],
19
- options: MirrorCliOptions = {},
20
- ): Promise<MirrorExecutionResult> => {
21
- if (options.dryRun) return { plan, applied: false, dryRun: true }
22
- if (!plan.allowDirty && (await isGitRepository(plan.cwd)) && (await isGitDirty(plan.cwd))) {
23
- throw new MirrorError('Git worktree is dirty. Commit changes or pass --allow-dirty.')
24
- }
25
- if (!options.yes) throw new MirrorError('Refusing to apply without confirmation. Pass --yes to apply the plan.')
26
-
27
- const config = await loadMirrorConfig(options)
28
-
29
- if (config.version.output.includes('package.json')) await writePackageVersion(config, plan.nextVersion)
30
- if (config.version.output.includes('jsr.json')) await writeJsrVersion(config, plan.nextVersion)
31
-
32
- for (const action of plan.actions) {
33
- if (action.type === 'git-commit') await createGitCommit(plan.cwd, action.paths, action.message)
34
- if (action.type === 'git-tag') await createGitTag(plan.cwd, action.tag)
35
- if (action.type === 'git-push') await pushGitRefs(plan.cwd, action.includeCommit, action.includeTags)
36
- }
37
-
38
- return { plan, applied: true, dryRun: false }
39
- }
package/source/flags.ts DELETED
@@ -1,84 +0,0 @@
1
- /**
2
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
3
- */
4
-
5
- import type { MirrorAdapterName, MirrorCliOptions, MirrorFormat } from './types'
6
- import { MirrorError } from './errors'
7
-
8
- const booleanFlags = new Set(['dry-run', 'commit', 'push', 'allow-dirty', 'yes', 'no-color', 'help', 'version'])
9
- const adapterNames = new Set(['package.json', 'jsr.json', 'git'])
10
-
11
- const shortFlagAliases: Record<string, string> = {
12
- '-dy': '--dry-run',
13
- '-y': '--yes',
14
- }
15
-
16
- const normalizeKey = (key: string) => key.replace(/-([a-z])/g, (_match, letter: string) => letter.toUpperCase())
17
-
18
- const expandShortFlags = (rawArgs: string[]) => rawArgs.map((token) => shortFlagAliases[token] ?? token)
19
-
20
- export const parseMirrorCliOptions = (rawArgs: string[]): MirrorCliOptions => {
21
- const parsed: Record<string, string | boolean | string[]> = {}
22
- const args = expandShortFlags(rawArgs)
23
-
24
- for (let index = 0; index < args.length; index += 1) {
25
- const token = args[index]
26
-
27
- if (!token?.startsWith('--')) continue
28
-
29
- const withoutPrefix = token.slice(2)
30
- const equalsIndex = withoutPrefix.indexOf('=')
31
- const rawKey = equalsIndex >= 0 ? withoutPrefix.slice(0, equalsIndex) : withoutPrefix
32
- const key = normalizeKey(rawKey)
33
-
34
- if (booleanFlags.has(rawKey)) {
35
- parsed[key] = true
36
- continue
37
- }
38
-
39
- const value =
40
- equalsIndex >= 0
41
- ? withoutPrefix.slice(equalsIndex + 1)
42
- : args[index + 1] && !args[index + 1]?.startsWith('-')
43
- ? args[++index] ?? ''
44
- : ''
45
-
46
- if (!value) throw new MirrorError(`Missing value for --${rawKey}`)
47
-
48
- if (key === 'output') {
49
- const nextValues = value.split(',').map((item) => item.trim()).filter(Boolean)
50
- const current = parsed['output']
51
- parsed['output'] = [...(Array.isArray(current) ? current : current ? [String(current)] : []), ...nextValues]
52
- continue
53
- }
54
-
55
- parsed[key] = value
56
- }
57
-
58
- return {
59
- cwd: typeof parsed['cwd'] === 'string' ? parsed['cwd'] : undefined,
60
- config: typeof parsed['config'] === 'string' ? parsed['config'] : undefined,
61
- format: typeof parsed['format'] === 'string' ? assertFormat(parsed['format']) : undefined,
62
- noColor: parsed['noColor'] === true,
63
- source: typeof parsed['source'] === 'string' ? assertAdapter(parsed['source'], '--source') : undefined,
64
- output: Array.isArray(parsed['output']) ? parsed['output'].map((value) => assertAdapter(value, '--output')) : undefined,
65
- packageFile: typeof parsed['packageFile'] === 'string' ? parsed['packageFile'] : undefined,
66
- jsrFile: typeof parsed['jsrFile'] === 'string' ? parsed['jsrFile'] : undefined,
67
- preid: typeof parsed['preid'] === 'string' ? parsed['preid'] : undefined,
68
- dryRun: parsed['dryRun'] === true,
69
- commit: parsed['commit'] === true,
70
- push: parsed['push'] === true,
71
- allowDirty: parsed['allowDirty'] === true,
72
- yes: parsed['yes'] === true,
73
- }
74
- }
75
-
76
- const assertAdapter = (value: string, flagName: string): MirrorAdapterName => {
77
- if (!adapterNames.has(value)) throw new MirrorError(`Invalid ${flagName} value: ${value}`)
78
- return value as MirrorAdapterName
79
- }
80
-
81
- const assertFormat = (value: string): MirrorFormat => {
82
- if (value !== 'text' && value !== 'json') throw new MirrorError(`Invalid --format value: ${value}`)
83
- return value
84
- }
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
4
- */
5
-
6
- import { runMirrorCli } from './guiho-mirror'
7
-
8
- await runMirrorCli()