@cosmicdrift/kumiko-feature-ast 0.393.0 → 0.395.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-feature-ast",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.395.0",
|
|
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.395.0",
|
|
26
|
+
"@cosmicdrift/kumiko-types": "0.395.0",
|
|
27
27
|
"ts-morph": "^28.0.0",
|
|
28
28
|
"zod": "^4.6.7"
|
|
29
29
|
},
|
|
@@ -236,3 +236,14 @@ type's moved members are nested (`{ dispatch: { query: ... } }`) and the body is
|
|
|
236
236
|
when a handler or job context is passed to that parameter somewhere in the project. A structural
|
|
237
237
|
type declared in another file is reported as `manual`, and structural parameters with other names are
|
|
238
238
|
invisible to it and show up as type errors afterwards.
|
|
239
|
+
|
|
240
|
+
## content-collection-format.ts
|
|
241
|
+
|
|
242
|
+
Content collections know two editor formats, `plain` and `document`. The codemod rewrites
|
|
243
|
+
`contentFormat: "rich"` and `contentFormat: "markdown"` to `"document"` in the collection declarations of
|
|
244
|
+
`createTemplateResolverFeature({ collections: [...] })` and `r.contentCollection({...})`. Record storage
|
|
245
|
+
literals such as `contentFormat: "markdown"` in write payloads or seeds use a different vocabulary and stay.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
bun node_modules/@cosmicdrift/kumiko-feature-ast/src/scripts/codemod/content-collection-format.ts <dir> [--check | --dry-run | --write] [--no-format]
|
|
249
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// content-collection-format — the editor format of a content collection is "plain" or "document":
|
|
3
|
+
// createTemplateResolverFeature({ collections: [{ contentFormat: "rich" | "markdown" }] })
|
|
4
|
+
// r.contentCollection({ contentFormat: "rich" | "markdown" }) -> contentFormat: "document"
|
|
5
|
+
// Only collection declarations are touched. Record storage literals (`contentFormat: "markdown"`
|
|
6
|
+
// in write payloads, seeds, upserts) use a different vocabulary and stay unchanged.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// bun scripts/codemod/content-collection-format.ts <targetDir...> [--check | --dry-run | --write] [--no-format]
|
|
10
|
+
|
|
11
|
+
import { Node, type ObjectLiteralExpression, Project, SyntaxKind } from "ts-morph";
|
|
12
|
+
import {
|
|
13
|
+
applyEdits,
|
|
14
|
+
type CodemodResult,
|
|
15
|
+
runCodemodCli,
|
|
16
|
+
type SiteLists,
|
|
17
|
+
type TextEdit,
|
|
18
|
+
} from "./codemod-support.ts";
|
|
19
|
+
|
|
20
|
+
const CHEAP_PREFILTER = /contentCollection|createTemplateResolverFeature/;
|
|
21
|
+
const LEGACY_EDITOR_FORMATS: ReadonlySet<string> = new Set(["rich", "markdown"]);
|
|
22
|
+
|
|
23
|
+
function firstObjectArgument(call: Node): ObjectLiteralExpression | undefined {
|
|
24
|
+
if (!Node.isCallExpression(call)) return undefined;
|
|
25
|
+
const first = call.getArguments()[0];
|
|
26
|
+
return first !== undefined && Node.isObjectLiteralExpression(first) ? first : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function collectionDeclarations(call: Node): ObjectLiteralExpression[] {
|
|
30
|
+
const callee = call.asKind(SyntaxKind.CallExpression)?.getExpression().getText();
|
|
31
|
+
if (callee === undefined) return [];
|
|
32
|
+
if (callee === "createTemplateResolverFeature") {
|
|
33
|
+
const collections = firstObjectArgument(call)?.getProperty("collections");
|
|
34
|
+
const array = collections?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer();
|
|
35
|
+
if (array === undefined || !Node.isArrayLiteralExpression(array)) return [];
|
|
36
|
+
return array.getElements().filter(Node.isObjectLiteralExpression);
|
|
37
|
+
}
|
|
38
|
+
if (callee.endsWith(".contentCollection")) {
|
|
39
|
+
const declaration = firstObjectArgument(call);
|
|
40
|
+
return declaration === undefined ? [] : [declaration];
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function transformContentCollectionFormatSource(
|
|
46
|
+
source: string,
|
|
47
|
+
filePath = "source.ts",
|
|
48
|
+
): CodemodResult {
|
|
49
|
+
const result: SiteLists = { rewrites: [], manual: [] };
|
|
50
|
+
if (!CHEAP_PREFILTER.test(source)) return { output: source, ...result };
|
|
51
|
+
const project = new Project({
|
|
52
|
+
useInMemoryFileSystem: true,
|
|
53
|
+
skipAddingFilesFromTsConfig: true,
|
|
54
|
+
skipFileDependencyResolution: true,
|
|
55
|
+
});
|
|
56
|
+
const sourceFile = project.createSourceFile(filePath, source, { overwrite: true });
|
|
57
|
+
const edits: TextEdit[] = [];
|
|
58
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
59
|
+
for (const declaration of collectionDeclarations(call)) {
|
|
60
|
+
const property = declaration
|
|
61
|
+
.getProperty("contentFormat")
|
|
62
|
+
?.asKind(SyntaxKind.PropertyAssignment);
|
|
63
|
+
const value = property?.getInitializer();
|
|
64
|
+
if (value === undefined || !Node.isStringLiteral(value)) continue;
|
|
65
|
+
const current = value.getLiteralValue();
|
|
66
|
+
if (!LEGACY_EDITOR_FORMATS.has(current)) continue;
|
|
67
|
+
result.rewrites.push({
|
|
68
|
+
file: filePath,
|
|
69
|
+
line: value.getStartLineNumber(),
|
|
70
|
+
description: `contentFormat "${current}" -> "document"`,
|
|
71
|
+
});
|
|
72
|
+
edits.push({ start: value.getStart() + 1, end: value.getEnd() - 1, text: "document" });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { output: applyEdits(source, edits), ...result };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (import.meta.main) {
|
|
79
|
+
await runCodemodCli(
|
|
80
|
+
{
|
|
81
|
+
name: "content-collection-format",
|
|
82
|
+
transform: transformContentCollectionFormatSource,
|
|
83
|
+
isSkippedPath: () => false,
|
|
84
|
+
},
|
|
85
|
+
process.argv.slice(2),
|
|
86
|
+
);
|
|
87
|
+
}
|