@momentumcms/core 0.1.9 → 0.1.10
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 +11 -0
- package/generators/generator.cjs +813 -0
- package/generators/generator.js +782 -0
- package/package.json +2 -2
- package/src/generators/field-to-typescript.d.ts +44 -0
- package/src/generators/generator.d.ts +185 -0
- package/src/lib/config.d.ts +34 -1
- package/src/lib/plugins.d.ts +23 -0
- package/generators/types/generator.cjs +0 -292
- package/generators/types/generator.js +0 -271
- package/src/generators/types/generator.d.ts +0 -27
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
// libs/core/src/generators/types/generator.ts
|
|
2
|
-
import { writeFileSync, watch } from "node:fs";
|
|
3
|
-
import { dirname, resolve, join } from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
-
function fieldTypeToTS(field) {
|
|
6
|
-
switch (field.type) {
|
|
7
|
-
case "text":
|
|
8
|
-
case "textarea":
|
|
9
|
-
case "richText":
|
|
10
|
-
case "email":
|
|
11
|
-
case "password":
|
|
12
|
-
case "slug":
|
|
13
|
-
return "string";
|
|
14
|
-
case "number":
|
|
15
|
-
return "number";
|
|
16
|
-
case "checkbox":
|
|
17
|
-
return "boolean";
|
|
18
|
-
case "date":
|
|
19
|
-
return "string";
|
|
20
|
-
case "select":
|
|
21
|
-
case "radio":
|
|
22
|
-
if (field.options && field.options.length > 0) {
|
|
23
|
-
return field.options.map((opt) => `'${opt.value}'`).join(" | ");
|
|
24
|
-
}
|
|
25
|
-
return "string";
|
|
26
|
-
case "relationship": {
|
|
27
|
-
const baseType = "string";
|
|
28
|
-
return field.hasMany ? `${baseType}[]` : baseType;
|
|
29
|
-
}
|
|
30
|
-
case "upload":
|
|
31
|
-
return field.hasMany ? "string[]" : "string";
|
|
32
|
-
case "array":
|
|
33
|
-
if (field.fields && field.fields.length > 0) {
|
|
34
|
-
const arrayItemType = generateFieldsInterface(field.fields, " ");
|
|
35
|
-
return `Array<{
|
|
36
|
-
${arrayItemType}
|
|
37
|
-
}>`;
|
|
38
|
-
}
|
|
39
|
-
return "unknown[]";
|
|
40
|
-
case "group":
|
|
41
|
-
if (field.fields && field.fields.length > 0) {
|
|
42
|
-
const groupType = generateFieldsInterface(field.fields, " ");
|
|
43
|
-
return `{
|
|
44
|
-
${groupType}
|
|
45
|
-
}`;
|
|
46
|
-
}
|
|
47
|
-
return "Record<string, unknown>";
|
|
48
|
-
case "blocks":
|
|
49
|
-
return "unknown[]";
|
|
50
|
-
case "json":
|
|
51
|
-
return "Record<string, unknown>";
|
|
52
|
-
case "point":
|
|
53
|
-
return "[number, number]";
|
|
54
|
-
default:
|
|
55
|
-
return "unknown";
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function generateFieldsInterface(fields, indent = "") {
|
|
59
|
-
return fields.map((field) => {
|
|
60
|
-
const tsType = fieldTypeToTS(field);
|
|
61
|
-
const optional = field.required ? "" : "?";
|
|
62
|
-
return `${indent} ${field.name}${optional}: ${tsType};`;
|
|
63
|
-
}).join("\n");
|
|
64
|
-
}
|
|
65
|
-
function slugToPascalCase(slug) {
|
|
66
|
-
return slug.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
67
|
-
}
|
|
68
|
-
function getFieldWhereType(field) {
|
|
69
|
-
switch (field.type) {
|
|
70
|
-
case "text":
|
|
71
|
-
case "textarea":
|
|
72
|
-
case "richText":
|
|
73
|
-
case "email":
|
|
74
|
-
case "slug":
|
|
75
|
-
return `string | { equals?: string; not?: string; contains?: string; in?: string[] }`;
|
|
76
|
-
case "number":
|
|
77
|
-
return `number | { equals?: number; not?: number; gt?: number; gte?: number; lt?: number; lte?: number; in?: number[] }`;
|
|
78
|
-
case "checkbox":
|
|
79
|
-
return `boolean | { equals?: boolean }`;
|
|
80
|
-
case "date":
|
|
81
|
-
return `string | { equals?: string; not?: string; gt?: string; gte?: string; lt?: string; lte?: string }`;
|
|
82
|
-
case "select":
|
|
83
|
-
case "radio":
|
|
84
|
-
if (field.options && field.options.length > 0) {
|
|
85
|
-
const options = field.options.map((opt) => `'${opt.value}'`).join(" | ");
|
|
86
|
-
return `${options} | { equals?: ${options}; not?: ${options}; in?: (${options})[] }`;
|
|
87
|
-
}
|
|
88
|
-
return `string | { equals?: string; in?: string[] }`;
|
|
89
|
-
case "relationship":
|
|
90
|
-
return `string | { equals?: string; not?: string; in?: string[] }`;
|
|
91
|
-
default:
|
|
92
|
-
return "unknown";
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function generateWhereClauseInterface(collection) {
|
|
96
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
97
|
-
const lines = [];
|
|
98
|
-
lines.push(`/**`);
|
|
99
|
-
lines.push(` * Where clause type for querying the "${collection.slug}" collection.`);
|
|
100
|
-
lines.push(` */`);
|
|
101
|
-
lines.push(`export interface ${interfaceName}WhereClause {`);
|
|
102
|
-
lines.push(` id?: string | { equals?: string; not?: string; in?: string[] };`);
|
|
103
|
-
for (const field of collection.fields) {
|
|
104
|
-
const whereType = getFieldWhereType(field);
|
|
105
|
-
lines.push(` ${field.name}?: ${whereType};`);
|
|
106
|
-
}
|
|
107
|
-
const hasTimestamps = collection.timestamps !== false;
|
|
108
|
-
if (hasTimestamps) {
|
|
109
|
-
lines.push(
|
|
110
|
-
` createdAt?: string | { equals?: string; gt?: string; gte?: string; lt?: string; lte?: string };`
|
|
111
|
-
);
|
|
112
|
-
lines.push(
|
|
113
|
-
` updatedAt?: string | { equals?: string; gt?: string; gte?: string; lt?: string; lte?: string };`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
lines.push(`}`);
|
|
117
|
-
return lines.join("\n");
|
|
118
|
-
}
|
|
119
|
-
function generateTypes(config) {
|
|
120
|
-
const lines = [
|
|
121
|
-
"/**",
|
|
122
|
-
" * Auto-generated types from Momentum CMS collection definitions.",
|
|
123
|
-
" * DO NOT EDIT - This file is regenerated when collections change.",
|
|
124
|
-
` * Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
125
|
-
" */",
|
|
126
|
-
""
|
|
127
|
-
];
|
|
128
|
-
for (const collection of config.collections) {
|
|
129
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
130
|
-
lines.push(`/**`);
|
|
131
|
-
lines.push(` * Document type for the "${collection.slug}" collection.`);
|
|
132
|
-
lines.push(` */`);
|
|
133
|
-
lines.push(`export interface ${interfaceName} {`);
|
|
134
|
-
lines.push(` /** Unique document identifier */`);
|
|
135
|
-
lines.push(` id: string;`);
|
|
136
|
-
const fieldsCode = generateFieldsInterface(collection.fields);
|
|
137
|
-
if (fieldsCode) {
|
|
138
|
-
lines.push(fieldsCode);
|
|
139
|
-
}
|
|
140
|
-
const hasTimestamps = collection.timestamps !== false;
|
|
141
|
-
if (hasTimestamps) {
|
|
142
|
-
lines.push(` /** Document creation timestamp */`);
|
|
143
|
-
lines.push(` createdAt: string;`);
|
|
144
|
-
lines.push(` /** Document last update timestamp */`);
|
|
145
|
-
lines.push(` updatedAt: string;`);
|
|
146
|
-
}
|
|
147
|
-
lines.push(`}`);
|
|
148
|
-
lines.push("");
|
|
149
|
-
}
|
|
150
|
-
for (const collection of config.collections) {
|
|
151
|
-
lines.push(generateWhereClauseInterface(collection));
|
|
152
|
-
lines.push("");
|
|
153
|
-
}
|
|
154
|
-
lines.push(`/**`);
|
|
155
|
-
lines.push(` * All collection slugs in this Momentum CMS instance.`);
|
|
156
|
-
lines.push(` */`);
|
|
157
|
-
const slugs = config.collections.map((c) => `'${c.slug}'`).join(" | ");
|
|
158
|
-
lines.push(`export type CollectionSlug = ${slugs || "never"};`);
|
|
159
|
-
lines.push("");
|
|
160
|
-
lines.push(`/**`);
|
|
161
|
-
lines.push(` * Mapping from collection slug to document type.`);
|
|
162
|
-
lines.push(` */`);
|
|
163
|
-
lines.push(`export interface MomentumCollections {`);
|
|
164
|
-
for (const collection of config.collections) {
|
|
165
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
166
|
-
lines.push(` '${collection.slug}': ${interfaceName};`);
|
|
167
|
-
}
|
|
168
|
-
lines.push(`}`);
|
|
169
|
-
lines.push("");
|
|
170
|
-
lines.push(`/**`);
|
|
171
|
-
lines.push(` * Type-safe collection mapping for use with injectTypedMomentumAPI().`);
|
|
172
|
-
lines.push(` * Includes both document types and where clause types.`);
|
|
173
|
-
lines.push(` *`);
|
|
174
|
-
lines.push(` * @example`);
|
|
175
|
-
lines.push(` * \`\`\`typescript`);
|
|
176
|
-
lines.push(` * import { injectTypedMomentumAPI } from '@momentumcms/admin';`);
|
|
177
|
-
lines.push(` * import type { TypedMomentumCollections } from './types/momentum.generated';`);
|
|
178
|
-
lines.push(` *`);
|
|
179
|
-
lines.push(` * const api = injectTypedMomentumAPI<TypedMomentumCollections>();`);
|
|
180
|
-
lines.push(` * const posts = await api.posts.find({ where: { status: 'published' } });`);
|
|
181
|
-
lines.push(` * \`\`\``);
|
|
182
|
-
lines.push(` */`);
|
|
183
|
-
lines.push(`export type TypedMomentumCollections = {`);
|
|
184
|
-
for (const collection of config.collections) {
|
|
185
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
186
|
-
lines.push(
|
|
187
|
-
` '${collection.slug}': { doc: ${interfaceName}; where: ${interfaceName}WhereClause };`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
lines.push(`};`);
|
|
191
|
-
lines.push("");
|
|
192
|
-
lines.push(`/**`);
|
|
193
|
-
lines.push(` * Helper type for getting document type from collection slug.`);
|
|
194
|
-
lines.push(` */`);
|
|
195
|
-
lines.push(`export type DocumentType<S extends CollectionSlug> = MomentumCollections[S];`);
|
|
196
|
-
lines.push("");
|
|
197
|
-
lines.push(`/**`);
|
|
198
|
-
lines.push(` * Helper type for getting where clause type from collection slug.`);
|
|
199
|
-
lines.push(` */`);
|
|
200
|
-
lines.push(
|
|
201
|
-
`export type WhereClauseType<S extends CollectionSlug> = TypedMomentumCollections[S]['where'];`
|
|
202
|
-
);
|
|
203
|
-
lines.push("");
|
|
204
|
-
return lines.join("\n");
|
|
205
|
-
}
|
|
206
|
-
async function loadConfig(configPath) {
|
|
207
|
-
try {
|
|
208
|
-
const configUrl = pathToFileURL(configPath).href;
|
|
209
|
-
const configModule = await import(configUrl);
|
|
210
|
-
return configModule.default || configModule;
|
|
211
|
-
} catch (error) {
|
|
212
|
-
throw new Error(`Failed to load config from ${configPath}: ${error}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
async function runExecutor(options, context) {
|
|
216
|
-
const projectRoot = context.projectsConfigurations?.projects[context.projectName ?? ""]?.root;
|
|
217
|
-
const root = projectRoot ? join(context.root, projectRoot) : context.root;
|
|
218
|
-
const configPath = resolve(root, options.configPath);
|
|
219
|
-
const outputPath = resolve(root, options.outputPath);
|
|
220
|
-
console.info(`Generating types from: ${configPath}`);
|
|
221
|
-
console.info(`Output to: ${outputPath}`);
|
|
222
|
-
async function generate() {
|
|
223
|
-
try {
|
|
224
|
-
const config = await loadConfig(configPath);
|
|
225
|
-
const types = generateTypes(config);
|
|
226
|
-
writeFileSync(outputPath, types, "utf-8");
|
|
227
|
-
console.info(`Types generated successfully!`);
|
|
228
|
-
} catch (error) {
|
|
229
|
-
console.error(`Error generating types:`, error);
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
await generate();
|
|
234
|
-
if (options.watch) {
|
|
235
|
-
console.info(`Watching for changes...`);
|
|
236
|
-
const configDir = dirname(configPath);
|
|
237
|
-
watch(configDir, { recursive: true }, async (eventType, filename) => {
|
|
238
|
-
if (filename?.endsWith(".ts")) {
|
|
239
|
-
console.info(`Change detected: ${filename}`);
|
|
240
|
-
try {
|
|
241
|
-
await generate();
|
|
242
|
-
} catch {
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
return new Promise(() => {
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
return { success: true };
|
|
250
|
-
}
|
|
251
|
-
if (process.argv[1]?.endsWith("generator.ts") || process.argv[1]?.endsWith("generator.js") || process.argv[1]?.endsWith("generator.cjs")) {
|
|
252
|
-
const args = process.argv.slice(2);
|
|
253
|
-
const configPath = args[0];
|
|
254
|
-
const outputPath = args[1] || "src/types/momentum.generated.ts";
|
|
255
|
-
const watchMode = args.includes("--watch");
|
|
256
|
-
if (!configPath) {
|
|
257
|
-
console.error("Usage: npx ts-node generator.ts <config-path> [output-path] [--watch]");
|
|
258
|
-
process.exit(1);
|
|
259
|
-
}
|
|
260
|
-
runExecutor({ configPath, outputPath, watch: watchMode }, { root: process.cwd() }).then((result) => {
|
|
261
|
-
if (!result.success) {
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
}).catch((error) => {
|
|
265
|
-
console.error(error);
|
|
266
|
-
process.exit(1);
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
export {
|
|
270
|
-
runExecutor as default
|
|
271
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Momentum CMS Type Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates TypeScript interfaces from collection definitions.
|
|
5
|
-
* Run via: nx run <app>:generate-types
|
|
6
|
-
*/
|
|
7
|
-
interface GeneratorSchema {
|
|
8
|
-
configPath: string;
|
|
9
|
-
outputPath: string;
|
|
10
|
-
watch?: boolean;
|
|
11
|
-
}
|
|
12
|
-
interface ExecutorContext {
|
|
13
|
-
root: string;
|
|
14
|
-
projectName?: string;
|
|
15
|
-
projectsConfigurations?: {
|
|
16
|
-
projects: Record<string, {
|
|
17
|
-
root: string;
|
|
18
|
-
}>;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Main executor function.
|
|
23
|
-
*/
|
|
24
|
-
export default function runExecutor(options: GeneratorSchema, context: ExecutorContext): Promise<{
|
|
25
|
-
success: boolean;
|
|
26
|
-
}>;
|
|
27
|
-
export {};
|