@cosmicdrift/kumiko-feature-ast 0.391.1 → 0.392.1
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/package.json +3 -3
- package/src/changes.json +6 -0
- package/src/scripts/codemod/codemod-support.ts +17 -5
- package/src/scripts/codemod/ctx-namespaces.ts +4 -6
- package/src/scripts/codemod/feature-layout.ts +4 -4
- package/src/scripts/codemod/object-form.ts +9 -6
- package/src/scripts/codemod/package-merge.ts +10 -10
- package/src/scripts/codemod/testing-move.ts +9 -6
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-feature-ast",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.392.1",
|
|
4
4
|
"description": "Feature-file AST for Kumiko: parse, patch and render feature definitions, the pattern library, and the codemods kumiko-upgrade runs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
26
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
25
|
+
"@cosmicdrift/kumiko-framework": "0.392.1",
|
|
26
|
+
"@cosmicdrift/kumiko-types": "0.392.1",
|
|
27
27
|
"ts-morph": "^28.0.0",
|
|
28
28
|
"zod": "^4.6.7"
|
|
29
29
|
},
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.392.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Codemod entries return their exit code",
|
|
6
|
+
"detail": "The feature-ast codemods export main(argv) and runCodemod next to runCodemodCli; they return an exit code instead of calling process.exit, so callers can run them in-process. Invocation from the shell is unchanged."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.391.1",
|
|
4
10
|
"type": "fix",
|
|
@@ -286,13 +286,16 @@ export interface CodemodCliConfig {
|
|
|
286
286
|
readonly extraExtensions?: readonly string[];
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
function
|
|
289
|
+
function parseArgsOrUsageError(
|
|
290
|
+
codemodName: string,
|
|
291
|
+
argv: readonly string[],
|
|
292
|
+
): CliOptions | { readonly exitCode: number } {
|
|
290
293
|
try {
|
|
291
294
|
return parseArgs(argv);
|
|
292
295
|
} catch (err) {
|
|
293
296
|
if (err instanceof CliError) {
|
|
294
297
|
console.error(`[${codemodName}] ${err.message}`);
|
|
295
|
-
|
|
298
|
+
return { exitCode: 2 };
|
|
296
299
|
}
|
|
297
300
|
throw err;
|
|
298
301
|
}
|
|
@@ -329,8 +332,17 @@ function transformFiles(
|
|
|
329
332
|
export async function runCodemodCli(
|
|
330
333
|
config: CodemodCliConfig,
|
|
331
334
|
argv: readonly string[],
|
|
332
|
-
): Promise<
|
|
333
|
-
|
|
335
|
+
): Promise<never> {
|
|
336
|
+
process.exit(await runCodemod(config, argv));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Exit code instead of process.exit so the CLI logic can run in-process (tests).
|
|
340
|
+
export async function runCodemod(
|
|
341
|
+
config: CodemodCliConfig,
|
|
342
|
+
argv: readonly string[],
|
|
343
|
+
): Promise<number> {
|
|
344
|
+
const options = parseArgsOrUsageError(config.name, argv);
|
|
345
|
+
if ("exitCode" in options) return options.exitCode;
|
|
334
346
|
const files = collectSourceFiles(options.paths, {
|
|
335
347
|
isMigratableFile: (name) =>
|
|
336
348
|
isSourceFileName(name) ||
|
|
@@ -348,5 +360,5 @@ export async function runCodemodCli(
|
|
|
348
360
|
resolve(options.paths[0] ?? "."),
|
|
349
361
|
);
|
|
350
362
|
}
|
|
351
|
-
|
|
363
|
+
return options.mode === "check" && rewrites.length + manual.length > 0 ? 1 : 0;
|
|
352
364
|
}
|
|
@@ -124,14 +124,14 @@ function runGroup(
|
|
|
124
124
|
|
|
125
125
|
// kumiko-upgrade runs this as `bun ctx-namespaces.ts <repoDir> [--dry-run]` and treats a non-zero
|
|
126
126
|
// exit as a failed codemod, so manual sites alone must not fail write or dry-run.
|
|
127
|
-
async function main(argv: readonly string[]): Promise<
|
|
127
|
+
export async function main(argv: readonly string[]): Promise<number> {
|
|
128
128
|
let options: CliOptions;
|
|
129
129
|
try {
|
|
130
130
|
options = parseArgs(argv);
|
|
131
131
|
} catch (error) {
|
|
132
132
|
if (error instanceof CliError) {
|
|
133
133
|
console.error(`[${NAME}] ${error.message}`);
|
|
134
|
-
|
|
134
|
+
return 2;
|
|
135
135
|
}
|
|
136
136
|
throw error;
|
|
137
137
|
}
|
|
@@ -157,11 +157,9 @@ async function main(argv: readonly string[]): Promise<void> {
|
|
|
157
157
|
if (options.mode === "write" && options.format && totals.changedFiles.length > 0) {
|
|
158
158
|
await formatWithBiome(NAME, totals.changedFiles, resolve(options.paths[0] ?? "."));
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
options.mode === "check" && totals.rewrites.length + totals.manual.length > 0 ? 1 : 0,
|
|
162
|
-
);
|
|
160
|
+
return options.mode === "check" && totals.rewrites.length + totals.manual.length > 0 ? 1 : 0;
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
if (import.meta.main) {
|
|
166
|
-
await main(process.argv.slice(2));
|
|
164
|
+
process.exit(await main(process.argv.slice(2)));
|
|
167
165
|
}
|
|
@@ -389,13 +389,13 @@ function writeResult(result: FeatureLayoutResult): void {
|
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
async function main(argv: readonly string[]): Promise<
|
|
392
|
+
export async function main(argv: readonly string[]): Promise<number> {
|
|
393
393
|
let options: CliOptions;
|
|
394
394
|
try {
|
|
395
395
|
options = parseArgs(argv);
|
|
396
396
|
} catch (err) {
|
|
397
397
|
console.error(`[feature-layout] ${err instanceof Error ? err.message : String(err)}`);
|
|
398
|
-
|
|
398
|
+
return 2;
|
|
399
399
|
}
|
|
400
400
|
const files = readTargetFiles(options.paths);
|
|
401
401
|
const result = applyFeatureLayout(files, options);
|
|
@@ -418,7 +418,7 @@ async function main(argv: readonly string[]): Promise<void> {
|
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
const pending = result.markersRemoved.length + result.manual.length;
|
|
421
|
-
|
|
421
|
+
return options.mode === "check" && pending > 0 ? 1 : 0;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
if (import.meta.main) await main(process.argv.slice(2));
|
|
424
|
+
if (import.meta.main) process.exit(await main(process.argv.slice(2)));
|
|
@@ -683,13 +683,15 @@ function countByMethod(rewrites: readonly ObjectFormRewrite[]): string {
|
|
|
683
683
|
.join(", ");
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
function
|
|
686
|
+
function parseArgsOrUsageError(
|
|
687
|
+
argv: readonly string[],
|
|
688
|
+
): CliOptions | { readonly exitCode: number } {
|
|
687
689
|
try {
|
|
688
690
|
return parseArgs(argv);
|
|
689
691
|
} catch (err) {
|
|
690
692
|
if (err instanceof CliError) {
|
|
691
693
|
console.error(`[object-form] ${err.message}`);
|
|
692
|
-
|
|
694
|
+
return { exitCode: 2 };
|
|
693
695
|
}
|
|
694
696
|
throw err;
|
|
695
697
|
}
|
|
@@ -722,8 +724,9 @@ function transformFiles(files: readonly string[], mode: Mode): RunSummary {
|
|
|
722
724
|
return summary;
|
|
723
725
|
}
|
|
724
726
|
|
|
725
|
-
async function main(): Promise<
|
|
726
|
-
const options =
|
|
727
|
+
export async function main(argv: readonly string[]): Promise<number> {
|
|
728
|
+
const options = parseArgsOrUsageError(argv);
|
|
729
|
+
if ("exitCode" in options) return options.exitCode;
|
|
727
730
|
const files = collectSourceFiles(options.paths, { isMigratableFile }).filter(
|
|
728
731
|
(file) => !isFixturePath(file),
|
|
729
732
|
);
|
|
@@ -738,9 +741,9 @@ async function main(): Promise<void> {
|
|
|
738
741
|
}
|
|
739
742
|
|
|
740
743
|
const hasOpenSites = rewrites.length + manual.length > 0;
|
|
741
|
-
|
|
744
|
+
return options.mode === "check" && hasOpenSites ? 1 : 0;
|
|
742
745
|
}
|
|
743
746
|
|
|
744
747
|
if (import.meta.main) {
|
|
745
|
-
await main();
|
|
748
|
+
process.exit(await main(process.argv.slice(2)));
|
|
746
749
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { Node, Project } from "ts-morph";
|
|
16
16
|
import {
|
|
17
17
|
applyEdits,
|
|
18
|
+
type CodemodCliConfig,
|
|
18
19
|
type CodemodResult,
|
|
19
20
|
detectIndent,
|
|
20
21
|
isSortedKeys,
|
|
@@ -270,15 +271,14 @@ function isSkippedPath(file: string): boolean {
|
|
|
270
271
|
return SKIPPED_PATH_SUFFIXES.some((suffix) => file.endsWith(suffix));
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
export const PACKAGE_MERGE_CLI: CodemodCliConfig = {
|
|
275
|
+
name: "package-merge",
|
|
276
|
+
transform: (source, filePath) => transformPackageMergeSource(source, filePath),
|
|
277
|
+
isSkippedPath,
|
|
278
|
+
extraFileNames: ["package.json"],
|
|
279
|
+
extraExtensions: [".css"],
|
|
280
|
+
};
|
|
281
|
+
|
|
273
282
|
if (import.meta.main) {
|
|
274
|
-
await runCodemodCli(
|
|
275
|
-
{
|
|
276
|
-
name: "package-merge",
|
|
277
|
-
transform: (source, filePath) => transformPackageMergeSource(source, filePath),
|
|
278
|
-
isSkippedPath,
|
|
279
|
-
extraFileNames: ["package.json"],
|
|
280
|
-
extraExtensions: [".css"],
|
|
281
|
-
},
|
|
282
|
-
process.argv.slice(2),
|
|
283
|
-
);
|
|
283
|
+
await runCodemodCli(PACKAGE_MERGE_CLI, process.argv.slice(2));
|
|
284
284
|
}
|
|
@@ -506,20 +506,23 @@ function transformFiles(files: readonly string[], mode: Mode): RunSummary {
|
|
|
506
506
|
return summary;
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
function
|
|
509
|
+
function parseArgsOrUsageError(
|
|
510
|
+
argv: readonly string[],
|
|
511
|
+
): CliOptions | { readonly exitCode: number } {
|
|
510
512
|
try {
|
|
511
513
|
return parseArgs(argv);
|
|
512
514
|
} catch (err) {
|
|
513
515
|
if (err instanceof CliError) {
|
|
514
516
|
console.error(`[testing-move] ${err.message}`);
|
|
515
|
-
|
|
517
|
+
return { exitCode: 2 };
|
|
516
518
|
}
|
|
517
519
|
throw err;
|
|
518
520
|
}
|
|
519
521
|
}
|
|
520
522
|
|
|
521
|
-
async function main(): Promise<
|
|
522
|
-
const options =
|
|
523
|
+
export async function main(argv: readonly string[]): Promise<number> {
|
|
524
|
+
const options = parseArgsOrUsageError(argv);
|
|
525
|
+
if ("exitCode" in options) return options.exitCode;
|
|
523
526
|
const files = collectSourceFiles(options.paths, { isMigratableFile }).filter(
|
|
524
527
|
(file) => !isSkippedPath(file),
|
|
525
528
|
);
|
|
@@ -534,9 +537,9 @@ async function main(): Promise<void> {
|
|
|
534
537
|
}
|
|
535
538
|
|
|
536
539
|
const hasOpenSites = rewrites.length + manual.length > 0;
|
|
537
|
-
|
|
540
|
+
return options.mode === "check" && hasOpenSites ? 1 : 0;
|
|
538
541
|
}
|
|
539
542
|
|
|
540
543
|
if (import.meta.main) {
|
|
541
|
-
await main();
|
|
544
|
+
process.exit(await main(process.argv.slice(2)));
|
|
542
545
|
}
|
|
@@ -365,9 +365,9 @@ function migrateFile(sourceFile: SourceFile, skips: SkipEntry[]): number {
|
|
|
365
365
|
return removedCount;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
async function main(): Promise<
|
|
369
|
-
const positional =
|
|
370
|
-
const dryRun =
|
|
368
|
+
export async function main(argv: readonly string[]): Promise<number> {
|
|
369
|
+
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
370
|
+
const dryRun = argv.includes("--dry-run");
|
|
371
371
|
const rootDir = resolve(positional[0] ?? process.cwd());
|
|
372
372
|
|
|
373
373
|
const files = findTargetFiles(rootDir);
|
|
@@ -406,6 +406,7 @@ async function main(): Promise<void> {
|
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
console.log();
|
|
409
|
+
return 0;
|
|
409
410
|
}
|
|
410
411
|
|
|
411
|
-
await main();
|
|
412
|
+
if (import.meta.main) process.exit(await main(process.argv.slice(2)));
|