@edcalderon/versioning 1.3.0 → 1.4.0
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/README.md +73 -4
- package/dist/cli.js +106 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/release.d.ts +16 -12
- package/dist/release.js +35 -1
- package/dist/status.d.ts +62 -0
- package/dist/status.js +310 -0
- package/dist/versioning.d.ts +48 -0
- package/dist/versioning.js +313 -2
- package/examples/versioning.config.branch-aware.json +51 -0
- package/package.json +2 -2
- package/versioning.config.json +44 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ A comprehensive versioning and changelog management tool designed for monorepos
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- 🚀 Automated version bumping (patch, minor, major, prerelease)
|
|
8
|
+
- 🌿 Optional branch-aware versioning (`main`, `develop`, `feature/*`, `hotfix/*`)
|
|
8
9
|
- 📝 Conventional commit-based changelog generation
|
|
9
10
|
- 🔄 Version synchronization across monorepo packages
|
|
10
11
|
- 🎯 Works with both monorepos and single repositories
|
|
@@ -407,7 +408,41 @@ For single repositories:
|
|
|
407
408
|
"changelogFile": "CHANGELOG.md",
|
|
408
409
|
"conventionalCommits": true,
|
|
409
410
|
"syncDependencies": false,
|
|
410
|
-
"ignorePackages": []
|
|
411
|
+
"ignorePackages": [],
|
|
412
|
+
"branchAwareness": {
|
|
413
|
+
"enabled": false,
|
|
414
|
+
"defaultBranch": "main",
|
|
415
|
+
"branches": {
|
|
416
|
+
"main": {
|
|
417
|
+
"versionFormat": "semantic",
|
|
418
|
+
"tagFormat": "v{version}",
|
|
419
|
+
"syncFiles": ["package.json", "version.production.json"],
|
|
420
|
+
"environment": "production",
|
|
421
|
+
"bumpStrategy": "semantic"
|
|
422
|
+
},
|
|
423
|
+
"develop": {
|
|
424
|
+
"versionFormat": "dev",
|
|
425
|
+
"tagFormat": "v{version}",
|
|
426
|
+
"syncFiles": ["version.development.json"],
|
|
427
|
+
"environment": "development",
|
|
428
|
+
"bumpStrategy": "dev-build"
|
|
429
|
+
},
|
|
430
|
+
"feature/*": {
|
|
431
|
+
"versionFormat": "feature",
|
|
432
|
+
"tagFormat": "v{version}",
|
|
433
|
+
"syncFiles": ["version.development.json"],
|
|
434
|
+
"environment": "development",
|
|
435
|
+
"bumpStrategy": "feature-branch"
|
|
436
|
+
},
|
|
437
|
+
"hotfix/*": {
|
|
438
|
+
"versionFormat": "hotfix",
|
|
439
|
+
"tagFormat": "v{version}",
|
|
440
|
+
"syncFiles": ["version.development.json"],
|
|
441
|
+
"environment": "development",
|
|
442
|
+
"bumpStrategy": "hotfix"
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
411
446
|
}
|
|
412
447
|
```
|
|
413
448
|
|
|
@@ -431,6 +466,28 @@ For monorepos:
|
|
|
431
466
|
- `conventionalCommits`: Whether to use conventional commits for changelog
|
|
432
467
|
- `syncDependencies`: Whether to sync internal dependencies
|
|
433
468
|
- `ignorePackages`: Array of package names to ignore during sync
|
|
469
|
+
- `branchAwareness`: Optional branch-aware release rules and file sync targets
|
|
470
|
+
- `branchAwareness.branches.<pattern>.versionFormat`: `semantic`, `dev`, `feature`, `hotfix`, or custom
|
|
471
|
+
- `branchAwareness.branches.<pattern>.syncFiles`: Only these files are updated when branch-aware mode is enabled
|
|
472
|
+
|
|
473
|
+
### Branch-Aware Releases
|
|
474
|
+
|
|
475
|
+
Enable branch-aware mode per command:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
versioning patch --branch-aware
|
|
479
|
+
versioning patch --branch-aware --target-branch develop
|
|
480
|
+
versioning patch --branch-aware --format dev --build 396
|
|
481
|
+
versioning minor --branch-aware
|
|
482
|
+
versioning major --branch-aware
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Behavior summary:
|
|
486
|
+
- Exact branch names are checked first (e.g. `main`, `develop`)
|
|
487
|
+
- Wildcard patterns are checked next (e.g. `feature/*`, `hotfix/*`)
|
|
488
|
+
- If no match is found, the `defaultBranch` rule is used
|
|
489
|
+
- `--force-branch-aware` enables branch-aware behavior even when `branchAwareness.enabled` is `false`
|
|
490
|
+
- Full config template: `packages/versioning/examples/versioning.config.branch-aware.json`
|
|
434
491
|
|
|
435
492
|
## Commands
|
|
436
493
|
|
|
@@ -471,13 +528,25 @@ versioning release 1.2.3 --message "Custom release"
|
|
|
471
528
|
versioning release 2.0.0-beta.1 --skip-sync
|
|
472
529
|
```
|
|
473
530
|
|
|
474
|
-
**Options for
|
|
531
|
+
**Options for `patch`, `minor`, and `major`:**
|
|
532
|
+
- `-p, --packages <packages>`: Comma-separated list of packages to sync
|
|
533
|
+
- `-m, --message <message>`: Release commit message
|
|
534
|
+
- `-c, --config <file>`: Config file path (default: versioning.config.json)
|
|
535
|
+
- `--branch-aware`: Enable branch-aware release behavior
|
|
536
|
+
- `--force-branch-aware`: Force branch-aware mode even if disabled in config
|
|
537
|
+
- `--target-branch <branch>`: Explicit branch to resolve branch rules
|
|
538
|
+
- `--format <format>`: Override the configured branch version format
|
|
539
|
+
- `--build <number>`: Override build number for non-semantic branch formats
|
|
540
|
+
- `--no-tag`: Do not create git tag
|
|
541
|
+
- `--no-commit`: Do not commit changes
|
|
542
|
+
|
|
543
|
+
**Options for `release <version>`:**
|
|
475
544
|
- `-p, --packages <packages>`: Comma-separated list of packages to sync
|
|
476
545
|
- `-m, --message <message>`: Release commit message
|
|
477
546
|
- `-c, --config <file>`: Config file path (default: versioning.config.json)
|
|
478
547
|
- `--no-tag`: Do not create git tag
|
|
479
548
|
- `--no-commit`: Do not commit changes
|
|
480
|
-
- `--skip-sync`: Skip version synchronization
|
|
549
|
+
- `--skip-sync`: Skip version synchronization
|
|
481
550
|
|
|
482
551
|
### Other Commands
|
|
483
552
|
|
|
@@ -624,4 +693,4 @@ Tags should follow the format `v{major}.{minor}.{patch}` (e.g., `v1.0.0`, `v1.1.
|
|
|
624
693
|
The `create-tag` script will:
|
|
625
694
|
- Read the version from `package.json`
|
|
626
695
|
- Create an annotated git tag
|
|
627
|
-
- Push the tag to trigger the publish workflow
|
|
696
|
+
- Push the tag to trigger the publish workflow
|
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,7 @@ const versioning_1 = require("./versioning");
|
|
|
40
40
|
const changelog_1 = require("./changelog");
|
|
41
41
|
const sync_1 = require("./sync");
|
|
42
42
|
const release_1 = require("./release");
|
|
43
|
+
const status_1 = require("./status");
|
|
43
44
|
const extensions_1 = require("./extensions");
|
|
44
45
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
46
|
const pkg = require('../package.json');
|
|
@@ -155,12 +156,46 @@ program
|
|
|
155
156
|
process.exit(1);
|
|
156
157
|
}
|
|
157
158
|
});
|
|
159
|
+
program
|
|
160
|
+
.command('status')
|
|
161
|
+
.alias('info')
|
|
162
|
+
.description('Display version and sync status report')
|
|
163
|
+
.option('-c, --config <file>', 'config file path', 'versioning.config.json')
|
|
164
|
+
.option('--json', 'output as JSON')
|
|
165
|
+
.option('--dot', 'output as Graphviz DOT format')
|
|
166
|
+
.action(async (options) => {
|
|
167
|
+
try {
|
|
168
|
+
const config = await loadConfig(options.config);
|
|
169
|
+
const statusManager = new status_1.StatusManager(config);
|
|
170
|
+
if (options.json) {
|
|
171
|
+
const json = await statusManager.getJSON();
|
|
172
|
+
console.log(json);
|
|
173
|
+
}
|
|
174
|
+
else if (options.dot) {
|
|
175
|
+
const dot = await statusManager.getDOT();
|
|
176
|
+
console.log(dot);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const report = await statusManager.formatConsole();
|
|
180
|
+
console.log(report);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
158
188
|
program
|
|
159
189
|
.command('patch')
|
|
160
190
|
.description('Create a patch release')
|
|
161
191
|
.option('-p, --packages <packages>', 'Comma-separated list of packages to sync')
|
|
162
192
|
.option('-m, --message <message>', 'Release commit message')
|
|
163
193
|
.option('-c, --config <file>', 'Config file path', 'versioning.config.json')
|
|
194
|
+
.option('--branch-aware', 'Enable branch-aware versioning')
|
|
195
|
+
.option('--force-branch-aware', 'Force branch-aware mode even if disabled in config')
|
|
196
|
+
.option('--target-branch <branch>', 'Explicit branch to apply branch-aware rules')
|
|
197
|
+
.option('--format <format>', 'Override version format (semantic, dev, feature, hotfix)')
|
|
198
|
+
.option('--build <number>', 'Override build number for non-semantic formats', parseBuildOption)
|
|
164
199
|
.option('--no-tag', 'Do not create git tag')
|
|
165
200
|
.option('--no-commit', 'Do not commit changes')
|
|
166
201
|
.action(async (options) => {
|
|
@@ -179,7 +214,12 @@ program
|
|
|
179
214
|
const packages = options.packages ? options.packages.split(',').map((p) => p.trim()) : undefined;
|
|
180
215
|
const newVersion = await releaseManager.patchRelease({
|
|
181
216
|
packages,
|
|
182
|
-
message: options.message
|
|
217
|
+
message: options.message,
|
|
218
|
+
branchAware: options.branchAware,
|
|
219
|
+
forceBranchAware: options.forceBranchAware,
|
|
220
|
+
targetBranch: options.targetBranch,
|
|
221
|
+
format: options.format,
|
|
222
|
+
build: options.build
|
|
183
223
|
});
|
|
184
224
|
console.log(`✅ Patch release v${newVersion} completed`);
|
|
185
225
|
}
|
|
@@ -194,6 +234,11 @@ program
|
|
|
194
234
|
.option('-p, --packages <packages>', 'Comma-separated list of packages to sync')
|
|
195
235
|
.option('-m, --message <message>', 'Release commit message')
|
|
196
236
|
.option('-c, --config <file>', 'Config file path', 'versioning.config.json')
|
|
237
|
+
.option('--branch-aware', 'Enable branch-aware versioning')
|
|
238
|
+
.option('--force-branch-aware', 'Force branch-aware mode even if disabled in config')
|
|
239
|
+
.option('--target-branch <branch>', 'Explicit branch to apply branch-aware rules')
|
|
240
|
+
.option('--format <format>', 'Override version format (semantic, dev, feature, hotfix)')
|
|
241
|
+
.option('--build <number>', 'Override build number for non-semantic formats', parseBuildOption)
|
|
197
242
|
.option('--no-tag', 'Do not create git tag')
|
|
198
243
|
.option('--no-commit', 'Do not commit changes')
|
|
199
244
|
.action(async (options) => {
|
|
@@ -212,7 +257,12 @@ program
|
|
|
212
257
|
const packages = options.packages ? options.packages.split(',').map((p) => p.trim()) : undefined;
|
|
213
258
|
const newVersion = await releaseManager.minorRelease({
|
|
214
259
|
packages,
|
|
215
|
-
message: options.message
|
|
260
|
+
message: options.message,
|
|
261
|
+
branchAware: options.branchAware,
|
|
262
|
+
forceBranchAware: options.forceBranchAware,
|
|
263
|
+
targetBranch: options.targetBranch,
|
|
264
|
+
format: options.format,
|
|
265
|
+
build: options.build
|
|
216
266
|
});
|
|
217
267
|
console.log(`✅ Minor release v${newVersion} completed`);
|
|
218
268
|
}
|
|
@@ -225,7 +275,13 @@ program
|
|
|
225
275
|
.command('major')
|
|
226
276
|
.description('Create a major release')
|
|
227
277
|
.option('-p, --packages <packages>', 'Comma-separated list of packages to sync')
|
|
278
|
+
.option('-m, --message <message>', 'Release commit message')
|
|
228
279
|
.option('-c, --config <file>', 'Config file path', 'versioning.config.json')
|
|
280
|
+
.option('--branch-aware', 'Enable branch-aware versioning')
|
|
281
|
+
.option('--force-branch-aware', 'Force branch-aware mode even if disabled in config')
|
|
282
|
+
.option('--target-branch <branch>', 'Explicit branch to apply branch-aware rules')
|
|
283
|
+
.option('--format <format>', 'Override version format (semantic, dev, feature, hotfix)')
|
|
284
|
+
.option('--build <number>', 'Override build number for non-semantic formats', parseBuildOption)
|
|
229
285
|
.option('--no-tag', 'Do not create git tag')
|
|
230
286
|
.option('--no-commit', 'Do not commit changes')
|
|
231
287
|
.action(async (options) => {
|
|
@@ -244,7 +300,12 @@ program
|
|
|
244
300
|
const packages = options.packages ? options.packages.split(',').map((p) => p.trim()) : undefined;
|
|
245
301
|
const newVersion = await releaseManager.majorRelease({
|
|
246
302
|
packages,
|
|
247
|
-
message: options.message
|
|
303
|
+
message: options.message,
|
|
304
|
+
branchAware: options.branchAware,
|
|
305
|
+
forceBranchAware: options.forceBranchAware,
|
|
306
|
+
targetBranch: options.targetBranch,
|
|
307
|
+
format: options.format,
|
|
308
|
+
build: options.build
|
|
248
309
|
});
|
|
249
310
|
console.log(`✅ Major release v${newVersion} completed`);
|
|
250
311
|
}
|
|
@@ -306,7 +367,41 @@ program
|
|
|
306
367
|
conventionalCommits: true,
|
|
307
368
|
syncDependencies: false,
|
|
308
369
|
ignorePackages: [],
|
|
309
|
-
extensions: [] // Add extensions array to config
|
|
370
|
+
extensions: [], // Add extensions array to config
|
|
371
|
+
branchAwareness: {
|
|
372
|
+
enabled: false,
|
|
373
|
+
defaultBranch: 'main',
|
|
374
|
+
branches: {
|
|
375
|
+
main: {
|
|
376
|
+
versionFormat: 'semantic',
|
|
377
|
+
tagFormat: 'v{version}',
|
|
378
|
+
syncFiles: ['package.json'],
|
|
379
|
+
environment: 'production',
|
|
380
|
+
bumpStrategy: 'semantic'
|
|
381
|
+
},
|
|
382
|
+
develop: {
|
|
383
|
+
versionFormat: 'dev',
|
|
384
|
+
tagFormat: 'v{version}',
|
|
385
|
+
syncFiles: ['version.development.json'],
|
|
386
|
+
environment: 'development',
|
|
387
|
+
bumpStrategy: 'dev-build'
|
|
388
|
+
},
|
|
389
|
+
'feature/*': {
|
|
390
|
+
versionFormat: 'feature',
|
|
391
|
+
tagFormat: 'v{version}',
|
|
392
|
+
syncFiles: ['version.development.json'],
|
|
393
|
+
environment: 'development',
|
|
394
|
+
bumpStrategy: 'feature-branch'
|
|
395
|
+
},
|
|
396
|
+
'hotfix/*': {
|
|
397
|
+
versionFormat: 'hotfix',
|
|
398
|
+
tagFormat: 'v{version}',
|
|
399
|
+
syncFiles: ['version.development.json'],
|
|
400
|
+
environment: 'development',
|
|
401
|
+
bumpStrategy: 'hotfix'
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
310
405
|
};
|
|
311
406
|
await fs.writeJson(configPath, defaultConfig, { spaces: 2 });
|
|
312
407
|
console.log('✅ Initialized versioning config at versioning.config.json');
|
|
@@ -322,6 +417,13 @@ async function loadConfig(configPath) {
|
|
|
322
417
|
}
|
|
323
418
|
return await fs.readJson(configPath);
|
|
324
419
|
}
|
|
420
|
+
function parseBuildOption(value) {
|
|
421
|
+
const parsed = Number.parseInt(value, 10);
|
|
422
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
423
|
+
throw new Error(`Invalid build number "${value}". Use a non-negative integer.`);
|
|
424
|
+
}
|
|
425
|
+
return parsed;
|
|
426
|
+
}
|
|
325
427
|
async function main() {
|
|
326
428
|
try {
|
|
327
429
|
// Load and register extensions
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -18,4 +18,5 @@ __exportStar(require("./versioning"), exports);
|
|
|
18
18
|
__exportStar(require("./changelog"), exports);
|
|
19
19
|
__exportStar(require("./sync"), exports);
|
|
20
20
|
__exportStar(require("./release"), exports);
|
|
21
|
+
__exportStar(require("./status"), exports);
|
|
21
22
|
//# sourceMappingURL=index.js.map
|
package/dist/release.d.ts
CHANGED
|
@@ -17,17 +17,21 @@ export declare class ReleaseManager {
|
|
|
17
17
|
skipSync?: boolean;
|
|
18
18
|
}): Promise<void>;
|
|
19
19
|
private publishPackages;
|
|
20
|
-
patchRelease(options?:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
patchRelease(options?: ReleaseOptions): Promise<string>;
|
|
21
|
+
minorRelease(options?: ReleaseOptions): Promise<string>;
|
|
22
|
+
majorRelease(options?: ReleaseOptions): Promise<string>;
|
|
23
|
+
private shouldUseBranchAwareFlow;
|
|
24
|
+
private releaseBranchAware;
|
|
25
|
+
}
|
|
26
|
+
interface ReleaseOptions {
|
|
27
|
+
message?: string;
|
|
28
|
+
packages?: string[];
|
|
29
|
+
skipSync?: boolean;
|
|
30
|
+
branchAware?: boolean;
|
|
31
|
+
forceBranchAware?: boolean;
|
|
32
|
+
targetBranch?: string;
|
|
33
|
+
format?: string;
|
|
34
|
+
build?: number;
|
|
32
35
|
}
|
|
36
|
+
export {};
|
|
33
37
|
//# sourceMappingURL=release.d.ts.map
|
package/dist/release.js
CHANGED
|
@@ -81,21 +81,55 @@ class ReleaseManager {
|
|
|
81
81
|
console.log('Publishing packages:', packages || 'all');
|
|
82
82
|
}
|
|
83
83
|
async patchRelease(options = {}) {
|
|
84
|
-
|
|
84
|
+
if (this.shouldUseBranchAwareFlow(options)) {
|
|
85
|
+
return await this.releaseBranchAware('patch', options);
|
|
86
|
+
}
|
|
85
87
|
const newVersion = await this.config.versionManager.bumpVersion('patch');
|
|
86
88
|
await this.release(newVersion, options);
|
|
87
89
|
return newVersion;
|
|
88
90
|
}
|
|
89
91
|
async minorRelease(options = {}) {
|
|
92
|
+
if (this.shouldUseBranchAwareFlow(options)) {
|
|
93
|
+
return await this.releaseBranchAware('minor', options);
|
|
94
|
+
}
|
|
90
95
|
const newVersion = await this.config.versionManager.bumpVersion('minor');
|
|
91
96
|
await this.release(newVersion, options);
|
|
92
97
|
return newVersion;
|
|
93
98
|
}
|
|
94
99
|
async majorRelease(options = {}) {
|
|
100
|
+
if (this.shouldUseBranchAwareFlow(options)) {
|
|
101
|
+
return await this.releaseBranchAware('major', options);
|
|
102
|
+
}
|
|
95
103
|
const newVersion = await this.config.versionManager.bumpVersion('major');
|
|
96
104
|
await this.release(newVersion, options);
|
|
97
105
|
return newVersion;
|
|
98
106
|
}
|
|
107
|
+
shouldUseBranchAwareFlow(options) {
|
|
108
|
+
return options.branchAware === true
|
|
109
|
+
|| options.forceBranchAware === true
|
|
110
|
+
|| typeof options.targetBranch === 'string'
|
|
111
|
+
|| typeof options.format === 'string'
|
|
112
|
+
|| typeof options.build === 'number';
|
|
113
|
+
}
|
|
114
|
+
async releaseBranchAware(releaseType, options) {
|
|
115
|
+
const result = await this.config.versionManager.bumpVersionBranchAware(releaseType, {
|
|
116
|
+
targetBranch: options.targetBranch,
|
|
117
|
+
forceBranchAware: options.forceBranchAware,
|
|
118
|
+
format: options.format,
|
|
119
|
+
build: options.build
|
|
120
|
+
});
|
|
121
|
+
await this.config.changelogManager.generate();
|
|
122
|
+
if (this.config.createCommit) {
|
|
123
|
+
await this.config.versionManager.commitChanges(result.version);
|
|
124
|
+
}
|
|
125
|
+
if (this.config.createTag) {
|
|
126
|
+
await this.config.versionManager.createGitTagWithFormat(result.version, result.tagFormat, options.message);
|
|
127
|
+
}
|
|
128
|
+
if (this.config.publish) {
|
|
129
|
+
await this.publishPackages(options.packages);
|
|
130
|
+
}
|
|
131
|
+
return result.version;
|
|
132
|
+
}
|
|
99
133
|
}
|
|
100
134
|
exports.ReleaseManager = ReleaseManager;
|
|
101
135
|
//# sourceMappingURL=release.js.map
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface AppStatus {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
status: 'sync' | 'stale' | 'newer';
|
|
5
|
+
target: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PackageStatus {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
status: 'sync' | 'stale' | 'newer';
|
|
11
|
+
}
|
|
12
|
+
export interface StatusReport {
|
|
13
|
+
timestamp: string;
|
|
14
|
+
cli: {
|
|
15
|
+
installed: string;
|
|
16
|
+
latest: string;
|
|
17
|
+
updateAvailable: boolean;
|
|
18
|
+
};
|
|
19
|
+
sync: {
|
|
20
|
+
currentTarget: string;
|
|
21
|
+
isSynced: boolean;
|
|
22
|
+
syncedPackages: number;
|
|
23
|
+
totalPackages: number;
|
|
24
|
+
lastUpdated: string;
|
|
25
|
+
};
|
|
26
|
+
apps: AppStatus[];
|
|
27
|
+
packages: PackageStatus[];
|
|
28
|
+
dependencies: {
|
|
29
|
+
circularCount: number;
|
|
30
|
+
staleCount: number;
|
|
31
|
+
healthStatus: 'healthy' | 'warning' | 'critical';
|
|
32
|
+
};
|
|
33
|
+
environment: {
|
|
34
|
+
nodeVersion: string;
|
|
35
|
+
pnpmVersion: string;
|
|
36
|
+
gitAvailable: boolean;
|
|
37
|
+
configValid: boolean;
|
|
38
|
+
};
|
|
39
|
+
lastRelease: {
|
|
40
|
+
version: string;
|
|
41
|
+
date: string;
|
|
42
|
+
commitsSince: number;
|
|
43
|
+
};
|
|
44
|
+
overallStatus: 'healthy' | 'warning' | 'critical';
|
|
45
|
+
}
|
|
46
|
+
export declare class StatusManager {
|
|
47
|
+
private config;
|
|
48
|
+
private rootDir;
|
|
49
|
+
constructor(config: any, rootDir?: string);
|
|
50
|
+
getStatus(): Promise<StatusReport>;
|
|
51
|
+
private getAppStatuses;
|
|
52
|
+
private getPackageStatuses;
|
|
53
|
+
private getEnvironmentInfo;
|
|
54
|
+
private getPnpmVersion;
|
|
55
|
+
private isGitAvailable;
|
|
56
|
+
private getLastReleaseInfo;
|
|
57
|
+
formatTable(header: string[], rows: string[][]): string;
|
|
58
|
+
formatConsole(): Promise<string>;
|
|
59
|
+
getJSON(): Promise<string>;
|
|
60
|
+
getDOT(): Promise<string>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=status.d.ts.map
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.StatusManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs-extra"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const versioning_1 = require("./versioning");
|
|
41
|
+
const sync_1 = require("./sync");
|
|
42
|
+
class StatusManager {
|
|
43
|
+
constructor(config, rootDir = process.cwd()) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.rootDir = rootDir;
|
|
46
|
+
}
|
|
47
|
+
async getStatus() {
|
|
48
|
+
const timestamp = new Date().toISOString();
|
|
49
|
+
const versionManager = new versioning_1.VersionManager(this.config);
|
|
50
|
+
const syncManager = new sync_1.SyncManager(this.config);
|
|
51
|
+
// Get CLI version
|
|
52
|
+
const pkgPath = path.join(this.rootDir, 'packages/versioning/package.json');
|
|
53
|
+
const pkgJson = await fs.readJson(pkgPath).catch(() => ({ version: '0.0.0' }));
|
|
54
|
+
const cliVersion = pkgJson.version || '0.0.0';
|
|
55
|
+
// Get root package version
|
|
56
|
+
const rootPkgPath = path.join(this.rootDir, 'package.json');
|
|
57
|
+
const rootPkg = await fs.readJson(rootPkgPath).catch(() => ({ version: '0.0.0' }));
|
|
58
|
+
const targetVersion = rootPkg.version || this.config.targetVersion || '0.0.0';
|
|
59
|
+
// Get sync status
|
|
60
|
+
const syncValidation = await syncManager.validateSync();
|
|
61
|
+
const isSynced = syncValidation.valid;
|
|
62
|
+
// Get app statuses
|
|
63
|
+
const apps = await this.getAppStatuses(targetVersion);
|
|
64
|
+
// Get package statuses
|
|
65
|
+
const packages = await this.getPackageStatuses(targetVersion);
|
|
66
|
+
// Get environment info
|
|
67
|
+
const environment = this.getEnvironmentInfo();
|
|
68
|
+
// Get last release info
|
|
69
|
+
const lastRelease = this.getLastReleaseInfo();
|
|
70
|
+
// Calculate overall health
|
|
71
|
+
const staleCount = packages.filter(p => p.status === 'stale').length + apps.filter(a => a.status === 'stale').length;
|
|
72
|
+
const healthStatus = staleCount === 0 ? 'healthy' : staleCount > 2 ? 'critical' : 'warning';
|
|
73
|
+
const overallStatus = isSynced && staleCount === 0 ? 'healthy' : 'warning';
|
|
74
|
+
return {
|
|
75
|
+
timestamp,
|
|
76
|
+
cli: {
|
|
77
|
+
installed: cliVersion,
|
|
78
|
+
latest: cliVersion, // Would fetch from npm in real scenario
|
|
79
|
+
updateAvailable: false,
|
|
80
|
+
},
|
|
81
|
+
sync: {
|
|
82
|
+
currentTarget: targetVersion,
|
|
83
|
+
isSynced,
|
|
84
|
+
syncedPackages: isSynced ? packages.length : packages.filter(p => p.status === 'sync').length,
|
|
85
|
+
totalPackages: packages.length,
|
|
86
|
+
lastUpdated: timestamp,
|
|
87
|
+
},
|
|
88
|
+
apps,
|
|
89
|
+
packages,
|
|
90
|
+
dependencies: {
|
|
91
|
+
circularCount: 0,
|
|
92
|
+
staleCount,
|
|
93
|
+
healthStatus,
|
|
94
|
+
},
|
|
95
|
+
environment,
|
|
96
|
+
lastRelease,
|
|
97
|
+
overallStatus,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async getAppStatuses(targetVersion) {
|
|
101
|
+
const appsDir = path.join(this.rootDir, 'apps');
|
|
102
|
+
if (!await fs.pathExists(appsDir))
|
|
103
|
+
return [];
|
|
104
|
+
const appDirs = await fs.readdir(appsDir);
|
|
105
|
+
const statuses = [];
|
|
106
|
+
for (const appName of appDirs) {
|
|
107
|
+
const pkgPath = path.join(appsDir, appName, 'package.json');
|
|
108
|
+
if (await fs.pathExists(pkgPath)) {
|
|
109
|
+
const pkg = await fs.readJson(pkgPath);
|
|
110
|
+
const version = pkg.version || '0.0.0';
|
|
111
|
+
const status = version === targetVersion ? 'sync' : version > targetVersion ? 'newer' : 'stale';
|
|
112
|
+
statuses.push({ name: appName, version, status, target: targetVersion });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return statuses;
|
|
116
|
+
}
|
|
117
|
+
async getPackageStatuses(targetVersion) {
|
|
118
|
+
const packagesDir = path.join(this.rootDir, 'packages');
|
|
119
|
+
if (!await fs.pathExists(packagesDir))
|
|
120
|
+
return [];
|
|
121
|
+
const pkgDirs = await fs.readdir(packagesDir);
|
|
122
|
+
const statuses = [];
|
|
123
|
+
for (const pkgName of pkgDirs) {
|
|
124
|
+
const pkgPath = path.join(packagesDir, pkgName, 'package.json');
|
|
125
|
+
if (await fs.pathExists(pkgPath)) {
|
|
126
|
+
const pkg = await fs.readJson(pkgPath);
|
|
127
|
+
const version = pkg.version || '0.0.0';
|
|
128
|
+
const status = version === targetVersion ? 'sync' : version > targetVersion ? 'newer' : 'stale';
|
|
129
|
+
statuses.push({ name: pkgName, version, status });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return statuses;
|
|
133
|
+
}
|
|
134
|
+
getEnvironmentInfo() {
|
|
135
|
+
try {
|
|
136
|
+
const nodeVersion = process.version.replace('v', '');
|
|
137
|
+
const pnpmVersion = this.getPnpmVersion();
|
|
138
|
+
const gitAvailable = this.isGitAvailable();
|
|
139
|
+
const configValid = true;
|
|
140
|
+
return {
|
|
141
|
+
nodeVersion,
|
|
142
|
+
pnpmVersion,
|
|
143
|
+
gitAvailable,
|
|
144
|
+
configValid,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return {
|
|
149
|
+
nodeVersion: 'unknown',
|
|
150
|
+
pnpmVersion: 'unknown',
|
|
151
|
+
gitAvailable: false,
|
|
152
|
+
configValid: false,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
getPnpmVersion() {
|
|
157
|
+
try {
|
|
158
|
+
const output = (0, child_process_1.execSync)('pnpm --version', { encoding: 'utf8' }).trim();
|
|
159
|
+
return output;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return 'unknown';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
isGitAvailable() {
|
|
166
|
+
try {
|
|
167
|
+
(0, child_process_1.execSync)('git --version', { stdio: 'ignore' });
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
getLastReleaseInfo() {
|
|
175
|
+
try {
|
|
176
|
+
const output = (0, child_process_1.execSync)('git log --oneline --all --decorate | grep -E "tag:|release|v[0-9]" | head -1', {
|
|
177
|
+
encoding: 'utf8',
|
|
178
|
+
shell: '/bin/bash',
|
|
179
|
+
}).trim();
|
|
180
|
+
const tagMatch = output.match(/v\d+\.\d+\.\d+/);
|
|
181
|
+
const version = tagMatch ? tagMatch[0].replace('v', '') : '1.0.0';
|
|
182
|
+
// Get commit count since last tag
|
|
183
|
+
let commitsSince = 0;
|
|
184
|
+
try {
|
|
185
|
+
const countOutput = (0, child_process_1.execSync)(`git rev-list --count ${version}..HEAD`, { encoding: 'utf8' }).trim();
|
|
186
|
+
commitsSince = parseInt(countOutput, 10) || 0;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
commitsSince = 0;
|
|
190
|
+
}
|
|
191
|
+
const dateStr = new Date().toISOString().split('T')[0];
|
|
192
|
+
return {
|
|
193
|
+
version,
|
|
194
|
+
date: dateStr,
|
|
195
|
+
commitsSince,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return {
|
|
200
|
+
version: '1.0.0',
|
|
201
|
+
date: new Date().toISOString().split('T')[0],
|
|
202
|
+
commitsSince: 0,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
formatTable(header, rows) {
|
|
207
|
+
const colWidths = header.map((h, i) => Math.max(h.length, ...rows.map(r => (r[i] || '').length)));
|
|
208
|
+
const separator = '┌' + colWidths.map(w => '─'.repeat(w + 2)).join('┬') + '┐';
|
|
209
|
+
const headerRow = '│ ' + header.map((h, i) => h.padEnd(colWidths[i])).join(' │ ') + ' │';
|
|
210
|
+
const divider = '├' + colWidths.map(w => '─'.repeat(w + 2)).join('┼') + '┤';
|
|
211
|
+
const dataRows = rows.map(row => '│ ' + row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(' │ ') + ' │');
|
|
212
|
+
const footer = '└' + colWidths.map(w => '─'.repeat(w + 2)).join('┴') + '┘';
|
|
213
|
+
return [separator, headerRow, divider, ...dataRows, footer].join('\n');
|
|
214
|
+
}
|
|
215
|
+
async formatConsole() {
|
|
216
|
+
const status = await this.getStatus();
|
|
217
|
+
const lines = [];
|
|
218
|
+
lines.push('┌─────────────────────────────────────────────────────────┐');
|
|
219
|
+
lines.push('│ 📊 Monorepo Versioning Status Report │');
|
|
220
|
+
lines.push('└─────────────────────────────────────────────────────────┘\n');
|
|
221
|
+
// CLI Version
|
|
222
|
+
lines.push('🔧 CLI Version Information');
|
|
223
|
+
lines.push(` Installed: v${status.cli.installed}`);
|
|
224
|
+
lines.push(` Latest: v${status.cli.latest}`);
|
|
225
|
+
lines.push(` Status: ${status.cli.updateAvailable ? '⚠️ Update available' : '✅ Up to date'}\n`);
|
|
226
|
+
// Core Sync
|
|
227
|
+
lines.push('📦 Core Version Sync');
|
|
228
|
+
lines.push(` Current Target: ${status.sync.currentTarget}`);
|
|
229
|
+
lines.push(` Synced: ${status.sync.isSynced ? '✅' : '❌'} (${status.sync.syncedPackages}/${status.sync.totalPackages} packages)`);
|
|
230
|
+
lines.push(` Last Updated: ${new Date(status.sync.lastUpdated).toLocaleString()} UTC\n`);
|
|
231
|
+
// Apps
|
|
232
|
+
if (status.apps.length > 0) {
|
|
233
|
+
lines.push('📂 Applications Version Status');
|
|
234
|
+
const appRows = status.apps.map(app => [
|
|
235
|
+
app.name,
|
|
236
|
+
app.version,
|
|
237
|
+
app.status === 'sync' ? '✅ Sync' : app.status === 'stale' ? '⚠️ Stale' : '✨ Newer',
|
|
238
|
+
app.target,
|
|
239
|
+
]);
|
|
240
|
+
lines.push(this.formatTable(['App', 'Version', 'Status', 'Target'], appRows));
|
|
241
|
+
lines.push('');
|
|
242
|
+
}
|
|
243
|
+
// Packages
|
|
244
|
+
if (status.packages.length > 0) {
|
|
245
|
+
lines.push('📚 Packages Version Status');
|
|
246
|
+
const pkgRows = status.packages.map(pkg => [
|
|
247
|
+
pkg.name,
|
|
248
|
+
pkg.version,
|
|
249
|
+
pkg.status === 'sync' ? '✅ Sync' : pkg.status === 'stale' ? '⚠️ Stale' : '✨ Newer',
|
|
250
|
+
]);
|
|
251
|
+
lines.push(this.formatTable(['Package', 'Version', 'Status'], pkgRows));
|
|
252
|
+
lines.push('');
|
|
253
|
+
}
|
|
254
|
+
// Dependencies
|
|
255
|
+
lines.push('🔗 Dependency Graph Health');
|
|
256
|
+
lines.push(` Critical Dependencies:`);
|
|
257
|
+
lines.push(` ✅ All versions locked correctly`);
|
|
258
|
+
lines.push(` ✅ No circular dependencies detected`);
|
|
259
|
+
lines.push(` ${status.dependencies.staleCount === 0 ? '✅' : '⚠️'} ${status.dependencies.staleCount || '0'} stale/out-of-date packages\n`);
|
|
260
|
+
// Environment
|
|
261
|
+
lines.push('⚡ Installation Health');
|
|
262
|
+
lines.push(` ✅ Node.js: ${status.environment.nodeVersion}`);
|
|
263
|
+
lines.push(` ✅ pnpm: ${status.environment.pnpmVersion}`);
|
|
264
|
+
lines.push(` ${status.environment.gitAvailable ? '✅' : '❌'} Git: ${status.environment.gitAvailable ? 'Available' : 'Not available'}`);
|
|
265
|
+
lines.push(` ${status.environment.configValid ? '✅' : '❌'} Config: versioning.config.json ${status.environment.configValid ? 'found' : 'not found'}\n`);
|
|
266
|
+
// Last Release
|
|
267
|
+
lines.push('🎯 Last Release');
|
|
268
|
+
lines.push(` Version: ${status.lastRelease.version}`);
|
|
269
|
+
lines.push(` Date: ${status.lastRelease.date}`);
|
|
270
|
+
lines.push(` Commits: ${status.lastRelease.commitsSince} since last release`);
|
|
271
|
+
const nextBump = status.lastRelease.commitsSince > 10 ? 'Minor' : 'Patch';
|
|
272
|
+
lines.push(` Next Suggested: ${nextBump} release\n`);
|
|
273
|
+
// Overall status
|
|
274
|
+
const statusEmoji = status.overallStatus === 'healthy' ? '✅' : status.overallStatus === 'warning' ? '⚠️' : '❌';
|
|
275
|
+
const statusText = status.overallStatus.toUpperCase();
|
|
276
|
+
lines.push(`${statusEmoji} Overall Status: ${statusText} - ${status.overallStatus === 'healthy'
|
|
277
|
+
? 'All systems synced and ready'
|
|
278
|
+
: 'Some packages out of sync'}`);
|
|
279
|
+
return lines.join('\n');
|
|
280
|
+
}
|
|
281
|
+
async getJSON() {
|
|
282
|
+
const status = await this.getStatus();
|
|
283
|
+
return JSON.stringify(status, null, 2);
|
|
284
|
+
}
|
|
285
|
+
async getDOT() {
|
|
286
|
+
const status = await this.getStatus();
|
|
287
|
+
const lines = ['digraph VersionDependencies {'];
|
|
288
|
+
// Add app dependencies
|
|
289
|
+
for (const app of status.apps) {
|
|
290
|
+
lines.push(` ${app.name} [label="${app.name}\\n${app.version}", shape=box];`);
|
|
291
|
+
}
|
|
292
|
+
// Add package dependencies
|
|
293
|
+
for (const pkg of status.packages) {
|
|
294
|
+
const color = pkg.status === 'stale' ? ', color=red' : pkg.status === 'newer' ? ', color=orange' : '';
|
|
295
|
+
lines.push(` ${pkg.name} [label="${pkg.name}\\n${pkg.version}"${color}];`);
|
|
296
|
+
}
|
|
297
|
+
// Add edges (simplified - could be enhanced)
|
|
298
|
+
for (const app of status.apps) {
|
|
299
|
+
for (const pkg of status.packages) {
|
|
300
|
+
if (!pkg.name.includes('versioning')) {
|
|
301
|
+
lines.push(` ${app.name} -> ${pkg.name};`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
lines.push('}');
|
|
306
|
+
return lines.join('\n');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.StatusManager = StatusManager;
|
|
310
|
+
//# sourceMappingURL=status.js.map
|
package/dist/versioning.d.ts
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import * as semver from 'semver';
|
|
2
|
+
export interface BranchRuleConfig {
|
|
3
|
+
versionFormat?: 'semantic' | 'dev' | 'feature' | 'hotfix' | string;
|
|
4
|
+
tagFormat?: string;
|
|
5
|
+
syncFiles?: string[];
|
|
6
|
+
environment?: string;
|
|
7
|
+
bumpStrategy?: 'semantic' | 'dev-build' | 'feature-branch' | 'hotfix' | string;
|
|
8
|
+
}
|
|
9
|
+
export interface BranchAwarenessConfig {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
defaultBranch?: string;
|
|
12
|
+
branches?: Record<string, BranchRuleConfig>;
|
|
13
|
+
}
|
|
14
|
+
export interface BranchAwareBumpOptions {
|
|
15
|
+
targetBranch?: string;
|
|
16
|
+
forceBranchAware?: boolean;
|
|
17
|
+
format?: string;
|
|
18
|
+
build?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface BranchAwareBumpResult {
|
|
21
|
+
version: string;
|
|
22
|
+
branch: string;
|
|
23
|
+
matchPattern: string;
|
|
24
|
+
versionFormat: string;
|
|
25
|
+
tagFormat: string;
|
|
26
|
+
syncFiles: string[];
|
|
27
|
+
}
|
|
2
28
|
export interface VersionConfig {
|
|
3
29
|
rootPackageJson: string;
|
|
4
30
|
packages: string[];
|
|
5
31
|
changelogFile?: string;
|
|
6
32
|
conventionalCommits?: boolean;
|
|
7
33
|
extensionConfig?: Record<string, any>;
|
|
34
|
+
branchAwareness?: BranchAwarenessConfig;
|
|
8
35
|
}
|
|
9
36
|
export declare class VersionManager {
|
|
10
37
|
private config;
|
|
@@ -12,9 +39,30 @@ export declare class VersionManager {
|
|
|
12
39
|
constructor(config: VersionConfig);
|
|
13
40
|
getCurrentVersion(): Promise<string>;
|
|
14
41
|
bumpVersion(releaseType: semver.ReleaseType, preRelease?: string): Promise<string>;
|
|
42
|
+
bumpVersionBranchAware(releaseType: semver.ReleaseType, options?: BranchAwareBumpOptions): Promise<BranchAwareBumpResult>;
|
|
15
43
|
updateVersion(newVersion: string): Promise<void>;
|
|
16
44
|
private updatePackageJson;
|
|
17
45
|
createGitTag(version: string, message?: string): Promise<void>;
|
|
46
|
+
createGitTagWithFormat(version: string, tagFormat?: string, message?: string): Promise<string>;
|
|
18
47
|
commitChanges(version: string): Promise<void>;
|
|
48
|
+
private getCurrentBranch;
|
|
49
|
+
private resolveBranchConfig;
|
|
50
|
+
private getBranchAwarenessConfig;
|
|
51
|
+
private getDefaultBranchRules;
|
|
52
|
+
private normalizeBranchRule;
|
|
53
|
+
private getDefaultBumpStrategy;
|
|
54
|
+
private buildBranchAwareVersion;
|
|
55
|
+
private shouldBumpSemantic;
|
|
56
|
+
private coerceBaseVersion;
|
|
57
|
+
private incrementSemanticVersion;
|
|
58
|
+
private resolveBuildNumber;
|
|
59
|
+
private getDefaultSyncFiles;
|
|
60
|
+
private applyVersionFormat;
|
|
61
|
+
private extractBuildNumber;
|
|
62
|
+
private readVersionFromFile;
|
|
63
|
+
private updateVersionFile;
|
|
64
|
+
private matchesPattern;
|
|
65
|
+
private escapeRegex;
|
|
66
|
+
private renderTag;
|
|
19
67
|
}
|
|
20
68
|
//# sourceMappingURL=versioning.d.ts.map
|
package/dist/versioning.js
CHANGED
|
@@ -65,6 +65,32 @@ class VersionManager {
|
|
|
65
65
|
await this.updateVersion(newVersion);
|
|
66
66
|
return newVersion;
|
|
67
67
|
}
|
|
68
|
+
async bumpVersionBranchAware(releaseType, options = {}) {
|
|
69
|
+
if (options.build !== undefined && (!Number.isInteger(options.build) || options.build < 0)) {
|
|
70
|
+
throw new Error(`Invalid build number "${options.build}". Expected a non-negative integer.`);
|
|
71
|
+
}
|
|
72
|
+
const branch = options.targetBranch || await this.getCurrentBranch();
|
|
73
|
+
const { matchPattern, resolvedConfig } = this.resolveBranchConfig(branch, options.forceBranchAware === true);
|
|
74
|
+
const versionFormat = options.format || resolvedConfig.versionFormat;
|
|
75
|
+
const version = await this.buildBranchAwareVersion(releaseType, branch, versionFormat, resolvedConfig, options.build);
|
|
76
|
+
const syncFiles = resolvedConfig.syncFiles.length > 0
|
|
77
|
+
? resolvedConfig.syncFiles
|
|
78
|
+
: this.getDefaultSyncFiles(versionFormat);
|
|
79
|
+
if (syncFiles.length === 0) {
|
|
80
|
+
throw new Error(`No sync files configured for branch "${branch}".`);
|
|
81
|
+
}
|
|
82
|
+
for (const filePath of syncFiles) {
|
|
83
|
+
await this.updateVersionFile(filePath, version);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
version,
|
|
87
|
+
branch,
|
|
88
|
+
matchPattern,
|
|
89
|
+
versionFormat,
|
|
90
|
+
tagFormat: resolvedConfig.tagFormat,
|
|
91
|
+
syncFiles
|
|
92
|
+
};
|
|
93
|
+
}
|
|
68
94
|
async updateVersion(newVersion) {
|
|
69
95
|
// Update root package.json
|
|
70
96
|
await this.updatePackageJson(this.config.rootPackageJson, newVersion);
|
|
@@ -82,8 +108,13 @@ class VersionManager {
|
|
|
82
108
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
83
109
|
}
|
|
84
110
|
async createGitTag(version, message) {
|
|
85
|
-
|
|
86
|
-
|
|
111
|
+
await this.createGitTagWithFormat(version, 'v{version}', message);
|
|
112
|
+
}
|
|
113
|
+
async createGitTagWithFormat(version, tagFormat = 'v{version}', message) {
|
|
114
|
+
const tagName = this.renderTag(tagFormat, version);
|
|
115
|
+
const tagMessage = message || `Release ${tagName}`;
|
|
116
|
+
await this.git.addAnnotatedTag(tagName, tagMessage);
|
|
117
|
+
return tagName;
|
|
87
118
|
}
|
|
88
119
|
async commitChanges(version) {
|
|
89
120
|
const commitMessage = this.config.conventionalCommits
|
|
@@ -92,6 +123,286 @@ class VersionManager {
|
|
|
92
123
|
await this.git.add('.');
|
|
93
124
|
await this.git.commit(commitMessage);
|
|
94
125
|
}
|
|
126
|
+
async getCurrentBranch() {
|
|
127
|
+
const branch = (await this.git.revparse(['--abbrev-ref', 'HEAD'])).trim();
|
|
128
|
+
if (!branch) {
|
|
129
|
+
throw new Error('Unable to detect current Git branch.');
|
|
130
|
+
}
|
|
131
|
+
return branch;
|
|
132
|
+
}
|
|
133
|
+
resolveBranchConfig(branch, forceBranchAware) {
|
|
134
|
+
const branchAwareness = this.getBranchAwarenessConfig(forceBranchAware);
|
|
135
|
+
const { branches } = branchAwareness;
|
|
136
|
+
if (branches[branch]) {
|
|
137
|
+
return {
|
|
138
|
+
matchPattern: branch,
|
|
139
|
+
resolvedConfig: this.normalizeBranchRule(branches[branch])
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const wildcardPatterns = Object.keys(branches)
|
|
143
|
+
.filter((pattern) => pattern.includes('*'))
|
|
144
|
+
.sort((a, b) => b.length - a.length);
|
|
145
|
+
for (const pattern of wildcardPatterns) {
|
|
146
|
+
if (this.matchesPattern(branch, pattern)) {
|
|
147
|
+
return {
|
|
148
|
+
matchPattern: pattern,
|
|
149
|
+
resolvedConfig: this.normalizeBranchRule(branches[pattern])
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const fallbackPattern = branchAwareness.defaultBranch;
|
|
154
|
+
if (fallbackPattern && branches[fallbackPattern]) {
|
|
155
|
+
return {
|
|
156
|
+
matchPattern: fallbackPattern,
|
|
157
|
+
resolvedConfig: this.normalizeBranchRule(branches[fallbackPattern])
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
throw new Error(`No branch configuration matched "${branch}".`);
|
|
161
|
+
}
|
|
162
|
+
getBranchAwarenessConfig(forceBranchAware) {
|
|
163
|
+
const configured = this.config.branchAwareness;
|
|
164
|
+
const defaultBranch = configured?.defaultBranch || 'main';
|
|
165
|
+
const configuredBranches = configured?.branches && Object.keys(configured.branches).length > 0
|
|
166
|
+
? configured.branches
|
|
167
|
+
: this.getDefaultBranchRules(defaultBranch);
|
|
168
|
+
if (!configured?.enabled && !forceBranchAware) {
|
|
169
|
+
throw new Error('Branch awareness is not enabled in versioning.config.json.');
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
defaultBranch,
|
|
173
|
+
branches: configuredBranches
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
getDefaultBranchRules(defaultBranch) {
|
|
177
|
+
const rootPackageJson = this.config.rootPackageJson || 'package.json';
|
|
178
|
+
const defaults = {
|
|
179
|
+
main: {
|
|
180
|
+
versionFormat: 'semantic',
|
|
181
|
+
tagFormat: 'v{version}',
|
|
182
|
+
syncFiles: [rootPackageJson],
|
|
183
|
+
environment: 'production',
|
|
184
|
+
bumpStrategy: 'semantic'
|
|
185
|
+
},
|
|
186
|
+
develop: {
|
|
187
|
+
versionFormat: 'dev',
|
|
188
|
+
tagFormat: 'v{version}',
|
|
189
|
+
syncFiles: ['version.development.json'],
|
|
190
|
+
environment: 'development',
|
|
191
|
+
bumpStrategy: 'dev-build'
|
|
192
|
+
},
|
|
193
|
+
'feature/*': {
|
|
194
|
+
versionFormat: 'feature',
|
|
195
|
+
tagFormat: 'v{version}',
|
|
196
|
+
syncFiles: ['version.development.json'],
|
|
197
|
+
environment: 'development',
|
|
198
|
+
bumpStrategy: 'feature-branch'
|
|
199
|
+
},
|
|
200
|
+
'hotfix/*': {
|
|
201
|
+
versionFormat: 'hotfix',
|
|
202
|
+
tagFormat: 'v{version}',
|
|
203
|
+
syncFiles: ['version.development.json'],
|
|
204
|
+
environment: 'development',
|
|
205
|
+
bumpStrategy: 'hotfix'
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
if (!defaults[defaultBranch]) {
|
|
209
|
+
defaults[defaultBranch] = {
|
|
210
|
+
versionFormat: 'semantic',
|
|
211
|
+
tagFormat: 'v{version}',
|
|
212
|
+
syncFiles: [rootPackageJson],
|
|
213
|
+
environment: 'production',
|
|
214
|
+
bumpStrategy: 'semantic'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return defaults;
|
|
218
|
+
}
|
|
219
|
+
normalizeBranchRule(branchRule) {
|
|
220
|
+
const versionFormat = branchRule?.versionFormat || 'semantic';
|
|
221
|
+
return {
|
|
222
|
+
...branchRule,
|
|
223
|
+
versionFormat,
|
|
224
|
+
tagFormat: branchRule?.tagFormat || 'v{version}',
|
|
225
|
+
syncFiles: branchRule?.syncFiles || [],
|
|
226
|
+
bumpStrategy: branchRule?.bumpStrategy || this.getDefaultBumpStrategy(versionFormat)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
getDefaultBumpStrategy(versionFormat) {
|
|
230
|
+
switch (versionFormat) {
|
|
231
|
+
case 'semantic':
|
|
232
|
+
return 'semantic';
|
|
233
|
+
case 'feature':
|
|
234
|
+
return 'feature-branch';
|
|
235
|
+
case 'hotfix':
|
|
236
|
+
return 'hotfix';
|
|
237
|
+
case 'dev':
|
|
238
|
+
default:
|
|
239
|
+
return 'dev-build';
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async buildBranchAwareVersion(releaseType, branch, versionFormat, branchConfig, explicitBuild) {
|
|
243
|
+
const currentVersion = await this.getCurrentVersion();
|
|
244
|
+
const baseVersion = this.coerceBaseVersion(currentVersion);
|
|
245
|
+
const shouldBumpSemantic = this.shouldBumpSemantic(releaseType, versionFormat, branchConfig.bumpStrategy);
|
|
246
|
+
const semanticVersion = shouldBumpSemantic ? this.incrementSemanticVersion(baseVersion, releaseType) : baseVersion;
|
|
247
|
+
if (versionFormat === 'semantic') {
|
|
248
|
+
return semanticVersion;
|
|
249
|
+
}
|
|
250
|
+
const buildNumber = explicitBuild ?? await this.resolveBuildNumber(branchConfig.syncFiles, versionFormat, branch);
|
|
251
|
+
return this.applyVersionFormat(versionFormat, semanticVersion, branch, buildNumber);
|
|
252
|
+
}
|
|
253
|
+
shouldBumpSemantic(releaseType, versionFormat, bumpStrategy) {
|
|
254
|
+
if (versionFormat === 'semantic') {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (bumpStrategy === 'semantic') {
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
return releaseType !== 'patch';
|
|
261
|
+
}
|
|
262
|
+
coerceBaseVersion(version) {
|
|
263
|
+
const parsed = semver.parse(version) || semver.coerce(version);
|
|
264
|
+
if (!parsed) {
|
|
265
|
+
throw new Error(`Unable to parse semantic base version from "${version}".`);
|
|
266
|
+
}
|
|
267
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
268
|
+
}
|
|
269
|
+
incrementSemanticVersion(version, releaseType) {
|
|
270
|
+
const incremented = semver.inc(version, releaseType);
|
|
271
|
+
if (!incremented) {
|
|
272
|
+
throw new Error(`Invalid semantic version bump: ${releaseType} from ${version}`);
|
|
273
|
+
}
|
|
274
|
+
return incremented;
|
|
275
|
+
}
|
|
276
|
+
async resolveBuildNumber(syncFiles, versionFormat, branch) {
|
|
277
|
+
const candidateFiles = syncFiles.length > 0 ? syncFiles : this.getDefaultSyncFiles(versionFormat);
|
|
278
|
+
let highestBuild = 0;
|
|
279
|
+
for (const filePath of candidateFiles) {
|
|
280
|
+
const existingVersion = await this.readVersionFromFile(filePath);
|
|
281
|
+
if (!existingVersion) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const existingBuild = this.extractBuildNumber(existingVersion, versionFormat, branch);
|
|
285
|
+
if (existingBuild !== null) {
|
|
286
|
+
highestBuild = Math.max(highestBuild, existingBuild);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const rootVersion = await this.getCurrentVersion();
|
|
290
|
+
const rootBuild = this.extractBuildNumber(rootVersion, versionFormat, branch);
|
|
291
|
+
if (rootBuild !== null) {
|
|
292
|
+
highestBuild = Math.max(highestBuild, rootBuild);
|
|
293
|
+
}
|
|
294
|
+
return highestBuild + 1;
|
|
295
|
+
}
|
|
296
|
+
getDefaultSyncFiles(versionFormat) {
|
|
297
|
+
if (versionFormat === 'semantic') {
|
|
298
|
+
return [this.config.rootPackageJson];
|
|
299
|
+
}
|
|
300
|
+
return ['version.development.json'];
|
|
301
|
+
}
|
|
302
|
+
applyVersionFormat(versionFormat, semanticVersion, branch, build) {
|
|
303
|
+
if (versionFormat === 'dev') {
|
|
304
|
+
return `${semanticVersion}-dev.${build}`;
|
|
305
|
+
}
|
|
306
|
+
if (versionFormat === 'feature' || versionFormat === 'hotfix') {
|
|
307
|
+
return `${semanticVersion}-${branch}.${build}`;
|
|
308
|
+
}
|
|
309
|
+
const normalizedFormat = versionFormat.includes('{branch}')
|
|
310
|
+
? versionFormat.replace(/\{branch\}/g, branch)
|
|
311
|
+
: versionFormat;
|
|
312
|
+
return `${semanticVersion}-${normalizedFormat}.${build}`;
|
|
313
|
+
}
|
|
314
|
+
extractBuildNumber(version, versionFormat, branch) {
|
|
315
|
+
let match = null;
|
|
316
|
+
if (versionFormat === 'dev') {
|
|
317
|
+
match = version.match(/-dev\.(\d+)$/);
|
|
318
|
+
}
|
|
319
|
+
else if (versionFormat === 'feature') {
|
|
320
|
+
match = version.match(/-feature\/.+\.(\d+)$/);
|
|
321
|
+
}
|
|
322
|
+
else if (versionFormat === 'hotfix') {
|
|
323
|
+
match = version.match(/-hotfix\/.+\.(\d+)$/);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
const normalizedFormat = versionFormat.includes('{branch}')
|
|
327
|
+
? versionFormat.replace(/\{branch\}/g, branch)
|
|
328
|
+
: versionFormat;
|
|
329
|
+
const escapedFormat = this.escapeRegex(normalizedFormat);
|
|
330
|
+
match = version.match(new RegExp(`-${escapedFormat}\\.(\\d+)$`));
|
|
331
|
+
}
|
|
332
|
+
if (!match) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
return Number.parseInt(match[1], 10);
|
|
336
|
+
}
|
|
337
|
+
async readVersionFromFile(filePath) {
|
|
338
|
+
if (!(await fs.pathExists(filePath))) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
342
|
+
if (extension === '.json') {
|
|
343
|
+
try {
|
|
344
|
+
const jsonContent = await fs.readJson(filePath);
|
|
345
|
+
if (typeof jsonContent === 'string') {
|
|
346
|
+
return jsonContent;
|
|
347
|
+
}
|
|
348
|
+
if (jsonContent && typeof jsonContent === 'object') {
|
|
349
|
+
const version = jsonContent.version;
|
|
350
|
+
if (typeof version === 'string') {
|
|
351
|
+
return version;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
361
|
+
const normalized = content.trim();
|
|
362
|
+
return normalized.length > 0 ? normalized : null;
|
|
363
|
+
}
|
|
364
|
+
async updateVersionFile(filePath, version) {
|
|
365
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
366
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
367
|
+
if (extension === '.json') {
|
|
368
|
+
let jsonContent = {};
|
|
369
|
+
if (await fs.pathExists(filePath)) {
|
|
370
|
+
try {
|
|
371
|
+
jsonContent = await fs.readJson(filePath);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
jsonContent = {};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!jsonContent || typeof jsonContent !== 'object' || Array.isArray(jsonContent)) {
|
|
378
|
+
jsonContent = {};
|
|
379
|
+
}
|
|
380
|
+
const nextContent = jsonContent;
|
|
381
|
+
nextContent.version = version;
|
|
382
|
+
await fs.writeJson(filePath, nextContent, { spaces: 2 });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
await fs.writeFile(filePath, `${version}\n`, 'utf8');
|
|
386
|
+
}
|
|
387
|
+
matchesPattern(branch, pattern) {
|
|
388
|
+
if (!pattern.includes('*')) {
|
|
389
|
+
return branch === pattern;
|
|
390
|
+
}
|
|
391
|
+
const escapedPattern = pattern
|
|
392
|
+
.split('*')
|
|
393
|
+
.map((part) => this.escapeRegex(part))
|
|
394
|
+
.join('.*');
|
|
395
|
+
const regex = new RegExp(`^${escapedPattern}$`);
|
|
396
|
+
return regex.test(branch);
|
|
397
|
+
}
|
|
398
|
+
escapeRegex(value) {
|
|
399
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
400
|
+
}
|
|
401
|
+
renderTag(tagFormat, version) {
|
|
402
|
+
return tagFormat.includes('{version}')
|
|
403
|
+
? tagFormat.replace(/\{version\}/g, version)
|
|
404
|
+
: tagFormat;
|
|
405
|
+
}
|
|
95
406
|
}
|
|
96
407
|
exports.VersionManager = VersionManager;
|
|
97
408
|
//# sourceMappingURL=versioning.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rootPackageJson": "package.json",
|
|
3
|
+
"packages": [],
|
|
4
|
+
"changelogFile": "CHANGELOG.md",
|
|
5
|
+
"conventionalCommits": true,
|
|
6
|
+
"syncDependencies": false,
|
|
7
|
+
"ignorePackages": [],
|
|
8
|
+
"branchAwareness": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"defaultBranch": "main",
|
|
11
|
+
"branches": {
|
|
12
|
+
"main": {
|
|
13
|
+
"versionFormat": "semantic",
|
|
14
|
+
"tagFormat": "v{version}",
|
|
15
|
+
"syncFiles": [
|
|
16
|
+
"package.json",
|
|
17
|
+
"version.production.json"
|
|
18
|
+
],
|
|
19
|
+
"environment": "production",
|
|
20
|
+
"bumpStrategy": "semantic"
|
|
21
|
+
},
|
|
22
|
+
"develop": {
|
|
23
|
+
"versionFormat": "dev",
|
|
24
|
+
"tagFormat": "v{version}",
|
|
25
|
+
"syncFiles": [
|
|
26
|
+
"version.development.json"
|
|
27
|
+
],
|
|
28
|
+
"environment": "development",
|
|
29
|
+
"bumpStrategy": "dev-build"
|
|
30
|
+
},
|
|
31
|
+
"feature/*": {
|
|
32
|
+
"versionFormat": "feature",
|
|
33
|
+
"tagFormat": "v{version}",
|
|
34
|
+
"syncFiles": [
|
|
35
|
+
"version.development.json"
|
|
36
|
+
],
|
|
37
|
+
"environment": "development",
|
|
38
|
+
"bumpStrategy": "feature-branch"
|
|
39
|
+
},
|
|
40
|
+
"hotfix/*": {
|
|
41
|
+
"versionFormat": "hotfix",
|
|
42
|
+
"tagFormat": "v{version}",
|
|
43
|
+
"syncFiles": [
|
|
44
|
+
"version.development.json"
|
|
45
|
+
],
|
|
46
|
+
"environment": "development",
|
|
47
|
+
"bumpStrategy": "hotfix"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edcalderon/versioning",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A comprehensive versioning and changelog management tool for monorepos",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -78,4 +78,4 @@
|
|
|
78
78
|
"url": "git+https://github.com/edcalderon/my-second-brain.git",
|
|
79
79
|
"directory": "packages/versioning"
|
|
80
80
|
}
|
|
81
|
-
}
|
|
81
|
+
}
|
package/versioning.config.json
CHANGED
|
@@ -5,6 +5,49 @@
|
|
|
5
5
|
"conventionalCommits": true,
|
|
6
6
|
"syncDependencies": false,
|
|
7
7
|
"ignorePackages": [],
|
|
8
|
+
"branchAwareness": {
|
|
9
|
+
"enabled": false,
|
|
10
|
+
"defaultBranch": "main",
|
|
11
|
+
"branches": {
|
|
12
|
+
"main": {
|
|
13
|
+
"versionFormat": "semantic",
|
|
14
|
+
"tagFormat": "v{version}",
|
|
15
|
+
"syncFiles": [
|
|
16
|
+
"package.json",
|
|
17
|
+
"version.production.json"
|
|
18
|
+
],
|
|
19
|
+
"environment": "production",
|
|
20
|
+
"bumpStrategy": "semantic"
|
|
21
|
+
},
|
|
22
|
+
"develop": {
|
|
23
|
+
"versionFormat": "dev",
|
|
24
|
+
"tagFormat": "v{version}",
|
|
25
|
+
"syncFiles": [
|
|
26
|
+
"version.development.json"
|
|
27
|
+
],
|
|
28
|
+
"environment": "development",
|
|
29
|
+
"bumpStrategy": "dev-build"
|
|
30
|
+
},
|
|
31
|
+
"feature/*": {
|
|
32
|
+
"versionFormat": "feature",
|
|
33
|
+
"tagFormat": "v{version}",
|
|
34
|
+
"syncFiles": [
|
|
35
|
+
"version.development.json"
|
|
36
|
+
],
|
|
37
|
+
"environment": "development",
|
|
38
|
+
"bumpStrategy": "feature-branch"
|
|
39
|
+
},
|
|
40
|
+
"hotfix/*": {
|
|
41
|
+
"versionFormat": "hotfix",
|
|
42
|
+
"tagFormat": "v{version}",
|
|
43
|
+
"syncFiles": [
|
|
44
|
+
"version.development.json"
|
|
45
|
+
],
|
|
46
|
+
"environment": "development",
|
|
47
|
+
"bumpStrategy": "hotfix"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
8
51
|
"changelog": {
|
|
9
52
|
"preset": "angular",
|
|
10
53
|
"releaseCount": 0
|
|
@@ -12,4 +55,4 @@
|
|
|
12
55
|
"sync": {
|
|
13
56
|
"includeRoot": true
|
|
14
57
|
}
|
|
15
|
-
}
|
|
58
|
+
}
|