@otl-core/cli 1.0.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/LICENSE +13 -0
- package/dist/index.cjs +661 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +638 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2025 OTL Core
|
|
2
|
+
|
|
3
|
+
Licensed under the PolyForm Shield License, Version 1.0.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may
|
|
5
|
+
obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
https://polyformproject.org/licenses/shield/1.0.0/
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
11
|
+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
12
|
+
License for the specific language governing permissions and limitations
|
|
13
|
+
under the License.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
"use strict";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// src/index.ts
|
|
28
|
+
var import_commander = require("commander");
|
|
29
|
+
|
|
30
|
+
// src/commands/add.ts
|
|
31
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
32
|
+
var path3 = __toESM(require("path"), 1);
|
|
33
|
+
|
|
34
|
+
// src/api/client.ts
|
|
35
|
+
function buildUrl(config, path5) {
|
|
36
|
+
const base = config.apiUrl.replace(/\/+$/, "");
|
|
37
|
+
return `${base}/api/v1/public/deployments/${encodeURIComponent(config.deploymentId)}${path5}`;
|
|
38
|
+
}
|
|
39
|
+
function authHeaders(config) {
|
|
40
|
+
return {
|
|
41
|
+
Authorization: `Bearer ${config.accessToken}`,
|
|
42
|
+
Accept: "application/json"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function fetchJson(url, headers) {
|
|
46
|
+
const response = await fetch(url, { headers });
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const body = await response.text().catch(() => "");
|
|
49
|
+
throw new Error(
|
|
50
|
+
`API request failed (${response.status} ${response.statusText}): ${body}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return response.json();
|
|
54
|
+
}
|
|
55
|
+
async function fetchBlockSchemas(config) {
|
|
56
|
+
const url = buildUrl(config, "/schemas/blocks");
|
|
57
|
+
const result = await fetchJson(
|
|
58
|
+
url,
|
|
59
|
+
authHeaders(config)
|
|
60
|
+
);
|
|
61
|
+
return result.data.schemas;
|
|
62
|
+
}
|
|
63
|
+
async function findBlockSchemaByType(config, typeName) {
|
|
64
|
+
const schemas = await fetchBlockSchemas(config);
|
|
65
|
+
return schemas.find((s) => s.type === typeName);
|
|
66
|
+
}
|
|
67
|
+
async function fetchSectionSchemas(config) {
|
|
68
|
+
const url = buildUrl(config, "/schemas/sections");
|
|
69
|
+
const result = await fetchJson(
|
|
70
|
+
url,
|
|
71
|
+
authHeaders(config)
|
|
72
|
+
);
|
|
73
|
+
return result.data;
|
|
74
|
+
}
|
|
75
|
+
async function findSectionSchemaByType(config, typeName) {
|
|
76
|
+
const schemas = await fetchSectionSchemas(config);
|
|
77
|
+
return schemas.find((s) => s.type === typeName);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/utils/naming.ts
|
|
81
|
+
function kebabToPascal(kebab) {
|
|
82
|
+
return kebab.split("-").map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
|
|
83
|
+
}
|
|
84
|
+
function fieldIdToCamel(id) {
|
|
85
|
+
return id.replace(/[-_]([a-z])/g, (_, char) => char.toUpperCase());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/codegen/type-mapper.ts
|
|
89
|
+
function createEmptyImports() {
|
|
90
|
+
return {
|
|
91
|
+
ColorReference: false,
|
|
92
|
+
MediaReference: false,
|
|
93
|
+
ResponsiveValue: false,
|
|
94
|
+
BlockInstance: false,
|
|
95
|
+
LocalizedString: false
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function buildTypeImportLine(imports) {
|
|
99
|
+
const needed = [];
|
|
100
|
+
if (imports.ColorReference) needed.push("ColorReference");
|
|
101
|
+
if (imports.MediaReference) needed.push("MediaReference");
|
|
102
|
+
if (imports.ResponsiveValue) needed.push("ResponsiveValue");
|
|
103
|
+
if (imports.BlockInstance) needed.push("BlockInstance");
|
|
104
|
+
if (imports.LocalizedString) needed.push("LocalizedString");
|
|
105
|
+
if (needed.length === 0) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return `import type { ${needed.join(", ")} } from "@otl-core/cms-types";`;
|
|
109
|
+
}
|
|
110
|
+
var SIMPLE_TYPE_MAP = {
|
|
111
|
+
text: "string",
|
|
112
|
+
textarea: "string",
|
|
113
|
+
url: "string",
|
|
114
|
+
markdown: "string",
|
|
115
|
+
html: "string",
|
|
116
|
+
code: "string",
|
|
117
|
+
richtext: "string",
|
|
118
|
+
date: "string",
|
|
119
|
+
number: "number",
|
|
120
|
+
boolean: "boolean",
|
|
121
|
+
color: "string",
|
|
122
|
+
"form-selector": "string",
|
|
123
|
+
"form-page": "string",
|
|
124
|
+
json: "Record<string, unknown>",
|
|
125
|
+
object: "Record<string, unknown>",
|
|
126
|
+
array: "unknown[]",
|
|
127
|
+
"container-behavior": '"boxed" | "edged" | "ignore"'
|
|
128
|
+
};
|
|
129
|
+
function resolveFieldType(field, imports) {
|
|
130
|
+
const simple = SIMPLE_TYPE_MAP[field.type];
|
|
131
|
+
if (simple !== void 0) {
|
|
132
|
+
return simple;
|
|
133
|
+
}
|
|
134
|
+
switch (field.type) {
|
|
135
|
+
// Select can be single or multi
|
|
136
|
+
case "select":
|
|
137
|
+
return field.multiple ? "string[]" : "string";
|
|
138
|
+
// Theme color types
|
|
139
|
+
case "theme-color":
|
|
140
|
+
case "theme-background-color":
|
|
141
|
+
case "theme-foreground-color":
|
|
142
|
+
imports.ColorReference = true;
|
|
143
|
+
return "ColorReference";
|
|
144
|
+
// Image / media
|
|
145
|
+
case "image":
|
|
146
|
+
imports.MediaReference = true;
|
|
147
|
+
return "MediaReference";
|
|
148
|
+
// Responsive value types
|
|
149
|
+
case "spacing":
|
|
150
|
+
case "css-value":
|
|
151
|
+
case "columns":
|
|
152
|
+
imports.ResponsiveValue = true;
|
|
153
|
+
return "ResponsiveValue<string>";
|
|
154
|
+
// Blocks
|
|
155
|
+
case "blocks":
|
|
156
|
+
imports.BlockInstance = true;
|
|
157
|
+
return "BlockInstance[]";
|
|
158
|
+
// Localized text
|
|
159
|
+
case "localized-text":
|
|
160
|
+
imports.LocalizedString = true;
|
|
161
|
+
return "LocalizedString";
|
|
162
|
+
// Group -- will be handled by the interface generator to produce a nested
|
|
163
|
+
// interface. At the type level we return the interface name.
|
|
164
|
+
case "group":
|
|
165
|
+
return "__GROUP__";
|
|
166
|
+
default:
|
|
167
|
+
return "unknown";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function generateInterface(interfaceName, fields) {
|
|
171
|
+
const imports = createEmptyImports();
|
|
172
|
+
const extraInterfaces = [];
|
|
173
|
+
const lines = buildInterfaceLines(
|
|
174
|
+
interfaceName,
|
|
175
|
+
fields,
|
|
176
|
+
imports,
|
|
177
|
+
extraInterfaces
|
|
178
|
+
);
|
|
179
|
+
const mainInterface = [`interface ${interfaceName} {`, ...lines, "}"].join(
|
|
180
|
+
"\n"
|
|
181
|
+
);
|
|
182
|
+
const declarations = [...extraInterfaces, mainInterface].join("\n\n");
|
|
183
|
+
return { name: interfaceName, declarations, imports };
|
|
184
|
+
}
|
|
185
|
+
function buildInterfaceLines(parentName, fields, imports, extraInterfaces) {
|
|
186
|
+
const lines = [];
|
|
187
|
+
for (const field of fields) {
|
|
188
|
+
const camelId = fieldIdToCamel(field.id);
|
|
189
|
+
let tsType = resolveFieldType(field, imports);
|
|
190
|
+
if (tsType === "__GROUP__" && field.fields && field.fields.length > 0) {
|
|
191
|
+
const nestedName = parentName + kebabToPascal(field.id);
|
|
192
|
+
const nestedLines = buildInterfaceLines(
|
|
193
|
+
nestedName,
|
|
194
|
+
field.fields,
|
|
195
|
+
imports,
|
|
196
|
+
extraInterfaces
|
|
197
|
+
);
|
|
198
|
+
const nestedDecl = [
|
|
199
|
+
`interface ${nestedName} {`,
|
|
200
|
+
...nestedLines,
|
|
201
|
+
"}"
|
|
202
|
+
].join("\n");
|
|
203
|
+
extraInterfaces.push(nestedDecl);
|
|
204
|
+
tsType = nestedName;
|
|
205
|
+
} else if (tsType === "__GROUP__") {
|
|
206
|
+
tsType = "Record<string, unknown>";
|
|
207
|
+
}
|
|
208
|
+
if (field.description) {
|
|
209
|
+
lines.push(` /** ${field.description} */`);
|
|
210
|
+
}
|
|
211
|
+
lines.push(` ${camelId}?: ${tsType};`);
|
|
212
|
+
}
|
|
213
|
+
return lines;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/codegen/generate-block.ts
|
|
217
|
+
function generateBlockComponent(typeName, fields) {
|
|
218
|
+
const pascal = kebabToPascal(typeName);
|
|
219
|
+
const configName = `${pascal}Config`;
|
|
220
|
+
const componentName = `${pascal}Block`;
|
|
221
|
+
const result = generateInterface(configName, fields);
|
|
222
|
+
const importLines = [];
|
|
223
|
+
const typeImportLine = buildTypeImportLine(result.imports);
|
|
224
|
+
if (typeImportLine) {
|
|
225
|
+
const typesInBraces = typeImportLine.replace("import type { ", "").replace(' } from "@otl-core/cms-types";', "");
|
|
226
|
+
importLines.push(
|
|
227
|
+
`import type { BlockComponentProps, ${typesInBraces} } from "@otl-core/cms-types";`
|
|
228
|
+
);
|
|
229
|
+
} else {
|
|
230
|
+
importLines.push(
|
|
231
|
+
'import type { BlockComponentProps } from "@otl-core/cms-types";'
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
const fieldIds = fields.map((f) => fieldIdToCamel(f.id)).filter((id) => id.length > 0);
|
|
235
|
+
const destructuring = fieldIds.length > 0 ? ` const { ${fieldIds.join(", ")} } = config;
|
|
236
|
+
` : "";
|
|
237
|
+
const parts = [
|
|
238
|
+
importLines.join("\n"),
|
|
239
|
+
"",
|
|
240
|
+
result.declarations,
|
|
241
|
+
"",
|
|
242
|
+
`export function ${componentName}({ config }: BlockComponentProps<${configName}>) {`,
|
|
243
|
+
destructuring,
|
|
244
|
+
" return (",
|
|
245
|
+
" <div>",
|
|
246
|
+
` {/* TODO: Implement ${typeName} block */}`,
|
|
247
|
+
" </div>",
|
|
248
|
+
" );",
|
|
249
|
+
"}",
|
|
250
|
+
""
|
|
251
|
+
];
|
|
252
|
+
return parts.join("\n");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/codegen/generate-section.ts
|
|
256
|
+
function generateSectionComponent(typeName, fields) {
|
|
257
|
+
const pascal = kebabToPascal(typeName);
|
|
258
|
+
const configName = `${pascal}Config`;
|
|
259
|
+
const componentName = `${pascal}Section`;
|
|
260
|
+
const result = generateInterface(configName, fields);
|
|
261
|
+
const importLines = [];
|
|
262
|
+
const typeImportLine = buildTypeImportLine(result.imports);
|
|
263
|
+
if (typeImportLine) {
|
|
264
|
+
const typesInBraces = typeImportLine.replace("import type { ", "").replace(' } from "@otl-core/cms-types";', "");
|
|
265
|
+
importLines.push(
|
|
266
|
+
`import type { SectionComponentProps, ${typesInBraces} } from "@otl-core/cms-types";`
|
|
267
|
+
);
|
|
268
|
+
} else {
|
|
269
|
+
importLines.push(
|
|
270
|
+
'import type { SectionComponentProps } from "@otl-core/cms-types";'
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const fieldIds = fields.map((f) => fieldIdToCamel(f.id)).filter((id) => id.length > 0);
|
|
274
|
+
const destructuring = fieldIds.length > 0 ? ` const { ${fieldIds.join(", ")} } = config;
|
|
275
|
+
` : "";
|
|
276
|
+
const parts = [
|
|
277
|
+
importLines.join("\n"),
|
|
278
|
+
"",
|
|
279
|
+
result.declarations,
|
|
280
|
+
"",
|
|
281
|
+
`export function ${componentName}({ config }: SectionComponentProps<${configName}>) {`,
|
|
282
|
+
destructuring,
|
|
283
|
+
" return (",
|
|
284
|
+
" <div>",
|
|
285
|
+
` {/* TODO: Implement ${typeName} section */}`,
|
|
286
|
+
" </div>",
|
|
287
|
+
" );",
|
|
288
|
+
"}",
|
|
289
|
+
""
|
|
290
|
+
];
|
|
291
|
+
return parts.join("\n");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/codegen/registry-updater.ts
|
|
295
|
+
var fs = __toESM(require("fs"), 1);
|
|
296
|
+
function addToBlockRegistry(registryPath, typeName) {
|
|
297
|
+
const pascal = kebabToPascal(typeName);
|
|
298
|
+
const componentName = `${pascal}Block`;
|
|
299
|
+
const importPath = `@/components/blocks/${typeName}`;
|
|
300
|
+
const importLine = `import { ${componentName} } from "${importPath}";`;
|
|
301
|
+
const registerLine = `blockRegistry.register("${typeName}", ${componentName});`;
|
|
302
|
+
updateRegistryFile(registryPath, importLine, registerLine);
|
|
303
|
+
}
|
|
304
|
+
function addToSectionRegistry(registryPath, typeName) {
|
|
305
|
+
const pascal = kebabToPascal(typeName);
|
|
306
|
+
const componentName = `${pascal}Section`;
|
|
307
|
+
const importPath = `@/components/sections/${typeName}`;
|
|
308
|
+
const importLine = `import { ${componentName} } from "${importPath}";`;
|
|
309
|
+
const registerLine = `sectionRegistry.register("${typeName}", ${componentName});`;
|
|
310
|
+
updateRegistryFile(registryPath, importLine, registerLine);
|
|
311
|
+
}
|
|
312
|
+
function updateRegistryFile(filePath, importLine, registerLine) {
|
|
313
|
+
let content = fs.readFileSync(filePath, "utf-8");
|
|
314
|
+
if (content.includes(registerLine)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const importRegex = /^import .+$/gm;
|
|
318
|
+
let lastImportMatch = null;
|
|
319
|
+
let match;
|
|
320
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
321
|
+
lastImportMatch = match;
|
|
322
|
+
}
|
|
323
|
+
if (lastImportMatch) {
|
|
324
|
+
const insertPos = lastImportMatch.index + lastImportMatch[0].length;
|
|
325
|
+
content = content.slice(0, insertPos) + "\n" + importLine + content.slice(insertPos);
|
|
326
|
+
} else {
|
|
327
|
+
content = importLine + "\n" + content;
|
|
328
|
+
}
|
|
329
|
+
const customCommentIndex = content.indexOf("/**\n * Custom ");
|
|
330
|
+
if (customCommentIndex !== -1) {
|
|
331
|
+
content = content.slice(0, customCommentIndex) + registerLine + "\n\n" + content.slice(customCommentIndex);
|
|
332
|
+
} else {
|
|
333
|
+
content = content.trimEnd() + "\n" + registerLine + "\n";
|
|
334
|
+
}
|
|
335
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/utils/env.ts
|
|
339
|
+
var dotenv = __toESM(require("dotenv"), 1);
|
|
340
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
341
|
+
var path = __toESM(require("path"), 1);
|
|
342
|
+
function loadConfig() {
|
|
343
|
+
const envLocalPath = path.resolve(process.cwd(), ".env.local");
|
|
344
|
+
let fileEnv = {};
|
|
345
|
+
if (fs2.existsSync(envLocalPath)) {
|
|
346
|
+
const parsed = dotenv.parse(fs2.readFileSync(envLocalPath, "utf-8"));
|
|
347
|
+
fileEnv = parsed;
|
|
348
|
+
}
|
|
349
|
+
const deploymentId = fileEnv["DEPLOYMENT_ID"] || process.env["DEPLOYMENT_ID"] || "";
|
|
350
|
+
const accessToken = fileEnv["DEPLOYMENT_ACCESS_TOKEN"] || process.env["DEPLOYMENT_ACCESS_TOKEN"] || "";
|
|
351
|
+
const apiUrl = fileEnv["API_URL"] || process.env["API_URL"] || "http://localhost:8080";
|
|
352
|
+
if (!deploymentId || !accessToken) {
|
|
353
|
+
console.error(
|
|
354
|
+
"Error: Missing DEPLOYMENT_ID and/or DEPLOYMENT_ACCESS_TOKEN."
|
|
355
|
+
);
|
|
356
|
+
console.error(
|
|
357
|
+
"Run 'npx @otl-core/cli init' to configure your environment."
|
|
358
|
+
);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
return { deploymentId, accessToken, apiUrl };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/utils/project.ts
|
|
365
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
366
|
+
var path2 = __toESM(require("path"), 1);
|
|
367
|
+
var ENGINE_MARKERS = [
|
|
368
|
+
"@otl-core/block-registry",
|
|
369
|
+
"@otl-core/section-registry"
|
|
370
|
+
];
|
|
371
|
+
function readPackageJson(dir) {
|
|
372
|
+
const filePath = path2.join(dir, "package.json");
|
|
373
|
+
if (!fs3.existsSync(filePath)) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
378
|
+
return JSON.parse(raw);
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function isEngineProject(dir) {
|
|
384
|
+
const pkg = readPackageJson(dir);
|
|
385
|
+
if (!pkg) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
const allDeps = {
|
|
389
|
+
...pkg.dependencies,
|
|
390
|
+
...pkg.devDependencies
|
|
391
|
+
};
|
|
392
|
+
return ENGINE_MARKERS.some((marker) => marker in allDeps);
|
|
393
|
+
}
|
|
394
|
+
function requireEngineProject() {
|
|
395
|
+
let current = process.cwd();
|
|
396
|
+
for (let i = 0; i < 10; i++) {
|
|
397
|
+
if (isEngineProject(current)) {
|
|
398
|
+
return {
|
|
399
|
+
root: current,
|
|
400
|
+
blocksDir: path2.join(current, "src", "components", "blocks"),
|
|
401
|
+
sectionsDir: path2.join(current, "src", "components", "sections"),
|
|
402
|
+
blockRegistryFile: path2.join(
|
|
403
|
+
current,
|
|
404
|
+
"src",
|
|
405
|
+
"lib",
|
|
406
|
+
"registries",
|
|
407
|
+
"block-registry.ts"
|
|
408
|
+
),
|
|
409
|
+
sectionRegistryFile: path2.join(
|
|
410
|
+
current,
|
|
411
|
+
"src",
|
|
412
|
+
"lib",
|
|
413
|
+
"registries",
|
|
414
|
+
"section-registry.ts"
|
|
415
|
+
)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const parent = path2.dirname(current);
|
|
419
|
+
if (parent === current) {
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
current = parent;
|
|
423
|
+
}
|
|
424
|
+
console.error("Error: Not in a OTL engine project.");
|
|
425
|
+
console.error("Could not find a package.json with @otl-core/ dependencies.");
|
|
426
|
+
console.error("Run this command from your engine project root.");
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/commands/add.ts
|
|
431
|
+
function registerAddCommands(program2) {
|
|
432
|
+
const add = program2.command("add").description("Generate a component from a schema definition");
|
|
433
|
+
add.command("block <name>").description("Generate a block component from its schema").option("--force", "Overwrite existing files").action(async (name, opts) => {
|
|
434
|
+
try {
|
|
435
|
+
const paths = requireEngineProject();
|
|
436
|
+
const config = loadConfig();
|
|
437
|
+
const schema = await findBlockSchemaByType(config, name);
|
|
438
|
+
if (!schema) {
|
|
439
|
+
console.error(`Error: No block schema found with type "${name}".`);
|
|
440
|
+
console.error(
|
|
441
|
+
"Run 'npx @otl-core/cli list blocks' to see available schemas."
|
|
442
|
+
);
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
const outputFile = path3.join(paths.blocksDir, `${name}.tsx`);
|
|
446
|
+
if (fs4.existsSync(outputFile) && !opts.force) {
|
|
447
|
+
console.error(
|
|
448
|
+
`Error: ${path3.relative(paths.root, outputFile)} already exists.`
|
|
449
|
+
);
|
|
450
|
+
console.error("Use --force to overwrite.");
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
const content = generateBlockComponent(name, schema.fields);
|
|
454
|
+
fs4.mkdirSync(path3.dirname(outputFile), { recursive: true });
|
|
455
|
+
fs4.writeFileSync(outputFile, content, "utf-8");
|
|
456
|
+
const relPath = path3.relative(paths.root, outputFile);
|
|
457
|
+
console.log(`Created ${relPath}`);
|
|
458
|
+
if (fs4.existsSync(paths.blockRegistryFile)) {
|
|
459
|
+
addToBlockRegistry(paths.blockRegistryFile, name);
|
|
460
|
+
console.log(
|
|
461
|
+
`Updated ${path3.relative(paths.root, paths.blockRegistryFile)}`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
console.log("");
|
|
465
|
+
console.log(`Next: implement your component in ${relPath}`);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (err instanceof Error) {
|
|
468
|
+
console.error(`Error: ${err.message}`);
|
|
469
|
+
}
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
add.command("section <name>").description("Generate a section component from its schema").option("--force", "Overwrite existing files").action(async (name, opts) => {
|
|
474
|
+
try {
|
|
475
|
+
const paths = requireEngineProject();
|
|
476
|
+
const config = loadConfig();
|
|
477
|
+
const schema = await findSectionSchemaByType(config, name);
|
|
478
|
+
if (!schema) {
|
|
479
|
+
console.error(`Error: No section schema found with type "${name}".`);
|
|
480
|
+
console.error(
|
|
481
|
+
"Run 'npx @otl-core/cli list sections' to see available schemas."
|
|
482
|
+
);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
const outputFile = path3.join(paths.sectionsDir, `${name}.tsx`);
|
|
486
|
+
if (fs4.existsSync(outputFile) && !opts.force) {
|
|
487
|
+
console.error(
|
|
488
|
+
`Error: ${path3.relative(paths.root, outputFile)} already exists.`
|
|
489
|
+
);
|
|
490
|
+
console.error("Use --force to overwrite.");
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
const content = generateSectionComponent(name, schema.fields);
|
|
494
|
+
fs4.mkdirSync(path3.dirname(outputFile), { recursive: true });
|
|
495
|
+
fs4.writeFileSync(outputFile, content, "utf-8");
|
|
496
|
+
const relPath = path3.relative(paths.root, outputFile);
|
|
497
|
+
console.log(`Created ${relPath}`);
|
|
498
|
+
if (fs4.existsSync(paths.sectionRegistryFile)) {
|
|
499
|
+
addToSectionRegistry(paths.sectionRegistryFile, name);
|
|
500
|
+
console.log(
|
|
501
|
+
`Updated ${path3.relative(paths.root, paths.sectionRegistryFile)}`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
console.log("");
|
|
505
|
+
console.log(`Next: implement your component in ${relPath}`);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
if (err instanceof Error) {
|
|
508
|
+
console.error(`Error: ${err.message}`);
|
|
509
|
+
}
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/commands/init.ts
|
|
516
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
517
|
+
var path4 = __toESM(require("path"), 1);
|
|
518
|
+
var readline = __toESM(require("readline"), 1);
|
|
519
|
+
function registerInitCommand(program2) {
|
|
520
|
+
program2.command("init").description("Configure deployment credentials in .env.local").action(async () => {
|
|
521
|
+
try {
|
|
522
|
+
const rl = readline.createInterface({
|
|
523
|
+
input: process.stdin,
|
|
524
|
+
output: process.stdout
|
|
525
|
+
});
|
|
526
|
+
const ask = (question) => new Promise((resolve3) => {
|
|
527
|
+
rl.question(question, (answer) => resolve3(answer.trim()));
|
|
528
|
+
});
|
|
529
|
+
console.log("OTL CMS -- Engine Configuration");
|
|
530
|
+
console.log("");
|
|
531
|
+
const deploymentId = await ask("Deployment ID: ");
|
|
532
|
+
if (!deploymentId) {
|
|
533
|
+
console.error("Error: Deployment ID is required.");
|
|
534
|
+
rl.close();
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
const accessToken = await ask("Deployment Access Token: ");
|
|
538
|
+
if (!accessToken) {
|
|
539
|
+
console.error("Error: Access token is required.");
|
|
540
|
+
rl.close();
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
const apiUrlInput = await ask("API URL [http://localhost:8080]: ");
|
|
544
|
+
const apiUrl = apiUrlInput || "http://localhost:8080";
|
|
545
|
+
rl.close();
|
|
546
|
+
const envPath = path4.resolve(process.cwd(), ".env.local");
|
|
547
|
+
let content = "";
|
|
548
|
+
if (fs5.existsSync(envPath)) {
|
|
549
|
+
content = fs5.readFileSync(envPath, "utf-8");
|
|
550
|
+
content = upsertEnvVar(content, "DEPLOYMENT_ID", deploymentId);
|
|
551
|
+
content = upsertEnvVar(
|
|
552
|
+
content,
|
|
553
|
+
"DEPLOYMENT_ACCESS_TOKEN",
|
|
554
|
+
accessToken
|
|
555
|
+
);
|
|
556
|
+
content = upsertEnvVar(content, "API_URL", apiUrl);
|
|
557
|
+
} else {
|
|
558
|
+
content = [
|
|
559
|
+
"# OTL CMS Engine Configuration",
|
|
560
|
+
`DEPLOYMENT_ID=${deploymentId}`,
|
|
561
|
+
`DEPLOYMENT_ACCESS_TOKEN=${accessToken}`,
|
|
562
|
+
`API_URL=${apiUrl}`,
|
|
563
|
+
""
|
|
564
|
+
].join("\n");
|
|
565
|
+
}
|
|
566
|
+
fs5.writeFileSync(envPath, content, "utf-8");
|
|
567
|
+
console.log("");
|
|
568
|
+
console.log("Written to .env.local");
|
|
569
|
+
} catch (err) {
|
|
570
|
+
if (err instanceof Error) {
|
|
571
|
+
console.error(`Error: ${err.message}`);
|
|
572
|
+
}
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function upsertEnvVar(content, key, value) {
|
|
578
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
579
|
+
if (regex.test(content)) {
|
|
580
|
+
return content.replace(regex, `${key}=${value}`);
|
|
581
|
+
}
|
|
582
|
+
const base = content.endsWith("\n") ? content : content + "\n";
|
|
583
|
+
return base + `${key}=${value}
|
|
584
|
+
`;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/commands/list.ts
|
|
588
|
+
function registerListCommands(program2) {
|
|
589
|
+
const list = program2.command("list").description("List available schemas for the deployment");
|
|
590
|
+
list.command("blocks").description("List all block schemas").action(async () => {
|
|
591
|
+
try {
|
|
592
|
+
const config = loadConfig();
|
|
593
|
+
const schemas = await fetchBlockSchemas(config);
|
|
594
|
+
if (schemas.length === 0) {
|
|
595
|
+
console.log("No block schemas found for this deployment.");
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const typeCol = 24;
|
|
599
|
+
const nameCol = 28;
|
|
600
|
+
const fieldsCol = 8;
|
|
601
|
+
console.log(
|
|
602
|
+
padRight("TYPE", typeCol) + padRight("NAME", nameCol) + padRight("FIELDS", fieldsCol)
|
|
603
|
+
);
|
|
604
|
+
console.log("-".repeat(typeCol + nameCol + fieldsCol));
|
|
605
|
+
for (const schema of schemas) {
|
|
606
|
+
const fieldCount = schema.fields ? schema.fields.length : 0;
|
|
607
|
+
console.log(
|
|
608
|
+
padRight(schema.type, typeCol) + padRight(schema.name, nameCol) + String(fieldCount)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
} catch (err) {
|
|
612
|
+
if (err instanceof Error) {
|
|
613
|
+
console.error(`Error: ${err.message}`);
|
|
614
|
+
}
|
|
615
|
+
process.exit(1);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
list.command("sections").description("List all section schemas").action(async () => {
|
|
619
|
+
try {
|
|
620
|
+
const config = loadConfig();
|
|
621
|
+
const schemas = await fetchSectionSchemas(config);
|
|
622
|
+
if (schemas.length === 0) {
|
|
623
|
+
console.log("No section schemas found for this deployment.");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const typeCol = 24;
|
|
627
|
+
const nameCol = 28;
|
|
628
|
+
const fieldsCol = 8;
|
|
629
|
+
console.log(
|
|
630
|
+
padRight("TYPE", typeCol) + padRight("NAME", nameCol) + padRight("FIELDS", fieldsCol)
|
|
631
|
+
);
|
|
632
|
+
console.log("-".repeat(typeCol + nameCol + fieldsCol));
|
|
633
|
+
for (const schema of schemas) {
|
|
634
|
+
const fieldCount = schema.fields ? schema.fields.length : 0;
|
|
635
|
+
console.log(
|
|
636
|
+
padRight(schema.type, typeCol) + padRight(schema.name, nameCol) + String(fieldCount)
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
} catch (err) {
|
|
640
|
+
if (err instanceof Error) {
|
|
641
|
+
console.error(`Error: ${err.message}`);
|
|
642
|
+
}
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
function padRight(str, width) {
|
|
648
|
+
if (str.length >= width) {
|
|
649
|
+
return str + " ";
|
|
650
|
+
}
|
|
651
|
+
return str + " ".repeat(width - str.length);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/index.ts
|
|
655
|
+
var program = new import_commander.Command();
|
|
656
|
+
program.name("otl-cli").description("OTL CMS CLI -- scaffold and manage engine components").version("0.1.0");
|
|
657
|
+
registerAddCommands(program);
|
|
658
|
+
registerListCommands(program);
|
|
659
|
+
registerInitCommand(program);
|
|
660
|
+
program.parse();
|
|
661
|
+
//# sourceMappingURL=index.cjs.map
|