@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momentumcms/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Core collection config, fields, hooks, and access control for Momentum CMS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Momentum CMS Contributors",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"main": "./index.cjs",
|
|
29
29
|
"types": "./src/index.d.ts",
|
|
30
30
|
"bin": {
|
|
31
|
-
"momentum-generate
|
|
31
|
+
"momentum-generate": "./generators/generator.cjs"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {},
|
|
34
34
|
"module": "./index.js"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-to-TypeScript mapping utilities for Momentum CMS code generation.
|
|
3
|
+
*
|
|
4
|
+
* Converts collection/global field definitions into TypeScript type strings.
|
|
5
|
+
* Handles layout field flattening, block discriminated unions, and where clauses.
|
|
6
|
+
*/
|
|
7
|
+
import type { Field, BlocksField } from '../lib/fields/field.types';
|
|
8
|
+
/**
|
|
9
|
+
* Convert a slug like 'auth-user' to PascalCase like 'AuthUser'.
|
|
10
|
+
* Strips any characters that are not valid in a TypeScript identifier.
|
|
11
|
+
*/
|
|
12
|
+
export declare function slugToPascalCase(slug: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Safely quote a string value for use in generated TypeScript.
|
|
15
|
+
* Uses JSON.stringify to handle escaping (produces double-quoted strings).
|
|
16
|
+
*/
|
|
17
|
+
export declare function safeQuote(value: string | number): string;
|
|
18
|
+
/**
|
|
19
|
+
* Map a single field to its TypeScript type string.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fieldTypeToTS(field: Field): string;
|
|
22
|
+
/**
|
|
23
|
+
* Generate interface field lines from an array of field definitions.
|
|
24
|
+
* Flattens layout fields (tabs, collapsible, row) before processing.
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateFieldsInterface(fields: Field[], indent?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Generate discriminated union types for a blocks field.
|
|
29
|
+
*
|
|
30
|
+
* Returns the block interface definitions and union type as a string,
|
|
31
|
+
* plus the union type name to use in the parent interface.
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateBlockTypes(collectionName: string, fieldName: string, blocksField: BlocksField): {
|
|
34
|
+
declarations: string;
|
|
35
|
+
unionTypeName: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get the where clause type for a field based on its type.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getFieldWhereType(field: Field): string;
|
|
41
|
+
/**
|
|
42
|
+
* Generate a where clause interface for a collection.
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateWhereClauseInterface(slug: string, fields: Field[], hasTimestamps: boolean): string;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Momentum CMS Unified Code Generator
|
|
3
|
+
*
|
|
4
|
+
* Single-pass generator that reads momentum.config.ts and produces two output files:
|
|
5
|
+
* 1. Types file (--types): TypeScript interfaces for all collections + globals
|
|
6
|
+
* 2. Admin config file (--config): Browser-safe Angular config with inlined, stripped collections
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx generator.ts <configPath> --types <typesOutput> --config <configOutput> [--watch]
|
|
10
|
+
*/
|
|
11
|
+
interface FieldDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
type: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
unique?: boolean;
|
|
16
|
+
hasMany?: boolean;
|
|
17
|
+
label?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
options?: Array<{
|
|
20
|
+
value: string | number;
|
|
21
|
+
label?: string;
|
|
22
|
+
}>;
|
|
23
|
+
fields?: FieldDefinition[];
|
|
24
|
+
blocks?: Array<{
|
|
25
|
+
slug: string;
|
|
26
|
+
fields: FieldDefinition[];
|
|
27
|
+
labels?: {
|
|
28
|
+
singular?: string;
|
|
29
|
+
plural?: string;
|
|
30
|
+
};
|
|
31
|
+
editor?: Record<string, unknown>;
|
|
32
|
+
}>;
|
|
33
|
+
tabs?: Array<{
|
|
34
|
+
label: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
fields: FieldDefinition[];
|
|
37
|
+
}>;
|
|
38
|
+
collection?: () => unknown;
|
|
39
|
+
relationTo?: string | Array<() => unknown>;
|
|
40
|
+
from?: string;
|
|
41
|
+
defaultValue?: unknown;
|
|
42
|
+
defaultOpen?: boolean;
|
|
43
|
+
onDelete?: string;
|
|
44
|
+
filterOptions?: unknown;
|
|
45
|
+
admin?: Record<string, unknown>;
|
|
46
|
+
access?: unknown;
|
|
47
|
+
hooks?: unknown;
|
|
48
|
+
validate?: unknown;
|
|
49
|
+
displayFormat?: Record<string, unknown>;
|
|
50
|
+
minLength?: number;
|
|
51
|
+
maxLength?: number;
|
|
52
|
+
rows?: number;
|
|
53
|
+
min?: number;
|
|
54
|
+
max?: number;
|
|
55
|
+
step?: number;
|
|
56
|
+
minRows?: number;
|
|
57
|
+
maxRows?: number;
|
|
58
|
+
displayField?: string;
|
|
59
|
+
mimeTypes?: string[];
|
|
60
|
+
maxSize?: number;
|
|
61
|
+
}
|
|
62
|
+
interface CollectionDefinition {
|
|
63
|
+
slug: string;
|
|
64
|
+
labels?: {
|
|
65
|
+
singular?: string;
|
|
66
|
+
plural?: string;
|
|
67
|
+
};
|
|
68
|
+
fields: FieldDefinition[];
|
|
69
|
+
admin?: Record<string, unknown>;
|
|
70
|
+
access?: unknown;
|
|
71
|
+
hooks?: unknown;
|
|
72
|
+
auth?: unknown;
|
|
73
|
+
timestamps?: boolean | {
|
|
74
|
+
createdAt?: boolean;
|
|
75
|
+
updatedAt?: boolean;
|
|
76
|
+
};
|
|
77
|
+
versions?: boolean | {
|
|
78
|
+
drafts?: boolean;
|
|
79
|
+
maxPerDoc?: number;
|
|
80
|
+
};
|
|
81
|
+
softDelete?: boolean | {
|
|
82
|
+
field?: string;
|
|
83
|
+
retentionDays?: number;
|
|
84
|
+
};
|
|
85
|
+
managed?: boolean;
|
|
86
|
+
dbName?: string;
|
|
87
|
+
indexes?: unknown[];
|
|
88
|
+
defaultSort?: string;
|
|
89
|
+
graphQL?: unknown;
|
|
90
|
+
defaultWhere?: unknown;
|
|
91
|
+
endpoints?: unknown[];
|
|
92
|
+
webhooks?: unknown[];
|
|
93
|
+
}
|
|
94
|
+
interface GlobalDefinition {
|
|
95
|
+
slug: string;
|
|
96
|
+
label?: string;
|
|
97
|
+
fields: FieldDefinition[];
|
|
98
|
+
admin?: Record<string, unknown>;
|
|
99
|
+
access?: unknown;
|
|
100
|
+
hooks?: unknown;
|
|
101
|
+
versions?: boolean | {
|
|
102
|
+
drafts?: boolean;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
interface PluginBrowserImport {
|
|
106
|
+
path: string;
|
|
107
|
+
exportName: string;
|
|
108
|
+
}
|
|
109
|
+
interface PluginBrowserImports {
|
|
110
|
+
collections?: PluginBrowserImport;
|
|
111
|
+
adminRoutes?: PluginBrowserImport;
|
|
112
|
+
modifyCollections?: PluginBrowserImport;
|
|
113
|
+
}
|
|
114
|
+
interface PluginDescriptor {
|
|
115
|
+
name: string;
|
|
116
|
+
collections?: Array<{
|
|
117
|
+
slug: string;
|
|
118
|
+
fields?: FieldDefinition[];
|
|
119
|
+
}>;
|
|
120
|
+
adminRoutes?: Array<{
|
|
121
|
+
path: string;
|
|
122
|
+
loadComponent: unknown;
|
|
123
|
+
data?: Record<string, unknown>;
|
|
124
|
+
label: string;
|
|
125
|
+
icon: string;
|
|
126
|
+
group?: string;
|
|
127
|
+
}>;
|
|
128
|
+
modifyCollections?(collections: unknown[]): void;
|
|
129
|
+
browserImports?: PluginBrowserImports;
|
|
130
|
+
}
|
|
131
|
+
interface MomentumConfig {
|
|
132
|
+
collections: CollectionDefinition[];
|
|
133
|
+
globals?: GlobalDefinition[];
|
|
134
|
+
admin?: {
|
|
135
|
+
basePath?: string;
|
|
136
|
+
branding?: {
|
|
137
|
+
logo?: string;
|
|
138
|
+
title?: string;
|
|
139
|
+
};
|
|
140
|
+
toasts?: boolean;
|
|
141
|
+
};
|
|
142
|
+
plugins?: PluginDescriptor[];
|
|
143
|
+
}
|
|
144
|
+
interface GeneratorOptions {
|
|
145
|
+
configPath: string;
|
|
146
|
+
typesOutputPath: string;
|
|
147
|
+
configOutputPath: string;
|
|
148
|
+
watch?: boolean;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generate TypeScript types from collection and global definitions.
|
|
152
|
+
*/
|
|
153
|
+
export declare function generateTypes(config: MomentumConfig): string;
|
|
154
|
+
/**
|
|
155
|
+
* Serialize a value to a TypeScript literal string.
|
|
156
|
+
* Skips functions and undefined values.
|
|
157
|
+
*/
|
|
158
|
+
export declare function serializeValue(value: unknown, indent?: string): string;
|
|
159
|
+
/**
|
|
160
|
+
* Serialize a field definition, stripping server-only properties.
|
|
161
|
+
* For relationship fields, resolves collection() at build time into an inline stub.
|
|
162
|
+
*/
|
|
163
|
+
export declare function serializeField(field: FieldDefinition, indent?: string): string;
|
|
164
|
+
/**
|
|
165
|
+
* Serialize a collection definition, stripping server-only properties.
|
|
166
|
+
*/
|
|
167
|
+
export declare function serializeCollection(collection: CollectionDefinition, indent?: string): string;
|
|
168
|
+
/**
|
|
169
|
+
* Serialize a global definition, stripping server-only properties.
|
|
170
|
+
*/
|
|
171
|
+
export declare function serializeGlobal(global: GlobalDefinition, indent?: string): string;
|
|
172
|
+
/**
|
|
173
|
+
* Compute a relative import path from one file to another (without .ts extension).
|
|
174
|
+
*/
|
|
175
|
+
export declare function computeRelativeImport(fromFile: string, toFile: string): string;
|
|
176
|
+
/**
|
|
177
|
+
* Generate the browser-safe admin config TypeScript file.
|
|
178
|
+
* Collections and globals are inlined with server-only properties stripped.
|
|
179
|
+
* Only plugin admin routes are still imported (they have loadComponent functions).
|
|
180
|
+
*/
|
|
181
|
+
export declare function generateAdminConfig(config: MomentumConfig, typesRelPath: string): string;
|
|
182
|
+
export default function runGenerator(options: GeneratorOptions): Promise<{
|
|
183
|
+
success: boolean;
|
|
184
|
+
}>;
|
|
185
|
+
export {};
|
package/src/lib/config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CollectionConfig, GlobalConfig } from './collections';
|
|
2
2
|
import type { SeedingConfig, SeedingOptions } from './seeding';
|
|
3
3
|
import type { DocumentVersion, DocumentStatus, VersionQueryOptions, VersionCountOptions, CreateVersionOptions } from './versions';
|
|
4
|
-
import type { MomentumPlugin } from './plugins';
|
|
4
|
+
import type { MomentumPlugin, PluginAdminRouteDescriptor } from './plugins';
|
|
5
5
|
import type { StorageAdapter } from './storage';
|
|
6
6
|
/**
|
|
7
7
|
* Minimum password length for user accounts.
|
|
@@ -251,6 +251,39 @@ export interface LoggingConfig {
|
|
|
251
251
|
* Resolved logging config with defaults applied.
|
|
252
252
|
*/
|
|
253
253
|
export type ResolvedLoggingConfig = Required<LoggingConfig>;
|
|
254
|
+
/**
|
|
255
|
+
* Browser-safe plugin descriptor for admin UI.
|
|
256
|
+
* Contains only static data needed for admin route generation.
|
|
257
|
+
*/
|
|
258
|
+
export interface AdminPluginDescriptor {
|
|
259
|
+
/** Unique plugin name */
|
|
260
|
+
name: string;
|
|
261
|
+
/** Static collections declared by this plugin */
|
|
262
|
+
collections?: CollectionConfig[];
|
|
263
|
+
/** Static admin routes declared by this plugin */
|
|
264
|
+
adminRoutes?: PluginAdminRouteDescriptor[];
|
|
265
|
+
/** Synchronously transform the merged collections at config time */
|
|
266
|
+
modifyCollections?(collections: CollectionConfig[]): void;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Browser-safe config for admin route generation.
|
|
270
|
+
* Contains only the data needed by `momentumAdminRoutes()` — no db/storage/server.
|
|
271
|
+
* This is the output type of the unified `generate` Nx executor (--config output).
|
|
272
|
+
*/
|
|
273
|
+
export interface MomentumAdminConfig<CS extends string = string, GS extends string = string> {
|
|
274
|
+
/** Collection definitions */
|
|
275
|
+
collections: Array<CollectionConfig & {
|
|
276
|
+
slug: CS;
|
|
277
|
+
}>;
|
|
278
|
+
/** Global definitions (singleton documents) */
|
|
279
|
+
globals?: Array<GlobalConfig & {
|
|
280
|
+
slug: GS;
|
|
281
|
+
}>;
|
|
282
|
+
/** Admin panel configuration */
|
|
283
|
+
admin?: AdminPanelConfig;
|
|
284
|
+
/** Browser-safe plugin descriptors */
|
|
285
|
+
plugins?: AdminPluginDescriptor[];
|
|
286
|
+
}
|
|
254
287
|
/**
|
|
255
288
|
* Momentum CMS configuration.
|
|
256
289
|
*/
|
package/src/lib/plugins.d.ts
CHANGED
|
@@ -173,4 +173,27 @@ export interface MomentumPlugin {
|
|
|
173
173
|
onReady?(context: PluginReadyContext): void | Promise<void>;
|
|
174
174
|
/** Called on graceful shutdown. Clean up resources. */
|
|
175
175
|
onShutdown?(context: PluginContext): void | Promise<void>;
|
|
176
|
+
/** Browser-safe import descriptors for the admin config generator.
|
|
177
|
+
* Tells the generator where to import collections, admin routes, and
|
|
178
|
+
* modifyCollections from browser-safe sub-paths instead of the main
|
|
179
|
+
* server-only entry point. */
|
|
180
|
+
browserImports?: PluginBrowserImports;
|
|
181
|
+
}
|
|
182
|
+
/** A single browser-safe import descriptor for a plugin export. */
|
|
183
|
+
export interface PluginBrowserImport {
|
|
184
|
+
/** The import path (e.g., '@momentumcms/auth/collections') */
|
|
185
|
+
path: string;
|
|
186
|
+
/** The named export to import (e.g., 'BASE_AUTH_COLLECTIONS') */
|
|
187
|
+
exportName: string;
|
|
188
|
+
}
|
|
189
|
+
/** Browser-safe import map for the admin config generator.
|
|
190
|
+
* Plugins declare which sub-paths expose browser-safe versions
|
|
191
|
+
* of their collections, admin routes, and modifyCollections. */
|
|
192
|
+
export interface PluginBrowserImports {
|
|
193
|
+
/** Import path for browser-safe plugin collections */
|
|
194
|
+
collections?: PluginBrowserImport;
|
|
195
|
+
/** Import path for browser-safe plugin admin routes */
|
|
196
|
+
adminRoutes?: PluginBrowserImport;
|
|
197
|
+
/** Import path for browser-safe modifyCollections function */
|
|
198
|
+
modifyCollections?: PluginBrowserImport;
|
|
176
199
|
}
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// libs/core/src/generators/types/generator.ts
|
|
21
|
-
var generator_exports = {};
|
|
22
|
-
__export(generator_exports, {
|
|
23
|
-
default: () => runExecutor
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(generator_exports);
|
|
26
|
-
var import_node_fs = require("node:fs");
|
|
27
|
-
var import_node_path = require("node:path");
|
|
28
|
-
var import_node_url = require("node:url");
|
|
29
|
-
function fieldTypeToTS(field) {
|
|
30
|
-
switch (field.type) {
|
|
31
|
-
case "text":
|
|
32
|
-
case "textarea":
|
|
33
|
-
case "richText":
|
|
34
|
-
case "email":
|
|
35
|
-
case "password":
|
|
36
|
-
case "slug":
|
|
37
|
-
return "string";
|
|
38
|
-
case "number":
|
|
39
|
-
return "number";
|
|
40
|
-
case "checkbox":
|
|
41
|
-
return "boolean";
|
|
42
|
-
case "date":
|
|
43
|
-
return "string";
|
|
44
|
-
case "select":
|
|
45
|
-
case "radio":
|
|
46
|
-
if (field.options && field.options.length > 0) {
|
|
47
|
-
return field.options.map((opt) => `'${opt.value}'`).join(" | ");
|
|
48
|
-
}
|
|
49
|
-
return "string";
|
|
50
|
-
case "relationship": {
|
|
51
|
-
const baseType = "string";
|
|
52
|
-
return field.hasMany ? `${baseType}[]` : baseType;
|
|
53
|
-
}
|
|
54
|
-
case "upload":
|
|
55
|
-
return field.hasMany ? "string[]" : "string";
|
|
56
|
-
case "array":
|
|
57
|
-
if (field.fields && field.fields.length > 0) {
|
|
58
|
-
const arrayItemType = generateFieldsInterface(field.fields, " ");
|
|
59
|
-
return `Array<{
|
|
60
|
-
${arrayItemType}
|
|
61
|
-
}>`;
|
|
62
|
-
}
|
|
63
|
-
return "unknown[]";
|
|
64
|
-
case "group":
|
|
65
|
-
if (field.fields && field.fields.length > 0) {
|
|
66
|
-
const groupType = generateFieldsInterface(field.fields, " ");
|
|
67
|
-
return `{
|
|
68
|
-
${groupType}
|
|
69
|
-
}`;
|
|
70
|
-
}
|
|
71
|
-
return "Record<string, unknown>";
|
|
72
|
-
case "blocks":
|
|
73
|
-
return "unknown[]";
|
|
74
|
-
case "json":
|
|
75
|
-
return "Record<string, unknown>";
|
|
76
|
-
case "point":
|
|
77
|
-
return "[number, number]";
|
|
78
|
-
default:
|
|
79
|
-
return "unknown";
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function generateFieldsInterface(fields, indent = "") {
|
|
83
|
-
return fields.map((field) => {
|
|
84
|
-
const tsType = fieldTypeToTS(field);
|
|
85
|
-
const optional = field.required ? "" : "?";
|
|
86
|
-
return `${indent} ${field.name}${optional}: ${tsType};`;
|
|
87
|
-
}).join("\n");
|
|
88
|
-
}
|
|
89
|
-
function slugToPascalCase(slug) {
|
|
90
|
-
return slug.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
91
|
-
}
|
|
92
|
-
function getFieldWhereType(field) {
|
|
93
|
-
switch (field.type) {
|
|
94
|
-
case "text":
|
|
95
|
-
case "textarea":
|
|
96
|
-
case "richText":
|
|
97
|
-
case "email":
|
|
98
|
-
case "slug":
|
|
99
|
-
return `string | { equals?: string; not?: string; contains?: string; in?: string[] }`;
|
|
100
|
-
case "number":
|
|
101
|
-
return `number | { equals?: number; not?: number; gt?: number; gte?: number; lt?: number; lte?: number; in?: number[] }`;
|
|
102
|
-
case "checkbox":
|
|
103
|
-
return `boolean | { equals?: boolean }`;
|
|
104
|
-
case "date":
|
|
105
|
-
return `string | { equals?: string; not?: string; gt?: string; gte?: string; lt?: string; lte?: string }`;
|
|
106
|
-
case "select":
|
|
107
|
-
case "radio":
|
|
108
|
-
if (field.options && field.options.length > 0) {
|
|
109
|
-
const options = field.options.map((opt) => `'${opt.value}'`).join(" | ");
|
|
110
|
-
return `${options} | { equals?: ${options}; not?: ${options}; in?: (${options})[] }`;
|
|
111
|
-
}
|
|
112
|
-
return `string | { equals?: string; in?: string[] }`;
|
|
113
|
-
case "relationship":
|
|
114
|
-
return `string | { equals?: string; not?: string; in?: string[] }`;
|
|
115
|
-
default:
|
|
116
|
-
return "unknown";
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function generateWhereClauseInterface(collection) {
|
|
120
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
121
|
-
const lines = [];
|
|
122
|
-
lines.push(`/**`);
|
|
123
|
-
lines.push(` * Where clause type for querying the "${collection.slug}" collection.`);
|
|
124
|
-
lines.push(` */`);
|
|
125
|
-
lines.push(`export interface ${interfaceName}WhereClause {`);
|
|
126
|
-
lines.push(` id?: string | { equals?: string; not?: string; in?: string[] };`);
|
|
127
|
-
for (const field of collection.fields) {
|
|
128
|
-
const whereType = getFieldWhereType(field);
|
|
129
|
-
lines.push(` ${field.name}?: ${whereType};`);
|
|
130
|
-
}
|
|
131
|
-
const hasTimestamps = collection.timestamps !== false;
|
|
132
|
-
if (hasTimestamps) {
|
|
133
|
-
lines.push(
|
|
134
|
-
` createdAt?: string | { equals?: string; gt?: string; gte?: string; lt?: string; lte?: string };`
|
|
135
|
-
);
|
|
136
|
-
lines.push(
|
|
137
|
-
` updatedAt?: string | { equals?: string; gt?: string; gte?: string; lt?: string; lte?: string };`
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
lines.push(`}`);
|
|
141
|
-
return lines.join("\n");
|
|
142
|
-
}
|
|
143
|
-
function generateTypes(config) {
|
|
144
|
-
const lines = [
|
|
145
|
-
"/**",
|
|
146
|
-
" * Auto-generated types from Momentum CMS collection definitions.",
|
|
147
|
-
" * DO NOT EDIT - This file is regenerated when collections change.",
|
|
148
|
-
` * Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
149
|
-
" */",
|
|
150
|
-
""
|
|
151
|
-
];
|
|
152
|
-
for (const collection of config.collections) {
|
|
153
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
154
|
-
lines.push(`/**`);
|
|
155
|
-
lines.push(` * Document type for the "${collection.slug}" collection.`);
|
|
156
|
-
lines.push(` */`);
|
|
157
|
-
lines.push(`export interface ${interfaceName} {`);
|
|
158
|
-
lines.push(` /** Unique document identifier */`);
|
|
159
|
-
lines.push(` id: string;`);
|
|
160
|
-
const fieldsCode = generateFieldsInterface(collection.fields);
|
|
161
|
-
if (fieldsCode) {
|
|
162
|
-
lines.push(fieldsCode);
|
|
163
|
-
}
|
|
164
|
-
const hasTimestamps = collection.timestamps !== false;
|
|
165
|
-
if (hasTimestamps) {
|
|
166
|
-
lines.push(` /** Document creation timestamp */`);
|
|
167
|
-
lines.push(` createdAt: string;`);
|
|
168
|
-
lines.push(` /** Document last update timestamp */`);
|
|
169
|
-
lines.push(` updatedAt: string;`);
|
|
170
|
-
}
|
|
171
|
-
lines.push(`}`);
|
|
172
|
-
lines.push("");
|
|
173
|
-
}
|
|
174
|
-
for (const collection of config.collections) {
|
|
175
|
-
lines.push(generateWhereClauseInterface(collection));
|
|
176
|
-
lines.push("");
|
|
177
|
-
}
|
|
178
|
-
lines.push(`/**`);
|
|
179
|
-
lines.push(` * All collection slugs in this Momentum CMS instance.`);
|
|
180
|
-
lines.push(` */`);
|
|
181
|
-
const slugs = config.collections.map((c) => `'${c.slug}'`).join(" | ");
|
|
182
|
-
lines.push(`export type CollectionSlug = ${slugs || "never"};`);
|
|
183
|
-
lines.push("");
|
|
184
|
-
lines.push(`/**`);
|
|
185
|
-
lines.push(` * Mapping from collection slug to document type.`);
|
|
186
|
-
lines.push(` */`);
|
|
187
|
-
lines.push(`export interface MomentumCollections {`);
|
|
188
|
-
for (const collection of config.collections) {
|
|
189
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
190
|
-
lines.push(` '${collection.slug}': ${interfaceName};`);
|
|
191
|
-
}
|
|
192
|
-
lines.push(`}`);
|
|
193
|
-
lines.push("");
|
|
194
|
-
lines.push(`/**`);
|
|
195
|
-
lines.push(` * Type-safe collection mapping for use with injectTypedMomentumAPI().`);
|
|
196
|
-
lines.push(` * Includes both document types and where clause types.`);
|
|
197
|
-
lines.push(` *`);
|
|
198
|
-
lines.push(` * @example`);
|
|
199
|
-
lines.push(` * \`\`\`typescript`);
|
|
200
|
-
lines.push(` * import { injectTypedMomentumAPI } from '@momentumcms/admin';`);
|
|
201
|
-
lines.push(` * import type { TypedMomentumCollections } from './types/momentum.generated';`);
|
|
202
|
-
lines.push(` *`);
|
|
203
|
-
lines.push(` * const api = injectTypedMomentumAPI<TypedMomentumCollections>();`);
|
|
204
|
-
lines.push(` * const posts = await api.posts.find({ where: { status: 'published' } });`);
|
|
205
|
-
lines.push(` * \`\`\``);
|
|
206
|
-
lines.push(` */`);
|
|
207
|
-
lines.push(`export type TypedMomentumCollections = {`);
|
|
208
|
-
for (const collection of config.collections) {
|
|
209
|
-
const interfaceName = slugToPascalCase(collection.slug);
|
|
210
|
-
lines.push(
|
|
211
|
-
` '${collection.slug}': { doc: ${interfaceName}; where: ${interfaceName}WhereClause };`
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
lines.push(`};`);
|
|
215
|
-
lines.push("");
|
|
216
|
-
lines.push(`/**`);
|
|
217
|
-
lines.push(` * Helper type for getting document type from collection slug.`);
|
|
218
|
-
lines.push(` */`);
|
|
219
|
-
lines.push(`export type DocumentType<S extends CollectionSlug> = MomentumCollections[S];`);
|
|
220
|
-
lines.push("");
|
|
221
|
-
lines.push(`/**`);
|
|
222
|
-
lines.push(` * Helper type for getting where clause type from collection slug.`);
|
|
223
|
-
lines.push(` */`);
|
|
224
|
-
lines.push(
|
|
225
|
-
`export type WhereClauseType<S extends CollectionSlug> = TypedMomentumCollections[S]['where'];`
|
|
226
|
-
);
|
|
227
|
-
lines.push("");
|
|
228
|
-
return lines.join("\n");
|
|
229
|
-
}
|
|
230
|
-
async function loadConfig(configPath) {
|
|
231
|
-
try {
|
|
232
|
-
const configUrl = (0, import_node_url.pathToFileURL)(configPath).href;
|
|
233
|
-
const configModule = await import(configUrl);
|
|
234
|
-
return configModule.default || configModule;
|
|
235
|
-
} catch (error) {
|
|
236
|
-
throw new Error(`Failed to load config from ${configPath}: ${error}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async function runExecutor(options, context) {
|
|
240
|
-
const projectRoot = context.projectsConfigurations?.projects[context.projectName ?? ""]?.root;
|
|
241
|
-
const root = projectRoot ? (0, import_node_path.join)(context.root, projectRoot) : context.root;
|
|
242
|
-
const configPath = (0, import_node_path.resolve)(root, options.configPath);
|
|
243
|
-
const outputPath = (0, import_node_path.resolve)(root, options.outputPath);
|
|
244
|
-
console.info(`Generating types from: ${configPath}`);
|
|
245
|
-
console.info(`Output to: ${outputPath}`);
|
|
246
|
-
async function generate() {
|
|
247
|
-
try {
|
|
248
|
-
const config = await loadConfig(configPath);
|
|
249
|
-
const types = generateTypes(config);
|
|
250
|
-
(0, import_node_fs.writeFileSync)(outputPath, types, "utf-8");
|
|
251
|
-
console.info(`Types generated successfully!`);
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.error(`Error generating types:`, error);
|
|
254
|
-
throw error;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
await generate();
|
|
258
|
-
if (options.watch) {
|
|
259
|
-
console.info(`Watching for changes...`);
|
|
260
|
-
const configDir = (0, import_node_path.dirname)(configPath);
|
|
261
|
-
(0, import_node_fs.watch)(configDir, { recursive: true }, async (eventType, filename) => {
|
|
262
|
-
if (filename?.endsWith(".ts")) {
|
|
263
|
-
console.info(`Change detected: ${filename}`);
|
|
264
|
-
try {
|
|
265
|
-
await generate();
|
|
266
|
-
} catch {
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
return new Promise(() => {
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
return { success: true };
|
|
274
|
-
}
|
|
275
|
-
if (process.argv[1]?.endsWith("generator.ts") || process.argv[1]?.endsWith("generator.js") || process.argv[1]?.endsWith("generator.cjs")) {
|
|
276
|
-
const args = process.argv.slice(2);
|
|
277
|
-
const configPath = args[0];
|
|
278
|
-
const outputPath = args[1] || "src/types/momentum.generated.ts";
|
|
279
|
-
const watchMode = args.includes("--watch");
|
|
280
|
-
if (!configPath) {
|
|
281
|
-
console.error("Usage: npx ts-node generator.ts <config-path> [output-path] [--watch]");
|
|
282
|
-
process.exit(1);
|
|
283
|
-
}
|
|
284
|
-
runExecutor({ configPath, outputPath, watch: watchMode }, { root: process.cwd() }).then((result) => {
|
|
285
|
-
if (!result.success) {
|
|
286
|
-
process.exit(1);
|
|
287
|
-
}
|
|
288
|
-
}).catch((error) => {
|
|
289
|
-
console.error(error);
|
|
290
|
-
process.exit(1);
|
|
291
|
-
});
|
|
292
|
-
}
|