@onexapis/cli 1.1.65 → 1.1.70
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/README.md +1 -1
- package/dist/cli.js +1688 -801
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1684 -797
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +784 -405
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +779 -400
- package/dist/index.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +89 -7
- package/package.json +2 -2
- package/templates/default/package.json +5 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import chalk4 from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import fs8 from 'fs/promises';
|
|
7
|
-
import crypto from 'crypto';
|
|
4
|
+
import fs3 from 'fs';
|
|
5
|
+
import path13 from 'path';
|
|
8
6
|
import { glob } from 'glob';
|
|
7
|
+
import fs8 from 'fs-extra';
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
import fs11 from 'fs/promises';
|
|
10
|
+
import * as esbuild from 'esbuild';
|
|
9
11
|
import { createRequire } from 'module';
|
|
10
12
|
import http from 'http';
|
|
11
|
-
import fs3 from 'fs';
|
|
12
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
13
14
|
import os from 'os';
|
|
14
15
|
import dotenv from 'dotenv';
|
|
15
|
-
import fs from 'fs-extra';
|
|
16
16
|
import ejs from 'ejs';
|
|
17
17
|
import { execSync, spawn } from 'child_process';
|
|
18
|
-
import { Command } from 'commander';
|
|
18
|
+
import { Command, Option } from 'commander';
|
|
19
19
|
import inquirer from 'inquirer';
|
|
20
20
|
import archiver from 'archiver';
|
|
21
21
|
import FormData from 'form-data';
|
|
@@ -89,6 +89,308 @@ var init_logger = __esm({
|
|
|
89
89
|
logger = new Logger();
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
|
+
function isNextjsProject(dir) {
|
|
93
|
+
return fs3.existsSync(path13.join(dir, "next.config.ts")) || fs3.existsSync(path13.join(dir, "next.config.js")) || fs3.existsSync(path13.join(dir, "next.config.mjs"));
|
|
94
|
+
}
|
|
95
|
+
var init_detect_nextjs = __esm({
|
|
96
|
+
"src/utils/detect-nextjs.ts"() {
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
function sortedCopy(value) {
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
return value.map((v) => sortedCopy(v));
|
|
102
|
+
}
|
|
103
|
+
if (value && typeof value === "object") {
|
|
104
|
+
const sorted = {};
|
|
105
|
+
for (const key of Object.keys(value).sort()) {
|
|
106
|
+
sorted[key] = sortedCopy(value[key]);
|
|
107
|
+
}
|
|
108
|
+
return sorted;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
function normalizeField(raw) {
|
|
113
|
+
const out = {
|
|
114
|
+
id: String(raw.id),
|
|
115
|
+
type: String(raw.type)
|
|
116
|
+
};
|
|
117
|
+
if (raw.required === true) out.required = true;
|
|
118
|
+
if (raw.default !== void 0) out.default = raw.default;
|
|
119
|
+
if (Array.isArray(raw.aliases) && raw.aliases.length > 0) {
|
|
120
|
+
out.aliases = [...raw.aliases].map(String).sort();
|
|
121
|
+
}
|
|
122
|
+
if (typeof raw.maxLength === "number") out.maxLength = raw.maxLength;
|
|
123
|
+
if (typeof raw.min === "number") out.min = raw.min;
|
|
124
|
+
if (typeof raw.max === "number") out.max = raw.max;
|
|
125
|
+
if (typeof raw.step === "number") out.step = raw.step;
|
|
126
|
+
if (Array.isArray(raw.accept)) {
|
|
127
|
+
out.accept = [...raw.accept].map(String).sort();
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(raw.options)) {
|
|
130
|
+
out.options = raw.options.map((o) => String(o?.value ?? o)).sort();
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
function normalizeBlock(raw) {
|
|
135
|
+
return {
|
|
136
|
+
type: String(raw.type),
|
|
137
|
+
settings: Array.isArray(raw.settings) ? raw.settings.map(normalizeField).sort(sortFieldsById) : [],
|
|
138
|
+
defaults: raw.defaults && typeof raw.defaults === "object" ? sortedCopy(raw.defaults) : {},
|
|
139
|
+
...typeof raw.limit === "number" ? { limit: raw.limit } : {},
|
|
140
|
+
...typeof raw.min === "number" ? { min: raw.min } : {},
|
|
141
|
+
...raw.sortable === true ? { sortable: true } : {},
|
|
142
|
+
...raw.baseType ? { baseType: String(raw.baseType) } : {}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function normalizeTemplate(raw) {
|
|
146
|
+
const out = { id: String(raw.id) };
|
|
147
|
+
if (raw.isDefault === true) out.isDefault = true;
|
|
148
|
+
if (Array.isArray(raw.settings)) {
|
|
149
|
+
out.settings = raw.settings.map(normalizeField).sort(sortFieldsById);
|
|
150
|
+
}
|
|
151
|
+
if (raw.defaults && typeof raw.defaults === "object") {
|
|
152
|
+
out.defaults = sortedCopy(raw.defaults);
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function sortFieldsById(a, b) {
|
|
157
|
+
return a.id.localeCompare(b.id);
|
|
158
|
+
}
|
|
159
|
+
function sortByType(a, b) {
|
|
160
|
+
return a.type.localeCompare(b.type);
|
|
161
|
+
}
|
|
162
|
+
function normalizeSection(raw) {
|
|
163
|
+
return {
|
|
164
|
+
type: String(raw.type),
|
|
165
|
+
settings: Array.isArray(raw.settings) ? raw.settings.map(normalizeField).sort(sortFieldsById) : [],
|
|
166
|
+
defaults: raw.defaults && typeof raw.defaults === "object" ? sortedCopy(raw.defaults) : {},
|
|
167
|
+
blocks: Array.isArray(raw.blocks) ? raw.blocks.map(normalizeBlock).sort(sortByType) : [],
|
|
168
|
+
templates: Array.isArray(raw.templates) ? raw.templates.map(normalizeTemplate).sort(sortFieldsById) : [],
|
|
169
|
+
dataRequirements: raw.dataRequirements && typeof raw.dataRequirements === "object" ? sortedCopy(raw.dataRequirements) : null,
|
|
170
|
+
...raw.global === true ? { global: true } : {},
|
|
171
|
+
...typeof raw.maxBlocks === "number" ? { maxBlocks: raw.maxBlocks } : {}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async function extractSchemas(themePath) {
|
|
175
|
+
const { createJiti } = await import('jiti');
|
|
176
|
+
const jiti = createJiti(import.meta.url);
|
|
177
|
+
const schemaFiles = await glob("sections/**/*.schema.ts", { cwd: themePath });
|
|
178
|
+
const sections = {};
|
|
179
|
+
for (const file of schemaFiles) {
|
|
180
|
+
try {
|
|
181
|
+
const mod = await jiti.import(path13.join(themePath, file));
|
|
182
|
+
const exports$1 = mod;
|
|
183
|
+
for (const value of Object.values(exports$1)) {
|
|
184
|
+
if (value && typeof value === "object" && typeof value.type === "string" && Array.isArray(value.settings)) {
|
|
185
|
+
const section = normalizeSection(value);
|
|
186
|
+
sections[section.type] = section;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const manifest = {
|
|
193
|
+
manifestVersion: 1,
|
|
194
|
+
sections: {}
|
|
195
|
+
};
|
|
196
|
+
for (const type of Object.keys(sections).sort()) {
|
|
197
|
+
manifest.sections[type] = sections[type];
|
|
198
|
+
}
|
|
199
|
+
return manifest;
|
|
200
|
+
}
|
|
201
|
+
function serializeManifest(manifest) {
|
|
202
|
+
return JSON.stringify(sortedCopy(manifest), null, 2);
|
|
203
|
+
}
|
|
204
|
+
var init_extract_schemas = __esm({
|
|
205
|
+
"src/utils/extract-schemas.ts"() {
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
function isVideoAsset(filePath) {
|
|
209
|
+
const lower = filePath.toLowerCase();
|
|
210
|
+
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
211
|
+
}
|
|
212
|
+
function mimeFor(filename) {
|
|
213
|
+
const ext = path13.extname(filename).toLowerCase();
|
|
214
|
+
return MIME_MAP[ext] || "application/octet-stream";
|
|
215
|
+
}
|
|
216
|
+
async function sha256Prefix(absPath, len) {
|
|
217
|
+
const buf = await fs8.readFile(absPath);
|
|
218
|
+
return crypto.createHash("sha256").update(buf).digest("hex").slice(0, len);
|
|
219
|
+
}
|
|
220
|
+
function insertHashIntoName(relPath, hash) {
|
|
221
|
+
const dir = path13.posix.dirname(relPath);
|
|
222
|
+
const base = path13.posix.basename(relPath);
|
|
223
|
+
const ext = path13.posix.extname(base);
|
|
224
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
225
|
+
const hashed = `${stem}-${hash}${ext}`;
|
|
226
|
+
return dir === "." ? hashed : `${dir}/${hashed}`;
|
|
227
|
+
}
|
|
228
|
+
async function scanThemeAssets(distDir) {
|
|
229
|
+
const assetsDir = path13.join(distDir, "theme-assets");
|
|
230
|
+
if (!await fs8.pathExists(assetsDir)) return [];
|
|
231
|
+
const files = await glob("**/*", {
|
|
232
|
+
cwd: assetsDir,
|
|
233
|
+
nodir: true,
|
|
234
|
+
dot: false
|
|
235
|
+
});
|
|
236
|
+
const results = [];
|
|
237
|
+
for (const rel of files) {
|
|
238
|
+
const absPath = path13.join(assetsDir, rel);
|
|
239
|
+
const stat = await fs8.stat(absPath);
|
|
240
|
+
if (!stat.isFile()) continue;
|
|
241
|
+
const originalPath = rel.split(path13.sep).join("/");
|
|
242
|
+
const hash = await sha256Prefix(absPath, HASH_LEN);
|
|
243
|
+
const hashedPath = insertHashIntoName(originalPath, hash);
|
|
244
|
+
const contentType = mimeFor(rel);
|
|
245
|
+
results.push({
|
|
246
|
+
originalPath,
|
|
247
|
+
hashedPath,
|
|
248
|
+
hash,
|
|
249
|
+
size: stat.size,
|
|
250
|
+
contentType,
|
|
251
|
+
absPath
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
results.sort((a, b) => a.originalPath.localeCompare(b.originalPath));
|
|
255
|
+
return results;
|
|
256
|
+
}
|
|
257
|
+
function buildAssetMap(entries) {
|
|
258
|
+
const map = {};
|
|
259
|
+
for (const e of entries) {
|
|
260
|
+
map[e.originalPath] = e.hashedPath;
|
|
261
|
+
}
|
|
262
|
+
return map;
|
|
263
|
+
}
|
|
264
|
+
var MIME_MAP, HASH_LEN, VIDEO_EXTENSIONS;
|
|
265
|
+
var init_scan_theme_assets = __esm({
|
|
266
|
+
"src/utils/scan-theme-assets.ts"() {
|
|
267
|
+
MIME_MAP = {
|
|
268
|
+
".png": "image/png",
|
|
269
|
+
".jpg": "image/jpeg",
|
|
270
|
+
".jpeg": "image/jpeg",
|
|
271
|
+
".gif": "image/gif",
|
|
272
|
+
".webp": "image/webp",
|
|
273
|
+
".avif": "image/avif",
|
|
274
|
+
".svg": "image/svg+xml",
|
|
275
|
+
".ico": "image/x-icon",
|
|
276
|
+
".bmp": "image/bmp",
|
|
277
|
+
".woff": "font/woff",
|
|
278
|
+
".woff2": "font/woff2",
|
|
279
|
+
".ttf": "font/ttf",
|
|
280
|
+
".otf": "font/otf",
|
|
281
|
+
".eot": "application/vnd.ms-fontobject",
|
|
282
|
+
".mp4": "video/mp4",
|
|
283
|
+
".webm": "video/webm",
|
|
284
|
+
".mov": "video/quicktime",
|
|
285
|
+
".ogg": "video/ogg",
|
|
286
|
+
".json": "application/json"
|
|
287
|
+
};
|
|
288
|
+
HASH_LEN = 8;
|
|
289
|
+
VIDEO_EXTENSIONS = [
|
|
290
|
+
".mp4",
|
|
291
|
+
".webm",
|
|
292
|
+
".ogg",
|
|
293
|
+
".mov",
|
|
294
|
+
".avi",
|
|
295
|
+
".mkv"
|
|
296
|
+
];
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
async function scanAppDirectory(themePath) {
|
|
300
|
+
const appDir = path13.join(themePath, "app");
|
|
301
|
+
let pageFiles;
|
|
302
|
+
try {
|
|
303
|
+
pageFiles = await glob("**/page.{tsx,ts,jsx,js}", { cwd: appDir });
|
|
304
|
+
} catch {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
if (pageFiles.length === 0) return [];
|
|
308
|
+
const pages = [];
|
|
309
|
+
for (const pageFile of pageFiles) {
|
|
310
|
+
const routePath = deriveRoutePath(pageFile);
|
|
311
|
+
const absPageFile = path13.join(appDir, pageFile);
|
|
312
|
+
let source;
|
|
313
|
+
try {
|
|
314
|
+
source = await fs11.readFile(absPageFile, "utf-8");
|
|
315
|
+
} catch {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const sections = await extractSectionsFromPage(source, themePath);
|
|
319
|
+
pages.push({
|
|
320
|
+
routePath,
|
|
321
|
+
sourceFile: path13.join("app", pageFile),
|
|
322
|
+
sections
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return pages;
|
|
326
|
+
}
|
|
327
|
+
function deriveRoutePath(pageFile) {
|
|
328
|
+
const dir = path13.dirname(pageFile);
|
|
329
|
+
if (dir === ".") return "/";
|
|
330
|
+
return "/" + dir;
|
|
331
|
+
}
|
|
332
|
+
async function extractSectionsFromPage(source, themePath) {
|
|
333
|
+
const importRegex = /import\s+\w+\s+from\s+["'](@\/|\.\.?\/)(components\/[^"']+)["']/g;
|
|
334
|
+
const sections = [];
|
|
335
|
+
const seen = /* @__PURE__ */ new Set();
|
|
336
|
+
for (const match of source.matchAll(importRegex)) {
|
|
337
|
+
const rawImportPath = match[2];
|
|
338
|
+
const componentDir = path13.dirname(rawImportPath);
|
|
339
|
+
const absComponentDir = path13.join(themePath, componentDir);
|
|
340
|
+
if (seen.has(componentDir)) continue;
|
|
341
|
+
seen.add(componentDir);
|
|
342
|
+
const sectionJsonPath = path13.join(absComponentDir, "section.json");
|
|
343
|
+
let sectionJson;
|
|
344
|
+
try {
|
|
345
|
+
const raw = await fs11.readFile(sectionJsonPath, "utf-8");
|
|
346
|
+
sectionJson = JSON.parse(raw);
|
|
347
|
+
} catch {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (sectionJson.type !== "opaque-react") continue;
|
|
351
|
+
if (!sectionJson.entry) continue;
|
|
352
|
+
sections.push({
|
|
353
|
+
type: "opaque-react",
|
|
354
|
+
name: sectionJson.name ?? path13.basename(componentDir),
|
|
355
|
+
entry: path13.join(componentDir, sectionJson.entry),
|
|
356
|
+
componentDir
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return sections;
|
|
360
|
+
}
|
|
361
|
+
function buildNextjsPagesMap(pages, themeId) {
|
|
362
|
+
const result = {};
|
|
363
|
+
for (const page of pages) {
|
|
364
|
+
const id = page.routePath === "/" ? "home" : page.routePath.replace(/\//g, "-").replace(/^-/, "");
|
|
365
|
+
const makeSectionType = (name) => `${themeId}-${name.toLowerCase().replace(/\s+/g, "-")}`;
|
|
366
|
+
result[id] = {
|
|
367
|
+
id,
|
|
368
|
+
name: id.charAt(0).toUpperCase() + id.slice(1),
|
|
369
|
+
path: page.routePath,
|
|
370
|
+
config: {
|
|
371
|
+
id,
|
|
372
|
+
path: page.routePath,
|
|
373
|
+
sections: page.sections.map((s, i) => ({
|
|
374
|
+
id: `${id}-section-${i}`,
|
|
375
|
+
type: makeSectionType(s.name),
|
|
376
|
+
sectionType: "opaque-react",
|
|
377
|
+
settings: {}
|
|
378
|
+
}))
|
|
379
|
+
},
|
|
380
|
+
sections: page.sections.map((s, i) => ({
|
|
381
|
+
id: `${id}-section-${i}`,
|
|
382
|
+
type: makeSectionType(s.name),
|
|
383
|
+
sectionType: "opaque-react",
|
|
384
|
+
settings: {}
|
|
385
|
+
}))
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
var init_nextjs_page_scanner = __esm({
|
|
391
|
+
"src/utils/nextjs-page-scanner.ts"() {
|
|
392
|
+
}
|
|
393
|
+
});
|
|
92
394
|
|
|
93
395
|
// src/utils/compile-theme.ts
|
|
94
396
|
var compile_theme_exports = {};
|
|
@@ -104,8 +406,8 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
104
406
|
const tailwindcss = (await import('tailwindcss')).default;
|
|
105
407
|
const tailwindConfig = {
|
|
106
408
|
content: [
|
|
107
|
-
|
|
108
|
-
|
|
409
|
+
path13.join(themePath, "sections/**/*.{ts,tsx}"),
|
|
410
|
+
path13.join(themePath, "components/**/*.{ts,tsx}")
|
|
109
411
|
],
|
|
110
412
|
theme: { extend: {} },
|
|
111
413
|
plugins: []
|
|
@@ -115,7 +417,7 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
115
417
|
inputCSS,
|
|
116
418
|
{ from: void 0 }
|
|
117
419
|
);
|
|
118
|
-
await
|
|
420
|
+
await fs11.writeFile(path13.join(outDir, "bundle.css"), result.css);
|
|
119
421
|
logger.info("Generated bundle.css");
|
|
120
422
|
} catch (err) {
|
|
121
423
|
logger.warning(
|
|
@@ -126,12 +428,12 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
126
428
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
127
429
|
let dir = startDir;
|
|
128
430
|
while (true) {
|
|
129
|
-
const candidate =
|
|
431
|
+
const candidate = path13.join(dir, "node_modules", relativePath);
|
|
130
432
|
try {
|
|
131
|
-
await
|
|
433
|
+
await fs11.access(candidate);
|
|
132
434
|
return candidate;
|
|
133
435
|
} catch {
|
|
134
|
-
const parent =
|
|
436
|
+
const parent = path13.dirname(dir);
|
|
135
437
|
if (parent === dir) break;
|
|
136
438
|
dir = parent;
|
|
137
439
|
}
|
|
@@ -155,7 +457,7 @@ async function scanImportsFromPackage(sourceDir, packageName) {
|
|
|
155
457
|
});
|
|
156
458
|
for (const file of sourceFiles) {
|
|
157
459
|
try {
|
|
158
|
-
const content = await
|
|
460
|
+
const content = await fs11.readFile(path13.join(sourceDir, file), "utf-8");
|
|
159
461
|
for (const match of content.matchAll(namespaceImportRegex)) {
|
|
160
462
|
const subpath = match[1] ? match[1].slice(1) : "";
|
|
161
463
|
if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
|
|
@@ -209,17 +511,17 @@ function createCoreGlobalPlugin(themePath) {
|
|
|
209
511
|
const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
|
|
210
512
|
let distPath = await resolveNodeModulesFile(
|
|
211
513
|
themePath,
|
|
212
|
-
|
|
514
|
+
path13.join("@onexapis", "core", "dist", distFileName)
|
|
213
515
|
);
|
|
214
516
|
if (!distPath) {
|
|
215
517
|
distPath = await resolveNodeModulesFile(
|
|
216
518
|
__dirname,
|
|
217
|
-
|
|
519
|
+
path13.join("@onexapis", "core", "dist", distFileName)
|
|
218
520
|
);
|
|
219
521
|
}
|
|
220
522
|
try {
|
|
221
523
|
if (!distPath) throw new Error("not found");
|
|
222
|
-
const distContent = await
|
|
524
|
+
const distContent = await fs11.readFile(distPath, "utf-8");
|
|
223
525
|
const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
|
|
224
526
|
for (const m of exportMatches) {
|
|
225
527
|
const names = m[1].split(",").map((n) => {
|
|
@@ -266,180 +568,6 @@ ${namedExportLines}
|
|
|
266
568
|
}
|
|
267
569
|
};
|
|
268
570
|
}
|
|
269
|
-
function createThemeDepsStubPlugin(themePath) {
|
|
270
|
-
return {
|
|
271
|
-
name: "theme-deps-stub",
|
|
272
|
-
setup(build2) {
|
|
273
|
-
const tryResolveOrStub = (filter, namespace) => {
|
|
274
|
-
build2.onResolve({ filter }, async (args) => {
|
|
275
|
-
if (args.pluginData?.skipStub) return void 0;
|
|
276
|
-
try {
|
|
277
|
-
const result = await build2.resolve(args.path, {
|
|
278
|
-
kind: args.kind,
|
|
279
|
-
resolveDir: args.resolveDir || themePath,
|
|
280
|
-
importer: args.importer,
|
|
281
|
-
namespace: "file",
|
|
282
|
-
pluginData: { skipStub: true }
|
|
283
|
-
});
|
|
284
|
-
if (!result.errors.length) return result;
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
try {
|
|
288
|
-
const req = createRequire(import.meta.url || __filename);
|
|
289
|
-
const resolved = req.resolve(args.path);
|
|
290
|
-
if (resolved) return { path: resolved, namespace: "file" };
|
|
291
|
-
} catch {
|
|
292
|
-
}
|
|
293
|
-
return { path: args.path, namespace };
|
|
294
|
-
});
|
|
295
|
-
};
|
|
296
|
-
tryResolveOrStub(/^next\//, "next-stub");
|
|
297
|
-
build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
|
|
298
|
-
const stubs = {
|
|
299
|
-
"next/image": `
|
|
300
|
-
import React from 'react';
|
|
301
|
-
const Image = React.forwardRef((props, ref) => {
|
|
302
|
-
const { src, alt, width, height, fill, priority, sizes, quality, placeholder, blurDataURL, onLoad, onError, style, className, ...rest } = props;
|
|
303
|
-
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
304
|
-
const fillStyle = fill ? { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: style?.objectFit || 'cover', display: 'block' } : {};
|
|
305
|
-
const mergedStyle = { ...fillStyle, ...style };
|
|
306
|
-
return React.createElement('img', {
|
|
307
|
-
ref, src: imgSrc, alt,
|
|
308
|
-
width: fill ? undefined : width, height: fill ? undefined : height,
|
|
309
|
-
loading: priority ? 'eager' : 'lazy',
|
|
310
|
-
style: Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined,
|
|
311
|
-
className, onLoad, onError, ...rest,
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
export default Image;
|
|
315
|
-
`,
|
|
316
|
-
"next/link": `
|
|
317
|
-
import React from 'react';
|
|
318
|
-
const Link = ({ href, children, ...rest }) => React.createElement('a', { href, ...rest }, children);
|
|
319
|
-
export default Link;
|
|
320
|
-
`,
|
|
321
|
-
"next/navigation": `
|
|
322
|
-
export function useRouter() { return { push(u){window.location.href=u}, replace(u){window.location.href=u}, back(){window.history.back()}, forward(){window.history.forward()}, refresh(){window.location.reload()}, prefetch(){} }; }
|
|
323
|
-
export function usePathname() { return window.location.pathname; }
|
|
324
|
-
export function useSearchParams() { return new URLSearchParams(window.location.search); }
|
|
325
|
-
export function useParams() { return {}; }
|
|
326
|
-
export function redirect(url) { window.location.href = url; }
|
|
327
|
-
export function notFound() { throw new Error('Not Found'); }
|
|
328
|
-
`,
|
|
329
|
-
"next/headers": `
|
|
330
|
-
export function cookies() { return { get(){}, getAll(){ return []; }, set(){}, delete(){}, has(){ return false; } }; }
|
|
331
|
-
export function headers() { return new Headers(); }
|
|
332
|
-
`
|
|
333
|
-
};
|
|
334
|
-
return {
|
|
335
|
-
contents: stubs[args.path] || "export default {};",
|
|
336
|
-
loader: "jsx",
|
|
337
|
-
resolveDir: themePath
|
|
338
|
-
};
|
|
339
|
-
});
|
|
340
|
-
const lucideImports = /* @__PURE__ */ new Set();
|
|
341
|
-
let lucideThemeScanned = false;
|
|
342
|
-
tryResolveOrStub(/^lucide-react/, "lucide-stub");
|
|
343
|
-
build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
|
|
344
|
-
if (!lucideThemeScanned) {
|
|
345
|
-
lucideThemeScanned = true;
|
|
346
|
-
try {
|
|
347
|
-
const scanned = await scanImportsFromPackage(
|
|
348
|
-
themePath,
|
|
349
|
-
"lucide-react"
|
|
350
|
-
);
|
|
351
|
-
for (const names of Object.values(scanned)) {
|
|
352
|
-
for (const name of names) lucideImports.add(name);
|
|
353
|
-
}
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const iconNames = [...lucideImports];
|
|
358
|
-
const exports$1 = iconNames.map((n) => `icon as ${n}`).join(", ");
|
|
359
|
-
return {
|
|
360
|
-
contents: `
|
|
361
|
-
const icon = (props) => null;
|
|
362
|
-
export { ${exports$1} };
|
|
363
|
-
export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
|
|
364
|
-
`.trim(),
|
|
365
|
-
loader: "jsx"
|
|
366
|
-
};
|
|
367
|
-
});
|
|
368
|
-
tryResolveOrStub(/^framer-motion/, "motion-stub");
|
|
369
|
-
build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
|
|
370
|
-
contents: `
|
|
371
|
-
import React from 'react';
|
|
372
|
-
const handler = { get: (_, name) => {
|
|
373
|
-
if (name === '__esModule') return true;
|
|
374
|
-
return React.forwardRef((props, ref) => React.createElement(name, { ...props, ref }));
|
|
375
|
-
}};
|
|
376
|
-
export const motion = new Proxy({}, handler);
|
|
377
|
-
export const AnimatePresence = ({ children }) => children || null;
|
|
378
|
-
export function useInView() { return true; }
|
|
379
|
-
export default { motion, AnimatePresence, useInView };
|
|
380
|
-
`.trim(),
|
|
381
|
-
loader: "jsx",
|
|
382
|
-
resolveDir: themePath
|
|
383
|
-
}));
|
|
384
|
-
tryResolveOrStub(/^sonner$/, "sonner-stub");
|
|
385
|
-
build2.onLoad({ filter: /.*/, namespace: "sonner-stub" }, () => ({
|
|
386
|
-
contents: `
|
|
387
|
-
export const toast = new Proxy(() => {}, { get: () => () => {} });
|
|
388
|
-
export const Toaster = () => null;
|
|
389
|
-
export default { toast, Toaster };
|
|
390
|
-
`.trim(),
|
|
391
|
-
loader: "jsx"
|
|
392
|
-
}));
|
|
393
|
-
tryResolveOrStub(/^react-hook-form$/, "rhf-stub");
|
|
394
|
-
build2.onLoad({ filter: /.*/, namespace: "rhf-stub" }, () => ({
|
|
395
|
-
contents: `
|
|
396
|
-
export function useForm() {
|
|
397
|
-
return {
|
|
398
|
-
register: () => ({}),
|
|
399
|
-
handleSubmit: (fn) => (e) => { e?.preventDefault?.(); fn({}); },
|
|
400
|
-
formState: { errors: {}, isSubmitting: false, isValid: true },
|
|
401
|
-
watch: () => undefined,
|
|
402
|
-
setValue: () => {},
|
|
403
|
-
reset: () => {},
|
|
404
|
-
control: {},
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
export function useController() { return { field: {}, fieldState: {} }; }
|
|
408
|
-
export function useFormContext() { return useForm(); }
|
|
409
|
-
`.trim(),
|
|
410
|
-
loader: "js"
|
|
411
|
-
}));
|
|
412
|
-
tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
|
|
413
|
-
build2.onLoad(
|
|
414
|
-
{ filter: /.*/, namespace: "hookform-resolvers-stub" },
|
|
415
|
-
() => ({
|
|
416
|
-
contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
|
|
417
|
-
loader: "js"
|
|
418
|
-
})
|
|
419
|
-
);
|
|
420
|
-
tryResolveOrStub(/^next-intl$/, "next-intl-stub");
|
|
421
|
-
build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
|
|
422
|
-
contents: `
|
|
423
|
-
export function useTranslations(ns) {
|
|
424
|
-
return (key) => ns ? ns + '.' + key : key;
|
|
425
|
-
}
|
|
426
|
-
export function useLocale() { return 'en'; }
|
|
427
|
-
export function useMessages() { return {}; }
|
|
428
|
-
`.trim(),
|
|
429
|
-
loader: "js"
|
|
430
|
-
}));
|
|
431
|
-
tryResolveOrStub(/^zod$/, "zod-stub");
|
|
432
|
-
build2.onLoad({ filter: /.*/, namespace: "zod-stub" }, () => ({
|
|
433
|
-
contents: `
|
|
434
|
-
const schema = () => ({ parse: (v) => v, safeParse: (v) => ({ success: true, data: v }), optional: schema, min: schema, max: schema, email: schema, url: schema, regex: schema, refine: schema, transform: schema });
|
|
435
|
-
export const z = { string: schema, number: schema, boolean: schema, object: (s) => ({ ...schema(), shape: s }), array: schema, enum: schema, union: schema, literal: schema, infer: undefined };
|
|
436
|
-
export default z;
|
|
437
|
-
`.trim(),
|
|
438
|
-
loader: "js"
|
|
439
|
-
}));
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
571
|
async function generateThemeData(themePath, outputDir, themeId) {
|
|
444
572
|
const { createJiti } = await import('jiti');
|
|
445
573
|
const jiti = createJiti(import.meta.url);
|
|
@@ -448,7 +576,7 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
448
576
|
const pages = {};
|
|
449
577
|
for (const ext of [".ts", ".js"]) {
|
|
450
578
|
try {
|
|
451
|
-
const mod = await jiti.import(
|
|
579
|
+
const mod = await jiti.import(path13.join(themePath, `theme.config${ext}`));
|
|
452
580
|
themeConfig = mod.default || mod;
|
|
453
581
|
break;
|
|
454
582
|
} catch {
|
|
@@ -456,20 +584,20 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
456
584
|
}
|
|
457
585
|
for (const ext of [".ts", ".js"]) {
|
|
458
586
|
try {
|
|
459
|
-
const mod = await jiti.import(
|
|
587
|
+
const mod = await jiti.import(path13.join(themePath, `theme.layout${ext}`));
|
|
460
588
|
layoutConfig = mod.default || mod;
|
|
461
589
|
break;
|
|
462
590
|
} catch {
|
|
463
591
|
}
|
|
464
592
|
}
|
|
465
593
|
const schemas = {};
|
|
466
|
-
const sectionsDir =
|
|
594
|
+
const sectionsDir = path13.join(themePath, "sections");
|
|
467
595
|
try {
|
|
468
|
-
const sectionDirs = await
|
|
596
|
+
const sectionDirs = await fs11.readdir(sectionsDir);
|
|
469
597
|
for (const dir of sectionDirs) {
|
|
470
|
-
const schemaFile =
|
|
598
|
+
const schemaFile = path13.join(sectionsDir, dir, `${dir}.schema.ts`);
|
|
471
599
|
try {
|
|
472
|
-
await
|
|
600
|
+
await fs11.access(schemaFile);
|
|
473
601
|
const mod = await jiti.import(schemaFile);
|
|
474
602
|
for (const [key, value] of Object.entries(mod)) {
|
|
475
603
|
if (key.endsWith("Schema") && value && typeof value === "object" && value.type) {
|
|
@@ -481,14 +609,14 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
481
609
|
}
|
|
482
610
|
} catch {
|
|
483
611
|
}
|
|
484
|
-
const pagesDir =
|
|
612
|
+
const pagesDir = path13.join(themePath, "pages");
|
|
485
613
|
try {
|
|
486
|
-
const files = await
|
|
614
|
+
const files = await fs11.readdir(pagesDir);
|
|
487
615
|
for (const file of files) {
|
|
488
616
|
if (!file.match(/\.(ts|js)$/)) continue;
|
|
489
617
|
const name = file.replace(/\.(ts|js)$/, "");
|
|
490
618
|
try {
|
|
491
|
-
const mod = await jiti.import(
|
|
619
|
+
const mod = await jiti.import(path13.join(pagesDir, file));
|
|
492
620
|
const config = mod.default || mod;
|
|
493
621
|
const sections = (config.sections || []).map((section) => {
|
|
494
622
|
const schema = schemas[section.type];
|
|
@@ -516,8 +644,16 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
516
644
|
}
|
|
517
645
|
} catch {
|
|
518
646
|
}
|
|
519
|
-
|
|
520
|
-
|
|
647
|
+
if (isNextjsProject(themePath)) {
|
|
648
|
+
const nextjsPages = await scanAppDirectory(themePath);
|
|
649
|
+
if (nextjsPages.length > 0) {
|
|
650
|
+
const nextjsPagesMap = buildNextjsPagesMap(nextjsPages, themeId);
|
|
651
|
+
Object.assign(pages, nextjsPagesMap);
|
|
652
|
+
logger.info(`Scanned ${nextjsPages.length} Next.js app/ pages`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
await fs11.writeFile(
|
|
656
|
+
path13.join(outputDir, "theme-data.json"),
|
|
521
657
|
JSON.stringify(
|
|
522
658
|
{
|
|
523
659
|
themeId,
|
|
@@ -540,22 +676,22 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
540
676
|
logger.info(`Generated theme-data.json (${Object.keys(pages).length} pages)`);
|
|
541
677
|
}
|
|
542
678
|
async function contentHashEntry(outputDir) {
|
|
543
|
-
const entryPath =
|
|
544
|
-
const mapPath =
|
|
679
|
+
const entryPath = path13.join(outputDir, "bundle-entry.js");
|
|
680
|
+
const mapPath = path13.join(outputDir, "bundle-entry.js.map");
|
|
545
681
|
let entryContent;
|
|
546
682
|
try {
|
|
547
|
-
entryContent = await
|
|
683
|
+
entryContent = await fs11.readFile(entryPath, "utf-8");
|
|
548
684
|
} catch {
|
|
549
|
-
const indexPath =
|
|
685
|
+
const indexPath = path13.join(outputDir, "index.js");
|
|
550
686
|
try {
|
|
551
|
-
entryContent = await
|
|
687
|
+
entryContent = await fs11.readFile(indexPath, "utf-8");
|
|
552
688
|
} catch {
|
|
553
689
|
logger.warning("No entry file found in output, skipping content hash");
|
|
554
690
|
return;
|
|
555
691
|
}
|
|
556
692
|
const hash2 = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
557
693
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
558
|
-
const indexMapPath =
|
|
694
|
+
const indexMapPath = path13.join(outputDir, "index.js.map");
|
|
559
695
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
560
696
|
entryContent = entryContent.replace(
|
|
561
697
|
/\/\/# sourceMappingURL=index\.js\.map/,
|
|
@@ -563,18 +699,18 @@ async function contentHashEntry(outputDir) {
|
|
|
563
699
|
);
|
|
564
700
|
const oldFiles2 = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
565
701
|
for (const f of oldFiles2) {
|
|
566
|
-
await
|
|
702
|
+
await fs11.unlink(path13.join(outputDir, f));
|
|
567
703
|
}
|
|
568
|
-
await
|
|
569
|
-
await
|
|
704
|
+
await fs11.writeFile(path13.join(outputDir, hashedName2), entryContent);
|
|
705
|
+
await fs11.unlink(indexPath);
|
|
570
706
|
try {
|
|
571
|
-
await
|
|
707
|
+
await fs11.unlink(entryPath);
|
|
572
708
|
} catch {
|
|
573
709
|
}
|
|
574
|
-
await
|
|
710
|
+
await fs11.writeFile(entryPath, entryContent);
|
|
575
711
|
try {
|
|
576
|
-
await
|
|
577
|
-
await
|
|
712
|
+
await fs11.access(indexMapPath);
|
|
713
|
+
await fs11.rename(indexMapPath, path13.join(outputDir, hashedMapName2));
|
|
578
714
|
} catch {
|
|
579
715
|
}
|
|
580
716
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
@@ -589,17 +725,17 @@ async function contentHashEntry(outputDir) {
|
|
|
589
725
|
);
|
|
590
726
|
const oldFiles = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
591
727
|
for (const f of oldFiles) {
|
|
592
|
-
await
|
|
728
|
+
await fs11.unlink(path13.join(outputDir, f));
|
|
593
729
|
}
|
|
594
|
-
await
|
|
730
|
+
await fs11.writeFile(path13.join(outputDir, hashedName), entryContent);
|
|
595
731
|
try {
|
|
596
|
-
await
|
|
732
|
+
await fs11.unlink(entryPath);
|
|
597
733
|
} catch {
|
|
598
734
|
}
|
|
599
|
-
await
|
|
735
|
+
await fs11.writeFile(entryPath, entryContent);
|
|
600
736
|
try {
|
|
601
|
-
await
|
|
602
|
-
await
|
|
737
|
+
await fs11.access(mapPath);
|
|
738
|
+
await fs11.rename(mapPath, path13.join(outputDir, hashedMapName));
|
|
603
739
|
} catch {
|
|
604
740
|
}
|
|
605
741
|
logger.info(`Entry hashed: ${hashedName}`);
|
|
@@ -611,7 +747,7 @@ async function extractDataRequirements(themePath) {
|
|
|
611
747
|
const requirements = {};
|
|
612
748
|
for (const file of schemaFiles) {
|
|
613
749
|
try {
|
|
614
|
-
const mod = await jiti.import(
|
|
750
|
+
const mod = await jiti.import(path13.join(themePath, file));
|
|
615
751
|
const exports$1 = mod;
|
|
616
752
|
for (const value of Object.values(exports$1)) {
|
|
617
753
|
if (value && typeof value === "object" && typeof value.type === "string" && value.dataRequirements && typeof value.dataRequirements === "object") {
|
|
@@ -626,12 +762,105 @@ async function extractDataRequirements(themePath) {
|
|
|
626
762
|
}
|
|
627
763
|
return requirements;
|
|
628
764
|
}
|
|
765
|
+
async function writeGateManifests(themePath, outputDir) {
|
|
766
|
+
try {
|
|
767
|
+
const schemas = await extractSchemas(themePath);
|
|
768
|
+
await fs11.writeFile(
|
|
769
|
+
path13.join(outputDir, "schemas.json"),
|
|
770
|
+
serializeManifest(schemas)
|
|
771
|
+
);
|
|
772
|
+
logger.info(
|
|
773
|
+
`Generated schemas.json (${Object.keys(schemas.sections).length} sections)`
|
|
774
|
+
);
|
|
775
|
+
} catch (err) {
|
|
776
|
+
logger.warning(
|
|
777
|
+
`schemas.json not written: ${err instanceof Error ? err.message : String(err)}`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
const entries = await scanThemeAssets(outputDir);
|
|
782
|
+
const assets = entries.map((e) => ({
|
|
783
|
+
path: e.originalPath,
|
|
784
|
+
hash: e.hash,
|
|
785
|
+
size: e.size,
|
|
786
|
+
contentType: e.contentType
|
|
787
|
+
}));
|
|
788
|
+
await fs11.writeFile(
|
|
789
|
+
path13.join(outputDir, "asset-manifest.json"),
|
|
790
|
+
JSON.stringify({ manifestVersion: 1, assets }, null, 2)
|
|
791
|
+
);
|
|
792
|
+
logger.info(`Generated asset-manifest.json (${assets.length} assets)`);
|
|
793
|
+
} catch (err) {
|
|
794
|
+
logger.warning(
|
|
795
|
+
`asset-manifest.json not written: ${err instanceof Error ? err.message : String(err)}`
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async function compileSections(themePath, outputDir) {
|
|
800
|
+
const sectionsDir = path13.join(themePath, "sections");
|
|
801
|
+
let sectionDirs;
|
|
802
|
+
try {
|
|
803
|
+
sectionDirs = await fs11.readdir(sectionsDir);
|
|
804
|
+
} catch {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
for (const dirName of sectionDirs) {
|
|
808
|
+
const sectionSrc = path13.join(sectionsDir, dirName);
|
|
809
|
+
const sectionOut = path13.join(outputDir, "sections", dirName);
|
|
810
|
+
let section = null;
|
|
811
|
+
try {
|
|
812
|
+
const raw = await fs11.readFile(
|
|
813
|
+
path13.join(sectionSrc, "section.manifest.json"),
|
|
814
|
+
"utf-8"
|
|
815
|
+
);
|
|
816
|
+
section = JSON.parse(raw);
|
|
817
|
+
} catch {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
switch (section.type) {
|
|
821
|
+
case "editable":
|
|
822
|
+
case "opaque-react":
|
|
823
|
+
break;
|
|
824
|
+
case "html": {
|
|
825
|
+
await fs11.mkdir(sectionOut, { recursive: true });
|
|
826
|
+
const htmlSrc = path13.join(sectionSrc, section.html);
|
|
827
|
+
let htmlContent = await fs11.readFile(htmlSrc, "utf-8");
|
|
828
|
+
htmlContent = htmlContent.replace(
|
|
829
|
+
/<script[^>]+src=["']https?:\/\/[^"']*["'][^>]*><\/script>/gi,
|
|
830
|
+
""
|
|
831
|
+
);
|
|
832
|
+
await fs11.writeFile(path13.join(sectionOut, path13.basename(section.html)), htmlContent);
|
|
833
|
+
if (section.css) {
|
|
834
|
+
await fs11.copyFile(
|
|
835
|
+
path13.join(sectionSrc, section.css),
|
|
836
|
+
path13.join(sectionOut, path13.basename(section.css))
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
case "iframe":
|
|
842
|
+
break;
|
|
843
|
+
case "webcomponent": {
|
|
844
|
+
await fs11.mkdir(sectionOut, { recursive: true });
|
|
845
|
+
await fs11.copyFile(
|
|
846
|
+
path13.join(sectionSrc, section.bundle),
|
|
847
|
+
path13.join(sectionOut, path13.basename(section.bundle))
|
|
848
|
+
);
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
default:
|
|
852
|
+
throw new Error(
|
|
853
|
+
`Unknown section type. Valid types: editable, opaque-react, html, iframe, webcomponent`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
629
858
|
async function generateManifest(themeName, themePath, outputDir) {
|
|
630
859
|
let version2 = "1.0.0";
|
|
631
860
|
let themeId = themeName;
|
|
632
861
|
try {
|
|
633
|
-
const pkgContent = await
|
|
634
|
-
|
|
862
|
+
const pkgContent = await fs11.readFile(
|
|
863
|
+
path13.join(themePath, "package.json"),
|
|
635
864
|
"utf-8"
|
|
636
865
|
);
|
|
637
866
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -649,7 +878,7 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
649
878
|
const dataRequirements = await extractDataRequirements(themePath);
|
|
650
879
|
let hasThemeConfig = false;
|
|
651
880
|
try {
|
|
652
|
-
await
|
|
881
|
+
await fs11.access(path13.join(themePath, "theme.config.ts"));
|
|
653
882
|
hasThemeConfig = true;
|
|
654
883
|
} catch {
|
|
655
884
|
}
|
|
@@ -690,24 +919,34 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
690
919
|
// Section data requirements for server-side prefetching (keyed by section type)
|
|
691
920
|
dataRequirements
|
|
692
921
|
};
|
|
693
|
-
await
|
|
694
|
-
|
|
922
|
+
await fs11.writeFile(
|
|
923
|
+
path13.join(outputDir, "manifest.json"),
|
|
695
924
|
JSON.stringify(manifest, null, 2)
|
|
696
925
|
);
|
|
697
926
|
}
|
|
698
927
|
async function compileStandaloneTheme(themePath, themeName) {
|
|
699
|
-
const outputDir =
|
|
700
|
-
const
|
|
701
|
-
|
|
928
|
+
const outputDir = path13.join(themePath, "dist");
|
|
929
|
+
const isNextjs = isNextjsProject(themePath);
|
|
930
|
+
if (isNextjs) {
|
|
931
|
+
logger.info("Detected Next.js project \u2014 using next/* shims");
|
|
932
|
+
}
|
|
933
|
+
const bundleEntry = path13.join(themePath, "bundle-entry.ts");
|
|
934
|
+
const indexEntry = path13.join(themePath, "index.ts");
|
|
702
935
|
let entryPoint = indexEntry;
|
|
703
936
|
try {
|
|
704
|
-
await
|
|
937
|
+
await fs11.access(bundleEntry);
|
|
705
938
|
entryPoint = bundleEntry;
|
|
706
939
|
} catch {
|
|
707
940
|
}
|
|
708
|
-
const shimPath =
|
|
709
|
-
await
|
|
710
|
-
await
|
|
941
|
+
const shimPath = path13.join(outputDir, ".process-shim.js");
|
|
942
|
+
await fs11.mkdir(outputDir, { recursive: true });
|
|
943
|
+
await fs11.writeFile(shimPath, PROCESS_SHIM);
|
|
944
|
+
const plugins = [
|
|
945
|
+
reactGlobalPlugin,
|
|
946
|
+
reactQueryGlobalPlugin,
|
|
947
|
+
createCoreGlobalPlugin(themePath)
|
|
948
|
+
];
|
|
949
|
+
if (isNextjs) plugins.unshift(nextShimPlugin);
|
|
711
950
|
const buildOptions = {
|
|
712
951
|
entryPoints: [entryPoint],
|
|
713
952
|
bundle: true,
|
|
@@ -719,12 +958,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
719
958
|
banner: {
|
|
720
959
|
js: '"use client";'
|
|
721
960
|
},
|
|
722
|
-
plugins
|
|
723
|
-
reactGlobalPlugin,
|
|
724
|
-
reactQueryGlobalPlugin,
|
|
725
|
-
createCoreGlobalPlugin(themePath),
|
|
726
|
-
createThemeDepsStubPlugin(themePath)
|
|
727
|
-
],
|
|
961
|
+
plugins,
|
|
728
962
|
external: [],
|
|
729
963
|
alias: {
|
|
730
964
|
events: "events/",
|
|
@@ -758,19 +992,21 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
758
992
|
try {
|
|
759
993
|
const result = await esbuild.build(buildOptions);
|
|
760
994
|
try {
|
|
761
|
-
await
|
|
995
|
+
await fs11.unlink(shimPath);
|
|
762
996
|
} catch {
|
|
763
997
|
}
|
|
998
|
+
await compileSections(themePath, outputDir);
|
|
764
999
|
await contentHashEntry(outputDir);
|
|
765
|
-
const themeAssetsDir =
|
|
766
|
-
const distThemeAssets =
|
|
1000
|
+
const themeAssetsDir = path13.join(themePath, "assets");
|
|
1001
|
+
const distThemeAssets = path13.join(outputDir, "theme-assets");
|
|
767
1002
|
try {
|
|
768
|
-
await
|
|
769
|
-
await
|
|
1003
|
+
await fs11.access(themeAssetsDir);
|
|
1004
|
+
await fs11.cp(themeAssetsDir, distThemeAssets, { recursive: true });
|
|
770
1005
|
logger.info("Copied static assets to dist/theme-assets/");
|
|
771
1006
|
} catch {
|
|
772
1007
|
}
|
|
773
1008
|
await generateManifest(themeName, themePath, outputDir);
|
|
1009
|
+
await writeGateManifests(themePath, outputDir);
|
|
774
1010
|
await generateThemeData(themePath, outputDir, themeName);
|
|
775
1011
|
await generateThemeCSS(themePath, outputDir);
|
|
776
1012
|
if (result.metafile) {
|
|
@@ -785,7 +1021,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
785
1021
|
return true;
|
|
786
1022
|
} catch (error) {
|
|
787
1023
|
try {
|
|
788
|
-
await
|
|
1024
|
+
await fs11.unlink(shimPath);
|
|
789
1025
|
} catch {
|
|
790
1026
|
}
|
|
791
1027
|
logger.error(`esbuild compilation failed: ${error}`);
|
|
@@ -793,18 +1029,25 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
793
1029
|
}
|
|
794
1030
|
}
|
|
795
1031
|
async function compileStandaloneThemeDev(themePath, themeName) {
|
|
796
|
-
const outputDir =
|
|
797
|
-
const
|
|
798
|
-
const
|
|
1032
|
+
const outputDir = path13.join(themePath, "dist");
|
|
1033
|
+
const isNextjs = isNextjsProject(themePath);
|
|
1034
|
+
const bundleEntry = path13.join(themePath, "bundle-entry.ts");
|
|
1035
|
+
const indexEntry = path13.join(themePath, "index.ts");
|
|
799
1036
|
let entryPoint = indexEntry;
|
|
800
1037
|
try {
|
|
801
|
-
await
|
|
1038
|
+
await fs11.access(bundleEntry);
|
|
802
1039
|
entryPoint = bundleEntry;
|
|
803
1040
|
} catch {
|
|
804
1041
|
}
|
|
805
|
-
const shimPath =
|
|
806
|
-
await
|
|
807
|
-
await
|
|
1042
|
+
const shimPath = path13.join(outputDir, ".process-shim.js");
|
|
1043
|
+
await fs11.mkdir(outputDir, { recursive: true });
|
|
1044
|
+
await fs11.writeFile(shimPath, PROCESS_SHIM);
|
|
1045
|
+
const devPlugins = [
|
|
1046
|
+
reactGlobalPlugin,
|
|
1047
|
+
reactQueryGlobalPlugin,
|
|
1048
|
+
createCoreGlobalPlugin(themePath)
|
|
1049
|
+
];
|
|
1050
|
+
if (isNextjs) devPlugins.unshift(nextShimPlugin);
|
|
808
1051
|
const buildOptions = {
|
|
809
1052
|
entryPoints: [entryPoint],
|
|
810
1053
|
bundle: true,
|
|
@@ -815,12 +1058,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
815
1058
|
banner: {
|
|
816
1059
|
js: '"use client";'
|
|
817
1060
|
},
|
|
818
|
-
plugins:
|
|
819
|
-
reactGlobalPlugin,
|
|
820
|
-
reactQueryGlobalPlugin,
|
|
821
|
-
createCoreGlobalPlugin(themePath),
|
|
822
|
-
createThemeDepsStubPlugin(themePath)
|
|
823
|
-
],
|
|
1061
|
+
plugins: devPlugins,
|
|
824
1062
|
external: [],
|
|
825
1063
|
alias: {
|
|
826
1064
|
events: "events/",
|
|
@@ -858,18 +1096,18 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
858
1096
|
return { context: context2, outputDir };
|
|
859
1097
|
}
|
|
860
1098
|
async function compilePreviewRuntime(themePath) {
|
|
861
|
-
const outputDir =
|
|
862
|
-
await
|
|
863
|
-
const outputPath =
|
|
1099
|
+
const outputDir = path13.join(themePath, "dist");
|
|
1100
|
+
await fs11.mkdir(outputDir, { recursive: true });
|
|
1101
|
+
const outputPath = path13.join(outputDir, "preview-runtime.js");
|
|
864
1102
|
const locations = [
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1103
|
+
path13.join(__dirname, "..", "preview", "preview-app.tsx"),
|
|
1104
|
+
path13.join(__dirname, "preview", "preview-app.tsx"),
|
|
1105
|
+
path13.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
|
|
868
1106
|
];
|
|
869
1107
|
let previewEntryPath = null;
|
|
870
1108
|
for (const loc of locations) {
|
|
871
1109
|
try {
|
|
872
|
-
await
|
|
1110
|
+
await fs11.access(loc);
|
|
873
1111
|
previewEntryPath = loc;
|
|
874
1112
|
break;
|
|
875
1113
|
} catch {
|
|
@@ -952,10 +1190,10 @@ ${locations.join("\n")}`
|
|
|
952
1190
|
if (!lucideScanned) {
|
|
953
1191
|
lucideScanned = true;
|
|
954
1192
|
const coreSrcCandidates = [
|
|
955
|
-
|
|
956
|
-
|
|
1193
|
+
path13.join(themePath, "node_modules", "@onexapis", "core", "src"),
|
|
1194
|
+
path13.join(themePath, "..", "..", "packages", "core", "src"),
|
|
957
1195
|
// monorepo sibling
|
|
958
|
-
|
|
1196
|
+
path13.join(
|
|
959
1197
|
__dirname,
|
|
960
1198
|
"..",
|
|
961
1199
|
"..",
|
|
@@ -970,7 +1208,7 @@ ${locations.join("\n")}`
|
|
|
970
1208
|
let coreSourceDir = null;
|
|
971
1209
|
for (const candidate of coreSrcCandidates) {
|
|
972
1210
|
try {
|
|
973
|
-
await
|
|
1211
|
+
await fs11.access(candidate);
|
|
974
1212
|
coreSourceDir = candidate;
|
|
975
1213
|
break;
|
|
976
1214
|
} catch {
|
|
@@ -989,21 +1227,21 @@ ${locations.join("\n")}`
|
|
|
989
1227
|
}
|
|
990
1228
|
} else {
|
|
991
1229
|
const coreDistCandidates = [
|
|
992
|
-
|
|
1230
|
+
path13.join(themePath, "node_modules", "@onexapis", "core", "dist")
|
|
993
1231
|
];
|
|
994
1232
|
const resolvedDist = await resolveNodeModulesFile(
|
|
995
1233
|
__dirname,
|
|
996
|
-
|
|
1234
|
+
path13.join("@onexapis", "core", "dist")
|
|
997
1235
|
);
|
|
998
1236
|
if (resolvedDist) coreDistCandidates.push(resolvedDist);
|
|
999
1237
|
for (const candidate of coreDistCandidates) {
|
|
1000
1238
|
try {
|
|
1001
|
-
await
|
|
1239
|
+
await fs11.access(candidate);
|
|
1002
1240
|
const mjsFiles = await glob("*.mjs", { cwd: candidate });
|
|
1003
1241
|
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
1004
1242
|
for (const file of mjsFiles) {
|
|
1005
|
-
const content = await
|
|
1006
|
-
|
|
1243
|
+
const content = await fs11.readFile(
|
|
1244
|
+
path13.join(candidate, file),
|
|
1007
1245
|
"utf-8"
|
|
1008
1246
|
);
|
|
1009
1247
|
for (const match of content.matchAll(importRegex)) {
|
|
@@ -1058,7 +1296,7 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
|
|
|
1058
1296
|
const req = createRequire(import.meta.url || __filename);
|
|
1059
1297
|
const cjsPath = req.resolve("framer-motion");
|
|
1060
1298
|
const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
|
|
1061
|
-
const esmEntry =
|
|
1299
|
+
const esmEntry = path13.join(pkgDir, "dist", "es", "index.mjs");
|
|
1062
1300
|
const { existsSync } = await import('fs');
|
|
1063
1301
|
if (existsSync(esmEntry)) {
|
|
1064
1302
|
return { path: esmEntry, namespace: "file" };
|
|
@@ -1157,8 +1395,8 @@ export function headers() { return new Headers(); }
|
|
|
1157
1395
|
});
|
|
1158
1396
|
}
|
|
1159
1397
|
};
|
|
1160
|
-
const shimPath =
|
|
1161
|
-
await
|
|
1398
|
+
const shimPath = path13.join(outputDir, ".process-shim-preview.js");
|
|
1399
|
+
await fs11.writeFile(shimPath, PROCESS_SHIM);
|
|
1162
1400
|
await esbuild.build({
|
|
1163
1401
|
entryPoints: [previewEntryPath],
|
|
1164
1402
|
bundle: true,
|
|
@@ -1193,15 +1431,19 @@ export function headers() { return new Headers(); }
|
|
|
1193
1431
|
}
|
|
1194
1432
|
});
|
|
1195
1433
|
try {
|
|
1196
|
-
await
|
|
1434
|
+
await fs11.unlink(shimPath);
|
|
1197
1435
|
} catch {
|
|
1198
1436
|
}
|
|
1199
1437
|
return outputPath;
|
|
1200
1438
|
}
|
|
1201
|
-
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin;
|
|
1439
|
+
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin, nextShimPlugin;
|
|
1202
1440
|
var init_compile_theme = __esm({
|
|
1203
1441
|
"src/utils/compile-theme.ts"() {
|
|
1204
1442
|
init_logger();
|
|
1443
|
+
init_extract_schemas();
|
|
1444
|
+
init_scan_theme_assets();
|
|
1445
|
+
init_detect_nextjs();
|
|
1446
|
+
init_nextjs_page_scanner();
|
|
1205
1447
|
PROCESS_SHIM = `
|
|
1206
1448
|
if (typeof process === "undefined") {
|
|
1207
1449
|
globalThis.process = {
|
|
@@ -1347,6 +1589,145 @@ export const {
|
|
|
1347
1589
|
}));
|
|
1348
1590
|
}
|
|
1349
1591
|
};
|
|
1592
|
+
nextShimPlugin = {
|
|
1593
|
+
name: "next-shim",
|
|
1594
|
+
setup(build2) {
|
|
1595
|
+
for (const serverModule of ["next/headers", "next/server", "next/cache"]) {
|
|
1596
|
+
build2.onResolve({ filter: new RegExp(`^${serverModule.replace("/", "\\/")}`) }, (args) => ({
|
|
1597
|
+
path: args.path,
|
|
1598
|
+
namespace: "next-server-error"
|
|
1599
|
+
}));
|
|
1600
|
+
}
|
|
1601
|
+
build2.onLoad({ filter: /.*/, namespace: "next-server-error" }, (args) => ({
|
|
1602
|
+
errors: [
|
|
1603
|
+
{
|
|
1604
|
+
text: `"${args.path}" is server-only and cannot be used in a OneX theme bundle. Use client-side equivalents or remove the import.`
|
|
1605
|
+
}
|
|
1606
|
+
]
|
|
1607
|
+
}));
|
|
1608
|
+
build2.onResolve({ filter: /^next\/navigation$/ }, () => ({
|
|
1609
|
+
path: "next-navigation-shim",
|
|
1610
|
+
namespace: "next-shim"
|
|
1611
|
+
}));
|
|
1612
|
+
build2.onLoad({ filter: /^next-navigation-shim$/, namespace: "next-shim" }, () => ({
|
|
1613
|
+
contents: `
|
|
1614
|
+
export function usePathname() {
|
|
1615
|
+
if (typeof window === 'undefined') return '/';
|
|
1616
|
+
return window.location.pathname;
|
|
1617
|
+
}
|
|
1618
|
+
export function useSearchParams() {
|
|
1619
|
+
if (typeof window === 'undefined') return new URLSearchParams();
|
|
1620
|
+
return new URLSearchParams(window.location.search);
|
|
1621
|
+
}
|
|
1622
|
+
export function useParams() {
|
|
1623
|
+
if (typeof window === 'undefined') return {};
|
|
1624
|
+
return (globalThis.__ONEX_ROUTE_PARAMS__) ?? {};
|
|
1625
|
+
}
|
|
1626
|
+
export function useRouter() {
|
|
1627
|
+
return {
|
|
1628
|
+
push(url) { if (typeof window !== 'undefined') window.location.href = url; },
|
|
1629
|
+
replace(url) { if (typeof window !== 'undefined') window.location.replace(url); },
|
|
1630
|
+
back() { if (typeof window !== 'undefined') window.history.back(); },
|
|
1631
|
+
forward() { if (typeof window !== 'undefined') window.history.forward(); },
|
|
1632
|
+
refresh() { if (typeof window !== 'undefined') window.location.reload(); },
|
|
1633
|
+
prefetch() {},
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
export function redirect(url) {
|
|
1637
|
+
if (typeof window !== 'undefined') window.location.href = url;
|
|
1638
|
+
throw new Error('redirect');
|
|
1639
|
+
}
|
|
1640
|
+
export function notFound() { throw new Error('not-found'); }
|
|
1641
|
+
`.trim(),
|
|
1642
|
+
loader: "js"
|
|
1643
|
+
}));
|
|
1644
|
+
build2.onResolve({ filter: /^next\/font\// }, () => ({
|
|
1645
|
+
path: "next-font-shim",
|
|
1646
|
+
namespace: "next-shim"
|
|
1647
|
+
}));
|
|
1648
|
+
build2.onLoad({ filter: /^next-font-shim$/, namespace: "next-shim" }, () => ({
|
|
1649
|
+
contents: `
|
|
1650
|
+
function makeFont(family) {
|
|
1651
|
+
return function(_opts) {
|
|
1652
|
+
return {
|
|
1653
|
+
className: '',
|
|
1654
|
+
style: { fontFamily: family + ', system-ui, sans-serif' },
|
|
1655
|
+
variable: '--font-' + family.toLowerCase().replace(/\\s+/g, '-'),
|
|
1656
|
+
};
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
export const Inter = makeFont('Inter');
|
|
1660
|
+
export const Roboto = makeFont('Roboto');
|
|
1661
|
+
export const Open_Sans = makeFont('Open Sans');
|
|
1662
|
+
export const Lato = makeFont('Lato');
|
|
1663
|
+
export const Montserrat = makeFont('Montserrat');
|
|
1664
|
+
export const Poppins = makeFont('Poppins');
|
|
1665
|
+
export const Raleway = makeFont('Raleway');
|
|
1666
|
+
export const Nunito = makeFont('Nunito');
|
|
1667
|
+
export const Geist = makeFont('Geist');
|
|
1668
|
+
export const Geist_Mono = makeFont('Geist Mono');
|
|
1669
|
+
export const DM_Sans = makeFont('DM Sans');
|
|
1670
|
+
export const Plus_Jakarta_Sans = makeFont('Plus Jakarta Sans');
|
|
1671
|
+
export function localFont(_opts) {
|
|
1672
|
+
return { className: '', style: { fontFamily: 'system-ui, sans-serif' }, variable: '--font-local' };
|
|
1673
|
+
}
|
|
1674
|
+
`.trim(),
|
|
1675
|
+
loader: "js"
|
|
1676
|
+
}));
|
|
1677
|
+
build2.onResolve({ filter: /^next\/dynamic$/ }, () => ({
|
|
1678
|
+
path: "next-dynamic-shim",
|
|
1679
|
+
namespace: "next-shim"
|
|
1680
|
+
}));
|
|
1681
|
+
build2.onLoad({ filter: /^next-dynamic-shim$/, namespace: "next-shim" }, () => ({
|
|
1682
|
+
contents: `
|
|
1683
|
+
import { lazy, Suspense, createElement } from 'react';
|
|
1684
|
+
export default function dynamic(loader, opts) {
|
|
1685
|
+
const Lazy = lazy(loader);
|
|
1686
|
+
return function DynamicComponent(props) {
|
|
1687
|
+
return createElement(Suspense, { fallback: opts?.loading ? createElement(opts.loading) : null },
|
|
1688
|
+
createElement(Lazy, props));
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
`.trim(),
|
|
1692
|
+
loader: "js"
|
|
1693
|
+
}));
|
|
1694
|
+
build2.onResolve({ filter: /^next\/image$/ }, () => ({
|
|
1695
|
+
path: "next-image-shim",
|
|
1696
|
+
namespace: "next-shim"
|
|
1697
|
+
}));
|
|
1698
|
+
build2.onLoad({ filter: /^next-image-shim$/, namespace: "next-shim" }, () => ({
|
|
1699
|
+
contents: `
|
|
1700
|
+
import { createElement } from 'react';
|
|
1701
|
+
export default function Image({ src, alt, width, height, style, className, ...rest }) {
|
|
1702
|
+
return createElement('img', { src, alt, width, height, style, className, ...rest });
|
|
1703
|
+
}
|
|
1704
|
+
`.trim(),
|
|
1705
|
+
loader: "js"
|
|
1706
|
+
}));
|
|
1707
|
+
build2.onResolve({ filter: /^next\/link$/ }, () => ({
|
|
1708
|
+
path: "next-link-shim",
|
|
1709
|
+
namespace: "next-shim"
|
|
1710
|
+
}));
|
|
1711
|
+
build2.onLoad({ filter: /^next-link-shim$/, namespace: "next-shim" }, () => ({
|
|
1712
|
+
contents: `
|
|
1713
|
+
import { createElement } from 'react';
|
|
1714
|
+
export default function Link({ href, children, className, style, ...rest }) {
|
|
1715
|
+
return createElement('a', { href, className, style, ...rest }, children);
|
|
1716
|
+
}
|
|
1717
|
+
`.trim(),
|
|
1718
|
+
loader: "js"
|
|
1719
|
+
}));
|
|
1720
|
+
build2.onResolve({ filter: /^next\// }, () => ({
|
|
1721
|
+
path: "next-noop-shim",
|
|
1722
|
+
namespace: "next-shim"
|
|
1723
|
+
}));
|
|
1724
|
+
build2.onLoad({ filter: /^next-noop-shim$/, namespace: "next-shim" }, () => ({
|
|
1725
|
+
contents: `export default {};
|
|
1726
|
+
`,
|
|
1727
|
+
loader: "js"
|
|
1728
|
+
}));
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1350
1731
|
}
|
|
1351
1732
|
});
|
|
1352
1733
|
|
|
@@ -1357,7 +1738,7 @@ __export(dev_server_exports, {
|
|
|
1357
1738
|
});
|
|
1358
1739
|
function createDevServer(options) {
|
|
1359
1740
|
const clients = /* @__PURE__ */ new Set();
|
|
1360
|
-
const themeDataPath =
|
|
1741
|
+
const themeDataPath = path13.join(options.distDir, "theme-data.json");
|
|
1361
1742
|
const server = http.createServer((req, res) => {
|
|
1362
1743
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1363
1744
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
@@ -1383,8 +1764,8 @@ function createDevServer(options) {
|
|
|
1383
1764
|
if (pathname.startsWith("/_assets/")) {
|
|
1384
1765
|
const parts = pathname.replace(/^\/_assets\//, "").split("/");
|
|
1385
1766
|
const assetSubpath = parts.slice(1).join("/");
|
|
1386
|
-
const assetPath =
|
|
1387
|
-
if (!assetPath.startsWith(
|
|
1767
|
+
const assetPath = path13.join(options.themePath, "assets", assetSubpath);
|
|
1768
|
+
if (!assetPath.startsWith(path13.join(options.themePath, "assets"))) {
|
|
1388
1769
|
res.writeHead(403);
|
|
1389
1770
|
res.end("Forbidden");
|
|
1390
1771
|
return;
|
|
@@ -1395,8 +1776,8 @@ function createDevServer(options) {
|
|
|
1395
1776
|
if (pathname.startsWith("/themes/")) {
|
|
1396
1777
|
const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
|
|
1397
1778
|
if (match) {
|
|
1398
|
-
const assetPath =
|
|
1399
|
-
if (!assetPath.startsWith(
|
|
1779
|
+
const assetPath = path13.join(options.themePath, "assets", match[1]);
|
|
1780
|
+
if (!assetPath.startsWith(path13.join(options.themePath, "assets"))) {
|
|
1400
1781
|
res.writeHead(403);
|
|
1401
1782
|
res.end("Forbidden");
|
|
1402
1783
|
return;
|
|
@@ -1408,26 +1789,26 @@ function createDevServer(options) {
|
|
|
1408
1789
|
if (pathname.startsWith("/assets/")) {
|
|
1409
1790
|
const subpath = pathname.replace(/^\/assets\//, "");
|
|
1410
1791
|
const segments = subpath.split("/");
|
|
1411
|
-
const assetsBase =
|
|
1792
|
+
const assetsBase = path13.join(options.themePath, "assets");
|
|
1412
1793
|
let assetPath;
|
|
1413
1794
|
if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
|
|
1414
|
-
assetPath =
|
|
1795
|
+
assetPath = path13.join(assetsBase, segments.slice(1).join("/"));
|
|
1415
1796
|
} else {
|
|
1416
|
-
assetPath =
|
|
1797
|
+
assetPath = path13.join(assetsBase, subpath);
|
|
1417
1798
|
}
|
|
1418
1799
|
if (assetPath.startsWith(assetsBase) && fs3.existsSync(assetPath)) {
|
|
1419
1800
|
serveFile(res, assetPath);
|
|
1420
1801
|
return;
|
|
1421
1802
|
}
|
|
1422
1803
|
if (segments.length > 1) {
|
|
1423
|
-
const fallbackPath =
|
|
1804
|
+
const fallbackPath = path13.join(assetsBase, segments.slice(1).join("/"));
|
|
1424
1805
|
if (fallbackPath.startsWith(assetsBase) && fs3.existsSync(fallbackPath)) {
|
|
1425
1806
|
serveFile(res, fallbackPath);
|
|
1426
1807
|
return;
|
|
1427
1808
|
}
|
|
1428
1809
|
}
|
|
1429
1810
|
}
|
|
1430
|
-
const filePath =
|
|
1811
|
+
const filePath = path13.join(options.distDir, pathname);
|
|
1431
1812
|
if (!filePath.startsWith(options.distDir)) {
|
|
1432
1813
|
res.writeHead(403);
|
|
1433
1814
|
res.end("Forbidden");
|
|
@@ -1470,7 +1851,7 @@ function serveFile(res, filePath) {
|
|
|
1470
1851
|
res.end("Not Found");
|
|
1471
1852
|
return;
|
|
1472
1853
|
}
|
|
1473
|
-
const ext =
|
|
1854
|
+
const ext = path13.extname(filePath);
|
|
1474
1855
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1475
1856
|
const content = fs3.readFileSync(filePath);
|
|
1476
1857
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -1587,26 +1968,26 @@ var init_dev_server = __esm({
|
|
|
1587
1968
|
// src/utils/file-helpers.ts
|
|
1588
1969
|
init_logger();
|
|
1589
1970
|
async function renderTemplate(templatePath, data) {
|
|
1590
|
-
const template = await
|
|
1971
|
+
const template = await fs8.readFile(templatePath, "utf-8");
|
|
1591
1972
|
return ejs.render(template, data);
|
|
1592
1973
|
}
|
|
1593
1974
|
async function writeFile(filePath, content) {
|
|
1594
|
-
await
|
|
1595
|
-
await
|
|
1975
|
+
await fs8.ensureDir(path13.dirname(filePath));
|
|
1976
|
+
await fs8.writeFile(filePath, content, "utf-8");
|
|
1596
1977
|
}
|
|
1597
1978
|
function getTemplatesDir() {
|
|
1598
1979
|
const locations = [
|
|
1599
|
-
|
|
1980
|
+
path13.join(__dirname, "../../templates"),
|
|
1600
1981
|
// Development
|
|
1601
|
-
|
|
1982
|
+
path13.join(__dirname, "../templates"),
|
|
1602
1983
|
// Production (dist/)
|
|
1603
|
-
|
|
1984
|
+
path13.join(process.cwd(), "templates"),
|
|
1604
1985
|
// Fallback
|
|
1605
|
-
|
|
1986
|
+
path13.join(process.cwd(), "packages/cli/templates")
|
|
1606
1987
|
// Monorepo
|
|
1607
1988
|
];
|
|
1608
1989
|
for (const location of locations) {
|
|
1609
|
-
if (
|
|
1990
|
+
if (fs8.existsSync(location)) {
|
|
1610
1991
|
return location;
|
|
1611
1992
|
}
|
|
1612
1993
|
}
|
|
@@ -1614,18 +1995,18 @@ function getTemplatesDir() {
|
|
|
1614
1995
|
}
|
|
1615
1996
|
async function copyTemplate(templateName, targetDir, data) {
|
|
1616
1997
|
const templatesDir = getTemplatesDir();
|
|
1617
|
-
const templateDir =
|
|
1618
|
-
if (!
|
|
1998
|
+
const templateDir = path13.join(templatesDir, templateName);
|
|
1999
|
+
if (!fs8.existsSync(templateDir)) {
|
|
1619
2000
|
throw new Error(
|
|
1620
|
-
`Template "${templateName}" not found at ${templateDir}. Available templates: ${
|
|
2001
|
+
`Template "${templateName}" not found at ${templateDir}. Available templates: ${fs8.readdirSync(templatesDir).join(", ")}`
|
|
1621
2002
|
);
|
|
1622
2003
|
}
|
|
1623
|
-
await
|
|
1624
|
-
const files = await
|
|
2004
|
+
await fs8.ensureDir(targetDir);
|
|
2005
|
+
const files = await fs8.readdir(templateDir);
|
|
1625
2006
|
for (const file of files) {
|
|
1626
|
-
const templatePath =
|
|
1627
|
-
const targetPath =
|
|
1628
|
-
const stat = await
|
|
2007
|
+
const templatePath = path13.join(templateDir, file);
|
|
2008
|
+
const targetPath = path13.join(targetDir, file);
|
|
2009
|
+
const stat = await fs8.stat(templatePath);
|
|
1629
2010
|
if (stat.isDirectory()) {
|
|
1630
2011
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
1631
2012
|
} else if (file.endsWith(".ejs")) {
|
|
@@ -1633,17 +2014,17 @@ async function copyTemplate(templateName, targetDir, data) {
|
|
|
1633
2014
|
const outputPath = targetPath.replace(/\.ejs$/, "");
|
|
1634
2015
|
await writeFile(outputPath, content);
|
|
1635
2016
|
} else {
|
|
1636
|
-
await
|
|
2017
|
+
await fs8.copy(templatePath, targetPath);
|
|
1637
2018
|
}
|
|
1638
2019
|
}
|
|
1639
2020
|
}
|
|
1640
2021
|
async function copyTemplateDir(templateDir, targetDir, data) {
|
|
1641
|
-
await
|
|
1642
|
-
const files = await
|
|
2022
|
+
await fs8.ensureDir(targetDir);
|
|
2023
|
+
const files = await fs8.readdir(templateDir);
|
|
1643
2024
|
for (const file of files) {
|
|
1644
|
-
const templatePath =
|
|
1645
|
-
const targetPath =
|
|
1646
|
-
const stat = await
|
|
2025
|
+
const templatePath = path13.join(templateDir, file);
|
|
2026
|
+
const targetPath = path13.join(targetDir, file);
|
|
2027
|
+
const stat = await fs8.stat(templatePath);
|
|
1647
2028
|
if (stat.isDirectory()) {
|
|
1648
2029
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
1649
2030
|
} else if (file.endsWith(".ejs")) {
|
|
@@ -1651,38 +2032,38 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1651
2032
|
const outputPath = targetPath.replace(/\.ejs$/, "");
|
|
1652
2033
|
await writeFile(outputPath, content);
|
|
1653
2034
|
} else {
|
|
1654
|
-
await
|
|
2035
|
+
await fs8.copy(templatePath, targetPath);
|
|
1655
2036
|
}
|
|
1656
2037
|
}
|
|
1657
2038
|
}
|
|
1658
2039
|
function getProjectRoot() {
|
|
1659
2040
|
let currentDir = process.cwd();
|
|
1660
|
-
while (currentDir !==
|
|
1661
|
-
const packageJsonPath =
|
|
1662
|
-
if (
|
|
1663
|
-
const packageJson =
|
|
1664
|
-
if (packageJson.workspaces ||
|
|
2041
|
+
while (currentDir !== path13.parse(currentDir).root) {
|
|
2042
|
+
const packageJsonPath = path13.join(currentDir, "package.json");
|
|
2043
|
+
if (fs8.existsSync(packageJsonPath)) {
|
|
2044
|
+
const packageJson = fs8.readJsonSync(packageJsonPath);
|
|
2045
|
+
if (packageJson.workspaces || fs8.existsSync(path13.join(currentDir, "src/themes")) || fs8.existsSync(path13.join(currentDir, "themes"))) {
|
|
1665
2046
|
return currentDir;
|
|
1666
2047
|
}
|
|
1667
2048
|
}
|
|
1668
|
-
currentDir =
|
|
2049
|
+
currentDir = path13.dirname(currentDir);
|
|
1669
2050
|
}
|
|
1670
2051
|
return process.cwd();
|
|
1671
2052
|
}
|
|
1672
2053
|
function getThemesDir() {
|
|
1673
2054
|
const root = getProjectRoot();
|
|
1674
|
-
if (
|
|
1675
|
-
return
|
|
1676
|
-
if (
|
|
1677
|
-
return
|
|
1678
|
-
return
|
|
2055
|
+
if (fs8.existsSync(path13.join(root, "themes")))
|
|
2056
|
+
return path13.join(root, "themes");
|
|
2057
|
+
if (fs8.existsSync(path13.join(root, "src/themes")))
|
|
2058
|
+
return path13.join(root, "src/themes");
|
|
2059
|
+
return path13.dirname(root);
|
|
1679
2060
|
}
|
|
1680
2061
|
function getFeaturesDir() {
|
|
1681
|
-
return
|
|
2062
|
+
return path13.join(getProjectRoot(), "src/features");
|
|
1682
2063
|
}
|
|
1683
2064
|
function isOneXProject() {
|
|
1684
2065
|
const root = getProjectRoot();
|
|
1685
|
-
return
|
|
2066
|
+
return fs8.existsSync(path13.join(root, "themes")) || fs8.existsSync(path13.join(root, "src/themes")) || fs8.existsSync(path13.join(root, "theme.config.ts")) || fs8.existsSync(path13.join(root, "bundle-entry.ts"));
|
|
1686
2067
|
}
|
|
1687
2068
|
function ensureOneXProject() {
|
|
1688
2069
|
if (!isOneXProject()) {
|
|
@@ -1694,17 +2075,17 @@ function ensureOneXProject() {
|
|
|
1694
2075
|
}
|
|
1695
2076
|
function listThemes() {
|
|
1696
2077
|
const themesDir = getThemesDir();
|
|
1697
|
-
if (!
|
|
2078
|
+
if (!fs8.existsSync(themesDir)) {
|
|
1698
2079
|
return [];
|
|
1699
2080
|
}
|
|
1700
|
-
return
|
|
1701
|
-
const themePath =
|
|
1702
|
-
return
|
|
2081
|
+
return fs8.readdirSync(themesDir).filter((name) => {
|
|
2082
|
+
const themePath = path13.join(themesDir, name);
|
|
2083
|
+
return fs8.statSync(themePath).isDirectory() && (fs8.existsSync(path13.join(themePath, "theme.config.ts")) || fs8.existsSync(path13.join(themePath, "bundle-entry.ts")) || fs8.existsSync(path13.join(themePath, "manifest.ts")));
|
|
1703
2084
|
});
|
|
1704
2085
|
}
|
|
1705
2086
|
function themeExists(themeName) {
|
|
1706
|
-
const themePath =
|
|
1707
|
-
return
|
|
2087
|
+
const themePath = path13.join(getThemesDir(), themeName);
|
|
2088
|
+
return fs8.existsSync(themePath) && (fs8.existsSync(path13.join(themePath, "theme.config.ts")) || fs8.existsSync(path13.join(themePath, "bundle-entry.ts")) || fs8.existsSync(path13.join(themePath, "manifest.ts")));
|
|
1708
2089
|
}
|
|
1709
2090
|
function detectPackageManager() {
|
|
1710
2091
|
const userAgent = process.env.npm_config_user_agent || "";
|
|
@@ -1712,9 +2093,9 @@ function detectPackageManager() {
|
|
|
1712
2093
|
if (userAgent.includes("yarn")) return "yarn";
|
|
1713
2094
|
if (userAgent.includes("bun")) return "bun";
|
|
1714
2095
|
const cwd = process.cwd();
|
|
1715
|
-
if (
|
|
1716
|
-
if (
|
|
1717
|
-
if (
|
|
2096
|
+
if (fs8.existsSync(path13.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
2097
|
+
if (fs8.existsSync(path13.join(cwd, "yarn.lock"))) return "yarn";
|
|
2098
|
+
if (fs8.existsSync(path13.join(cwd, "bun.lockb"))) return "bun";
|
|
1718
2099
|
return "npm";
|
|
1719
2100
|
}
|
|
1720
2101
|
async function installDependencies(projectPath, packageManager = "npm") {
|
|
@@ -1763,22 +2144,23 @@ function getValidCategories() {
|
|
|
1763
2144
|
"contact"
|
|
1764
2145
|
];
|
|
1765
2146
|
}
|
|
1766
|
-
var AUTH_DIR =
|
|
2147
|
+
var AUTH_DIR = path13.join(os.homedir(), ".onexthm");
|
|
1767
2148
|
var ENV_URLS = {
|
|
1768
2149
|
dev: "https://platform-dev.onexeos.com",
|
|
1769
|
-
|
|
2150
|
+
staging: "https://platform-staging.onexeos.com",
|
|
2151
|
+
prod: "https://platform-apis.onexeos.com"
|
|
1770
2152
|
};
|
|
1771
2153
|
function getAuthFile(env = "dev") {
|
|
1772
|
-
const newFile =
|
|
2154
|
+
const newFile = path13.join(AUTH_DIR, `auth-${env}.json`);
|
|
1773
2155
|
if (env === "dev") {
|
|
1774
|
-
const legacyFile =
|
|
1775
|
-
if (
|
|
2156
|
+
const legacyFile = path13.join(AUTH_DIR, "auth.json");
|
|
2157
|
+
if (fs8.existsSync(legacyFile) && !fs8.existsSync(newFile)) {
|
|
1776
2158
|
try {
|
|
1777
|
-
|
|
2159
|
+
fs8.moveSync(legacyFile, newFile);
|
|
1778
2160
|
} catch {
|
|
1779
2161
|
try {
|
|
1780
|
-
|
|
1781
|
-
|
|
2162
|
+
fs8.copySync(legacyFile, newFile);
|
|
2163
|
+
fs8.removeSync(legacyFile);
|
|
1782
2164
|
} catch {
|
|
1783
2165
|
}
|
|
1784
2166
|
}
|
|
@@ -1790,17 +2172,17 @@ function getApiUrl(env = "dev") {
|
|
|
1790
2172
|
return process.env.ONEXTHM_API_URL || ENV_URLS[env];
|
|
1791
2173
|
}
|
|
1792
2174
|
async function saveAuthTokens(tokens, env = "dev") {
|
|
1793
|
-
await
|
|
2175
|
+
await fs8.ensureDir(AUTH_DIR);
|
|
1794
2176
|
const key = getMachineKey();
|
|
1795
2177
|
const data = JSON.stringify(tokens);
|
|
1796
2178
|
const encrypted = encrypt(data, key);
|
|
1797
|
-
await
|
|
2179
|
+
await fs8.writeFile(getAuthFile(env), encrypted, "utf-8");
|
|
1798
2180
|
}
|
|
1799
2181
|
function loadAuthTokens(env = "dev") {
|
|
1800
2182
|
try {
|
|
1801
2183
|
const file = getAuthFile(env);
|
|
1802
|
-
if (!
|
|
1803
|
-
const encrypted =
|
|
2184
|
+
if (!fs8.existsSync(file)) return null;
|
|
2185
|
+
const encrypted = fs8.readFileSync(file, "utf-8");
|
|
1804
2186
|
const key = getMachineKey();
|
|
1805
2187
|
const data = decrypt(encrypted, key);
|
|
1806
2188
|
return JSON.parse(data);
|
|
@@ -1810,7 +2192,7 @@ function loadAuthTokens(env = "dev") {
|
|
|
1810
2192
|
}
|
|
1811
2193
|
async function clearAuthTokens(env = "dev") {
|
|
1812
2194
|
try {
|
|
1813
|
-
await
|
|
2195
|
+
await fs8.remove(getAuthFile(env));
|
|
1814
2196
|
} catch {
|
|
1815
2197
|
}
|
|
1816
2198
|
}
|
|
@@ -1869,7 +2251,7 @@ function getMachineKey() {
|
|
|
1869
2251
|
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1870
2252
|
} else if (process.platform === "linux") {
|
|
1871
2253
|
try {
|
|
1872
|
-
seed = `onexthm:${
|
|
2254
|
+
seed = `onexthm:${fs8.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
1873
2255
|
} catch {
|
|
1874
2256
|
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1875
2257
|
}
|
|
@@ -1907,7 +2289,7 @@ function parseJwtClaims(idToken) {
|
|
|
1907
2289
|
}
|
|
1908
2290
|
|
|
1909
2291
|
// src/commands/init.ts
|
|
1910
|
-
async function initCommand(projectName, options
|
|
2292
|
+
async function initCommand(projectName, options) {
|
|
1911
2293
|
logger.header("Create New OneX Theme Project");
|
|
1912
2294
|
let name;
|
|
1913
2295
|
if (!projectName) {
|
|
@@ -1922,7 +2304,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1922
2304
|
if (!validateThemeName(kebabName)) {
|
|
1923
2305
|
return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
|
|
1924
2306
|
}
|
|
1925
|
-
if (fs3.existsSync(
|
|
2307
|
+
if (fs3.existsSync(path13.join(process.cwd(), kebabName))) {
|
|
1926
2308
|
return `Directory "${kebabName}" already exists`;
|
|
1927
2309
|
}
|
|
1928
2310
|
return true;
|
|
@@ -1933,14 +2315,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
1933
2315
|
} else {
|
|
1934
2316
|
name = toKebabCase(projectName);
|
|
1935
2317
|
}
|
|
1936
|
-
const projectPath =
|
|
2318
|
+
const projectPath = path13.join(process.cwd(), name);
|
|
1937
2319
|
if (fs3.existsSync(projectPath)) {
|
|
1938
2320
|
logger.error(`Directory "${name}" already exists.`);
|
|
1939
2321
|
process.exit(1);
|
|
1940
2322
|
}
|
|
1941
2323
|
if (!options.yes) {
|
|
1942
2324
|
try {
|
|
1943
|
-
const apiUrl = getApiUrl(options.env
|
|
2325
|
+
const apiUrl = getApiUrl(options.env);
|
|
1944
2326
|
const controller = new AbortController();
|
|
1945
2327
|
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
1946
2328
|
const response = await fetch(
|
|
@@ -2049,7 +2431,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
2049
2431
|
description,
|
|
2050
2432
|
author
|
|
2051
2433
|
);
|
|
2052
|
-
const mcpJsonPath =
|
|
2434
|
+
const mcpJsonPath = path13.join(projectPath, ".mcp.json");
|
|
2053
2435
|
if (fs3.existsSync(mcpJsonPath)) {
|
|
2054
2436
|
let mcpContent = fs3.readFileSync(mcpJsonPath, "utf-8");
|
|
2055
2437
|
if (figmaApiKey) {
|
|
@@ -2129,7 +2511,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
2129
2511
|
}
|
|
2130
2512
|
}
|
|
2131
2513
|
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
2132
|
-
const configPath =
|
|
2514
|
+
const configPath = path13.join(projectPath, "theme.config.ts");
|
|
2133
2515
|
if (fs3.existsSync(configPath)) {
|
|
2134
2516
|
let content = fs3.readFileSync(configPath, "utf-8");
|
|
2135
2517
|
content = content.replace(
|
|
@@ -2142,7 +2524,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
|
|
|
2142
2524
|
);
|
|
2143
2525
|
fs3.writeFileSync(configPath, content, "utf-8");
|
|
2144
2526
|
}
|
|
2145
|
-
const pkgPath =
|
|
2527
|
+
const pkgPath = path13.join(projectPath, "package.json");
|
|
2146
2528
|
if (fs3.existsSync(pkgPath)) {
|
|
2147
2529
|
let content = fs3.readFileSync(pkgPath, "utf-8");
|
|
2148
2530
|
content = content.replace(
|
|
@@ -2164,10 +2546,10 @@ async function createSectionCommand(name, options) {
|
|
|
2164
2546
|
ensureOneXProject();
|
|
2165
2547
|
if (!options.theme) {
|
|
2166
2548
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
2167
|
-
(f) =>
|
|
2549
|
+
(f) => fs8.existsSync(path13.join(process.cwd(), f))
|
|
2168
2550
|
);
|
|
2169
2551
|
if (isStandaloneTheme) {
|
|
2170
|
-
options.theme =
|
|
2552
|
+
options.theme = path13.basename(process.cwd());
|
|
2171
2553
|
}
|
|
2172
2554
|
}
|
|
2173
2555
|
const sectionName = toKebabCase(name);
|
|
@@ -2230,35 +2612,35 @@ async function createSectionCommand(name, options) {
|
|
|
2230
2612
|
};
|
|
2231
2613
|
logger.startSpinner("Creating section files...");
|
|
2232
2614
|
try {
|
|
2233
|
-
const themePath =
|
|
2234
|
-
const sectionPath =
|
|
2615
|
+
const themePath = path13.join(getThemesDir(), themeName);
|
|
2616
|
+
const sectionPath = path13.join(themePath, "sections", sectionName);
|
|
2235
2617
|
const schemaContent = generateSectionSchema(data);
|
|
2236
2618
|
await writeFile(
|
|
2237
|
-
|
|
2619
|
+
path13.join(sectionPath, `${sectionName}.schema.ts`),
|
|
2238
2620
|
schemaContent
|
|
2239
2621
|
);
|
|
2240
2622
|
if (createTemplate) {
|
|
2241
2623
|
const templateContent = generateSectionTemplate(data);
|
|
2242
2624
|
await writeFile(
|
|
2243
|
-
|
|
2625
|
+
path13.join(sectionPath, `${sectionName}-default.tsx`),
|
|
2244
2626
|
templateContent
|
|
2245
2627
|
);
|
|
2246
2628
|
}
|
|
2247
2629
|
const indexContent = generateSectionIndex(data, createTemplate);
|
|
2248
|
-
await writeFile(
|
|
2630
|
+
await writeFile(path13.join(sectionPath, "index.ts"), indexContent);
|
|
2249
2631
|
logger.stopSpinner(true, "Section files created successfully!");
|
|
2250
2632
|
logger.newLine();
|
|
2251
2633
|
logger.section("Next steps:");
|
|
2252
2634
|
logger.log(
|
|
2253
|
-
` 1. Edit schema: ${
|
|
2635
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(sectionPath, `${sectionName}.schema.ts`))}`
|
|
2254
2636
|
);
|
|
2255
2637
|
if (createTemplate) {
|
|
2256
2638
|
logger.log(
|
|
2257
|
-
` 2. Edit template: ${
|
|
2639
|
+
` 2. Edit template: ${path13.relative(process.cwd(), path13.join(sectionPath, `${sectionName}-default.tsx`))}`
|
|
2258
2640
|
);
|
|
2259
2641
|
}
|
|
2260
2642
|
logger.log(
|
|
2261
|
-
` 3. Add to theme manifest: ${
|
|
2643
|
+
` 3. Add to theme manifest: ${path13.relative(process.cwd(), path13.join(themePath, "manifest.ts"))}`
|
|
2262
2644
|
);
|
|
2263
2645
|
logger.newLine();
|
|
2264
2646
|
logger.success("Section created successfully!");
|
|
@@ -2406,10 +2788,10 @@ async function createBlockCommand(name, options) {
|
|
|
2406
2788
|
ensureOneXProject();
|
|
2407
2789
|
if (!options.theme) {
|
|
2408
2790
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
2409
|
-
(f) =>
|
|
2791
|
+
(f) => fs8.existsSync(path13.join(process.cwd(), f))
|
|
2410
2792
|
);
|
|
2411
2793
|
if (isStandaloneTheme) {
|
|
2412
|
-
options.theme =
|
|
2794
|
+
options.theme = path13.basename(process.cwd());
|
|
2413
2795
|
}
|
|
2414
2796
|
}
|
|
2415
2797
|
const blockName = toKebabCase(name);
|
|
@@ -2484,24 +2866,24 @@ async function createBlockCommand(name, options) {
|
|
|
2484
2866
|
};
|
|
2485
2867
|
logger.startSpinner("Creating block files...");
|
|
2486
2868
|
try {
|
|
2487
|
-
const blockPath = scope === "shared" ?
|
|
2869
|
+
const blockPath = scope === "shared" ? path13.join(getFeaturesDir(), "blocks", blockName) : path13.join(getThemesDir(), themeName, "blocks", blockName);
|
|
2488
2870
|
const schemaContent = generateBlockSchema(data);
|
|
2489
2871
|
await writeFile(
|
|
2490
|
-
|
|
2872
|
+
path13.join(blockPath, `${blockName}.schema.ts`),
|
|
2491
2873
|
schemaContent
|
|
2492
2874
|
);
|
|
2493
2875
|
const componentContent = generateBlockComponent(data);
|
|
2494
|
-
await writeFile(
|
|
2876
|
+
await writeFile(path13.join(blockPath, `${blockName}.tsx`), componentContent);
|
|
2495
2877
|
const indexContent = generateBlockIndex(data);
|
|
2496
|
-
await writeFile(
|
|
2878
|
+
await writeFile(path13.join(blockPath, "index.ts"), indexContent);
|
|
2497
2879
|
logger.stopSpinner(true, "Block files created successfully!");
|
|
2498
2880
|
logger.newLine();
|
|
2499
2881
|
logger.section("Next steps:");
|
|
2500
2882
|
logger.log(
|
|
2501
|
-
` 1. Edit schema: ${
|
|
2883
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(blockPath, `${blockName}.schema.ts`))}`
|
|
2502
2884
|
);
|
|
2503
2885
|
logger.log(
|
|
2504
|
-
` 2. Edit component: ${
|
|
2886
|
+
` 2. Edit component: ${path13.relative(process.cwd(), path13.join(blockPath, `${blockName}.tsx`))}`
|
|
2505
2887
|
);
|
|
2506
2888
|
logger.log(
|
|
2507
2889
|
` 3. Register in block registry: src/lib/registry/block-registry.ts`
|
|
@@ -2679,31 +3061,31 @@ async function createComponentCommand(name, options) {
|
|
|
2679
3061
|
};
|
|
2680
3062
|
logger.startSpinner("Creating component files...");
|
|
2681
3063
|
try {
|
|
2682
|
-
const componentPath =
|
|
3064
|
+
const componentPath = path13.join(
|
|
2683
3065
|
getFeaturesDir(),
|
|
2684
3066
|
"components",
|
|
2685
3067
|
componentName
|
|
2686
3068
|
);
|
|
2687
3069
|
const schemaContent = generateComponentSchema(data);
|
|
2688
3070
|
await writeFile(
|
|
2689
|
-
|
|
3071
|
+
path13.join(componentPath, `${componentName}.schema.ts`),
|
|
2690
3072
|
schemaContent
|
|
2691
3073
|
);
|
|
2692
3074
|
const componentContent = generateComponent(data);
|
|
2693
3075
|
await writeFile(
|
|
2694
|
-
|
|
3076
|
+
path13.join(componentPath, `${componentName}.tsx`),
|
|
2695
3077
|
componentContent
|
|
2696
3078
|
);
|
|
2697
3079
|
const indexContent = generateComponentIndex(data);
|
|
2698
|
-
await writeFile(
|
|
3080
|
+
await writeFile(path13.join(componentPath, "index.ts"), indexContent);
|
|
2699
3081
|
logger.stopSpinner(true, "Component files created successfully!");
|
|
2700
3082
|
logger.newLine();
|
|
2701
3083
|
logger.section("Next steps:");
|
|
2702
3084
|
logger.log(
|
|
2703
|
-
` 1. Edit schema: ${
|
|
3085
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(componentPath, `${componentName}.schema.ts`))}`
|
|
2704
3086
|
);
|
|
2705
3087
|
logger.log(
|
|
2706
|
-
` 2. Edit component: ${
|
|
3088
|
+
` 2. Edit component: ${path13.relative(process.cwd(), path13.join(componentPath, `${componentName}.tsx`))}`
|
|
2707
3089
|
);
|
|
2708
3090
|
logger.log(
|
|
2709
3091
|
` 3. Register in component registry: src/lib/registry/component-registry.ts`
|
|
@@ -2860,13 +3242,13 @@ async function listSections(themeFilter) {
|
|
|
2860
3242
|
return;
|
|
2861
3243
|
}
|
|
2862
3244
|
for (const theme of themes) {
|
|
2863
|
-
const sectionsDir =
|
|
2864
|
-
if (!
|
|
3245
|
+
const sectionsDir = path13.join(getThemesDir(), theme, "sections");
|
|
3246
|
+
if (!fs8.existsSync(sectionsDir)) {
|
|
2865
3247
|
continue;
|
|
2866
3248
|
}
|
|
2867
|
-
const sections =
|
|
2868
|
-
const sectionPath =
|
|
2869
|
-
return
|
|
3249
|
+
const sections = fs8.readdirSync(sectionsDir).filter((name) => {
|
|
3250
|
+
const sectionPath = path13.join(sectionsDir, name);
|
|
3251
|
+
return fs8.statSync(sectionPath).isDirectory() && fs8.existsSync(path13.join(sectionPath, "index.ts"));
|
|
2870
3252
|
});
|
|
2871
3253
|
if (sections.length > 0) {
|
|
2872
3254
|
logger.log(chalk4.cyan(`
|
|
@@ -2880,11 +3262,11 @@ async function listSections(themeFilter) {
|
|
|
2880
3262
|
}
|
|
2881
3263
|
async function listBlocks(themeFilter) {
|
|
2882
3264
|
logger.section("\u{1F9F1} Blocks");
|
|
2883
|
-
const sharedBlocksDir =
|
|
2884
|
-
if (
|
|
2885
|
-
const sharedBlocks =
|
|
2886
|
-
const blockPath =
|
|
2887
|
-
return
|
|
3265
|
+
const sharedBlocksDir = path13.join(getFeaturesDir(), "blocks");
|
|
3266
|
+
if (fs8.existsSync(sharedBlocksDir)) {
|
|
3267
|
+
const sharedBlocks = fs8.readdirSync(sharedBlocksDir).filter((name) => {
|
|
3268
|
+
const blockPath = path13.join(sharedBlocksDir, name);
|
|
3269
|
+
return fs8.statSync(blockPath).isDirectory() && fs8.existsSync(path13.join(blockPath, "index.ts"));
|
|
2888
3270
|
});
|
|
2889
3271
|
if (sharedBlocks.length > 0) {
|
|
2890
3272
|
logger.log(chalk4.cyan("\n Shared:"));
|
|
@@ -2895,13 +3277,13 @@ async function listBlocks(themeFilter) {
|
|
|
2895
3277
|
}
|
|
2896
3278
|
const themes = themeFilter ? [themeFilter] : listThemes();
|
|
2897
3279
|
for (const theme of themes) {
|
|
2898
|
-
const blocksDir =
|
|
2899
|
-
if (!
|
|
3280
|
+
const blocksDir = path13.join(getThemesDir(), theme, "blocks");
|
|
3281
|
+
if (!fs8.existsSync(blocksDir)) {
|
|
2900
3282
|
continue;
|
|
2901
3283
|
}
|
|
2902
|
-
const blocks =
|
|
2903
|
-
const blockPath =
|
|
2904
|
-
return
|
|
3284
|
+
const blocks = fs8.readdirSync(blocksDir).filter((name) => {
|
|
3285
|
+
const blockPath = path13.join(blocksDir, name);
|
|
3286
|
+
return fs8.statSync(blockPath).isDirectory() && fs8.existsSync(path13.join(blockPath, "index.ts"));
|
|
2905
3287
|
});
|
|
2906
3288
|
if (blocks.length > 0) {
|
|
2907
3289
|
logger.log(chalk4.cyan(`
|
|
@@ -2915,14 +3297,14 @@ async function listBlocks(themeFilter) {
|
|
|
2915
3297
|
}
|
|
2916
3298
|
async function listComponents() {
|
|
2917
3299
|
logger.section("\u2699\uFE0F Components");
|
|
2918
|
-
const componentsDir =
|
|
2919
|
-
if (!
|
|
3300
|
+
const componentsDir = path13.join(getFeaturesDir(), "components");
|
|
3301
|
+
if (!fs8.existsSync(componentsDir)) {
|
|
2920
3302
|
logger.warning("No components directory found");
|
|
2921
3303
|
return;
|
|
2922
3304
|
}
|
|
2923
|
-
const components =
|
|
2924
|
-
const componentPath =
|
|
2925
|
-
return
|
|
3305
|
+
const components = fs8.readdirSync(componentsDir).filter((name) => {
|
|
3306
|
+
const componentPath = path13.join(componentsDir, name);
|
|
3307
|
+
return fs8.statSync(componentPath).isDirectory() && fs8.existsSync(path13.join(componentPath, "index.ts"));
|
|
2926
3308
|
});
|
|
2927
3309
|
if (components.length === 0) {
|
|
2928
3310
|
logger.warning("No components found");
|
|
@@ -2943,13 +3325,13 @@ async function listThemesInfo() {
|
|
|
2943
3325
|
}
|
|
2944
3326
|
logger.log("");
|
|
2945
3327
|
for (const theme of themes) {
|
|
2946
|
-
const themeDir =
|
|
3328
|
+
const themeDir = path13.join(getThemesDir(), theme);
|
|
2947
3329
|
const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
|
|
2948
3330
|
let manifestContent = "";
|
|
2949
3331
|
for (const candidate of candidates) {
|
|
2950
|
-
const candidatePath =
|
|
2951
|
-
if (
|
|
2952
|
-
manifestContent =
|
|
3332
|
+
const candidatePath = path13.join(themeDir, candidate);
|
|
3333
|
+
if (fs8.existsSync(candidatePath)) {
|
|
3334
|
+
manifestContent = fs8.readFileSync(candidatePath, "utf-8");
|
|
2953
3335
|
break;
|
|
2954
3336
|
}
|
|
2955
3337
|
}
|
|
@@ -2969,6 +3351,7 @@ async function listThemesInfo() {
|
|
|
2969
3351
|
|
|
2970
3352
|
// src/commands/validate.ts
|
|
2971
3353
|
init_logger();
|
|
3354
|
+
init_detect_nextjs();
|
|
2972
3355
|
async function validateCommand(options) {
|
|
2973
3356
|
logger.header("Validate Theme");
|
|
2974
3357
|
ensureOneXProject();
|
|
@@ -2984,10 +3367,13 @@ async function validateCommand(options) {
|
|
|
2984
3367
|
const isThemeDir2 = [
|
|
2985
3368
|
"theme.config.ts",
|
|
2986
3369
|
"bundle-entry.ts",
|
|
2987
|
-
"manifest.ts"
|
|
2988
|
-
|
|
3370
|
+
"manifest.ts",
|
|
3371
|
+
"next.config.ts",
|
|
3372
|
+
"next.config.js",
|
|
3373
|
+
"next.config.mjs"
|
|
3374
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
2989
3375
|
if (isThemeDir2) {
|
|
2990
|
-
themeToValidate =
|
|
3376
|
+
themeToValidate = path13.basename(process.cwd());
|
|
2991
3377
|
logger.info(`Validating current theme: ${themeToValidate}`);
|
|
2992
3378
|
} else {
|
|
2993
3379
|
logger.error(
|
|
@@ -2996,11 +3382,11 @@ async function validateCommand(options) {
|
|
|
2996
3382
|
process.exit(1);
|
|
2997
3383
|
}
|
|
2998
3384
|
}
|
|
2999
|
-
const themePath =
|
|
3385
|
+
const themePath = path13.join(getThemesDir(), themeToValidate);
|
|
3000
3386
|
logger.startSpinner("Running validation checks...");
|
|
3001
3387
|
const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
|
|
3002
3388
|
const foundEntry = entryFiles.find(
|
|
3003
|
-
(f) =>
|
|
3389
|
+
(f) => fs8.existsSync(path13.join(themePath, f))
|
|
3004
3390
|
);
|
|
3005
3391
|
if (!foundEntry) {
|
|
3006
3392
|
issues.push({
|
|
@@ -3009,8 +3395,8 @@ async function validateCommand(options) {
|
|
|
3009
3395
|
message: "No theme entry file found (need at least one of: manifest.ts, theme.config.ts, bundle-entry.ts)"
|
|
3010
3396
|
});
|
|
3011
3397
|
} else if (foundEntry === "manifest.ts") {
|
|
3012
|
-
const manifestContent =
|
|
3013
|
-
|
|
3398
|
+
const manifestContent = fs8.readFileSync(
|
|
3399
|
+
path13.join(themePath, foundEntry),
|
|
3014
3400
|
"utf-8"
|
|
3015
3401
|
);
|
|
3016
3402
|
if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
|
|
@@ -3021,56 +3407,56 @@ async function validateCommand(options) {
|
|
|
3021
3407
|
});
|
|
3022
3408
|
}
|
|
3023
3409
|
}
|
|
3024
|
-
const configPath =
|
|
3025
|
-
if (!
|
|
3410
|
+
const configPath = path13.join(themePath, "theme.config.ts");
|
|
3411
|
+
if (!fs8.existsSync(configPath)) {
|
|
3026
3412
|
issues.push({
|
|
3027
3413
|
type: "warning",
|
|
3028
3414
|
file: "theme.config.ts",
|
|
3029
3415
|
message: "Theme config file not found (recommended)"
|
|
3030
3416
|
});
|
|
3031
3417
|
}
|
|
3032
|
-
const indexPath =
|
|
3033
|
-
if (!
|
|
3418
|
+
const indexPath = path13.join(themePath, "index.ts");
|
|
3419
|
+
if (!fs8.existsSync(indexPath)) {
|
|
3034
3420
|
issues.push({
|
|
3035
3421
|
type: "warning",
|
|
3036
3422
|
file: "index.ts",
|
|
3037
3423
|
message: "Index file not found (recommended)"
|
|
3038
3424
|
});
|
|
3039
3425
|
}
|
|
3040
|
-
const sectionsDir =
|
|
3041
|
-
if (!
|
|
3426
|
+
const sectionsDir = path13.join(themePath, "sections");
|
|
3427
|
+
if (!fs8.existsSync(sectionsDir)) {
|
|
3042
3428
|
issues.push({
|
|
3043
3429
|
type: "warning",
|
|
3044
3430
|
file: "sections/",
|
|
3045
3431
|
message: "Sections directory not found"
|
|
3046
3432
|
});
|
|
3047
3433
|
} else {
|
|
3048
|
-
const sections =
|
|
3049
|
-
(name) =>
|
|
3434
|
+
const sections = fs8.readdirSync(sectionsDir).filter(
|
|
3435
|
+
(name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory()
|
|
3050
3436
|
);
|
|
3051
3437
|
for (const sectionName of sections) {
|
|
3052
|
-
const sectionPath =
|
|
3053
|
-
const schemaFile =
|
|
3054
|
-
const defaultTemplate =
|
|
3438
|
+
const sectionPath = path13.join(sectionsDir, sectionName);
|
|
3439
|
+
const schemaFile = path13.join(sectionPath, `${sectionName}.schema.ts`);
|
|
3440
|
+
const defaultTemplate = path13.join(
|
|
3055
3441
|
sectionPath,
|
|
3056
3442
|
`${sectionName}-default.tsx`
|
|
3057
3443
|
);
|
|
3058
|
-
const indexFile =
|
|
3059
|
-
if (!
|
|
3444
|
+
const indexFile = path13.join(sectionPath, "index.ts");
|
|
3445
|
+
if (!fs8.existsSync(schemaFile)) {
|
|
3060
3446
|
issues.push({
|
|
3061
3447
|
type: "error",
|
|
3062
3448
|
file: `sections/${sectionName}/${sectionName}.schema.ts`,
|
|
3063
3449
|
message: "Section schema file missing"
|
|
3064
3450
|
});
|
|
3065
3451
|
}
|
|
3066
|
-
if (!
|
|
3452
|
+
if (!fs8.existsSync(indexFile)) {
|
|
3067
3453
|
issues.push({
|
|
3068
3454
|
type: "error",
|
|
3069
3455
|
file: `sections/${sectionName}/index.ts`,
|
|
3070
3456
|
message: "Section index file missing"
|
|
3071
3457
|
});
|
|
3072
3458
|
}
|
|
3073
|
-
if (!
|
|
3459
|
+
if (!fs8.existsSync(defaultTemplate)) {
|
|
3074
3460
|
issues.push({
|
|
3075
3461
|
type: "warning",
|
|
3076
3462
|
file: `sections/${sectionName}/${sectionName}-default.tsx`,
|
|
@@ -3079,29 +3465,29 @@ async function validateCommand(options) {
|
|
|
3079
3465
|
}
|
|
3080
3466
|
}
|
|
3081
3467
|
}
|
|
3082
|
-
const blocksDir =
|
|
3083
|
-
if (
|
|
3084
|
-
const blocks =
|
|
3468
|
+
const blocksDir = path13.join(themePath, "blocks");
|
|
3469
|
+
if (fs8.existsSync(blocksDir)) {
|
|
3470
|
+
const blocks = fs8.readdirSync(blocksDir).filter((name) => fs8.statSync(path13.join(blocksDir, name)).isDirectory());
|
|
3085
3471
|
for (const blockName of blocks) {
|
|
3086
|
-
const blockPath =
|
|
3087
|
-
const schemaFile =
|
|
3088
|
-
const componentFile =
|
|
3089
|
-
const indexFile =
|
|
3090
|
-
if (!
|
|
3472
|
+
const blockPath = path13.join(blocksDir, blockName);
|
|
3473
|
+
const schemaFile = path13.join(blockPath, `${blockName}.schema.ts`);
|
|
3474
|
+
const componentFile = path13.join(blockPath, `${blockName}.tsx`);
|
|
3475
|
+
const indexFile = path13.join(blockPath, "index.ts");
|
|
3476
|
+
if (!fs8.existsSync(schemaFile)) {
|
|
3091
3477
|
issues.push({
|
|
3092
3478
|
type: "error",
|
|
3093
3479
|
file: `blocks/${blockName}/${blockName}.schema.ts`,
|
|
3094
3480
|
message: "Block schema file missing"
|
|
3095
3481
|
});
|
|
3096
3482
|
}
|
|
3097
|
-
if (!
|
|
3483
|
+
if (!fs8.existsSync(componentFile)) {
|
|
3098
3484
|
issues.push({
|
|
3099
3485
|
type: "error",
|
|
3100
3486
|
file: `blocks/${blockName}/${blockName}.tsx`,
|
|
3101
3487
|
message: "Block component file missing"
|
|
3102
3488
|
});
|
|
3103
3489
|
}
|
|
3104
|
-
if (!
|
|
3490
|
+
if (!fs8.existsSync(indexFile)) {
|
|
3105
3491
|
issues.push({
|
|
3106
3492
|
type: "error",
|
|
3107
3493
|
file: `blocks/${blockName}/index.ts`,
|
|
@@ -3110,16 +3496,16 @@ async function validateCommand(options) {
|
|
|
3110
3496
|
}
|
|
3111
3497
|
}
|
|
3112
3498
|
}
|
|
3113
|
-
if (
|
|
3114
|
-
const sections =
|
|
3115
|
-
(name) =>
|
|
3499
|
+
if (fs8.existsSync(sectionsDir)) {
|
|
3500
|
+
const sections = fs8.readdirSync(sectionsDir).filter(
|
|
3501
|
+
(name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory()
|
|
3116
3502
|
);
|
|
3117
3503
|
for (const sectionName of sections) {
|
|
3118
|
-
const sectionPath =
|
|
3119
|
-
const tsxFiles =
|
|
3504
|
+
const sectionPath = path13.join(sectionsDir, sectionName);
|
|
3505
|
+
const tsxFiles = fs8.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
|
|
3120
3506
|
for (const tsxFile of tsxFiles) {
|
|
3121
|
-
const filePath =
|
|
3122
|
-
const content =
|
|
3507
|
+
const filePath = path13.join(sectionPath, tsxFile);
|
|
3508
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
3123
3509
|
const relPath = `sections/${sectionName}/${tsxFile}`;
|
|
3124
3510
|
if (!content.includes('"use client"') && !content.includes("'use client'")) {
|
|
3125
3511
|
issues.push({
|
|
@@ -3166,12 +3552,12 @@ async function validateCommand(options) {
|
|
|
3166
3552
|
}
|
|
3167
3553
|
}
|
|
3168
3554
|
}
|
|
3169
|
-
const registryPath =
|
|
3170
|
-
const bundleEntryPath =
|
|
3171
|
-
const registryContent =
|
|
3172
|
-
if (
|
|
3173
|
-
const sections =
|
|
3174
|
-
(name) =>
|
|
3555
|
+
const registryPath = path13.join(themePath, "sections-registry.ts");
|
|
3556
|
+
const bundleEntryPath = path13.join(themePath, "bundle-entry.ts");
|
|
3557
|
+
const registryContent = fs8.existsSync(registryPath) ? fs8.readFileSync(registryPath, "utf-8") : fs8.existsSync(bundleEntryPath) ? fs8.readFileSync(bundleEntryPath, "utf-8") : "";
|
|
3558
|
+
if (fs8.existsSync(sectionsDir) && registryContent) {
|
|
3559
|
+
const sections = fs8.readdirSync(sectionsDir).filter(
|
|
3560
|
+
(name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory()
|
|
3175
3561
|
);
|
|
3176
3562
|
for (const sectionName of sections) {
|
|
3177
3563
|
if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
|
|
@@ -3183,7 +3569,7 @@ async function validateCommand(options) {
|
|
|
3183
3569
|
}
|
|
3184
3570
|
}
|
|
3185
3571
|
}
|
|
3186
|
-
if (
|
|
3572
|
+
if (fs8.existsSync(sectionsDir)) {
|
|
3187
3573
|
const schemaTypes = await loadSchemaTypes(themePath, sectionsDir);
|
|
3188
3574
|
for (const { folderName, schemaType } of schemaTypes) {
|
|
3189
3575
|
if (schemaType && schemaType !== folderName) {
|
|
@@ -3194,8 +3580,8 @@ async function validateCommand(options) {
|
|
|
3194
3580
|
});
|
|
3195
3581
|
}
|
|
3196
3582
|
}
|
|
3197
|
-
const pagesDir =
|
|
3198
|
-
if (
|
|
3583
|
+
const pagesDir = path13.join(themePath, "pages");
|
|
3584
|
+
if (fs8.existsSync(pagesDir)) {
|
|
3199
3585
|
const allSchemaTypeSet = new Set(
|
|
3200
3586
|
schemaTypes.map((s) => s.schemaType || s.folderName)
|
|
3201
3587
|
);
|
|
@@ -3206,6 +3592,10 @@ async function validateCommand(options) {
|
|
|
3206
3592
|
issues.push(...pageIssues);
|
|
3207
3593
|
}
|
|
3208
3594
|
}
|
|
3595
|
+
if (isNextjsProject(themePath)) {
|
|
3596
|
+
const nextjsIssues = await validateNextjsComponents(themePath);
|
|
3597
|
+
issues.push(...nextjsIssues);
|
|
3598
|
+
}
|
|
3209
3599
|
logger.stopSpinner(true, "Validation complete");
|
|
3210
3600
|
const errors = issues.filter((i) => i.type === "error");
|
|
3211
3601
|
const warnings = issues.filter((i) => i.type === "warning");
|
|
@@ -3246,18 +3636,18 @@ async function validateCommand(options) {
|
|
|
3246
3636
|
}
|
|
3247
3637
|
async function loadSchemaTypes(themePath, sectionsDir) {
|
|
3248
3638
|
const results = [];
|
|
3249
|
-
const sections =
|
|
3639
|
+
const sections = fs8.readdirSync(sectionsDir).filter((name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory());
|
|
3250
3640
|
for (const sectionName of sections) {
|
|
3251
|
-
const schemaFile =
|
|
3641
|
+
const schemaFile = path13.join(
|
|
3252
3642
|
sectionsDir,
|
|
3253
3643
|
sectionName,
|
|
3254
3644
|
`${sectionName}.schema.ts`
|
|
3255
3645
|
);
|
|
3256
|
-
if (!
|
|
3646
|
+
if (!fs8.existsSync(schemaFile)) {
|
|
3257
3647
|
results.push({ folderName: sectionName, schemaType: null });
|
|
3258
3648
|
continue;
|
|
3259
3649
|
}
|
|
3260
|
-
const content =
|
|
3650
|
+
const content = fs8.readFileSync(schemaFile, "utf-8");
|
|
3261
3651
|
let schemaType = null;
|
|
3262
3652
|
const schemaExportMatch = content.match(
|
|
3263
3653
|
/:\s*SectionSchema\s*=\s*\{[\s\S]*?\btype:\s*["']([^"']+)["']/
|
|
@@ -3284,9 +3674,9 @@ async function loadSchemaTypes(themePath, sectionsDir) {
|
|
|
3284
3674
|
}
|
|
3285
3675
|
async function validatePageSectionTypes(pagesDir, validTypes) {
|
|
3286
3676
|
const issues = [];
|
|
3287
|
-
const files =
|
|
3677
|
+
const files = fs8.readdirSync(pagesDir).filter((f) => f.match(/\.(ts|js)$/));
|
|
3288
3678
|
for (const file of files) {
|
|
3289
|
-
const content =
|
|
3679
|
+
const content = fs8.readFileSync(path13.join(pagesDir, file), "utf-8");
|
|
3290
3680
|
const pageName = file.replace(/\.(ts|js)$/, "");
|
|
3291
3681
|
const sectionsMatch = content.match(/\bsections:\s*\[/);
|
|
3292
3682
|
if (!sectionsMatch || sectionsMatch.index === void 0) continue;
|
|
@@ -3299,9 +3689,13 @@ async function validatePageSectionTypes(pagesDir, validTypes) {
|
|
|
3299
3689
|
endIdx = i;
|
|
3300
3690
|
}
|
|
3301
3691
|
const sectionsBlock = content.slice(startIdx, endIdx);
|
|
3302
|
-
const
|
|
3303
|
-
|
|
3692
|
+
const sectionTypeMatches = sectionsBlock.matchAll(
|
|
3693
|
+
/\bid:\s*["'][^"']*["'],\s*\n?\s*type:\s*["']([^"']+)["']/g
|
|
3694
|
+
);
|
|
3695
|
+
for (const match of sectionTypeMatches) {
|
|
3304
3696
|
const sectionType = match[1];
|
|
3697
|
+
if (COMPONENT_TYPES.has(sectionType)) continue;
|
|
3698
|
+
if (BLOCK_TYPES.has(sectionType)) continue;
|
|
3305
3699
|
if (!validTypes.has(sectionType)) {
|
|
3306
3700
|
issues.push({
|
|
3307
3701
|
type: "error",
|
|
@@ -3354,9 +3748,128 @@ var FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
3354
3748
|
"inline_richtext",
|
|
3355
3749
|
"repeater"
|
|
3356
3750
|
]);
|
|
3751
|
+
var COMPONENT_TYPES = /* @__PURE__ */ new Set([
|
|
3752
|
+
"heading",
|
|
3753
|
+
"paragraph",
|
|
3754
|
+
"button",
|
|
3755
|
+
"image",
|
|
3756
|
+
"link",
|
|
3757
|
+
"icon",
|
|
3758
|
+
"badge",
|
|
3759
|
+
"divider",
|
|
3760
|
+
"spacer",
|
|
3761
|
+
"container",
|
|
3762
|
+
"grid",
|
|
3763
|
+
"columns",
|
|
3764
|
+
"card",
|
|
3765
|
+
"quote",
|
|
3766
|
+
"input",
|
|
3767
|
+
"textarea",
|
|
3768
|
+
"checkbox",
|
|
3769
|
+
"select",
|
|
3770
|
+
"video",
|
|
3771
|
+
"gallery",
|
|
3772
|
+
"alert",
|
|
3773
|
+
"progress",
|
|
3774
|
+
"rating",
|
|
3775
|
+
"timer",
|
|
3776
|
+
"list",
|
|
3777
|
+
"table",
|
|
3778
|
+
"accordion",
|
|
3779
|
+
"tabs",
|
|
3780
|
+
"code",
|
|
3781
|
+
"map",
|
|
3782
|
+
"product-card",
|
|
3783
|
+
"blog-card",
|
|
3784
|
+
"social-links",
|
|
3785
|
+
"hotline-contacts",
|
|
3786
|
+
"company-info",
|
|
3787
|
+
"torn-separator"
|
|
3788
|
+
]);
|
|
3789
|
+
var BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
3790
|
+
"brand-feature",
|
|
3791
|
+
"collection-item",
|
|
3792
|
+
"crafting-step",
|
|
3793
|
+
"testimonial-item",
|
|
3794
|
+
"stat-item",
|
|
3795
|
+
"footer-link",
|
|
3796
|
+
"navigation-links-block",
|
|
3797
|
+
"policy-section",
|
|
3798
|
+
"core-value-card",
|
|
3799
|
+
"faq-item",
|
|
3800
|
+
"feature-item",
|
|
3801
|
+
"gallery-item",
|
|
3802
|
+
"logo-item",
|
|
3803
|
+
"pricing-tier",
|
|
3804
|
+
"service-item",
|
|
3805
|
+
"stat-card",
|
|
3806
|
+
"team-member",
|
|
3807
|
+
"hero-content"
|
|
3808
|
+
]);
|
|
3809
|
+
var SERVER_ONLY_APIS = [
|
|
3810
|
+
"next/headers",
|
|
3811
|
+
"next/server",
|
|
3812
|
+
"next/cache",
|
|
3813
|
+
"cookies()",
|
|
3814
|
+
"headers()",
|
|
3815
|
+
"draftMode()"
|
|
3816
|
+
];
|
|
3817
|
+
async function validateNextjsComponents(themePath) {
|
|
3818
|
+
const issues = [];
|
|
3819
|
+
const { glob: glob6 } = await import('glob');
|
|
3820
|
+
const componentFiles = await glob6("components/**/*.{tsx,ts}", {
|
|
3821
|
+
cwd: themePath,
|
|
3822
|
+
ignore: ["**/node_modules/**"]
|
|
3823
|
+
});
|
|
3824
|
+
for (const relFile of componentFiles) {
|
|
3825
|
+
const absFile = path13.join(themePath, relFile);
|
|
3826
|
+
const content = fs8.readFileSync(absFile, "utf-8");
|
|
3827
|
+
const dir = path13.dirname(absFile);
|
|
3828
|
+
const hasSectionJson = fs8.existsSync(path13.join(dir, "section.json"));
|
|
3829
|
+
if (!hasSectionJson) continue;
|
|
3830
|
+
for (const api of SERVER_ONLY_APIS) {
|
|
3831
|
+
if (content.includes(`"${api}"`) || content.includes(`'${api}'`)) {
|
|
3832
|
+
issues.push({
|
|
3833
|
+
type: "error",
|
|
3834
|
+
file: relFile,
|
|
3835
|
+
message: `"${api}" is server-only and cannot be used in a OneX theme bundle. Remove this import or move data fetching to a client-side useEffect/useQuery.`
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
if (content.includes(api.replace("()", "") + "(")) {
|
|
3839
|
+
issues.push({
|
|
3840
|
+
type: "error",
|
|
3841
|
+
file: relFile,
|
|
3842
|
+
message: `${api} is server-only and cannot be called in a browser bundle. Use useQuery or fetch() inside useEffect instead.`
|
|
3843
|
+
});
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
const isAsyncComponent = /export\s+default\s+async\s+function/.test(content) || /export\s+default\s+async\s+\(/.test(content);
|
|
3847
|
+
if (isAsyncComponent) {
|
|
3848
|
+
const hasUseClient2 = content.includes('"use client"') || content.includes("'use client'");
|
|
3849
|
+
if (!hasUseClient2) {
|
|
3850
|
+
issues.push({
|
|
3851
|
+
type: "error",
|
|
3852
|
+
file: relFile,
|
|
3853
|
+
message: 'Async Server Components cannot be compiled to a browser bundle. Add "use client" at the top and convert data fetching to useQuery or useEffect.'
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
const usesHooks = /\buse[A-Z]\w+\s*\(/.test(content);
|
|
3858
|
+
const hasUseClient = content.includes('"use client"') || content.includes("'use client'");
|
|
3859
|
+
if (usesHooks && !hasUseClient) {
|
|
3860
|
+
issues.push({
|
|
3861
|
+
type: "warning",
|
|
3862
|
+
file: relFile,
|
|
3863
|
+
message: 'Component uses React hooks but is missing "use client" directive. Add "use client" at the top of the file.'
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
return issues;
|
|
3868
|
+
}
|
|
3357
3869
|
|
|
3358
3870
|
// src/commands/build.ts
|
|
3359
3871
|
init_logger();
|
|
3872
|
+
init_detect_nextjs();
|
|
3360
3873
|
async function buildCommand(options) {
|
|
3361
3874
|
logger.header("Build Theme");
|
|
3362
3875
|
let themePath;
|
|
@@ -3364,16 +3877,16 @@ async function buildCommand(options) {
|
|
|
3364
3877
|
if (options.theme) {
|
|
3365
3878
|
themeName = options.theme;
|
|
3366
3879
|
try {
|
|
3367
|
-
const workspaceThemePath =
|
|
3368
|
-
if (
|
|
3880
|
+
const workspaceThemePath = path13.join(getThemesDir(), themeName);
|
|
3881
|
+
if (fs8.existsSync(workspaceThemePath)) {
|
|
3369
3882
|
themePath = workspaceThemePath;
|
|
3370
3883
|
} else {
|
|
3371
|
-
themePath =
|
|
3884
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
3372
3885
|
}
|
|
3373
3886
|
} catch {
|
|
3374
|
-
themePath =
|
|
3887
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
3375
3888
|
}
|
|
3376
|
-
if (!
|
|
3889
|
+
if (!fs8.existsSync(themePath)) {
|
|
3377
3890
|
logger.error(`Theme "${themeName}" not found.`);
|
|
3378
3891
|
process.exit(1);
|
|
3379
3892
|
}
|
|
@@ -3381,11 +3894,14 @@ async function buildCommand(options) {
|
|
|
3381
3894
|
const isThemeDir2 = [
|
|
3382
3895
|
"theme.config.ts",
|
|
3383
3896
|
"bundle-entry.ts",
|
|
3384
|
-
"manifest.ts"
|
|
3385
|
-
|
|
3897
|
+
"manifest.ts",
|
|
3898
|
+
"next.config.ts",
|
|
3899
|
+
"next.config.js",
|
|
3900
|
+
"next.config.mjs"
|
|
3901
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
3386
3902
|
if (isThemeDir2) {
|
|
3387
3903
|
themePath = process.cwd();
|
|
3388
|
-
themeName =
|
|
3904
|
+
themeName = path13.basename(themePath);
|
|
3389
3905
|
logger.info(`Building current theme: ${themeName}`);
|
|
3390
3906
|
} else {
|
|
3391
3907
|
logger.error(
|
|
@@ -3394,8 +3910,8 @@ async function buildCommand(options) {
|
|
|
3394
3910
|
process.exit(1);
|
|
3395
3911
|
}
|
|
3396
3912
|
}
|
|
3397
|
-
const packageJsonPath =
|
|
3398
|
-
const hasPkgJson =
|
|
3913
|
+
const packageJsonPath = path13.join(themePath, "package.json");
|
|
3914
|
+
const hasPkgJson = fs8.existsSync(packageJsonPath);
|
|
3399
3915
|
if (!hasPkgJson) {
|
|
3400
3916
|
logger.warning(
|
|
3401
3917
|
"No package.json found in theme. Skipping build (themes in monorepo are built via turbo)."
|
|
@@ -3410,30 +3926,42 @@ async function buildCommand(options) {
|
|
|
3410
3926
|
}
|
|
3411
3927
|
logger.newLine();
|
|
3412
3928
|
logger.section("Build Steps");
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
logger.
|
|
3425
|
-
|
|
3426
|
-
|
|
3929
|
+
const pkgJson = fs8.readJsonSync(packageJsonPath);
|
|
3930
|
+
if (pkgJson.scripts?.["type-check"]) {
|
|
3931
|
+
logger.startSpinner("Running type check...");
|
|
3932
|
+
const typeCheckSuccess = await runCommand("pnpm", ["type-check"], themePath);
|
|
3933
|
+
if (!typeCheckSuccess) {
|
|
3934
|
+
logger.stopSpinner(false, "Type check failed");
|
|
3935
|
+
logger.error("Fix type errors before building.");
|
|
3936
|
+
process.exit(1);
|
|
3937
|
+
}
|
|
3938
|
+
logger.stopSpinner(true, "Type check passed");
|
|
3939
|
+
} else {
|
|
3940
|
+
logger.info("Skipping type check (no type-check script in package.json)");
|
|
3941
|
+
}
|
|
3942
|
+
const isNextjsForLint = isNextjsProject(themePath);
|
|
3943
|
+
if (!isNextjsForLint && pkgJson.scripts?.lint) {
|
|
3944
|
+
logger.startSpinner("Running linter...");
|
|
3945
|
+
const lintSuccess = await runCommand("pnpm", ["lint"], themePath);
|
|
3946
|
+
if (!lintSuccess) {
|
|
3947
|
+
logger.stopSpinner(false, "Lint failed");
|
|
3948
|
+
logger.error("Fix lint errors before building.");
|
|
3949
|
+
process.exit(1);
|
|
3950
|
+
}
|
|
3951
|
+
logger.stopSpinner(true, "Lint passed");
|
|
3952
|
+
} else if (isNextjsForLint) {
|
|
3953
|
+
logger.info("Skipping lint (Next.js project compiled via esbuild)");
|
|
3954
|
+
} else {
|
|
3955
|
+
logger.info("Skipping lint (no lint script in package.json)");
|
|
3427
3956
|
}
|
|
3428
|
-
logger.stopSpinner(true, "Lint passed");
|
|
3429
|
-
const pkgJson = fs.readJsonSync(packageJsonPath);
|
|
3430
3957
|
const buildScript = pkgJson.scripts?.build || "";
|
|
3431
3958
|
const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex build") || buildScript.includes("onex-cli build");
|
|
3959
|
+
const isNextjs = isNextjsProject(themePath);
|
|
3432
3960
|
logger.startSpinner(
|
|
3433
3961
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
3434
3962
|
);
|
|
3435
3963
|
let buildSuccess;
|
|
3436
|
-
if (isRecursive) {
|
|
3964
|
+
if (isRecursive || isNextjs) {
|
|
3437
3965
|
const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
3438
3966
|
buildSuccess = await compileStandaloneTheme2(themePath, themeName);
|
|
3439
3967
|
} else {
|
|
@@ -3450,10 +3978,10 @@ async function buildCommand(options) {
|
|
|
3450
3978
|
logger.success("\u2713 Theme built successfully!");
|
|
3451
3979
|
logger.newLine();
|
|
3452
3980
|
logger.info(`Theme: ${themeName}`);
|
|
3453
|
-
const distPath =
|
|
3454
|
-
if (
|
|
3455
|
-
logger.log(`Output: ${
|
|
3456
|
-
const files =
|
|
3981
|
+
const distPath = path13.join(themePath, "dist");
|
|
3982
|
+
if (fs8.existsSync(distPath)) {
|
|
3983
|
+
logger.log(`Output: ${path13.relative(process.cwd(), distPath)}`);
|
|
3984
|
+
const files = fs8.readdirSync(distPath);
|
|
3457
3985
|
logger.log(`Files: ${files.length}`);
|
|
3458
3986
|
}
|
|
3459
3987
|
}
|
|
@@ -3508,8 +4036,8 @@ async function packageCommand(options) {
|
|
|
3508
4036
|
let themeName;
|
|
3509
4037
|
if (options.theme) {
|
|
3510
4038
|
themeName = options.theme;
|
|
3511
|
-
themePath =
|
|
3512
|
-
if (!
|
|
4039
|
+
themePath = path13.join(getThemesDir(), themeName);
|
|
4040
|
+
if (!fs8.existsSync(themePath)) {
|
|
3513
4041
|
logger.error(`Theme "${themeName}" not found.`);
|
|
3514
4042
|
process.exit(1);
|
|
3515
4043
|
}
|
|
@@ -3517,11 +4045,14 @@ async function packageCommand(options) {
|
|
|
3517
4045
|
const isThemeDir2 = [
|
|
3518
4046
|
"theme.config.ts",
|
|
3519
4047
|
"bundle-entry.ts",
|
|
3520
|
-
"manifest.ts"
|
|
3521
|
-
|
|
4048
|
+
"manifest.ts",
|
|
4049
|
+
"next.config.ts",
|
|
4050
|
+
"next.config.js",
|
|
4051
|
+
"next.config.mjs"
|
|
4052
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
3522
4053
|
if (isThemeDir2) {
|
|
3523
4054
|
themePath = process.cwd();
|
|
3524
|
-
themeName =
|
|
4055
|
+
themeName = path13.basename(themePath);
|
|
3525
4056
|
logger.info(`Packaging current theme: ${themeName}`);
|
|
3526
4057
|
} else {
|
|
3527
4058
|
logger.error(
|
|
@@ -3530,22 +4061,19 @@ async function packageCommand(options) {
|
|
|
3530
4061
|
process.exit(1);
|
|
3531
4062
|
}
|
|
3532
4063
|
}
|
|
3533
|
-
const packageJsonPath =
|
|
4064
|
+
const packageJsonPath = path13.join(themePath, "package.json");
|
|
3534
4065
|
let version2 = "1.0.0";
|
|
3535
|
-
if (
|
|
3536
|
-
const packageJson = await
|
|
4066
|
+
if (fs8.existsSync(packageJsonPath)) {
|
|
4067
|
+
const packageJson = await fs8.readJson(packageJsonPath);
|
|
3537
4068
|
version2 = packageJson.version || "1.0.0";
|
|
3538
4069
|
}
|
|
3539
4070
|
logger.newLine();
|
|
3540
4071
|
logger.info(`Theme: ${themeName}`);
|
|
3541
4072
|
logger.info(`Version: ${version2}`);
|
|
3542
4073
|
logger.newLine();
|
|
3543
|
-
const
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
themeName,
|
|
3547
|
-
"dist"
|
|
3548
|
-
);
|
|
4074
|
+
const standaloneDistPath = path13.join(themePath, "dist");
|
|
4075
|
+
const monorepoDistPath = path13.join(process.cwd(), "themes", themeName, "dist");
|
|
4076
|
+
const compiledThemePath = fs8.existsSync(standaloneDistPath) ? standaloneDistPath : monorepoDistPath;
|
|
3549
4077
|
if (!options.skipBuild) {
|
|
3550
4078
|
logger.section("Step 1: Compile Theme");
|
|
3551
4079
|
logger.startSpinner("Compiling theme with esbuild...");
|
|
@@ -3566,7 +4094,7 @@ async function packageCommand(options) {
|
|
|
3566
4094
|
} else {
|
|
3567
4095
|
logger.info("Skipping build (--skip-build flag)");
|
|
3568
4096
|
}
|
|
3569
|
-
if (!
|
|
4097
|
+
if (!fs8.existsSync(compiledThemePath)) {
|
|
3570
4098
|
logger.error(`Compiled theme not found at: ${compiledThemePath}`);
|
|
3571
4099
|
logger.info("Run without --skip-build to compile first.");
|
|
3572
4100
|
process.exit(1);
|
|
@@ -3574,25 +4102,25 @@ async function packageCommand(options) {
|
|
|
3574
4102
|
logger.newLine();
|
|
3575
4103
|
logger.section("Step 2: Create Package");
|
|
3576
4104
|
const packageName = options.name || `${themeName}-${version2}`;
|
|
3577
|
-
const outputDir = options.output ||
|
|
3578
|
-
const outputPath =
|
|
3579
|
-
await
|
|
4105
|
+
const outputDir = options.output || path13.join(process.cwd(), "dist");
|
|
4106
|
+
const outputPath = path13.join(outputDir, `${packageName}.zip`);
|
|
4107
|
+
await fs8.ensureDir(outputDir);
|
|
3580
4108
|
logger.startSpinner("Creating zip archive...");
|
|
3581
4109
|
try {
|
|
3582
4110
|
await createZipArchive(compiledThemePath, outputPath);
|
|
3583
4111
|
logger.stopSpinner(true, "Package created");
|
|
3584
|
-
const stats = await
|
|
4112
|
+
const stats = await fs8.stat(outputPath);
|
|
3585
4113
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
3586
4114
|
logger.newLine();
|
|
3587
4115
|
logger.success("\u2713 Theme packaged successfully!");
|
|
3588
4116
|
logger.newLine();
|
|
3589
4117
|
logger.info(`Package: ${packageName}.zip`);
|
|
3590
4118
|
logger.log(`Size: ${sizeMB} MB`);
|
|
3591
|
-
logger.log(`Location: ${
|
|
4119
|
+
logger.log(`Location: ${path13.relative(process.cwd(), outputPath)}`);
|
|
3592
4120
|
logger.newLine();
|
|
3593
4121
|
logger.section("Next steps:");
|
|
3594
4122
|
logger.log(
|
|
3595
|
-
` onexthm deploy --package ${
|
|
4123
|
+
` onexthm deploy --package ${path13.relative(process.cwd(), outputPath)}`
|
|
3596
4124
|
);
|
|
3597
4125
|
} catch (error) {
|
|
3598
4126
|
logger.stopSpinner(false, "Failed to create package");
|
|
@@ -3626,7 +4154,7 @@ function runCommand2(command, args) {
|
|
|
3626
4154
|
}
|
|
3627
4155
|
async function createZipArchive(compiledThemePath, outputPath) {
|
|
3628
4156
|
return new Promise((resolve, reject) => {
|
|
3629
|
-
const output =
|
|
4157
|
+
const output = fs8.createWriteStream(outputPath);
|
|
3630
4158
|
const archive = archiver("zip", {
|
|
3631
4159
|
zlib: { level: 9 }
|
|
3632
4160
|
// Maximum compression
|
|
@@ -3650,14 +4178,14 @@ async function deployCommand(options) {
|
|
|
3650
4178
|
ensureOneXProject();
|
|
3651
4179
|
let packagePath;
|
|
3652
4180
|
if (options.package) {
|
|
3653
|
-
packagePath =
|
|
4181
|
+
packagePath = path13.resolve(options.package);
|
|
3654
4182
|
} else if (options.theme) {
|
|
3655
|
-
const distDir =
|
|
3656
|
-
if (!
|
|
4183
|
+
const distDir = path13.join(process.cwd(), "dist");
|
|
4184
|
+
if (!fs8.existsSync(distDir)) {
|
|
3657
4185
|
logger.error("No dist/ directory found. Run 'onexthm package' first.");
|
|
3658
4186
|
process.exit(1);
|
|
3659
4187
|
}
|
|
3660
|
-
const files =
|
|
4188
|
+
const files = fs8.readdirSync(distDir);
|
|
3661
4189
|
const packageFiles = files.filter(
|
|
3662
4190
|
(f) => f.startsWith(options.theme) && f.endsWith(".zip")
|
|
3663
4191
|
);
|
|
@@ -3667,7 +4195,7 @@ async function deployCommand(options) {
|
|
|
3667
4195
|
process.exit(1);
|
|
3668
4196
|
}
|
|
3669
4197
|
packageFiles.sort().reverse();
|
|
3670
|
-
packagePath =
|
|
4198
|
+
packagePath = path13.join(distDir, packageFiles[0]);
|
|
3671
4199
|
} else {
|
|
3672
4200
|
logger.error("Either --package or --theme must be specified.");
|
|
3673
4201
|
logger.info("Examples:");
|
|
@@ -3675,17 +4203,17 @@ async function deployCommand(options) {
|
|
|
3675
4203
|
logger.log(" onexthm deploy --theme tinan");
|
|
3676
4204
|
process.exit(1);
|
|
3677
4205
|
}
|
|
3678
|
-
if (!
|
|
4206
|
+
if (!fs8.existsSync(packagePath)) {
|
|
3679
4207
|
logger.error(`Package not found: ${packagePath}`);
|
|
3680
4208
|
process.exit(1);
|
|
3681
4209
|
}
|
|
3682
|
-
const stats = await
|
|
4210
|
+
const stats = await fs8.stat(packagePath);
|
|
3683
4211
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
3684
|
-
const fileName =
|
|
4212
|
+
const fileName = path13.basename(packagePath);
|
|
3685
4213
|
logger.newLine();
|
|
3686
4214
|
logger.info(`Package: ${fileName}`);
|
|
3687
4215
|
logger.log(`Size: ${sizeMB} MB`);
|
|
3688
|
-
logger.log(`Path: ${
|
|
4216
|
+
logger.log(`Path: ${path13.relative(process.cwd(), packagePath)}`);
|
|
3689
4217
|
logger.newLine();
|
|
3690
4218
|
const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
|
|
3691
4219
|
const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
|
|
@@ -3695,7 +4223,7 @@ async function deployCommand(options) {
|
|
|
3695
4223
|
logger.startSpinner("Uploading theme package...");
|
|
3696
4224
|
try {
|
|
3697
4225
|
const formData = new FormData();
|
|
3698
|
-
formData.append("theme",
|
|
4226
|
+
formData.append("theme", fs8.createReadStream(packagePath), {
|
|
3699
4227
|
filename: fileName,
|
|
3700
4228
|
contentType: "application/zip"
|
|
3701
4229
|
});
|
|
@@ -3842,24 +4370,24 @@ async function downloadBundleZip(apiUrl, themeId, version2) {
|
|
|
3842
4370
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
3843
4371
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
3844
4372
|
if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
|
|
3845
|
-
const hashedPath =
|
|
3846
|
-
const stablePath =
|
|
3847
|
-
if (await
|
|
3848
|
-
await
|
|
4373
|
+
const hashedPath = path13.join(outputDir, entryFile);
|
|
4374
|
+
const stablePath = path13.join(outputDir, "bundle-entry.js");
|
|
4375
|
+
if (await fs8.pathExists(hashedPath)) {
|
|
4376
|
+
await fs8.copy(hashedPath, stablePath);
|
|
3849
4377
|
const mapPath = hashedPath + ".map";
|
|
3850
|
-
if (await
|
|
3851
|
-
await
|
|
4378
|
+
if (await fs8.pathExists(mapPath)) {
|
|
4379
|
+
await fs8.copy(mapPath, stablePath + ".map");
|
|
3852
4380
|
}
|
|
3853
4381
|
}
|
|
3854
4382
|
}
|
|
3855
|
-
const sectionsRegistryPath =
|
|
4383
|
+
const sectionsRegistryPath = path13.join(outputDir, "sections-registry.js");
|
|
3856
4384
|
const content = `// Re-export all sections from bundle-entry
|
|
3857
4385
|
// This file exists to maintain compatibility with the import path
|
|
3858
4386
|
export * from './bundle-entry.js';
|
|
3859
4387
|
`;
|
|
3860
|
-
await
|
|
3861
|
-
const pkgJsonPath =
|
|
3862
|
-
await
|
|
4388
|
+
await fs8.writeFile(sectionsRegistryPath, content, "utf-8");
|
|
4389
|
+
const pkgJsonPath = path13.join(outputDir, "package.json");
|
|
4390
|
+
await fs8.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
|
|
3863
4391
|
}
|
|
3864
4392
|
function showDownloadFailureHelp(themeId, apiUrl) {
|
|
3865
4393
|
console.log();
|
|
@@ -3899,7 +4427,7 @@ function showDownloadFailureHelp(themeId, apiUrl) {
|
|
|
3899
4427
|
}
|
|
3900
4428
|
async function downloadCommand(options) {
|
|
3901
4429
|
logger.header("Download Theme");
|
|
3902
|
-
const env = options.env
|
|
4430
|
+
const env = options.env;
|
|
3903
4431
|
const apiUrl = getApiUrl(env);
|
|
3904
4432
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
3905
4433
|
const spinner = ora("Initializing download...").start();
|
|
@@ -3951,14 +4479,14 @@ async function downloadCommand(options) {
|
|
|
3951
4479
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
3952
4480
|
spinner.succeed(`Downloaded bundle.zip (${sizeMB} MB)`);
|
|
3953
4481
|
spinner.start("Extracting bundle...");
|
|
3954
|
-
await
|
|
3955
|
-
await
|
|
4482
|
+
await fs8.remove(outputDir);
|
|
4483
|
+
await fs8.ensureDir(outputDir);
|
|
3956
4484
|
const zip = new AdmZip(zipBuffer);
|
|
3957
4485
|
zip.extractAllTo(outputDir, true);
|
|
3958
4486
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
3959
4487
|
spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
|
|
3960
|
-
const manifestPath =
|
|
3961
|
-
const manifest = await
|
|
4488
|
+
const manifestPath = path13.join(outputDir, "manifest.json");
|
|
4489
|
+
const manifest = await fs8.readJson(manifestPath);
|
|
3962
4490
|
await createCompatibilityFiles(outputDir, manifest);
|
|
3963
4491
|
console.log();
|
|
3964
4492
|
logger.success(chalk4.green.bold("Theme downloaded successfully!"));
|
|
@@ -4078,9 +4606,9 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4078
4606
|
const oldPrefix = `${oldName}-`;
|
|
4079
4607
|
const newPrefix = `${newName}-`;
|
|
4080
4608
|
const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4081
|
-
const pkgPath =
|
|
4082
|
-
if (await
|
|
4083
|
-
const pkg = await
|
|
4609
|
+
const pkgPath = path13.join(themeDir, "package.json");
|
|
4610
|
+
if (await fs8.pathExists(pkgPath)) {
|
|
4611
|
+
const pkg = await fs8.readJson(pkgPath);
|
|
4084
4612
|
pkg.name = `@onex-themes/${newName}`;
|
|
4085
4613
|
if (pkg.description) {
|
|
4086
4614
|
pkg.description = pkg.description.replace(
|
|
@@ -4092,33 +4620,33 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4092
4620
|
if (pkg.devDependencies?.["@onexapis/cli"]) {
|
|
4093
4621
|
delete pkg.devDependencies["@onexapis/cli"];
|
|
4094
4622
|
}
|
|
4095
|
-
await
|
|
4623
|
+
await fs8.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
4096
4624
|
}
|
|
4097
|
-
const configPath =
|
|
4098
|
-
if (await
|
|
4099
|
-
let content = await
|
|
4625
|
+
const configPath = path13.join(themeDir, "theme.config.ts");
|
|
4626
|
+
if (await fs8.pathExists(configPath)) {
|
|
4627
|
+
let content = await fs8.readFile(configPath, "utf-8");
|
|
4100
4628
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
4101
4629
|
content = content.replace(
|
|
4102
4630
|
/name:\s*"[^"]*Theme"/,
|
|
4103
4631
|
`name: "${newDisplayName} Theme"`
|
|
4104
4632
|
);
|
|
4105
|
-
await
|
|
4633
|
+
await fs8.writeFile(configPath, content);
|
|
4106
4634
|
}
|
|
4107
|
-
const layoutPath =
|
|
4108
|
-
if (await
|
|
4109
|
-
let content = await
|
|
4635
|
+
const layoutPath = path13.join(themeDir, "theme.layout.ts");
|
|
4636
|
+
if (await fs8.pathExists(layoutPath)) {
|
|
4637
|
+
let content = await fs8.readFile(layoutPath, "utf-8");
|
|
4110
4638
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
4111
4639
|
content = content.replace(
|
|
4112
4640
|
/name:\s*"[^"]*Theme"/,
|
|
4113
4641
|
`name: "${newDisplayName} Theme"`
|
|
4114
4642
|
);
|
|
4115
|
-
await
|
|
4643
|
+
await fs8.writeFile(layoutPath, content);
|
|
4116
4644
|
}
|
|
4117
4645
|
const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4118
4646
|
const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
|
|
4119
4647
|
for (const file of tsFiles) {
|
|
4120
|
-
const filePath =
|
|
4121
|
-
let content = await
|
|
4648
|
+
const filePath = path13.join(themeDir, file);
|
|
4649
|
+
let content = await fs8.readFile(filePath, "utf-8");
|
|
4122
4650
|
const original = content;
|
|
4123
4651
|
content = content.replace(
|
|
4124
4652
|
new RegExp(`"${oldPrefix}`, "g"),
|
|
@@ -4133,13 +4661,13 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4133
4661
|
`${newDisplayName} Theme`
|
|
4134
4662
|
);
|
|
4135
4663
|
if (content !== original) {
|
|
4136
|
-
await
|
|
4664
|
+
await fs8.writeFile(filePath, content);
|
|
4137
4665
|
}
|
|
4138
4666
|
}
|
|
4139
4667
|
}
|
|
4140
4668
|
async function cloneCommand(themeName, options) {
|
|
4141
4669
|
logger.header("Clone Theme Source");
|
|
4142
|
-
const env = options.env
|
|
4670
|
+
const env = options.env;
|
|
4143
4671
|
const apiUrl = getApiUrl(env);
|
|
4144
4672
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
4145
4673
|
if (options.bucket) {
|
|
@@ -4160,8 +4688,8 @@ async function cloneCommand(themeName, options) {
|
|
|
4160
4688
|
}
|
|
4161
4689
|
const spinner = ora("Initializing clone...").start();
|
|
4162
4690
|
try {
|
|
4163
|
-
const outputDir = options.output ||
|
|
4164
|
-
if (await
|
|
4691
|
+
const outputDir = options.output || path13.resolve(process.cwd(), newName);
|
|
4692
|
+
if (await fs8.pathExists(outputDir)) {
|
|
4165
4693
|
spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
|
|
4166
4694
|
logger.info(
|
|
4167
4695
|
chalk4.gray(
|
|
@@ -4194,7 +4722,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4194
4722
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
4195
4723
|
spinner.succeed(`Downloaded source.zip (${sizeMB} MB)`);
|
|
4196
4724
|
spinner.start(`Extracting to ${outputDir}...`);
|
|
4197
|
-
await
|
|
4725
|
+
await fs8.ensureDir(outputDir);
|
|
4198
4726
|
const zip = new AdmZip(zipBuffer);
|
|
4199
4727
|
zip.extractAllTo(outputDir, true);
|
|
4200
4728
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
@@ -4206,9 +4734,9 @@ async function cloneCommand(themeName, options) {
|
|
|
4206
4734
|
spinner.succeed(
|
|
4207
4735
|
`Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
|
|
4208
4736
|
);
|
|
4209
|
-
const envExamplePath =
|
|
4210
|
-
if (!await
|
|
4211
|
-
await
|
|
4737
|
+
const envExamplePath = path13.join(outputDir, ".env.example");
|
|
4738
|
+
if (!await fs8.pathExists(envExamplePath)) {
|
|
4739
|
+
await fs8.writeFile(
|
|
4212
4740
|
envExamplePath,
|
|
4213
4741
|
[
|
|
4214
4742
|
"# API Configuration (enables real data in preview)",
|
|
@@ -4219,8 +4747,8 @@ async function cloneCommand(themeName, options) {
|
|
|
4219
4747
|
].join("\n")
|
|
4220
4748
|
);
|
|
4221
4749
|
}
|
|
4222
|
-
const mcpJsonPath =
|
|
4223
|
-
if (await
|
|
4750
|
+
const mcpJsonPath = path13.join(outputDir, ".mcp.json");
|
|
4751
|
+
if (await fs8.pathExists(mcpJsonPath)) {
|
|
4224
4752
|
const { default: inquirerMod } = await import('inquirer');
|
|
4225
4753
|
const { figmaApiKey } = await inquirerMod.prompt([
|
|
4226
4754
|
{
|
|
@@ -4229,7 +4757,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4229
4757
|
message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
|
|
4230
4758
|
}
|
|
4231
4759
|
]);
|
|
4232
|
-
let mcpContent = await
|
|
4760
|
+
let mcpContent = await fs8.readFile(mcpJsonPath, "utf-8");
|
|
4233
4761
|
if (figmaApiKey) {
|
|
4234
4762
|
mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
|
|
4235
4763
|
} else {
|
|
@@ -4240,11 +4768,11 @@ async function cloneCommand(themeName, options) {
|
|
|
4240
4768
|
} catch {
|
|
4241
4769
|
}
|
|
4242
4770
|
}
|
|
4243
|
-
await
|
|
4771
|
+
await fs8.writeFile(mcpJsonPath, mcpContent, "utf-8");
|
|
4244
4772
|
}
|
|
4245
4773
|
if (options.install !== false) {
|
|
4246
|
-
const hasPkgJson = await
|
|
4247
|
-
|
|
4774
|
+
const hasPkgJson = await fs8.pathExists(
|
|
4775
|
+
path13.join(outputDir, "package.json")
|
|
4248
4776
|
);
|
|
4249
4777
|
if (hasPkgJson) {
|
|
4250
4778
|
spinner.start("Installing dependencies...");
|
|
@@ -4272,7 +4800,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4272
4800
|
console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
|
|
4273
4801
|
console.log();
|
|
4274
4802
|
console.log(chalk4.cyan("Next steps:"));
|
|
4275
|
-
console.log(chalk4.gray(` cd ${
|
|
4803
|
+
console.log(chalk4.gray(` cd ${path13.relative(process.cwd(), outputDir)}`));
|
|
4276
4804
|
console.log(
|
|
4277
4805
|
chalk4.gray(" cp .env.example .env # then add your Company ID")
|
|
4278
4806
|
);
|
|
@@ -4299,16 +4827,16 @@ async function devCommand(options) {
|
|
|
4299
4827
|
if (options.theme) {
|
|
4300
4828
|
themeName = options.theme;
|
|
4301
4829
|
try {
|
|
4302
|
-
const workspaceThemePath =
|
|
4303
|
-
if (
|
|
4830
|
+
const workspaceThemePath = path13.join(getThemesDir(), themeName);
|
|
4831
|
+
if (fs8.existsSync(workspaceThemePath)) {
|
|
4304
4832
|
themePath = workspaceThemePath;
|
|
4305
4833
|
} else {
|
|
4306
|
-
themePath =
|
|
4834
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
4307
4835
|
}
|
|
4308
4836
|
} catch {
|
|
4309
|
-
themePath =
|
|
4837
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
4310
4838
|
}
|
|
4311
|
-
if (!
|
|
4839
|
+
if (!fs8.existsSync(themePath)) {
|
|
4312
4840
|
logger.error(`Theme "${themeName}" not found.`);
|
|
4313
4841
|
process.exit(1);
|
|
4314
4842
|
}
|
|
@@ -4316,11 +4844,14 @@ async function devCommand(options) {
|
|
|
4316
4844
|
const isThemeDir2 = [
|
|
4317
4845
|
"theme.config.ts",
|
|
4318
4846
|
"bundle-entry.ts",
|
|
4319
|
-
"manifest.ts"
|
|
4320
|
-
|
|
4847
|
+
"manifest.ts",
|
|
4848
|
+
"next.config.ts",
|
|
4849
|
+
"next.config.js",
|
|
4850
|
+
"next.config.mjs"
|
|
4851
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
4321
4852
|
if (isThemeDir2) {
|
|
4322
4853
|
themePath = process.cwd();
|
|
4323
|
-
themeName =
|
|
4854
|
+
themeName = path13.basename(themePath);
|
|
4324
4855
|
} else {
|
|
4325
4856
|
logger.error(
|
|
4326
4857
|
"Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
|
|
@@ -4389,9 +4920,9 @@ async function devCommand(options) {
|
|
|
4389
4920
|
watcher.close();
|
|
4390
4921
|
await context2.dispose();
|
|
4391
4922
|
server.close();
|
|
4392
|
-
const shimPath =
|
|
4923
|
+
const shimPath = path13.join(outputDir, ".process-shim.js");
|
|
4393
4924
|
try {
|
|
4394
|
-
await
|
|
4925
|
+
await fs11.unlink(shimPath);
|
|
4395
4926
|
} catch {
|
|
4396
4927
|
}
|
|
4397
4928
|
process.exit(0);
|
|
@@ -4400,8 +4931,8 @@ async function devCommand(options) {
|
|
|
4400
4931
|
|
|
4401
4932
|
// src/commands/config.ts
|
|
4402
4933
|
init_logger();
|
|
4403
|
-
var CONFIG_DIR =
|
|
4404
|
-
var CONFIG_FILE =
|
|
4934
|
+
var CONFIG_DIR = path13.join(os.homedir(), ".onexthm");
|
|
4935
|
+
var CONFIG_FILE = path13.join(CONFIG_DIR, ".env");
|
|
4405
4936
|
var CONFIG_ENTRIES = [
|
|
4406
4937
|
{
|
|
4407
4938
|
key: "AWS_ACCESS_KEY_ID",
|
|
@@ -4487,7 +5018,7 @@ async function configCommand() {
|
|
|
4487
5018
|
logger.header("OneX CLI Configuration");
|
|
4488
5019
|
let existing = {};
|
|
4489
5020
|
try {
|
|
4490
|
-
const content = await
|
|
5021
|
+
const content = await fs8.readFile(CONFIG_FILE, "utf-8");
|
|
4491
5022
|
existing = parseEnvFile(content);
|
|
4492
5023
|
logger.info(`Existing config found at: ${CONFIG_FILE}`);
|
|
4493
5024
|
logger.newLine();
|
|
@@ -4523,8 +5054,8 @@ async function configCommand() {
|
|
|
4523
5054
|
for (const key of Object.keys(merged)) {
|
|
4524
5055
|
if (!merged[key]) delete merged[key];
|
|
4525
5056
|
}
|
|
4526
|
-
await
|
|
4527
|
-
await
|
|
5057
|
+
await fs8.ensureDir(CONFIG_DIR);
|
|
5058
|
+
await fs8.writeFile(CONFIG_FILE, serializeEnv(merged));
|
|
4528
5059
|
logger.newLine();
|
|
4529
5060
|
logger.success(`Config saved to: ${CONFIG_FILE}`);
|
|
4530
5061
|
logger.newLine();
|
|
@@ -4543,8 +5074,8 @@ async function configCommand() {
|
|
|
4543
5074
|
|
|
4544
5075
|
// src/commands/login.ts
|
|
4545
5076
|
init_logger();
|
|
4546
|
-
async function loginCommand(options
|
|
4547
|
-
const env = options.env
|
|
5077
|
+
async function loginCommand(options) {
|
|
5078
|
+
const env = options.env;
|
|
4548
5079
|
const apiUrl = getApiUrl(env);
|
|
4549
5080
|
logger.header("OneX Theme Developer Login");
|
|
4550
5081
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
@@ -4633,8 +5164,8 @@ async function loginCommand(options = {}) {
|
|
|
4633
5164
|
|
|
4634
5165
|
// src/commands/logout.ts
|
|
4635
5166
|
init_logger();
|
|
4636
|
-
async function logoutCommand(options
|
|
4637
|
-
const env = options.env
|
|
5167
|
+
async function logoutCommand(options) {
|
|
5168
|
+
const env = options.env;
|
|
4638
5169
|
const tokens = loadAuthTokens(env);
|
|
4639
5170
|
if (!tokens) {
|
|
4640
5171
|
logger.info(`Not logged in to ${env} environment.`);
|
|
@@ -4646,8 +5177,8 @@ async function logoutCommand(options = {}) {
|
|
|
4646
5177
|
|
|
4647
5178
|
// src/commands/whoami.ts
|
|
4648
5179
|
init_logger();
|
|
4649
|
-
async function whoamiCommand(options
|
|
4650
|
-
const env = options.env
|
|
5180
|
+
async function whoamiCommand(options) {
|
|
5181
|
+
const env = options.env;
|
|
4651
5182
|
const tokens = loadAuthTokens(env);
|
|
4652
5183
|
if (!tokens) {
|
|
4653
5184
|
logger.error(
|
|
@@ -4669,96 +5200,362 @@ async function whoamiCommand(options = {}) {
|
|
|
4669
5200
|
|
|
4670
5201
|
// src/commands/publish.ts
|
|
4671
5202
|
init_logger();
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
5203
|
+
init_scan_theme_assets();
|
|
5204
|
+
|
|
5205
|
+
// src/utils/fetch-prior-schemas.ts
|
|
5206
|
+
async function fetchPriorGateManifests(themeId, env) {
|
|
5207
|
+
const apiUrl = getApiUrl(env);
|
|
5208
|
+
const url = `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/gate-manifests/latest`;
|
|
5209
|
+
let res;
|
|
5210
|
+
try {
|
|
5211
|
+
res = await authenticatedFetch(url, { method: "GET" }, env);
|
|
5212
|
+
} catch (err) {
|
|
5213
|
+
return {
|
|
5214
|
+
result: null,
|
|
5215
|
+
reason: `network error: ${err instanceof Error ? err.message : "unknown"}`
|
|
5216
|
+
};
|
|
5217
|
+
}
|
|
5218
|
+
if (res.status === 404) {
|
|
5219
|
+
return { result: null, reason: "no-prior" };
|
|
5220
|
+
}
|
|
5221
|
+
if (!res.ok) {
|
|
5222
|
+
return {
|
|
5223
|
+
result: null,
|
|
5224
|
+
reason: `server returned ${res.status} ${res.statusText}`
|
|
5225
|
+
};
|
|
5226
|
+
}
|
|
5227
|
+
let data;
|
|
5228
|
+
try {
|
|
5229
|
+
data = await res.json();
|
|
5230
|
+
} catch {
|
|
5231
|
+
return { result: null, reason: "non-JSON response from server" };
|
|
5232
|
+
}
|
|
5233
|
+
const body = data.statusCode ? data.body : data;
|
|
5234
|
+
if (!body || typeof body.version !== "string" || !body.schemas || !body.assets) {
|
|
5235
|
+
return { result: null, reason: "malformed response (missing fields)" };
|
|
5236
|
+
}
|
|
5237
|
+
return {
|
|
5238
|
+
result: {
|
|
5239
|
+
version: body.version,
|
|
5240
|
+
schemas: body.schemas,
|
|
5241
|
+
assets: body.assets
|
|
5242
|
+
},
|
|
5243
|
+
reason: null
|
|
5244
|
+
};
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
// src/utils/schema-diff.ts
|
|
5248
|
+
var SEVERITY = {
|
|
5249
|
+
safe: 0,
|
|
5250
|
+
"safe-rename": 1,
|
|
5251
|
+
"defaults-only": 2,
|
|
5252
|
+
additive: 3,
|
|
5253
|
+
breaking: 4,
|
|
5254
|
+
"breaking-asset": 5,
|
|
5255
|
+
"breaking-severe": 6
|
|
4692
5256
|
};
|
|
4693
|
-
var
|
|
4694
|
-
|
|
4695
|
-
"
|
|
4696
|
-
"
|
|
4697
|
-
"
|
|
4698
|
-
"
|
|
4699
|
-
"
|
|
4700
|
-
"
|
|
4701
|
-
|
|
4702
|
-
function
|
|
4703
|
-
|
|
4704
|
-
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
5257
|
+
var BUMP_FOR = {
|
|
5258
|
+
safe: "patch",
|
|
5259
|
+
"safe-rename": "patch",
|
|
5260
|
+
"defaults-only": "patch",
|
|
5261
|
+
additive: "minor",
|
|
5262
|
+
breaking: "major",
|
|
5263
|
+
"breaking-asset": "major",
|
|
5264
|
+
"breaking-severe": "major"
|
|
5265
|
+
};
|
|
5266
|
+
function bumpFor(kind) {
|
|
5267
|
+
return BUMP_FOR[kind];
|
|
4705
5268
|
}
|
|
4706
|
-
function
|
|
4707
|
-
|
|
4708
|
-
return MIME_MAP[ext] || "application/octet-stream";
|
|
5269
|
+
function maxSeverity(a, b) {
|
|
5270
|
+
return SEVERITY[a] >= SEVERITY[b] ? a : b;
|
|
4709
5271
|
}
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
5272
|
+
function classify(changes) {
|
|
5273
|
+
let highest = "safe";
|
|
5274
|
+
for (const c of changes) {
|
|
5275
|
+
highest = maxSeverity(highest, c.kind);
|
|
5276
|
+
}
|
|
5277
|
+
return { bump: bumpFor(highest), highest, changes };
|
|
4713
5278
|
}
|
|
4714
|
-
function
|
|
4715
|
-
const
|
|
4716
|
-
const
|
|
4717
|
-
const
|
|
4718
|
-
const
|
|
4719
|
-
|
|
4720
|
-
|
|
5279
|
+
function diffManifests(prior, current) {
|
|
5280
|
+
const changes = [];
|
|
5281
|
+
const priorSections = prior.schemas.sections;
|
|
5282
|
+
const currentSections = current.schemas.sections;
|
|
5283
|
+
const sectionTypes = /* @__PURE__ */ new Set([
|
|
5284
|
+
...Object.keys(priorSections),
|
|
5285
|
+
...Object.keys(currentSections)
|
|
5286
|
+
]);
|
|
5287
|
+
for (const type of [...sectionTypes].sort()) {
|
|
5288
|
+
const p = priorSections[type];
|
|
5289
|
+
const c = currentSections[type];
|
|
5290
|
+
if (p && !c) {
|
|
5291
|
+
changes.push({
|
|
5292
|
+
kind: "breaking-severe",
|
|
5293
|
+
path: `sections.${type}`,
|
|
5294
|
+
detail: `Section type "${type}" removed. Pages using this section will render empty.`
|
|
5295
|
+
});
|
|
5296
|
+
continue;
|
|
5297
|
+
}
|
|
5298
|
+
if (!p && c) {
|
|
5299
|
+
changes.push({
|
|
5300
|
+
kind: "additive",
|
|
5301
|
+
path: `sections.${type}`,
|
|
5302
|
+
detail: `Section type "${type}" added.`
|
|
5303
|
+
});
|
|
5304
|
+
continue;
|
|
5305
|
+
}
|
|
5306
|
+
if (p && c) diffSection(p, c, changes);
|
|
5307
|
+
}
|
|
5308
|
+
diffAssets(prior.assets, current.assets, changes);
|
|
5309
|
+
return changes;
|
|
4721
5310
|
}
|
|
4722
|
-
|
|
4723
|
-
const
|
|
4724
|
-
if (
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
});
|
|
4730
|
-
const results = [];
|
|
4731
|
-
for (const rel of files) {
|
|
4732
|
-
const absPath = path9.join(assetsDir, rel);
|
|
4733
|
-
const stat = await fs.stat(absPath);
|
|
4734
|
-
if (!stat.isFile()) continue;
|
|
4735
|
-
const originalPath = rel.split(path9.sep).join("/");
|
|
4736
|
-
const hash = await sha256Prefix(absPath, HASH_LEN);
|
|
4737
|
-
const hashedPath = insertHashIntoName(originalPath, hash);
|
|
4738
|
-
const contentType = mimeFor(rel);
|
|
4739
|
-
results.push({
|
|
4740
|
-
originalPath,
|
|
4741
|
-
hashedPath,
|
|
4742
|
-
hash,
|
|
4743
|
-
size: stat.size,
|
|
4744
|
-
contentType,
|
|
4745
|
-
absPath
|
|
5311
|
+
function diffSection(prior, current, out) {
|
|
5312
|
+
const type = current.type;
|
|
5313
|
+
if (JSON.stringify(prior.dataRequirements) !== JSON.stringify(current.dataRequirements)) {
|
|
5314
|
+
out.push({
|
|
5315
|
+
kind: "breaking",
|
|
5316
|
+
path: `sections.${type}.dataRequirements`,
|
|
5317
|
+
detail: "dataRequirements changed."
|
|
4746
5318
|
});
|
|
4747
5319
|
}
|
|
4748
|
-
|
|
4749
|
-
|
|
5320
|
+
diffFieldList(
|
|
5321
|
+
prior.settings,
|
|
5322
|
+
current.settings,
|
|
5323
|
+
`sections.${type}.settings`,
|
|
5324
|
+
out
|
|
5325
|
+
);
|
|
5326
|
+
diffDefaults(
|
|
5327
|
+
prior.defaults,
|
|
5328
|
+
current.defaults,
|
|
5329
|
+
`sections.${type}.defaults`,
|
|
5330
|
+
out
|
|
5331
|
+
);
|
|
5332
|
+
diffBlocks(prior.blocks, current.blocks, `sections.${type}.blocks`, out);
|
|
4750
5333
|
}
|
|
4751
|
-
function
|
|
4752
|
-
const
|
|
4753
|
-
|
|
4754
|
-
|
|
5334
|
+
function diffBlocks(prior, current, pathPrefix, out) {
|
|
5335
|
+
const priorByType = new Map(prior.map((b) => [b.type, b]));
|
|
5336
|
+
const currentByType = new Map(current.map((b) => [b.type, b]));
|
|
5337
|
+
for (const type of /* @__PURE__ */ new Set([
|
|
5338
|
+
...priorByType.keys(),
|
|
5339
|
+
...currentByType.keys()
|
|
5340
|
+
])) {
|
|
5341
|
+
const p = priorByType.get(type);
|
|
5342
|
+
const c = currentByType.get(type);
|
|
5343
|
+
if (p && !c) {
|
|
5344
|
+
out.push({
|
|
5345
|
+
kind: "breaking",
|
|
5346
|
+
path: `${pathPrefix}.${type}`,
|
|
5347
|
+
detail: `Block type "${type}" removed.`
|
|
5348
|
+
});
|
|
5349
|
+
continue;
|
|
5350
|
+
}
|
|
5351
|
+
if (!p && c) {
|
|
5352
|
+
out.push({
|
|
5353
|
+
kind: "additive",
|
|
5354
|
+
path: `${pathPrefix}.${type}`,
|
|
5355
|
+
detail: `Block type "${type}" added.`
|
|
5356
|
+
});
|
|
5357
|
+
continue;
|
|
5358
|
+
}
|
|
5359
|
+
if (p && c) {
|
|
5360
|
+
diffFieldList(
|
|
5361
|
+
p.settings,
|
|
5362
|
+
c.settings,
|
|
5363
|
+
`${pathPrefix}.${type}.settings`,
|
|
5364
|
+
out
|
|
5365
|
+
);
|
|
5366
|
+
diffDefaults(
|
|
5367
|
+
p.defaults,
|
|
5368
|
+
c.defaults,
|
|
5369
|
+
`${pathPrefix}.${type}.defaults`,
|
|
5370
|
+
out
|
|
5371
|
+
);
|
|
5372
|
+
}
|
|
4755
5373
|
}
|
|
4756
|
-
|
|
5374
|
+
}
|
|
5375
|
+
function diffFieldList(prior, current, pathPrefix, out) {
|
|
5376
|
+
const priorById = new Map(prior.map((f) => [f.id, f]));
|
|
5377
|
+
const currentById = new Map(current.map((f) => [f.id, f]));
|
|
5378
|
+
const aliasToCurrent = /* @__PURE__ */ new Map();
|
|
5379
|
+
for (const f of current) {
|
|
5380
|
+
if (f.aliases) {
|
|
5381
|
+
for (const alias of f.aliases) {
|
|
5382
|
+
aliasToCurrent.set(alias, f);
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
for (const [id, p] of priorById) {
|
|
5387
|
+
const c = currentById.get(id);
|
|
5388
|
+
if (c) {
|
|
5389
|
+
diffFieldPair(p, c, `${pathPrefix}.${id}`, out);
|
|
5390
|
+
continue;
|
|
5391
|
+
}
|
|
5392
|
+
const renamed = aliasToCurrent.get(id);
|
|
5393
|
+
if (renamed) {
|
|
5394
|
+
if (renamed.type === p.type) {
|
|
5395
|
+
out.push({
|
|
5396
|
+
kind: "safe-rename",
|
|
5397
|
+
path: `${pathPrefix}.${id}`,
|
|
5398
|
+
detail: `Field "${id}" renamed to "${renamed.id}" (alias preserved).`
|
|
5399
|
+
});
|
|
5400
|
+
} else {
|
|
5401
|
+
out.push({
|
|
5402
|
+
kind: "breaking",
|
|
5403
|
+
path: `${pathPrefix}.${id}`,
|
|
5404
|
+
detail: `Field "${id}" renamed to "${renamed.id}" but type changed (${p.type} \u2192 ${renamed.type}).`
|
|
5405
|
+
});
|
|
5406
|
+
}
|
|
5407
|
+
} else {
|
|
5408
|
+
out.push({
|
|
5409
|
+
kind: "breaking",
|
|
5410
|
+
path: `${pathPrefix}.${id}`,
|
|
5411
|
+
detail: `Field "${id}" removed. Consider adding aliases: ["${id}"] to the replacement field if this was a rename.`
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
for (const [id, c] of currentById) {
|
|
5416
|
+
if (priorById.has(id)) continue;
|
|
5417
|
+
const coveredByAlias = c.aliases?.some((a) => priorById.has(a)) ?? false;
|
|
5418
|
+
if (coveredByAlias) continue;
|
|
5419
|
+
if (c.required && c.default === void 0) {
|
|
5420
|
+
out.push({
|
|
5421
|
+
kind: "breaking",
|
|
5422
|
+
path: `${pathPrefix}.${id}`,
|
|
5423
|
+
detail: `Required field "${id}" added with no default. Existing instances cannot satisfy it.`
|
|
5424
|
+
});
|
|
5425
|
+
} else {
|
|
5426
|
+
out.push({
|
|
5427
|
+
kind: "additive",
|
|
5428
|
+
path: `${pathPrefix}.${id}`,
|
|
5429
|
+
detail: `Field "${id}" added.`
|
|
5430
|
+
});
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
function diffFieldPair(p, c, path25, out) {
|
|
5435
|
+
if (p.type !== c.type) {
|
|
5436
|
+
out.push({
|
|
5437
|
+
kind: "breaking",
|
|
5438
|
+
path: path25,
|
|
5439
|
+
detail: `Type changed (${p.type} \u2192 ${c.type}). Saved values may misrender.`
|
|
5440
|
+
});
|
|
5441
|
+
return;
|
|
5442
|
+
}
|
|
5443
|
+
if (p.required !== true && c.required === true) {
|
|
5444
|
+
out.push({
|
|
5445
|
+
kind: "breaking",
|
|
5446
|
+
path: path25,
|
|
5447
|
+
detail: "Field became required. Existing empty instances now invalid."
|
|
5448
|
+
});
|
|
5449
|
+
}
|
|
5450
|
+
if (typeof p.maxLength === "number" || typeof c.maxLength === "number") {
|
|
5451
|
+
if ((c.maxLength ?? Infinity) < (p.maxLength ?? Infinity)) {
|
|
5452
|
+
out.push({
|
|
5453
|
+
kind: "breaking",
|
|
5454
|
+
path: path25,
|
|
5455
|
+
detail: `maxLength tightened (${p.maxLength ?? "\u221E"} \u2192 ${c.maxLength}).`
|
|
5456
|
+
});
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
if (typeof p.min === "number" || typeof c.min === "number") {
|
|
5460
|
+
if ((c.min ?? -Infinity) > (p.min ?? -Infinity)) {
|
|
5461
|
+
out.push({
|
|
5462
|
+
kind: "breaking",
|
|
5463
|
+
path: path25,
|
|
5464
|
+
detail: `min raised (${p.min ?? "-\u221E"} \u2192 ${c.min}).`
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
5468
|
+
if (typeof p.max === "number" || typeof c.max === "number") {
|
|
5469
|
+
if ((c.max ?? Infinity) < (p.max ?? Infinity)) {
|
|
5470
|
+
out.push({
|
|
5471
|
+
kind: "breaking",
|
|
5472
|
+
path: path25,
|
|
5473
|
+
detail: `max lowered (${p.max ?? "\u221E"} \u2192 ${c.max}).`
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
if (p.options || c.options) {
|
|
5478
|
+
const priorOpts = new Set(p.options ?? []);
|
|
5479
|
+
const currentOpts = new Set(c.options ?? []);
|
|
5480
|
+
const removed = [...priorOpts].filter((o) => !currentOpts.has(o));
|
|
5481
|
+
const added = [...currentOpts].filter((o) => !priorOpts.has(o));
|
|
5482
|
+
if (removed.length > 0) {
|
|
5483
|
+
out.push({
|
|
5484
|
+
kind: "breaking",
|
|
5485
|
+
path: path25,
|
|
5486
|
+
detail: `Option(s) removed: ${removed.join(", ")}. Existing saved values may be orphaned.`
|
|
5487
|
+
});
|
|
5488
|
+
}
|
|
5489
|
+
if (added.length > 0) {
|
|
5490
|
+
out.push({
|
|
5491
|
+
kind: "additive",
|
|
5492
|
+
path: path25,
|
|
5493
|
+
detail: `Option(s) added: ${added.join(", ")}.`
|
|
5494
|
+
});
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
if (!deepEqual(p.default, c.default)) {
|
|
5498
|
+
out.push({
|
|
5499
|
+
kind: "defaults-only",
|
|
5500
|
+
path: path25,
|
|
5501
|
+
detail: `Default changed: ${JSON.stringify(p.default)} \u2192 ${JSON.stringify(c.default)}.`
|
|
5502
|
+
});
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
function diffDefaults(prior, current, pathPrefix, out) {
|
|
5506
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(prior), ...Object.keys(current)]);
|
|
5507
|
+
for (const key of [...keys].sort()) {
|
|
5508
|
+
if (!(key in prior) || !(key in current)) continue;
|
|
5509
|
+
if (!deepEqual(prior[key], current[key])) {
|
|
5510
|
+
out.push({
|
|
5511
|
+
kind: "defaults-only",
|
|
5512
|
+
path: `${pathPrefix}.${key}`,
|
|
5513
|
+
detail: `Default value changed.`
|
|
5514
|
+
});
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
}
|
|
5518
|
+
function diffAssets(prior, current, out) {
|
|
5519
|
+
const currentPaths = new Set(current.assets.map((a) => a.path));
|
|
5520
|
+
for (const a of prior.assets) {
|
|
5521
|
+
if (!currentPaths.has(a.path)) {
|
|
5522
|
+
out.push({
|
|
5523
|
+
kind: "breaking-asset",
|
|
5524
|
+
path: `theme-assets/${a.path}`,
|
|
5525
|
+
detail: `Asset "${a.path}" was present in the prior version and is now missing. Code that references it hardcoded will break.`
|
|
5526
|
+
});
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
function deepEqual(a, b) {
|
|
5531
|
+
if (a === b) return true;
|
|
5532
|
+
if (a === null || b === null) return false;
|
|
5533
|
+
if (typeof a !== typeof b) return false;
|
|
5534
|
+
if (typeof a !== "object") return false;
|
|
5535
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
5536
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
5537
|
+
if (a.length !== b.length) return false;
|
|
5538
|
+
for (let i = 0; i < a.length; i++) {
|
|
5539
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
5540
|
+
}
|
|
5541
|
+
return true;
|
|
5542
|
+
}
|
|
5543
|
+
const ak = Object.keys(a);
|
|
5544
|
+
const bk = Object.keys(b);
|
|
5545
|
+
if (ak.length !== bk.length) return false;
|
|
5546
|
+
for (const k of ak) {
|
|
5547
|
+
if (!deepEqual(
|
|
5548
|
+
a[k],
|
|
5549
|
+
b[k]
|
|
5550
|
+
))
|
|
5551
|
+
return false;
|
|
5552
|
+
}
|
|
5553
|
+
return true;
|
|
4757
5554
|
}
|
|
4758
5555
|
|
|
4759
5556
|
// src/commands/publish.ts
|
|
4760
5557
|
async function publishCommand(options) {
|
|
4761
|
-
const env = options.env
|
|
5558
|
+
const env = options.env;
|
|
4762
5559
|
logger.header("OneX Theme Publish");
|
|
4763
5560
|
logger.info(`Environment: ${env} (${getApiUrl(env)})`);
|
|
4764
5561
|
logger.newLine();
|
|
@@ -4772,13 +5569,13 @@ async function publishCommand(options) {
|
|
|
4772
5569
|
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
4773
5570
|
let themePath;
|
|
4774
5571
|
if (options.theme) {
|
|
4775
|
-
themePath =
|
|
5572
|
+
themePath = path13.resolve(options.theme);
|
|
4776
5573
|
} else {
|
|
4777
5574
|
const isThemeDir2 = [
|
|
4778
5575
|
"theme.config.ts",
|
|
4779
5576
|
"bundle-entry.ts",
|
|
4780
5577
|
"manifest.ts"
|
|
4781
|
-
].some((f) =>
|
|
5578
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
4782
5579
|
if (isThemeDir2) {
|
|
4783
5580
|
themePath = process.cwd();
|
|
4784
5581
|
} else {
|
|
@@ -4788,13 +5585,13 @@ async function publishCommand(options) {
|
|
|
4788
5585
|
process.exit(1);
|
|
4789
5586
|
}
|
|
4790
5587
|
}
|
|
4791
|
-
const pkgPath =
|
|
4792
|
-
if (!
|
|
5588
|
+
const pkgPath = path13.join(themePath, "package.json");
|
|
5589
|
+
if (!fs8.existsSync(pkgPath)) {
|
|
4793
5590
|
logger.error("No package.json found in theme directory");
|
|
4794
5591
|
process.exit(1);
|
|
4795
5592
|
}
|
|
4796
|
-
const pkg =
|
|
4797
|
-
const themeId = pkg.name?.replace("@onex-themes/", "") ||
|
|
5593
|
+
const pkg = fs8.readJsonSync(pkgPath);
|
|
5594
|
+
const themeId = pkg.name?.replace("@onex-themes/", "") || path13.basename(themePath);
|
|
4798
5595
|
if (options.bump) {
|
|
4799
5596
|
const currentVersion = pkg.version || "1.0.0";
|
|
4800
5597
|
const newVersion = semver.inc(currentVersion, options.bump);
|
|
@@ -4803,7 +5600,7 @@ async function publishCommand(options) {
|
|
|
4803
5600
|
process.exit(1);
|
|
4804
5601
|
}
|
|
4805
5602
|
pkg.version = newVersion;
|
|
4806
|
-
|
|
5603
|
+
fs8.writeJsonSync(pkgPath, pkg, { spaces: 2 });
|
|
4807
5604
|
logger.info(`Bumped version: ${currentVersion} -> ${newVersion}`);
|
|
4808
5605
|
}
|
|
4809
5606
|
const version2 = pkg.version || "1.0.0";
|
|
@@ -4818,57 +5615,62 @@ async function publishCommand(options) {
|
|
|
4818
5615
|
logger.info(`Version: ${version2}`);
|
|
4819
5616
|
logger.newLine();
|
|
4820
5617
|
const apiUrl = getApiUrl(env);
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
5618
|
+
if (!options.dryRun) {
|
|
5619
|
+
logger.startSpinner("Registering theme...");
|
|
5620
|
+
try {
|
|
5621
|
+
const regResponse = await authenticatedFetch(
|
|
5622
|
+
`${apiUrl}/website-api/themes/register`,
|
|
5623
|
+
{
|
|
5624
|
+
method: "POST",
|
|
5625
|
+
body: JSON.stringify({
|
|
5626
|
+
themeId,
|
|
5627
|
+
name: pkg.displayName || themeId,
|
|
5628
|
+
description: pkg.description || "",
|
|
5629
|
+
email: tokens.user.email,
|
|
5630
|
+
author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name || tokens.user.name || "",
|
|
5631
|
+
category: pkg.onex?.category || "MINIMAL",
|
|
5632
|
+
tags: pkg.keywords || [],
|
|
5633
|
+
thumbnail_url: pkg.onex?.thumbnail || ""
|
|
5634
|
+
})
|
|
5635
|
+
},
|
|
5636
|
+
env
|
|
5637
|
+
);
|
|
5638
|
+
const regData = await regResponse.json();
|
|
5639
|
+
const regBody = regData.statusCode ? regData.body : regData;
|
|
5640
|
+
if (!regResponse.ok) {
|
|
5641
|
+
const errMsg = regBody.error || regBody.message || "Registration failed";
|
|
5642
|
+
if (!errMsg.includes("already registered")) {
|
|
5643
|
+
logger.stopSpinner(false, "Registration failed");
|
|
5644
|
+
logger.error(errMsg);
|
|
5645
|
+
process.exit(1);
|
|
5646
|
+
}
|
|
4848
5647
|
}
|
|
5648
|
+
logger.stopSpinner(true, regBody.message || "Theme registered");
|
|
5649
|
+
} catch (error) {
|
|
5650
|
+
logger.stopSpinner(false, "Registration failed");
|
|
5651
|
+
logger.error(
|
|
5652
|
+
error instanceof Error ? error.message : "Connection failed"
|
|
5653
|
+
);
|
|
5654
|
+
process.exit(1);
|
|
4849
5655
|
}
|
|
4850
|
-
logger.stopSpinner(true, regBody.message || "Theme registered");
|
|
4851
|
-
} catch (error) {
|
|
4852
|
-
logger.stopSpinner(false, "Registration failed");
|
|
4853
|
-
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4854
|
-
process.exit(1);
|
|
4855
5656
|
}
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
5657
|
+
if (!options.dryRun) {
|
|
5658
|
+
logger.startSpinner("Checking version availability...");
|
|
5659
|
+
try {
|
|
5660
|
+
const checkResponse = await authenticatedFetch(
|
|
5661
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/exists`,
|
|
5662
|
+
{ method: "GET" },
|
|
5663
|
+
env
|
|
5664
|
+
);
|
|
5665
|
+
const checkData = await checkResponse.json();
|
|
5666
|
+
const checkBody = checkData.statusCode ? checkData.body : checkData;
|
|
5667
|
+
if (checkBody.exists) {
|
|
5668
|
+
logger.stopSpinner(false, "Version already published");
|
|
5669
|
+
const patchVer = semver.inc(version2, "patch") || "?";
|
|
5670
|
+
const minorVer = semver.inc(version2, "minor") || "?";
|
|
5671
|
+
const majorVer = semver.inc(version2, "major") || "?";
|
|
5672
|
+
logger.error(
|
|
5673
|
+
`
|
|
4872
5674
|
Version ${version2} of "${themeId}" is already published and cannot be overwritten.
|
|
4873
5675
|
|
|
4874
5676
|
To publish a new version:
|
|
@@ -4879,12 +5681,16 @@ Or use the --bump flag:
|
|
|
4879
5681
|
onexthm publish --bump patch (${version2} -> ${patchVer})
|
|
4880
5682
|
onexthm publish --bump minor (${version2} -> ${minorVer})
|
|
4881
5683
|
onexthm publish --bump major (${version2} -> ${majorVer})`
|
|
5684
|
+
);
|
|
5685
|
+
process.exit(1);
|
|
5686
|
+
}
|
|
5687
|
+
logger.stopSpinner(true, `Version ${version2} is available`);
|
|
5688
|
+
} catch (error) {
|
|
5689
|
+
logger.stopSpinner(
|
|
5690
|
+
true,
|
|
5691
|
+
"Version check skipped (endpoint not available)"
|
|
4882
5692
|
);
|
|
4883
|
-
process.exit(1);
|
|
4884
5693
|
}
|
|
4885
|
-
logger.stopSpinner(true, `Version ${version2} is available`);
|
|
4886
|
-
} catch (error) {
|
|
4887
|
-
logger.stopSpinner(true, "Version check skipped (endpoint not available)");
|
|
4888
5694
|
}
|
|
4889
5695
|
logger.startSpinner("Building theme...");
|
|
4890
5696
|
try {
|
|
@@ -4901,7 +5707,19 @@ Or use the --bump flag:
|
|
|
4901
5707
|
logger.error(error instanceof Error ? error.message : "Build error");
|
|
4902
5708
|
process.exit(1);
|
|
4903
5709
|
}
|
|
4904
|
-
const distDir =
|
|
5710
|
+
const distDir = path13.join(themePath, "dist");
|
|
5711
|
+
const classification = await runSchemaDiffGate(
|
|
5712
|
+
themeId,
|
|
5713
|
+
distDir,
|
|
5714
|
+
env,
|
|
5715
|
+
options
|
|
5716
|
+
);
|
|
5717
|
+
if (options.dryRun) {
|
|
5718
|
+
const exitCode = classification?.highest === "breaking" || classification?.highest === "breaking-severe" || classification?.highest === "breaking-asset" ? 2 : 0;
|
|
5719
|
+
logger.newLine();
|
|
5720
|
+
logger.info(`Dry run complete (exit ${exitCode}). No files uploaded.`);
|
|
5721
|
+
process.exit(exitCode);
|
|
5722
|
+
}
|
|
4905
5723
|
let assetEntries = [];
|
|
4906
5724
|
try {
|
|
4907
5725
|
assetEntries = await scanThemeAssets(distDir);
|
|
@@ -4938,8 +5756,8 @@ Or use the --bump flag:
|
|
|
4938
5756
|
for (const [originalPath, url] of Object.entries(videoUrls)) {
|
|
4939
5757
|
assetMap[originalPath] = url;
|
|
4940
5758
|
}
|
|
4941
|
-
const assetMapPath =
|
|
4942
|
-
await
|
|
5759
|
+
const assetMapPath = path13.join(distDir, "asset-map.json");
|
|
5760
|
+
await fs8.writeFile(assetMapPath, JSON.stringify(assetMap, null, 2));
|
|
4943
5761
|
} catch (error) {
|
|
4944
5762
|
logger.error(
|
|
4945
5763
|
`Failed to write asset-map.json: ${error instanceof Error ? error.message : "unknown"}`
|
|
@@ -5014,7 +5832,7 @@ Or use the --bump flag:
|
|
|
5014
5832
|
continue;
|
|
5015
5833
|
}
|
|
5016
5834
|
try {
|
|
5017
|
-
const buf = await
|
|
5835
|
+
const buf = await fs8.promises.readFile(entry.absPath);
|
|
5018
5836
|
const res = await fetch(item.upload_url, {
|
|
5019
5837
|
method: "PUT",
|
|
5020
5838
|
headers: {
|
|
@@ -5056,12 +5874,12 @@ Or use the --bump flag:
|
|
|
5056
5874
|
}
|
|
5057
5875
|
logger.startSpinner("Uploading bundle...");
|
|
5058
5876
|
try {
|
|
5059
|
-
if (!
|
|
5877
|
+
if (!fs8.existsSync(distDir)) {
|
|
5060
5878
|
logger.stopSpinner(false, "No dist/ directory");
|
|
5061
5879
|
logger.error("Build the theme first: onexthm build");
|
|
5062
5880
|
process.exit(1);
|
|
5063
5881
|
}
|
|
5064
|
-
const bundleZipPath =
|
|
5882
|
+
const bundleZipPath = path13.join(themePath, "dist", "bundle.zip");
|
|
5065
5883
|
await createZip(distDir, bundleZipPath, [
|
|
5066
5884
|
"bundle.zip",
|
|
5067
5885
|
"source.zip",
|
|
@@ -5070,7 +5888,7 @@ Or use the --bump flag:
|
|
|
5070
5888
|
"theme-assets",
|
|
5071
5889
|
"theme-assets/**"
|
|
5072
5890
|
]);
|
|
5073
|
-
const bundleBuffer =
|
|
5891
|
+
const bundleBuffer = fs8.readFileSync(bundleZipPath);
|
|
5074
5892
|
const bundleRes = await fetch(bundleUploadUrl, {
|
|
5075
5893
|
method: "PUT",
|
|
5076
5894
|
headers: { "Content-Type": "application/zip" },
|
|
@@ -5088,7 +5906,7 @@ Or use the --bump flag:
|
|
|
5088
5906
|
}
|
|
5089
5907
|
logger.startSpinner("Uploading source...");
|
|
5090
5908
|
try {
|
|
5091
|
-
const sourceZipPath =
|
|
5909
|
+
const sourceZipPath = path13.join(themePath, "dist", "source.zip");
|
|
5092
5910
|
await createZip(themePath, sourceZipPath, [
|
|
5093
5911
|
"node_modules",
|
|
5094
5912
|
"dist",
|
|
@@ -5096,7 +5914,7 @@ Or use the --bump flag:
|
|
|
5096
5914
|
".env",
|
|
5097
5915
|
".env.local"
|
|
5098
5916
|
]);
|
|
5099
|
-
const sourceBuffer =
|
|
5917
|
+
const sourceBuffer = fs8.readFileSync(sourceZipPath);
|
|
5100
5918
|
const sourceRes = await fetch(sourceUploadUrl, {
|
|
5101
5919
|
method: "PUT",
|
|
5102
5920
|
headers: { "Content-Type": "application/zip" },
|
|
@@ -5174,9 +5992,9 @@ async function uploadThumbnail(apiUrl, themeId, themePath, distDir, env = "dev")
|
|
|
5174
5992
|
let imageBase64 = null;
|
|
5175
5993
|
let mimeType = "image/png";
|
|
5176
5994
|
for (const { file, mime } of THUMBNAIL_CANDIDATES) {
|
|
5177
|
-
const candidate =
|
|
5178
|
-
if (
|
|
5179
|
-
const buf =
|
|
5995
|
+
const candidate = path13.join(themePath, file);
|
|
5996
|
+
if (fs8.existsSync(candidate)) {
|
|
5997
|
+
const buf = fs8.readFileSync(candidate);
|
|
5180
5998
|
imageBase64 = `data:${mime};base64,${buf.toString("base64")}`;
|
|
5181
5999
|
mimeType = mime;
|
|
5182
6000
|
logger.info(`Using local thumbnail: ${file}`);
|
|
@@ -5276,7 +6094,7 @@ async function screenshotHomePage(themePath, distDir) {
|
|
|
5276
6094
|
const { compilePreviewRuntime: compilePreviewRuntime2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
5277
6095
|
const { createDevServer: createDevServer2 } = await Promise.resolve().then(() => (init_dev_server(), dev_server_exports));
|
|
5278
6096
|
const previewRuntimePath = await compilePreviewRuntime2(themePath);
|
|
5279
|
-
const themeName =
|
|
6097
|
+
const themeName = path13.basename(themePath);
|
|
5280
6098
|
const port = await findFreePort(4500);
|
|
5281
6099
|
const server = createDevServer2({
|
|
5282
6100
|
port,
|
|
@@ -5333,7 +6151,7 @@ async function findFreePort(start) {
|
|
|
5333
6151
|
});
|
|
5334
6152
|
}
|
|
5335
6153
|
async function uploadVideoMultipart(apiUrl, themeId, video, env = "dev") {
|
|
5336
|
-
const fileName =
|
|
6154
|
+
const fileName = path13.basename(video.originalPath);
|
|
5337
6155
|
const videoInitUrl = `${apiUrl}/media/videos/multipart/init`;
|
|
5338
6156
|
const videoInitBody = {
|
|
5339
6157
|
file_name: fileName,
|
|
@@ -5369,7 +6187,7 @@ async function uploadVideoMultipart(apiUrl, themeId, video, env = "dev") {
|
|
|
5369
6187
|
);
|
|
5370
6188
|
}
|
|
5371
6189
|
const { upload_id, file_key, chunk_size, chunk_urls } = initBody;
|
|
5372
|
-
const fileBuffer = await
|
|
6190
|
+
const fileBuffer = await fs8.promises.readFile(video.absPath);
|
|
5373
6191
|
const CHUNK_CONCURRENCY = 4;
|
|
5374
6192
|
const queue = [...chunk_urls];
|
|
5375
6193
|
const parts = [];
|
|
@@ -5462,6 +6280,91 @@ async function createZip(sourceDir, outputPath, exclude) {
|
|
|
5462
6280
|
archive.finalize();
|
|
5463
6281
|
});
|
|
5464
6282
|
}
|
|
6283
|
+
async function runSchemaDiffGate(themeId, distDir, env, options) {
|
|
6284
|
+
logger.startSpinner("Fetching prior version for diff...");
|
|
6285
|
+
const { result: prior, reason } = await fetchPriorGateManifests(themeId, env);
|
|
6286
|
+
if (!prior) {
|
|
6287
|
+
if (reason === "no-prior") {
|
|
6288
|
+
logger.stopSpinner(true, "First publish \u2014 no prior version to diff");
|
|
6289
|
+
} else {
|
|
6290
|
+
logger.stopSpinner(true, `Gate skipped (${reason})`);
|
|
6291
|
+
}
|
|
6292
|
+
return null;
|
|
6293
|
+
}
|
|
6294
|
+
logger.stopSpinner(true, `Fetched prior version ${prior.version}`);
|
|
6295
|
+
let currentSchemas;
|
|
6296
|
+
let currentAssets;
|
|
6297
|
+
try {
|
|
6298
|
+
currentSchemas = JSON.parse(
|
|
6299
|
+
await fs8.readFile(path13.join(distDir, "schemas.json"), "utf-8")
|
|
6300
|
+
);
|
|
6301
|
+
} catch (err) {
|
|
6302
|
+
logger.warning(
|
|
6303
|
+
`Gate skipped: dist/schemas.json missing or unreadable (${err instanceof Error ? err.message : "unknown"})`
|
|
6304
|
+
);
|
|
6305
|
+
return null;
|
|
6306
|
+
}
|
|
6307
|
+
try {
|
|
6308
|
+
currentAssets = JSON.parse(
|
|
6309
|
+
await fs8.readFile(path13.join(distDir, "asset-manifest.json"), "utf-8")
|
|
6310
|
+
);
|
|
6311
|
+
} catch {
|
|
6312
|
+
currentAssets = { manifestVersion: 1, assets: [] };
|
|
6313
|
+
}
|
|
6314
|
+
const changes = diffManifests(
|
|
6315
|
+
{ schemas: prior.schemas, assets: prior.assets },
|
|
6316
|
+
{ schemas: currentSchemas, assets: currentAssets }
|
|
6317
|
+
);
|
|
6318
|
+
const classification = classify(changes);
|
|
6319
|
+
printGateReport(prior.version, classification);
|
|
6320
|
+
if (options.dryRun) return classification;
|
|
6321
|
+
const isBreaking = classification.highest === "breaking" || classification.highest === "breaking-severe" || classification.highest === "breaking-asset";
|
|
6322
|
+
if (isBreaking && !options.force) {
|
|
6323
|
+
logger.error(
|
|
6324
|
+
"\nPublish blocked: breaking changes detected.\n \u2022 Bump major version and ship migration notes, OR\n \u2022 Re-run with --force to override (logged in the audit trail)."
|
|
6325
|
+
);
|
|
6326
|
+
process.exit(1);
|
|
6327
|
+
}
|
|
6328
|
+
if (classification.highest === "defaults-only" && !options.confirmDefaults) {
|
|
6329
|
+
logger.error(
|
|
6330
|
+
"\nPublish blocked: default values changed.\nThese defaults will propagate to every customer site that hasn't overridden\nthe field. Re-run with --confirm-defaults to acknowledge the change, or\nrevert the default if it wasn't intentional."
|
|
6331
|
+
);
|
|
6332
|
+
process.exit(1);
|
|
6333
|
+
}
|
|
6334
|
+
return classification;
|
|
6335
|
+
}
|
|
6336
|
+
function printGateReport(priorVersion, classification) {
|
|
6337
|
+
logger.newLine();
|
|
6338
|
+
logger.info(`Schema diff vs. v${priorVersion}:`);
|
|
6339
|
+
if (classification.changes.length === 0) {
|
|
6340
|
+
logger.log(" \u2713 Safe \u2014 no schema changes detected");
|
|
6341
|
+
return;
|
|
6342
|
+
}
|
|
6343
|
+
for (const change of classification.changes) {
|
|
6344
|
+
const icon = iconFor(change.kind);
|
|
6345
|
+
logger.log(` ${icon} [${change.kind}] ${change.path} \u2014 ${change.detail}`);
|
|
6346
|
+
}
|
|
6347
|
+
logger.log(
|
|
6348
|
+
`
|
|
6349
|
+
\u2192 Classification: ${classification.highest}. Suggested bump: ${classification.bump}.`
|
|
6350
|
+
);
|
|
6351
|
+
}
|
|
6352
|
+
function iconFor(kind) {
|
|
6353
|
+
switch (kind) {
|
|
6354
|
+
case "safe":
|
|
6355
|
+
case "safe-rename":
|
|
6356
|
+
return "\u2713";
|
|
6357
|
+
case "additive":
|
|
6358
|
+
return "+";
|
|
6359
|
+
case "defaults-only":
|
|
6360
|
+
return "\u26A0";
|
|
6361
|
+
case "breaking":
|
|
6362
|
+
case "breaking-asset":
|
|
6363
|
+
return "\u2717";
|
|
6364
|
+
case "breaking-severe":
|
|
6365
|
+
return "\u2717\u2717";
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
5465
6368
|
|
|
5466
6369
|
// src/commands/mcp.ts
|
|
5467
6370
|
init_logger();
|
|
@@ -5473,24 +6376,24 @@ var AI_CONTEXT_FILES = [
|
|
|
5473
6376
|
".mcp.json"
|
|
5474
6377
|
];
|
|
5475
6378
|
function resolveTargetDir(opts) {
|
|
5476
|
-
return
|
|
6379
|
+
return path13.resolve(opts.cwd ?? process.cwd());
|
|
5477
6380
|
}
|
|
5478
6381
|
function resolveDefaultTemplateDir() {
|
|
5479
|
-
return
|
|
6382
|
+
return path13.join(getTemplatesDir(), "default");
|
|
5480
6383
|
}
|
|
5481
6384
|
function isThemeDir(dir) {
|
|
5482
|
-
return
|
|
6385
|
+
return fs8.existsSync(path13.join(dir, "theme.config.ts")) || fs8.existsSync(path13.join(dir, "theme.config.js"));
|
|
5483
6386
|
}
|
|
5484
6387
|
function inspectFiles(templateDir, targetDir) {
|
|
5485
6388
|
return AI_CONTEXT_FILES.map((name) => {
|
|
5486
|
-
const templatePath =
|
|
5487
|
-
const targetPath =
|
|
5488
|
-
const exists =
|
|
6389
|
+
const templatePath = path13.join(templateDir, name);
|
|
6390
|
+
const targetPath = path13.join(targetDir, name);
|
|
6391
|
+
const exists = fs8.existsSync(targetPath);
|
|
5489
6392
|
let identical = false;
|
|
5490
|
-
if (exists &&
|
|
6393
|
+
if (exists && fs8.existsSync(templatePath)) {
|
|
5491
6394
|
try {
|
|
5492
|
-
const a =
|
|
5493
|
-
const b =
|
|
6395
|
+
const a = fs8.readFileSync(templatePath, "utf-8");
|
|
6396
|
+
const b = fs8.readFileSync(targetPath, "utf-8");
|
|
5494
6397
|
identical = a.replace(/\r\n/g, "\n") === b.replace(/\r\n/g, "\n");
|
|
5495
6398
|
} catch {
|
|
5496
6399
|
identical = false;
|
|
@@ -5540,11 +6443,11 @@ async function mcpSetupCommand(options = {}) {
|
|
|
5540
6443
|
}
|
|
5541
6444
|
}
|
|
5542
6445
|
for (const s of missing) {
|
|
5543
|
-
if (!
|
|
6446
|
+
if (!fs8.existsSync(s.templatePath)) {
|
|
5544
6447
|
logger.warning(` ! ${s.name} not in template \u2014 skipped`);
|
|
5545
6448
|
continue;
|
|
5546
6449
|
}
|
|
5547
|
-
await
|
|
6450
|
+
await fs8.copy(s.templatePath, s.targetPath);
|
|
5548
6451
|
logger.success(` \u2713 ${s.name}`);
|
|
5549
6452
|
}
|
|
5550
6453
|
logger.log("");
|
|
@@ -5605,11 +6508,11 @@ async function mcpUpgradeCommand(options = {}) {
|
|
|
5605
6508
|
}
|
|
5606
6509
|
}
|
|
5607
6510
|
for (const s of toUpgrade) {
|
|
5608
|
-
if (!
|
|
6511
|
+
if (!fs8.existsSync(s.templatePath)) {
|
|
5609
6512
|
logger.warning(` ! ${s.name} not in template \u2014 skipped`);
|
|
5610
6513
|
continue;
|
|
5611
6514
|
}
|
|
5612
|
-
await
|
|
6515
|
+
await fs8.copy(s.templatePath, s.targetPath, { overwrite: true });
|
|
5613
6516
|
logger.success(` \u2713 ${s.name}`);
|
|
5614
6517
|
}
|
|
5615
6518
|
logger.log("");
|
|
@@ -5627,12 +6530,12 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
5627
6530
|
return;
|
|
5628
6531
|
}
|
|
5629
6532
|
logger.success("theme.config.ts present");
|
|
5630
|
-
const mcpJsonPath =
|
|
5631
|
-
if (!
|
|
6533
|
+
const mcpJsonPath = path13.join(targetDir, ".mcp.json");
|
|
6534
|
+
if (!fs8.existsSync(mcpJsonPath)) {
|
|
5632
6535
|
logger.error(".mcp.json missing \u2014 run `onexthm mcp setup`");
|
|
5633
6536
|
} else {
|
|
5634
6537
|
try {
|
|
5635
|
-
const mcpJson = JSON.parse(
|
|
6538
|
+
const mcpJson = JSON.parse(fs8.readFileSync(mcpJsonPath, "utf-8"));
|
|
5636
6539
|
const servers = mcpJson?.mcpServers ?? {};
|
|
5637
6540
|
if (servers.onexthm) {
|
|
5638
6541
|
logger.success(".mcp.json registers `onexthm`");
|
|
@@ -5665,8 +6568,8 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
5665
6568
|
logger.success(`${s.name} up to date`);
|
|
5666
6569
|
}
|
|
5667
6570
|
}
|
|
5668
|
-
const registryPath =
|
|
5669
|
-
if (
|
|
6571
|
+
const registryPath = path13.join(targetDir, "sections-registry.ts");
|
|
6572
|
+
if (fs8.existsSync(registryPath)) {
|
|
5670
6573
|
logger.success("sections-registry.ts present");
|
|
5671
6574
|
} else {
|
|
5672
6575
|
logger.warning(
|
|
@@ -5677,24 +6580,27 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
5677
6580
|
|
|
5678
6581
|
// src/cli.ts
|
|
5679
6582
|
dotenv.config({
|
|
5680
|
-
path:
|
|
6583
|
+
path: path13.join(process.cwd(), ".env.local"),
|
|
5681
6584
|
override: true
|
|
5682
6585
|
});
|
|
5683
|
-
dotenv.config({ path:
|
|
6586
|
+
dotenv.config({ path: path13.join(process.cwd(), ".env") });
|
|
5684
6587
|
try {
|
|
5685
6588
|
const projectRoot = getProjectRoot();
|
|
5686
|
-
if (
|
|
6589
|
+
if (path13.resolve(projectRoot) !== path13.resolve(process.cwd())) {
|
|
5687
6590
|
dotenv.config({
|
|
5688
|
-
path:
|
|
6591
|
+
path: path13.join(projectRoot, ".env.local")
|
|
5689
6592
|
});
|
|
5690
|
-
dotenv.config({ path:
|
|
6593
|
+
dotenv.config({ path: path13.join(projectRoot, ".env") });
|
|
5691
6594
|
}
|
|
5692
6595
|
} catch {
|
|
5693
6596
|
}
|
|
5694
6597
|
dotenv.config({
|
|
5695
|
-
path:
|
|
6598
|
+
path: path13.join(os.homedir(), ".onexthm", ".env"),
|
|
5696
6599
|
quiet: true
|
|
5697
6600
|
});
|
|
6601
|
+
function envOpt() {
|
|
6602
|
+
return new Option("--env <env>", "Target environment: dev, staging, or prod").choices(["dev", "staging", "prod"]).makeOptionMandatory();
|
|
6603
|
+
}
|
|
5698
6604
|
var require2 = createRequire(import.meta.url);
|
|
5699
6605
|
var { version } = require2("../package.json");
|
|
5700
6606
|
var program = new Command();
|
|
@@ -5703,11 +6609,7 @@ program.command("init").description("Create a new OneX theme project").argument(
|
|
|
5703
6609
|
"-t, --template <template>",
|
|
5704
6610
|
"Template to use (default, minimal)",
|
|
5705
6611
|
"default"
|
|
5706
|
-
).option("--no-install", "Skip installing dependencies").option("--git", "Initialize git repository").option("-y, --yes", "Skip prompts and use defaults").
|
|
5707
|
-
"--env <env>",
|
|
5708
|
-
"Target environment: dev or prod (default: dev)",
|
|
5709
|
-
"dev"
|
|
5710
|
-
).action(initCommand);
|
|
6612
|
+
).option("--no-install", "Skip installing dependencies").option("--git", "Initialize git repository").option("-y, --yes", "Skip prompts and use defaults").addOption(envOpt()).action(initCommand);
|
|
5711
6613
|
program.command("create:section").alias("cs").description("Create a new section").argument("<name>", "Name of the section (e.g., hero, features)").option("-t, --theme <theme>", "Theme to create section in").option(
|
|
5712
6614
|
"-c, --category <category>",
|
|
5713
6615
|
"Section category (headers, content, footers)"
|
|
@@ -5734,36 +6636,16 @@ program.command("download").description("Download a published theme via the webs
|
|
|
5734
6636
|
"-v, --version <version>",
|
|
5735
6637
|
"Theme version (default: latest)",
|
|
5736
6638
|
"latest"
|
|
5737
|
-
).option(
|
|
5738
|
-
"--env <env>",
|
|
5739
|
-
"Target environment: dev or prod (default: dev)",
|
|
5740
|
-
"dev"
|
|
5741
|
-
).option("-b, --bucket <name>", "[deprecated] ignored").option("-o, --output <dir>", "Output directory", "./active-theme").action(downloadCommand);
|
|
6639
|
+
).addOption(envOpt()).option("-b, --bucket <name>", "[deprecated] ignored").option("-o, --output <dir>", "Output directory", "./active-theme").action(downloadCommand);
|
|
5742
6640
|
program.command("clone").description("Clone theme source code via the website-api").argument("<theme-name>", "Theme to clone").option(
|
|
5743
6641
|
"-v, --version <version>",
|
|
5744
6642
|
"Theme version (default: latest)",
|
|
5745
6643
|
"latest"
|
|
5746
|
-
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option(
|
|
5747
|
-
"--env <env>",
|
|
5748
|
-
"Target environment: dev or prod (default: dev)",
|
|
5749
|
-
"dev"
|
|
5750
|
-
).option("-b, --bucket <name>", "[deprecated] ignored").option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
6644
|
+
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").addOption(envOpt()).option("-b, --bucket <name>", "[deprecated] ignored").option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
5751
6645
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
5752
|
-
program.command("login").description("Login to OneX platform").
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
"dev"
|
|
5756
|
-
).action(loginCommand);
|
|
5757
|
-
program.command("logout").description("Logout from OneX platform").option(
|
|
5758
|
-
"--env <env>",
|
|
5759
|
-
"Target environment: dev or prod (default: dev)",
|
|
5760
|
-
"dev"
|
|
5761
|
-
).action(logoutCommand);
|
|
5762
|
-
program.command("whoami").description("Show current logged-in developer").option(
|
|
5763
|
-
"--env <env>",
|
|
5764
|
-
"Target environment: dev or prod (default: dev)",
|
|
5765
|
-
"dev"
|
|
5766
|
-
).action(whoamiCommand);
|
|
6646
|
+
program.command("login").description("Login to OneX platform").addOption(envOpt()).action(loginCommand);
|
|
6647
|
+
program.command("logout").description("Logout from OneX platform").addOption(envOpt()).action(logoutCommand);
|
|
6648
|
+
program.command("whoami").description("Show current logged-in developer").addOption(envOpt()).action(whoamiCommand);
|
|
5767
6649
|
var mcpCmd = program.command("mcp").description("Manage MCP server registration and AI-context files");
|
|
5768
6650
|
mcpCmd.command("setup").description(
|
|
5769
6651
|
"Install .mcp.json + CLAUDE.md + AGENTS.md + .cursorrules into the current theme"
|
|
@@ -5775,10 +6657,15 @@ mcpCmd.command("doctor").description("Diagnose MCP setup in the current theme di
|
|
|
5775
6657
|
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
|
|
5776
6658
|
"--bump <type>",
|
|
5777
6659
|
"Auto-bump version before publish (patch|minor|major)"
|
|
6660
|
+
).addOption(envOpt()).option(
|
|
6661
|
+
"--dry-run",
|
|
6662
|
+
"Build locally and print the schema-diff classification without publishing"
|
|
6663
|
+
).option(
|
|
6664
|
+
"--confirm-defaults",
|
|
6665
|
+
"Confirm that changed section/block defaults should propagate to live sites"
|
|
5778
6666
|
).option(
|
|
5779
|
-
"--
|
|
5780
|
-
"
|
|
5781
|
-
"dev"
|
|
6667
|
+
"--force",
|
|
6668
|
+
"Publish even when the diff gate detects a breaking change"
|
|
5782
6669
|
).action(publishCommand);
|
|
5783
6670
|
program.configureOutput({
|
|
5784
6671
|
writeErr: (str) => process.stderr.write(chalk4.red(str))
|