@guiho/mirror 3.0.0-alpha.4
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/CHANGELOG.md +16 -0
- package/LICENSE.md +23 -0
- package/README.md +190 -0
- package/bin/mirror.exe +0 -0
- package/jsr.json +12 -0
- package/library/adapters.d.ts +27 -0
- package/library/adapters.d.ts.map +1 -0
- package/library/adapters.js +152 -0
- package/library/cli.d.ts +25 -0
- package/library/cli.d.ts.map +1 -0
- package/library/cli.js +258 -0
- package/library/config.d.ts +14 -0
- package/library/config.d.ts.map +1 -0
- package/library/config.js +193 -0
- package/library/errors.d.ts +9 -0
- package/library/errors.d.ts.map +1 -0
- package/library/errors.js +15 -0
- package/library/executor.d.ts +7 -0
- package/library/executor.d.ts.map +1 -0
- package/library/executor.js +34 -0
- package/library/flags.d.ts +6 -0
- package/library/flags.d.ts.map +1 -0
- package/library/flags.js +69 -0
- package/library/guiho-mirror-bin.d.ts +6 -0
- package/library/guiho-mirror-bin.d.ts.map +1 -0
- package/library/guiho-mirror-bin.js +6 -0
- package/library/guiho-mirror.d.ts +14 -0
- package/library/guiho-mirror.d.ts.map +1 -0
- package/library/guiho-mirror.js +12 -0
- package/library/plan.d.ts +10 -0
- package/library/plan.d.ts.map +1 -0
- package/library/plan.js +81 -0
- package/library/reporter.d.ts +12 -0
- package/library/reporter.d.ts.map +1 -0
- package/library/reporter.js +121 -0
- package/library/types.d.ts +123 -0
- package/library/types.d.ts.map +1 -0
- package/library/types.js +4 -0
- package/library/version.d.ts +10 -0
- package/library/version.d.ts.map +1 -0
- package/library/version.js +31 -0
- package/package.json +81 -0
- package/source/adapters.ts +176 -0
- package/source/cli.ts +285 -0
- package/source/config.ts +224 -0
- package/source/errors.ts +17 -0
- package/source/executor.ts +39 -0
- package/source/flags.ts +84 -0
- package/source/guiho-mirror-bin.ts +8 -0
- package/source/guiho-mirror.spec.ts +501 -0
- package/source/guiho-mirror.ts +44 -0
- package/source/plan.ts +98 -0
- package/source/reporter.ts +127 -0
- package/source/types.ts +128 -0
- package/source/version.ts +39 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 3.0.0-alpha.2 (2026-05-17)
|
|
4
|
+
|
|
5
|
+
Initial open source release.
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- TOML-based configuration with `mirror.config.toml`
|
|
10
|
+
- Semantic versioning with `semver` support
|
|
11
|
+
- Adapters for `package.json`, `jsr.json`, and Git tags
|
|
12
|
+
- CLI with `mirror init`, `mirror config`, and `mirror version` commands
|
|
13
|
+
- Dry-run mode, dirty worktree protection, and interactive confirmation
|
|
14
|
+
- Text and JSON output formats
|
|
15
|
+
- Git commit, tag, and push automation
|
|
16
|
+
- Prerelease identifier support
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
@guiho/mirror
|
|
2
|
+
|
|
3
|
+
The MIT License (MIT)
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026 Cristóvão GUIHO
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# 🪞 GUIHO Mirror
|
|
2
|
+
|
|
3
|
+
**Open source project versioning for Bun, npm, JSR, and Git.**
|
|
4
|
+
|
|
5
|
+
Mirror is a powerful, deterministic CLI and TypeScript library for semantic project versioning. It reads a single version source, calculates the next semantic version, builds a transparent release plan, and safely applies it to configured outputs like `package.json`, `jsr.json`, and Git tags.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
source -> version engine -> plan -> outputs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🚀 Quick Start
|
|
14
|
+
|
|
15
|
+
### Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add -d @guiho/mirror
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Initializing
|
|
22
|
+
|
|
23
|
+
Create a default configuration file for your project type:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mirror init package.json
|
|
27
|
+
# or
|
|
28
|
+
mirror init jsr.json
|
|
29
|
+
# or
|
|
30
|
+
mirror init git
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Typical Workflow
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# See current version
|
|
37
|
+
mirror version current
|
|
38
|
+
|
|
39
|
+
# Preview a patch release
|
|
40
|
+
mirror version plan patch
|
|
41
|
+
|
|
42
|
+
# Apply a minor release and create a Git commit & tag
|
|
43
|
+
mirror version apply minor --commit --yes
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 📖 Documentation
|
|
49
|
+
|
|
50
|
+
Mirror's architecture is based on a predictable read-plan-apply lifecycle, making it safe and easy to automate. It is designed to be easily consumed by humans and AI agents.
|
|
51
|
+
|
|
52
|
+
### Project Model
|
|
53
|
+
|
|
54
|
+
Mirror uses a strict release model:
|
|
55
|
+
- **Project:** The package, repository, application, or directory being versioned.
|
|
56
|
+
- **Source:** The single adapter Mirror reads the current version from.
|
|
57
|
+
- **Output:** The adapters Mirror writes the next version to.
|
|
58
|
+
- **Plan:** The read-only description of all intended changes before any mutation occurs.
|
|
59
|
+
|
|
60
|
+
### Adapters
|
|
61
|
+
|
|
62
|
+
Adapters connect Mirror to different versioning ecosystems:
|
|
63
|
+
- `package.json`: Reads/writes the `version` field in a `package.json` file.
|
|
64
|
+
- `jsr.json`: Reads/writes the `version` field in a `jsr.json` file.
|
|
65
|
+
- `git`: Reads versions from Git tags and creates release tags/commits.
|
|
66
|
+
|
|
67
|
+
### CLI Commands
|
|
68
|
+
|
|
69
|
+
Mirror provides a concise CLI with three main command groups:
|
|
70
|
+
|
|
71
|
+
#### `mirror init`
|
|
72
|
+
Creates a `mirror.config.toml` file in the current directory.
|
|
73
|
+
- `mirror init package.json`
|
|
74
|
+
- `mirror init jsr.json`
|
|
75
|
+
- `mirror init git`
|
|
76
|
+
|
|
77
|
+
#### `mirror config`
|
|
78
|
+
Validates and inspects configuration.
|
|
79
|
+
- `mirror config show`: Prints the resolved configuration.
|
|
80
|
+
- `mirror config check`: Validates configuration without output.
|
|
81
|
+
- `mirror config schema`: Prints the comprehensive configuration reference.
|
|
82
|
+
|
|
83
|
+
#### `mirror version`
|
|
84
|
+
Manages the version lifecycle.
|
|
85
|
+
- `mirror version current`: Prints the current project version.
|
|
86
|
+
- `mirror version next <target>`: Prints the next version without side-effects.
|
|
87
|
+
- `mirror version plan <target>`: Builds and prints the release plan.
|
|
88
|
+
- `mirror version apply <target>`: Applies the release plan.
|
|
89
|
+
|
|
90
|
+
*Targets supported:* `major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, `prerelease`, or an exact semantic version (e.g., `2.0.0`).
|
|
91
|
+
|
|
92
|
+
### Configuration (`mirror.config.toml`)
|
|
93
|
+
|
|
94
|
+
Mirror looks for configuration via the `--config <path>` flag, `./mirror.config.toml`, or `./config/mirror.config.toml`.
|
|
95
|
+
|
|
96
|
+
```toml
|
|
97
|
+
schema = 1
|
|
98
|
+
|
|
99
|
+
[project]
|
|
100
|
+
name = "my-project" # Optional. Explicit project name.
|
|
101
|
+
name_source = "package.json" # Optional. "package.json" or "jsr.json"
|
|
102
|
+
|
|
103
|
+
[version]
|
|
104
|
+
scheme = "semver" # Required. Only "semver" is supported.
|
|
105
|
+
source = "package.json" # Required. "package.json", "jsr.json", or "git"
|
|
106
|
+
output = ["package.json", "git"] # Required. Adapters to write to.
|
|
107
|
+
prerelease_id = "alpha" # Optional. e.g., creates 1.0.1-alpha.0
|
|
108
|
+
|
|
109
|
+
[package]
|
|
110
|
+
path = "package.json" # Optional. Override path to package.json
|
|
111
|
+
|
|
112
|
+
[jsr]
|
|
113
|
+
path = "jsr.json" # Optional. Override path to jsr.json
|
|
114
|
+
|
|
115
|
+
[git]
|
|
116
|
+
tag_template = "{name}@{version}" # Optional. Supported: "v{version}", "{name}@{version}", "{name}/v{version}"
|
|
117
|
+
commit = false # Optional. Create release commits. Default: false.
|
|
118
|
+
push = false # Optional. Push release refs. Default: false.
|
|
119
|
+
allow_dirty = false # Optional. Allow dirty Git worktree. Default: false.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Safety & Git Automation
|
|
123
|
+
|
|
124
|
+
By default, `mirror version apply` prevents accidental mutations:
|
|
125
|
+
- Fails on a dirty Git worktree (unless `--allow-dirty` is used).
|
|
126
|
+
- Requires `--yes` to skip interactive confirmation (non-interactive environments).
|
|
127
|
+
- Never pushes release refs automatically unless configured or `--push` is passed.
|
|
128
|
+
|
|
129
|
+
When combining file outputs (`package.json`, `jsr.json`) with Git tag output, Mirror requires either `--commit` or `--push` so that the tag attaches to the release commit containing the updated files. For Git-only outputs, `--commit` will not create an empty commit; it simply tags `HEAD`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 💻 API Reference
|
|
134
|
+
|
|
135
|
+
Mirror exposes a fully-typed TypeScript API for custom automation scripts.
|
|
136
|
+
|
|
137
|
+
### Core Lifecycle Methods
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { buildVersionPlan, applyVersionPlan, executeVersionPlan } from '@guiho/mirror'
|
|
141
|
+
|
|
142
|
+
// 1. Build a read-only plan for a patch release
|
|
143
|
+
const plan = await buildVersionPlan('patch', { cwd: process.cwd() })
|
|
144
|
+
|
|
145
|
+
// 2. Inspect the plan
|
|
146
|
+
console.log(plan.currentVersion) // "1.0.0"
|
|
147
|
+
console.log(plan.nextVersion) // "1.0.1"
|
|
148
|
+
console.log(plan.actions) // Array of actions (write-file, git-commit, etc.)
|
|
149
|
+
|
|
150
|
+
// 3. Execute the plan manually
|
|
151
|
+
const result = await executeVersionPlan(plan, { dryRun: false, yes: true })
|
|
152
|
+
|
|
153
|
+
// OR: Build and apply in one step
|
|
154
|
+
await applyVersionPlan('minor', { cwd: process.cwd(), yes: true })
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Reading State
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { loadMirrorConfig, readCurrentVersion } from '@guiho/mirror'
|
|
161
|
+
|
|
162
|
+
// Load resolved configuration
|
|
163
|
+
const config = await loadMirrorConfig({ cwd: process.cwd() })
|
|
164
|
+
|
|
165
|
+
// Read the current version using the configured source
|
|
166
|
+
const version = await readCurrentVersion(config)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🛠️ Development
|
|
172
|
+
|
|
173
|
+
Development tasks require Bun and run from the `mirror/` directory:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
cd mirror
|
|
177
|
+
bun install
|
|
178
|
+
bun run typecheck
|
|
179
|
+
bun test
|
|
180
|
+
bun run build
|
|
181
|
+
bun run binary
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 🎵 The Origin of the Name
|
|
187
|
+
|
|
188
|
+
The name **Mirror** was inspired by the music track ["MIRROR" by SleepyThePrince & DJ Dadda](https://www.youtube.com/watch?v=vi0didVAqjU), featured on the album *"Blackout"*.
|
|
189
|
+
|
|
190
|
+
In a deeper sense, the name also fits the purpose of the library perfectly: just as looking in a mirror reflects an exact snapshot of who we are, this library reflects the exact state and version of whatever you are building.
|
package/bin/mirror.exe
ADDED
|
Binary file
|
package/jsr.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import type { MirrorConfig, MirrorJsonObject } from './types';
|
|
5
|
+
export declare const supportedGitTagTemplates: readonly ["v{version}", "{name}@{version}", "{name}/v{version}"];
|
|
6
|
+
export declare const readPackageJson: (path: string) => Promise<MirrorJsonObject>;
|
|
7
|
+
export declare const readJsrJson: (path: string) => Promise<MirrorJsonObject>;
|
|
8
|
+
export declare const writeJsonObject: (path: string, object: MirrorJsonObject) => Promise<void>;
|
|
9
|
+
export declare const readPackageVersion: (config: MirrorConfig) => Promise<string>;
|
|
10
|
+
export declare const readJsrVersion: (config: MirrorConfig) => Promise<string>;
|
|
11
|
+
export declare const readPackageName: (config: MirrorConfig) => Promise<string>;
|
|
12
|
+
export declare const readJsrName: (config: MirrorConfig) => Promise<string>;
|
|
13
|
+
export declare const writePackageVersion: (config: MirrorConfig, nextVersion: string) => Promise<void>;
|
|
14
|
+
export declare const writeJsrVersion: (config: MirrorConfig, nextVersion: string) => Promise<void>;
|
|
15
|
+
export declare const ensureAdapterFiles: (config: MirrorConfig) => Promise<void>;
|
|
16
|
+
export declare const resolveProjectName: (config: MirrorConfig) => Promise<string | undefined>;
|
|
17
|
+
export declare const readCurrentVersion: (config: MirrorConfig, projectName?: string) => Promise<string>;
|
|
18
|
+
export declare const readGitVersion: (config: MirrorConfig, projectName?: string) => Promise<string>;
|
|
19
|
+
export declare const renderGitTag: (template: string, version: string, projectName?: string) => string;
|
|
20
|
+
export declare const versionFromTag: (template: string, tag: string, projectName?: string) => string | undefined;
|
|
21
|
+
export declare const assertSupportedGitTagTemplate: (template: string) => void;
|
|
22
|
+
export declare const isGitRepository: (cwd: string) => Promise<boolean>;
|
|
23
|
+
export declare const isGitDirty: (cwd: string) => Promise<boolean>;
|
|
24
|
+
export declare const createGitCommit: (cwd: string, paths: string[], message: string) => Promise<void>;
|
|
25
|
+
export declare const createGitTag: (cwd: string, tag: string) => Promise<void>;
|
|
26
|
+
export declare const pushGitRefs: (cwd: string, includeCommit: boolean, includeTags: boolean) => Promise<void>;
|
|
27
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../source/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK7D,eAAO,MAAM,wBAAwB,kEAAmE,CAAA;AAExG,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAyC,CAAA;AACtH,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAqC,CAAA;AAE9G,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,EAAE,QAAQ,gBAAgB,kBAE3E,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,oBAAyF,CAAA;AACtJ,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,oBAAiF,CAAA;AAC1I,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,oBAAsF,CAAA;AAChJ,eAAO,MAAM,WAAW,GAAU,QAAQ,YAAY,oBAA8E,CAAA;AAEpI,eAAO,MAAM,mBAAmB,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACiB,CAAA;AAEpG,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACa,CAAA;AAE5F,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,kBAI5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,gCAK5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAIlF,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAc9E,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,SAAS,MAAM,EAAE,cAAc,MAAM,WAOnF,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,EAAE,KAAK,MAAM,EAAE,cAAc,MAAM,uBAmBjF,CAAA;AAED,eAAO,MAAM,6BAA6B,GAAI,UAAU,MAAM,SAI7D,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,qBAOhD,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,KAAK,MAAM,qBAG3C,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,EAAE,SAAS,MAAM,kBAGlF,CAAA;AAED,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,KAAK,MAAM,kBAE1D,CAAA;AAED,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,eAAe,OAAO,EAAE,aAAa,OAAO,kBAG1F,CAAA"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { $ } from 'bun';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { MirrorError } from './errors';
|
|
7
|
+
import { assertValidSemver, sortSemverDescending } from './version';
|
|
8
|
+
import { resolveMirrorPath } from './config';
|
|
9
|
+
export const supportedGitTagTemplates = ['v{version}', '{name}@{version}', '{name}/v{version}'];
|
|
10
|
+
export const readPackageJson = async (path) => readJsonObject(path, 'package.json');
|
|
11
|
+
export const readJsrJson = async (path) => readJsonObject(path, 'jsr.json');
|
|
12
|
+
export const writeJsonObject = async (path, object) => {
|
|
13
|
+
await Bun.write(path, `${JSON.stringify(object, null, 2)}\n`);
|
|
14
|
+
};
|
|
15
|
+
export const readPackageVersion = async (config) => readVersionField(resolveMirrorPath(config.cwd, config.package.path), 'package.json');
|
|
16
|
+
export const readJsrVersion = async (config) => readVersionField(resolveMirrorPath(config.cwd, config.jsr.path), 'jsr.json');
|
|
17
|
+
export const readPackageName = async (config) => readNameField(resolveMirrorPath(config.cwd, config.package.path), 'package.json');
|
|
18
|
+
export const readJsrName = async (config) => readNameField(resolveMirrorPath(config.cwd, config.jsr.path), 'jsr.json');
|
|
19
|
+
export const writePackageVersion = async (config, nextVersion) => writeVersionField(resolveMirrorPath(config.cwd, config.package.path), 'package.json', nextVersion);
|
|
20
|
+
export const writeJsrVersion = async (config, nextVersion) => writeVersionField(resolveMirrorPath(config.cwd, config.jsr.path), 'jsr.json', nextVersion);
|
|
21
|
+
export const ensureAdapterFiles = async (config) => {
|
|
22
|
+
if (usesAdapter(config, 'package.json'))
|
|
23
|
+
ensureFile(resolveMirrorPath(config.cwd, config.package.path), 'package.json');
|
|
24
|
+
if (usesAdapter(config, 'jsr.json'))
|
|
25
|
+
ensureFile(resolveMirrorPath(config.cwd, config.jsr.path), 'jsr.json');
|
|
26
|
+
if (usesAdapter(config, 'git'))
|
|
27
|
+
await ensureGitRepository(config.cwd);
|
|
28
|
+
};
|
|
29
|
+
export const resolveProjectName = async (config) => {
|
|
30
|
+
if (config.project.name)
|
|
31
|
+
return config.project.name;
|
|
32
|
+
if (config.project.nameSource === 'package.json')
|
|
33
|
+
return readPackageName(config);
|
|
34
|
+
if (config.project.nameSource === 'jsr.json')
|
|
35
|
+
return readJsrName(config);
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
export const readCurrentVersion = async (config, projectName) => {
|
|
39
|
+
if (config.version.source === 'package.json')
|
|
40
|
+
return readPackageVersion(config);
|
|
41
|
+
if (config.version.source === 'jsr.json')
|
|
42
|
+
return readJsrVersion(config);
|
|
43
|
+
return readGitVersion(config, projectName);
|
|
44
|
+
};
|
|
45
|
+
export const readGitVersion = async (config, projectName) => {
|
|
46
|
+
await ensureGitRepository(config.cwd);
|
|
47
|
+
const tagsOutput = await $ `git -C ${config.cwd} tag --list`.text();
|
|
48
|
+
const versions = tagsOutput
|
|
49
|
+
.split(/\r?\n/)
|
|
50
|
+
.map((line) => line.trim())
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.map((tag) => versionFromTag(config.git.tagTemplate, tag, projectName))
|
|
53
|
+
.filter((version) => Boolean(version));
|
|
54
|
+
if (versions.length === 0)
|
|
55
|
+
throw new MirrorError(`No Git tags match template: ${config.git.tagTemplate}`);
|
|
56
|
+
return sortSemverDescending(versions)[0] ?? '';
|
|
57
|
+
};
|
|
58
|
+
export const renderGitTag = (template, version, projectName) => {
|
|
59
|
+
assertSupportedGitTagTemplate(template);
|
|
60
|
+
assertValidSemver(version, 'Git tag version');
|
|
61
|
+
if (template.includes('{name}') && !projectName)
|
|
62
|
+
throw new MirrorError(`Tag template requires a project name: ${template}`);
|
|
63
|
+
return template.replaceAll('{version}', version).replaceAll('{name}', projectName ?? '');
|
|
64
|
+
};
|
|
65
|
+
export const versionFromTag = (template, tag, projectName) => {
|
|
66
|
+
assertSupportedGitTagTemplate(template);
|
|
67
|
+
if (template.includes('{name}') && !projectName)
|
|
68
|
+
throw new MirrorError(`Tag template requires a project name: ${template}`);
|
|
69
|
+
const escapedTemplate = escapeRegex(template)
|
|
70
|
+
.replaceAll('\\{version\\}', '(?<version>.+)')
|
|
71
|
+
.replaceAll('\\{name\\}', escapeRegex(projectName ?? ''));
|
|
72
|
+
const match = new RegExp(`^${escapedTemplate}$`).exec(tag);
|
|
73
|
+
const version = match?.groups?.['version'];
|
|
74
|
+
if (!version)
|
|
75
|
+
return undefined;
|
|
76
|
+
try {
|
|
77
|
+
assertValidSemver(version, 'Git tag version');
|
|
78
|
+
return version;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
export const assertSupportedGitTagTemplate = (template) => {
|
|
85
|
+
if (!supportedGitTagTemplates.includes(template)) {
|
|
86
|
+
throw new MirrorError(`Unsupported Git tag template: ${template}. Expected v{version}, {name}@{version}, or {name}/v{version}.`);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
export const isGitRepository = async (cwd) => {
|
|
90
|
+
try {
|
|
91
|
+
await $ `git -C ${cwd} rev-parse --is-inside-work-tree`.quiet();
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
export const isGitDirty = async (cwd) => {
|
|
99
|
+
const output = await $ `git -C ${cwd} status --porcelain`.quiet().text();
|
|
100
|
+
return output.trim().length > 0;
|
|
101
|
+
};
|
|
102
|
+
export const createGitCommit = async (cwd, paths, message) => {
|
|
103
|
+
for (const path of paths)
|
|
104
|
+
await $ `git -C ${cwd} add ${path}`.quiet();
|
|
105
|
+
await $ `git -C ${cwd} commit -m ${message}`.quiet();
|
|
106
|
+
};
|
|
107
|
+
export const createGitTag = async (cwd, tag) => {
|
|
108
|
+
await $ `git -C ${cwd} tag ${tag} -m ${`Release ${tag}`}`.quiet();
|
|
109
|
+
};
|
|
110
|
+
export const pushGitRefs = async (cwd, includeCommit, includeTags) => {
|
|
111
|
+
if (includeCommit)
|
|
112
|
+
await $ `git -C ${cwd} push`.quiet();
|
|
113
|
+
if (includeTags)
|
|
114
|
+
await $ `git -C ${cwd} push --tags`.quiet();
|
|
115
|
+
};
|
|
116
|
+
const readJsonObject = async (path, label) => {
|
|
117
|
+
ensureFile(path, label);
|
|
118
|
+
const json = await Bun.file(path).json();
|
|
119
|
+
if (typeof json !== 'object' || json === null || Array.isArray(json))
|
|
120
|
+
throw new MirrorError(`${label} must contain a JSON object: ${path}`);
|
|
121
|
+
return json;
|
|
122
|
+
};
|
|
123
|
+
const readVersionField = async (path, label) => {
|
|
124
|
+
const json = await readJsonObject(path, label);
|
|
125
|
+
const version = json['version'];
|
|
126
|
+
if (typeof version !== 'string')
|
|
127
|
+
throw new MirrorError(`${label} must contain a string version field: ${path}`);
|
|
128
|
+
assertValidSemver(version, `${label} version`);
|
|
129
|
+
return version;
|
|
130
|
+
};
|
|
131
|
+
const readNameField = async (path, label) => {
|
|
132
|
+
const json = await readJsonObject(path, label);
|
|
133
|
+
const name = json['name'];
|
|
134
|
+
if (typeof name !== 'string' || name.length === 0)
|
|
135
|
+
throw new MirrorError(`${label} must contain a string name field: ${path}`);
|
|
136
|
+
return name;
|
|
137
|
+
};
|
|
138
|
+
const writeVersionField = async (path, label, nextVersion) => {
|
|
139
|
+
const json = await readJsonObject(path, label);
|
|
140
|
+
json['version'] = nextVersion;
|
|
141
|
+
await writeJsonObject(path, json);
|
|
142
|
+
};
|
|
143
|
+
const ensureFile = (path, label) => {
|
|
144
|
+
if (!existsSync(path))
|
|
145
|
+
throw new MirrorError(`${label} file not found: ${path}`);
|
|
146
|
+
};
|
|
147
|
+
const ensureGitRepository = async (cwd) => {
|
|
148
|
+
if (!(await isGitRepository(cwd)))
|
|
149
|
+
throw new MirrorError(`Not a Git repository: ${cwd}`);
|
|
150
|
+
};
|
|
151
|
+
const usesAdapter = (config, adapter) => config.version.source === adapter || config.version.output.includes(adapter);
|
|
152
|
+
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
package/library/cli.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export declare const createMirrorCommand: () => import("citty").CommandDef<{
|
|
5
|
+
config: {
|
|
6
|
+
type: "string";
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
cwd: {
|
|
10
|
+
type: "string";
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
format: {
|
|
14
|
+
type: "enum";
|
|
15
|
+
options: string[];
|
|
16
|
+
default: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
'no-color': {
|
|
20
|
+
type: "boolean";
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
export declare const runMirrorCli: (rawArgs?: string[]) => Promise<void>;
|
|
25
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+CH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;EAa5B,CAAA;AAEJ,eAAO,MAAM,YAAY,GAAU,kBAA+B,kBAyCjE,CAAA"}
|