@onexapis/cli 1.1.66 → 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/dist/cli.js +901 -695
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +898 -692
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +571 -411
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +568 -408
- package/dist/index.mjs.map +1 -1
- 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
|
|
4
|
+
import fs3 from 'fs';
|
|
5
|
+
import path13 from 'path';
|
|
5
6
|
import { glob } from 'glob';
|
|
6
|
-
import
|
|
7
|
+
import fs8 from 'fs-extra';
|
|
7
8
|
import crypto from 'crypto';
|
|
9
|
+
import fs11 from 'fs/promises';
|
|
8
10
|
import * as esbuild from 'esbuild';
|
|
9
|
-
import fs9 from 'fs/promises';
|
|
10
11
|
import { createRequire } from 'module';
|
|
11
12
|
import http from 'http';
|
|
12
|
-
import fs3 from 'fs';
|
|
13
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
14
14
|
import os from 'os';
|
|
15
15
|
import dotenv from 'dotenv';
|
|
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,13 @@ 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
|
+
});
|
|
92
99
|
function sortedCopy(value) {
|
|
93
100
|
if (Array.isArray(value)) {
|
|
94
101
|
return value.map((v) => sortedCopy(v));
|
|
@@ -171,7 +178,7 @@ async function extractSchemas(themePath) {
|
|
|
171
178
|
const sections = {};
|
|
172
179
|
for (const file of schemaFiles) {
|
|
173
180
|
try {
|
|
174
|
-
const mod = await jiti.import(
|
|
181
|
+
const mod = await jiti.import(path13.join(themePath, file));
|
|
175
182
|
const exports$1 = mod;
|
|
176
183
|
for (const value of Object.values(exports$1)) {
|
|
177
184
|
if (value && typeof value === "object" && typeof value.type === "string" && Array.isArray(value.settings)) {
|
|
@@ -203,24 +210,24 @@ function isVideoAsset(filePath) {
|
|
|
203
210
|
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
204
211
|
}
|
|
205
212
|
function mimeFor(filename) {
|
|
206
|
-
const ext =
|
|
213
|
+
const ext = path13.extname(filename).toLowerCase();
|
|
207
214
|
return MIME_MAP[ext] || "application/octet-stream";
|
|
208
215
|
}
|
|
209
216
|
async function sha256Prefix(absPath, len) {
|
|
210
|
-
const buf = await
|
|
217
|
+
const buf = await fs8.readFile(absPath);
|
|
211
218
|
return crypto.createHash("sha256").update(buf).digest("hex").slice(0, len);
|
|
212
219
|
}
|
|
213
220
|
function insertHashIntoName(relPath, hash) {
|
|
214
|
-
const dir =
|
|
215
|
-
const base =
|
|
216
|
-
const ext =
|
|
221
|
+
const dir = path13.posix.dirname(relPath);
|
|
222
|
+
const base = path13.posix.basename(relPath);
|
|
223
|
+
const ext = path13.posix.extname(base);
|
|
217
224
|
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
218
225
|
const hashed = `${stem}-${hash}${ext}`;
|
|
219
226
|
return dir === "." ? hashed : `${dir}/${hashed}`;
|
|
220
227
|
}
|
|
221
228
|
async function scanThemeAssets(distDir) {
|
|
222
|
-
const assetsDir =
|
|
223
|
-
if (!await
|
|
229
|
+
const assetsDir = path13.join(distDir, "theme-assets");
|
|
230
|
+
if (!await fs8.pathExists(assetsDir)) return [];
|
|
224
231
|
const files = await glob("**/*", {
|
|
225
232
|
cwd: assetsDir,
|
|
226
233
|
nodir: true,
|
|
@@ -228,10 +235,10 @@ async function scanThemeAssets(distDir) {
|
|
|
228
235
|
});
|
|
229
236
|
const results = [];
|
|
230
237
|
for (const rel of files) {
|
|
231
|
-
const absPath =
|
|
232
|
-
const stat = await
|
|
238
|
+
const absPath = path13.join(assetsDir, rel);
|
|
239
|
+
const stat = await fs8.stat(absPath);
|
|
233
240
|
if (!stat.isFile()) continue;
|
|
234
|
-
const originalPath = rel.split(
|
|
241
|
+
const originalPath = rel.split(path13.sep).join("/");
|
|
235
242
|
const hash = await sha256Prefix(absPath, HASH_LEN);
|
|
236
243
|
const hashedPath = insertHashIntoName(originalPath, hash);
|
|
237
244
|
const contentType = mimeFor(rel);
|
|
@@ -289,6 +296,101 @@ var init_scan_theme_assets = __esm({
|
|
|
289
296
|
];
|
|
290
297
|
}
|
|
291
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
|
+
});
|
|
292
394
|
|
|
293
395
|
// src/utils/compile-theme.ts
|
|
294
396
|
var compile_theme_exports = {};
|
|
@@ -304,8 +406,8 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
304
406
|
const tailwindcss = (await import('tailwindcss')).default;
|
|
305
407
|
const tailwindConfig = {
|
|
306
408
|
content: [
|
|
307
|
-
|
|
308
|
-
|
|
409
|
+
path13.join(themePath, "sections/**/*.{ts,tsx}"),
|
|
410
|
+
path13.join(themePath, "components/**/*.{ts,tsx}")
|
|
309
411
|
],
|
|
310
412
|
theme: { extend: {} },
|
|
311
413
|
plugins: []
|
|
@@ -315,7 +417,7 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
315
417
|
inputCSS,
|
|
316
418
|
{ from: void 0 }
|
|
317
419
|
);
|
|
318
|
-
await
|
|
420
|
+
await fs11.writeFile(path13.join(outDir, "bundle.css"), result.css);
|
|
319
421
|
logger.info("Generated bundle.css");
|
|
320
422
|
} catch (err) {
|
|
321
423
|
logger.warning(
|
|
@@ -326,12 +428,12 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
326
428
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
327
429
|
let dir = startDir;
|
|
328
430
|
while (true) {
|
|
329
|
-
const candidate =
|
|
431
|
+
const candidate = path13.join(dir, "node_modules", relativePath);
|
|
330
432
|
try {
|
|
331
|
-
await
|
|
433
|
+
await fs11.access(candidate);
|
|
332
434
|
return candidate;
|
|
333
435
|
} catch {
|
|
334
|
-
const parent =
|
|
436
|
+
const parent = path13.dirname(dir);
|
|
335
437
|
if (parent === dir) break;
|
|
336
438
|
dir = parent;
|
|
337
439
|
}
|
|
@@ -355,7 +457,7 @@ async function scanImportsFromPackage(sourceDir, packageName) {
|
|
|
355
457
|
});
|
|
356
458
|
for (const file of sourceFiles) {
|
|
357
459
|
try {
|
|
358
|
-
const content = await
|
|
460
|
+
const content = await fs11.readFile(path13.join(sourceDir, file), "utf-8");
|
|
359
461
|
for (const match of content.matchAll(namespaceImportRegex)) {
|
|
360
462
|
const subpath = match[1] ? match[1].slice(1) : "";
|
|
361
463
|
if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
|
|
@@ -409,17 +511,17 @@ function createCoreGlobalPlugin(themePath) {
|
|
|
409
511
|
const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
|
|
410
512
|
let distPath = await resolveNodeModulesFile(
|
|
411
513
|
themePath,
|
|
412
|
-
|
|
514
|
+
path13.join("@onexapis", "core", "dist", distFileName)
|
|
413
515
|
);
|
|
414
516
|
if (!distPath) {
|
|
415
517
|
distPath = await resolveNodeModulesFile(
|
|
416
518
|
__dirname,
|
|
417
|
-
|
|
519
|
+
path13.join("@onexapis", "core", "dist", distFileName)
|
|
418
520
|
);
|
|
419
521
|
}
|
|
420
522
|
try {
|
|
421
523
|
if (!distPath) throw new Error("not found");
|
|
422
|
-
const distContent = await
|
|
524
|
+
const distContent = await fs11.readFile(distPath, "utf-8");
|
|
423
525
|
const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
|
|
424
526
|
for (const m of exportMatches) {
|
|
425
527
|
const names = m[1].split(",").map((n) => {
|
|
@@ -466,180 +568,6 @@ ${namedExportLines}
|
|
|
466
568
|
}
|
|
467
569
|
};
|
|
468
570
|
}
|
|
469
|
-
function createThemeDepsStubPlugin(themePath) {
|
|
470
|
-
return {
|
|
471
|
-
name: "theme-deps-stub",
|
|
472
|
-
setup(build2) {
|
|
473
|
-
const tryResolveOrStub = (filter, namespace) => {
|
|
474
|
-
build2.onResolve({ filter }, async (args) => {
|
|
475
|
-
if (args.pluginData?.skipStub) return void 0;
|
|
476
|
-
try {
|
|
477
|
-
const result = await build2.resolve(args.path, {
|
|
478
|
-
kind: args.kind,
|
|
479
|
-
resolveDir: args.resolveDir || themePath,
|
|
480
|
-
importer: args.importer,
|
|
481
|
-
namespace: "file",
|
|
482
|
-
pluginData: { skipStub: true }
|
|
483
|
-
});
|
|
484
|
-
if (!result.errors.length) return result;
|
|
485
|
-
} catch {
|
|
486
|
-
}
|
|
487
|
-
try {
|
|
488
|
-
const req = createRequire(import.meta.url || __filename);
|
|
489
|
-
const resolved = req.resolve(args.path);
|
|
490
|
-
if (resolved) return { path: resolved, namespace: "file" };
|
|
491
|
-
} catch {
|
|
492
|
-
}
|
|
493
|
-
return { path: args.path, namespace };
|
|
494
|
-
});
|
|
495
|
-
};
|
|
496
|
-
tryResolveOrStub(/^next\//, "next-stub");
|
|
497
|
-
build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
|
|
498
|
-
const stubs = {
|
|
499
|
-
"next/image": `
|
|
500
|
-
import React from 'react';
|
|
501
|
-
const Image = React.forwardRef((props, ref) => {
|
|
502
|
-
const { src, alt, width, height, fill, priority, sizes, quality, placeholder, blurDataURL, onLoad, onError, style, className, ...rest } = props;
|
|
503
|
-
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
504
|
-
const fillStyle = fill ? { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: style?.objectFit || 'cover', display: 'block' } : {};
|
|
505
|
-
const mergedStyle = { ...fillStyle, ...style };
|
|
506
|
-
return React.createElement('img', {
|
|
507
|
-
ref, src: imgSrc, alt,
|
|
508
|
-
width: fill ? undefined : width, height: fill ? undefined : height,
|
|
509
|
-
loading: priority ? 'eager' : 'lazy',
|
|
510
|
-
style: Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined,
|
|
511
|
-
className, onLoad, onError, ...rest,
|
|
512
|
-
});
|
|
513
|
-
});
|
|
514
|
-
export default Image;
|
|
515
|
-
`,
|
|
516
|
-
"next/link": `
|
|
517
|
-
import React from 'react';
|
|
518
|
-
const Link = ({ href, children, ...rest }) => React.createElement('a', { href, ...rest }, children);
|
|
519
|
-
export default Link;
|
|
520
|
-
`,
|
|
521
|
-
"next/navigation": `
|
|
522
|
-
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(){} }; }
|
|
523
|
-
export function usePathname() { return window.location.pathname; }
|
|
524
|
-
export function useSearchParams() { return new URLSearchParams(window.location.search); }
|
|
525
|
-
export function useParams() { return {}; }
|
|
526
|
-
export function redirect(url) { window.location.href = url; }
|
|
527
|
-
export function notFound() { throw new Error('Not Found'); }
|
|
528
|
-
`,
|
|
529
|
-
"next/headers": `
|
|
530
|
-
export function cookies() { return { get(){}, getAll(){ return []; }, set(){}, delete(){}, has(){ return false; } }; }
|
|
531
|
-
export function headers() { return new Headers(); }
|
|
532
|
-
`
|
|
533
|
-
};
|
|
534
|
-
return {
|
|
535
|
-
contents: stubs[args.path] || "export default {};",
|
|
536
|
-
loader: "jsx",
|
|
537
|
-
resolveDir: themePath
|
|
538
|
-
};
|
|
539
|
-
});
|
|
540
|
-
const lucideImports = /* @__PURE__ */ new Set();
|
|
541
|
-
let lucideThemeScanned = false;
|
|
542
|
-
tryResolveOrStub(/^lucide-react/, "lucide-stub");
|
|
543
|
-
build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
|
|
544
|
-
if (!lucideThemeScanned) {
|
|
545
|
-
lucideThemeScanned = true;
|
|
546
|
-
try {
|
|
547
|
-
const scanned = await scanImportsFromPackage(
|
|
548
|
-
themePath,
|
|
549
|
-
"lucide-react"
|
|
550
|
-
);
|
|
551
|
-
for (const names of Object.values(scanned)) {
|
|
552
|
-
for (const name of names) lucideImports.add(name);
|
|
553
|
-
}
|
|
554
|
-
} catch {
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
const iconNames = [...lucideImports];
|
|
558
|
-
const exports$1 = iconNames.map((n) => `icon as ${n}`).join(", ");
|
|
559
|
-
return {
|
|
560
|
-
contents: `
|
|
561
|
-
const icon = (props) => null;
|
|
562
|
-
export { ${exports$1} };
|
|
563
|
-
export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
|
|
564
|
-
`.trim(),
|
|
565
|
-
loader: "jsx"
|
|
566
|
-
};
|
|
567
|
-
});
|
|
568
|
-
tryResolveOrStub(/^framer-motion/, "motion-stub");
|
|
569
|
-
build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
|
|
570
|
-
contents: `
|
|
571
|
-
import React from 'react';
|
|
572
|
-
const handler = { get: (_, name) => {
|
|
573
|
-
if (name === '__esModule') return true;
|
|
574
|
-
return React.forwardRef((props, ref) => React.createElement(name, { ...props, ref }));
|
|
575
|
-
}};
|
|
576
|
-
export const motion = new Proxy({}, handler);
|
|
577
|
-
export const AnimatePresence = ({ children }) => children || null;
|
|
578
|
-
export function useInView() { return true; }
|
|
579
|
-
export default { motion, AnimatePresence, useInView };
|
|
580
|
-
`.trim(),
|
|
581
|
-
loader: "jsx",
|
|
582
|
-
resolveDir: themePath
|
|
583
|
-
}));
|
|
584
|
-
tryResolveOrStub(/^sonner$/, "sonner-stub");
|
|
585
|
-
build2.onLoad({ filter: /.*/, namespace: "sonner-stub" }, () => ({
|
|
586
|
-
contents: `
|
|
587
|
-
export const toast = new Proxy(() => {}, { get: () => () => {} });
|
|
588
|
-
export const Toaster = () => null;
|
|
589
|
-
export default { toast, Toaster };
|
|
590
|
-
`.trim(),
|
|
591
|
-
loader: "jsx"
|
|
592
|
-
}));
|
|
593
|
-
tryResolveOrStub(/^react-hook-form$/, "rhf-stub");
|
|
594
|
-
build2.onLoad({ filter: /.*/, namespace: "rhf-stub" }, () => ({
|
|
595
|
-
contents: `
|
|
596
|
-
export function useForm() {
|
|
597
|
-
return {
|
|
598
|
-
register: () => ({}),
|
|
599
|
-
handleSubmit: (fn) => (e) => { e?.preventDefault?.(); fn({}); },
|
|
600
|
-
formState: { errors: {}, isSubmitting: false, isValid: true },
|
|
601
|
-
watch: () => undefined,
|
|
602
|
-
setValue: () => {},
|
|
603
|
-
reset: () => {},
|
|
604
|
-
control: {},
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
export function useController() { return { field: {}, fieldState: {} }; }
|
|
608
|
-
export function useFormContext() { return useForm(); }
|
|
609
|
-
`.trim(),
|
|
610
|
-
loader: "js"
|
|
611
|
-
}));
|
|
612
|
-
tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
|
|
613
|
-
build2.onLoad(
|
|
614
|
-
{ filter: /.*/, namespace: "hookform-resolvers-stub" },
|
|
615
|
-
() => ({
|
|
616
|
-
contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
|
|
617
|
-
loader: "js"
|
|
618
|
-
})
|
|
619
|
-
);
|
|
620
|
-
tryResolveOrStub(/^next-intl$/, "next-intl-stub");
|
|
621
|
-
build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
|
|
622
|
-
contents: `
|
|
623
|
-
export function useTranslations(ns) {
|
|
624
|
-
return (key) => ns ? ns + '.' + key : key;
|
|
625
|
-
}
|
|
626
|
-
export function useLocale() { return 'en'; }
|
|
627
|
-
export function useMessages() { return {}; }
|
|
628
|
-
`.trim(),
|
|
629
|
-
loader: "js"
|
|
630
|
-
}));
|
|
631
|
-
tryResolveOrStub(/^zod$/, "zod-stub");
|
|
632
|
-
build2.onLoad({ filter: /.*/, namespace: "zod-stub" }, () => ({
|
|
633
|
-
contents: `
|
|
634
|
-
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 });
|
|
635
|
-
export const z = { string: schema, number: schema, boolean: schema, object: (s) => ({ ...schema(), shape: s }), array: schema, enum: schema, union: schema, literal: schema, infer: undefined };
|
|
636
|
-
export default z;
|
|
637
|
-
`.trim(),
|
|
638
|
-
loader: "js"
|
|
639
|
-
}));
|
|
640
|
-
}
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
571
|
async function generateThemeData(themePath, outputDir, themeId) {
|
|
644
572
|
const { createJiti } = await import('jiti');
|
|
645
573
|
const jiti = createJiti(import.meta.url);
|
|
@@ -648,7 +576,7 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
648
576
|
const pages = {};
|
|
649
577
|
for (const ext of [".ts", ".js"]) {
|
|
650
578
|
try {
|
|
651
|
-
const mod = await jiti.import(
|
|
579
|
+
const mod = await jiti.import(path13.join(themePath, `theme.config${ext}`));
|
|
652
580
|
themeConfig = mod.default || mod;
|
|
653
581
|
break;
|
|
654
582
|
} catch {
|
|
@@ -656,20 +584,20 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
656
584
|
}
|
|
657
585
|
for (const ext of [".ts", ".js"]) {
|
|
658
586
|
try {
|
|
659
|
-
const mod = await jiti.import(
|
|
587
|
+
const mod = await jiti.import(path13.join(themePath, `theme.layout${ext}`));
|
|
660
588
|
layoutConfig = mod.default || mod;
|
|
661
589
|
break;
|
|
662
590
|
} catch {
|
|
663
591
|
}
|
|
664
592
|
}
|
|
665
593
|
const schemas = {};
|
|
666
|
-
const sectionsDir =
|
|
594
|
+
const sectionsDir = path13.join(themePath, "sections");
|
|
667
595
|
try {
|
|
668
|
-
const sectionDirs = await
|
|
596
|
+
const sectionDirs = await fs11.readdir(sectionsDir);
|
|
669
597
|
for (const dir of sectionDirs) {
|
|
670
|
-
const schemaFile =
|
|
598
|
+
const schemaFile = path13.join(sectionsDir, dir, `${dir}.schema.ts`);
|
|
671
599
|
try {
|
|
672
|
-
await
|
|
600
|
+
await fs11.access(schemaFile);
|
|
673
601
|
const mod = await jiti.import(schemaFile);
|
|
674
602
|
for (const [key, value] of Object.entries(mod)) {
|
|
675
603
|
if (key.endsWith("Schema") && value && typeof value === "object" && value.type) {
|
|
@@ -681,14 +609,14 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
681
609
|
}
|
|
682
610
|
} catch {
|
|
683
611
|
}
|
|
684
|
-
const pagesDir =
|
|
612
|
+
const pagesDir = path13.join(themePath, "pages");
|
|
685
613
|
try {
|
|
686
|
-
const files = await
|
|
614
|
+
const files = await fs11.readdir(pagesDir);
|
|
687
615
|
for (const file of files) {
|
|
688
616
|
if (!file.match(/\.(ts|js)$/)) continue;
|
|
689
617
|
const name = file.replace(/\.(ts|js)$/, "");
|
|
690
618
|
try {
|
|
691
|
-
const mod = await jiti.import(
|
|
619
|
+
const mod = await jiti.import(path13.join(pagesDir, file));
|
|
692
620
|
const config = mod.default || mod;
|
|
693
621
|
const sections = (config.sections || []).map((section) => {
|
|
694
622
|
const schema = schemas[section.type];
|
|
@@ -716,8 +644,16 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
716
644
|
}
|
|
717
645
|
} catch {
|
|
718
646
|
}
|
|
719
|
-
|
|
720
|
-
|
|
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"),
|
|
721
657
|
JSON.stringify(
|
|
722
658
|
{
|
|
723
659
|
themeId,
|
|
@@ -740,22 +676,22 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
740
676
|
logger.info(`Generated theme-data.json (${Object.keys(pages).length} pages)`);
|
|
741
677
|
}
|
|
742
678
|
async function contentHashEntry(outputDir) {
|
|
743
|
-
const entryPath =
|
|
744
|
-
const mapPath =
|
|
679
|
+
const entryPath = path13.join(outputDir, "bundle-entry.js");
|
|
680
|
+
const mapPath = path13.join(outputDir, "bundle-entry.js.map");
|
|
745
681
|
let entryContent;
|
|
746
682
|
try {
|
|
747
|
-
entryContent = await
|
|
683
|
+
entryContent = await fs11.readFile(entryPath, "utf-8");
|
|
748
684
|
} catch {
|
|
749
|
-
const indexPath =
|
|
685
|
+
const indexPath = path13.join(outputDir, "index.js");
|
|
750
686
|
try {
|
|
751
|
-
entryContent = await
|
|
687
|
+
entryContent = await fs11.readFile(indexPath, "utf-8");
|
|
752
688
|
} catch {
|
|
753
689
|
logger.warning("No entry file found in output, skipping content hash");
|
|
754
690
|
return;
|
|
755
691
|
}
|
|
756
692
|
const hash2 = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
757
693
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
758
|
-
const indexMapPath =
|
|
694
|
+
const indexMapPath = path13.join(outputDir, "index.js.map");
|
|
759
695
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
760
696
|
entryContent = entryContent.replace(
|
|
761
697
|
/\/\/# sourceMappingURL=index\.js\.map/,
|
|
@@ -763,18 +699,18 @@ async function contentHashEntry(outputDir) {
|
|
|
763
699
|
);
|
|
764
700
|
const oldFiles2 = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
765
701
|
for (const f of oldFiles2) {
|
|
766
|
-
await
|
|
702
|
+
await fs11.unlink(path13.join(outputDir, f));
|
|
767
703
|
}
|
|
768
|
-
await
|
|
769
|
-
await
|
|
704
|
+
await fs11.writeFile(path13.join(outputDir, hashedName2), entryContent);
|
|
705
|
+
await fs11.unlink(indexPath);
|
|
770
706
|
try {
|
|
771
|
-
await
|
|
707
|
+
await fs11.unlink(entryPath);
|
|
772
708
|
} catch {
|
|
773
709
|
}
|
|
774
|
-
await
|
|
710
|
+
await fs11.writeFile(entryPath, entryContent);
|
|
775
711
|
try {
|
|
776
|
-
await
|
|
777
|
-
await
|
|
712
|
+
await fs11.access(indexMapPath);
|
|
713
|
+
await fs11.rename(indexMapPath, path13.join(outputDir, hashedMapName2));
|
|
778
714
|
} catch {
|
|
779
715
|
}
|
|
780
716
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
@@ -789,17 +725,17 @@ async function contentHashEntry(outputDir) {
|
|
|
789
725
|
);
|
|
790
726
|
const oldFiles = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
791
727
|
for (const f of oldFiles) {
|
|
792
|
-
await
|
|
728
|
+
await fs11.unlink(path13.join(outputDir, f));
|
|
793
729
|
}
|
|
794
|
-
await
|
|
730
|
+
await fs11.writeFile(path13.join(outputDir, hashedName), entryContent);
|
|
795
731
|
try {
|
|
796
|
-
await
|
|
732
|
+
await fs11.unlink(entryPath);
|
|
797
733
|
} catch {
|
|
798
734
|
}
|
|
799
|
-
await
|
|
735
|
+
await fs11.writeFile(entryPath, entryContent);
|
|
800
736
|
try {
|
|
801
|
-
await
|
|
802
|
-
await
|
|
737
|
+
await fs11.access(mapPath);
|
|
738
|
+
await fs11.rename(mapPath, path13.join(outputDir, hashedMapName));
|
|
803
739
|
} catch {
|
|
804
740
|
}
|
|
805
741
|
logger.info(`Entry hashed: ${hashedName}`);
|
|
@@ -811,7 +747,7 @@ async function extractDataRequirements(themePath) {
|
|
|
811
747
|
const requirements = {};
|
|
812
748
|
for (const file of schemaFiles) {
|
|
813
749
|
try {
|
|
814
|
-
const mod = await jiti.import(
|
|
750
|
+
const mod = await jiti.import(path13.join(themePath, file));
|
|
815
751
|
const exports$1 = mod;
|
|
816
752
|
for (const value of Object.values(exports$1)) {
|
|
817
753
|
if (value && typeof value === "object" && typeof value.type === "string" && value.dataRequirements && typeof value.dataRequirements === "object") {
|
|
@@ -829,8 +765,8 @@ async function extractDataRequirements(themePath) {
|
|
|
829
765
|
async function writeGateManifests(themePath, outputDir) {
|
|
830
766
|
try {
|
|
831
767
|
const schemas = await extractSchemas(themePath);
|
|
832
|
-
await
|
|
833
|
-
|
|
768
|
+
await fs11.writeFile(
|
|
769
|
+
path13.join(outputDir, "schemas.json"),
|
|
834
770
|
serializeManifest(schemas)
|
|
835
771
|
);
|
|
836
772
|
logger.info(
|
|
@@ -849,8 +785,8 @@ async function writeGateManifests(themePath, outputDir) {
|
|
|
849
785
|
size: e.size,
|
|
850
786
|
contentType: e.contentType
|
|
851
787
|
}));
|
|
852
|
-
await
|
|
853
|
-
|
|
788
|
+
await fs11.writeFile(
|
|
789
|
+
path13.join(outputDir, "asset-manifest.json"),
|
|
854
790
|
JSON.stringify({ manifestVersion: 1, assets }, null, 2)
|
|
855
791
|
);
|
|
856
792
|
logger.info(`Generated asset-manifest.json (${assets.length} assets)`);
|
|
@@ -860,12 +796,71 @@ async function writeGateManifests(themePath, outputDir) {
|
|
|
860
796
|
);
|
|
861
797
|
}
|
|
862
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
|
+
}
|
|
863
858
|
async function generateManifest(themeName, themePath, outputDir) {
|
|
864
859
|
let version2 = "1.0.0";
|
|
865
860
|
let themeId = themeName;
|
|
866
861
|
try {
|
|
867
|
-
const pkgContent = await
|
|
868
|
-
|
|
862
|
+
const pkgContent = await fs11.readFile(
|
|
863
|
+
path13.join(themePath, "package.json"),
|
|
869
864
|
"utf-8"
|
|
870
865
|
);
|
|
871
866
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -883,7 +878,7 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
883
878
|
const dataRequirements = await extractDataRequirements(themePath);
|
|
884
879
|
let hasThemeConfig = false;
|
|
885
880
|
try {
|
|
886
|
-
await
|
|
881
|
+
await fs11.access(path13.join(themePath, "theme.config.ts"));
|
|
887
882
|
hasThemeConfig = true;
|
|
888
883
|
} catch {
|
|
889
884
|
}
|
|
@@ -924,24 +919,34 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
924
919
|
// Section data requirements for server-side prefetching (keyed by section type)
|
|
925
920
|
dataRequirements
|
|
926
921
|
};
|
|
927
|
-
await
|
|
928
|
-
|
|
922
|
+
await fs11.writeFile(
|
|
923
|
+
path13.join(outputDir, "manifest.json"),
|
|
929
924
|
JSON.stringify(manifest, null, 2)
|
|
930
925
|
);
|
|
931
926
|
}
|
|
932
927
|
async function compileStandaloneTheme(themePath, themeName) {
|
|
933
|
-
const outputDir =
|
|
934
|
-
const
|
|
935
|
-
|
|
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");
|
|
936
935
|
let entryPoint = indexEntry;
|
|
937
936
|
try {
|
|
938
|
-
await
|
|
937
|
+
await fs11.access(bundleEntry);
|
|
939
938
|
entryPoint = bundleEntry;
|
|
940
939
|
} catch {
|
|
941
940
|
}
|
|
942
|
-
const shimPath =
|
|
943
|
-
await
|
|
944
|
-
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);
|
|
945
950
|
const buildOptions = {
|
|
946
951
|
entryPoints: [entryPoint],
|
|
947
952
|
bundle: true,
|
|
@@ -953,12 +958,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
953
958
|
banner: {
|
|
954
959
|
js: '"use client";'
|
|
955
960
|
},
|
|
956
|
-
plugins
|
|
957
|
-
reactGlobalPlugin,
|
|
958
|
-
reactQueryGlobalPlugin,
|
|
959
|
-
createCoreGlobalPlugin(themePath),
|
|
960
|
-
createThemeDepsStubPlugin(themePath)
|
|
961
|
-
],
|
|
961
|
+
plugins,
|
|
962
962
|
external: [],
|
|
963
963
|
alias: {
|
|
964
964
|
events: "events/",
|
|
@@ -992,15 +992,16 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
992
992
|
try {
|
|
993
993
|
const result = await esbuild.build(buildOptions);
|
|
994
994
|
try {
|
|
995
|
-
await
|
|
995
|
+
await fs11.unlink(shimPath);
|
|
996
996
|
} catch {
|
|
997
997
|
}
|
|
998
|
+
await compileSections(themePath, outputDir);
|
|
998
999
|
await contentHashEntry(outputDir);
|
|
999
|
-
const themeAssetsDir =
|
|
1000
|
-
const distThemeAssets =
|
|
1000
|
+
const themeAssetsDir = path13.join(themePath, "assets");
|
|
1001
|
+
const distThemeAssets = path13.join(outputDir, "theme-assets");
|
|
1001
1002
|
try {
|
|
1002
|
-
await
|
|
1003
|
-
await
|
|
1003
|
+
await fs11.access(themeAssetsDir);
|
|
1004
|
+
await fs11.cp(themeAssetsDir, distThemeAssets, { recursive: true });
|
|
1004
1005
|
logger.info("Copied static assets to dist/theme-assets/");
|
|
1005
1006
|
} catch {
|
|
1006
1007
|
}
|
|
@@ -1020,7 +1021,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
1020
1021
|
return true;
|
|
1021
1022
|
} catch (error) {
|
|
1022
1023
|
try {
|
|
1023
|
-
await
|
|
1024
|
+
await fs11.unlink(shimPath);
|
|
1024
1025
|
} catch {
|
|
1025
1026
|
}
|
|
1026
1027
|
logger.error(`esbuild compilation failed: ${error}`);
|
|
@@ -1028,18 +1029,25 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
1028
1029
|
}
|
|
1029
1030
|
}
|
|
1030
1031
|
async function compileStandaloneThemeDev(themePath, themeName) {
|
|
1031
|
-
const outputDir =
|
|
1032
|
-
const
|
|
1033
|
-
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");
|
|
1034
1036
|
let entryPoint = indexEntry;
|
|
1035
1037
|
try {
|
|
1036
|
-
await
|
|
1038
|
+
await fs11.access(bundleEntry);
|
|
1037
1039
|
entryPoint = bundleEntry;
|
|
1038
1040
|
} catch {
|
|
1039
1041
|
}
|
|
1040
|
-
const shimPath =
|
|
1041
|
-
await
|
|
1042
|
-
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);
|
|
1043
1051
|
const buildOptions = {
|
|
1044
1052
|
entryPoints: [entryPoint],
|
|
1045
1053
|
bundle: true,
|
|
@@ -1050,12 +1058,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
1050
1058
|
banner: {
|
|
1051
1059
|
js: '"use client";'
|
|
1052
1060
|
},
|
|
1053
|
-
plugins:
|
|
1054
|
-
reactGlobalPlugin,
|
|
1055
|
-
reactQueryGlobalPlugin,
|
|
1056
|
-
createCoreGlobalPlugin(themePath),
|
|
1057
|
-
createThemeDepsStubPlugin(themePath)
|
|
1058
|
-
],
|
|
1061
|
+
plugins: devPlugins,
|
|
1059
1062
|
external: [],
|
|
1060
1063
|
alias: {
|
|
1061
1064
|
events: "events/",
|
|
@@ -1093,18 +1096,18 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
1093
1096
|
return { context: context2, outputDir };
|
|
1094
1097
|
}
|
|
1095
1098
|
async function compilePreviewRuntime(themePath) {
|
|
1096
|
-
const outputDir =
|
|
1097
|
-
await
|
|
1098
|
-
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");
|
|
1099
1102
|
const locations = [
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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")
|
|
1103
1106
|
];
|
|
1104
1107
|
let previewEntryPath = null;
|
|
1105
1108
|
for (const loc of locations) {
|
|
1106
1109
|
try {
|
|
1107
|
-
await
|
|
1110
|
+
await fs11.access(loc);
|
|
1108
1111
|
previewEntryPath = loc;
|
|
1109
1112
|
break;
|
|
1110
1113
|
} catch {
|
|
@@ -1187,10 +1190,10 @@ ${locations.join("\n")}`
|
|
|
1187
1190
|
if (!lucideScanned) {
|
|
1188
1191
|
lucideScanned = true;
|
|
1189
1192
|
const coreSrcCandidates = [
|
|
1190
|
-
|
|
1191
|
-
|
|
1193
|
+
path13.join(themePath, "node_modules", "@onexapis", "core", "src"),
|
|
1194
|
+
path13.join(themePath, "..", "..", "packages", "core", "src"),
|
|
1192
1195
|
// monorepo sibling
|
|
1193
|
-
|
|
1196
|
+
path13.join(
|
|
1194
1197
|
__dirname,
|
|
1195
1198
|
"..",
|
|
1196
1199
|
"..",
|
|
@@ -1205,7 +1208,7 @@ ${locations.join("\n")}`
|
|
|
1205
1208
|
let coreSourceDir = null;
|
|
1206
1209
|
for (const candidate of coreSrcCandidates) {
|
|
1207
1210
|
try {
|
|
1208
|
-
await
|
|
1211
|
+
await fs11.access(candidate);
|
|
1209
1212
|
coreSourceDir = candidate;
|
|
1210
1213
|
break;
|
|
1211
1214
|
} catch {
|
|
@@ -1224,21 +1227,21 @@ ${locations.join("\n")}`
|
|
|
1224
1227
|
}
|
|
1225
1228
|
} else {
|
|
1226
1229
|
const coreDistCandidates = [
|
|
1227
|
-
|
|
1230
|
+
path13.join(themePath, "node_modules", "@onexapis", "core", "dist")
|
|
1228
1231
|
];
|
|
1229
1232
|
const resolvedDist = await resolveNodeModulesFile(
|
|
1230
1233
|
__dirname,
|
|
1231
|
-
|
|
1234
|
+
path13.join("@onexapis", "core", "dist")
|
|
1232
1235
|
);
|
|
1233
1236
|
if (resolvedDist) coreDistCandidates.push(resolvedDist);
|
|
1234
1237
|
for (const candidate of coreDistCandidates) {
|
|
1235
1238
|
try {
|
|
1236
|
-
await
|
|
1239
|
+
await fs11.access(candidate);
|
|
1237
1240
|
const mjsFiles = await glob("*.mjs", { cwd: candidate });
|
|
1238
1241
|
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
1239
1242
|
for (const file of mjsFiles) {
|
|
1240
|
-
const content = await
|
|
1241
|
-
|
|
1243
|
+
const content = await fs11.readFile(
|
|
1244
|
+
path13.join(candidate, file),
|
|
1242
1245
|
"utf-8"
|
|
1243
1246
|
);
|
|
1244
1247
|
for (const match of content.matchAll(importRegex)) {
|
|
@@ -1293,7 +1296,7 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
|
|
|
1293
1296
|
const req = createRequire(import.meta.url || __filename);
|
|
1294
1297
|
const cjsPath = req.resolve("framer-motion");
|
|
1295
1298
|
const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
|
|
1296
|
-
const esmEntry =
|
|
1299
|
+
const esmEntry = path13.join(pkgDir, "dist", "es", "index.mjs");
|
|
1297
1300
|
const { existsSync } = await import('fs');
|
|
1298
1301
|
if (existsSync(esmEntry)) {
|
|
1299
1302
|
return { path: esmEntry, namespace: "file" };
|
|
@@ -1392,8 +1395,8 @@ export function headers() { return new Headers(); }
|
|
|
1392
1395
|
});
|
|
1393
1396
|
}
|
|
1394
1397
|
};
|
|
1395
|
-
const shimPath =
|
|
1396
|
-
await
|
|
1398
|
+
const shimPath = path13.join(outputDir, ".process-shim-preview.js");
|
|
1399
|
+
await fs11.writeFile(shimPath, PROCESS_SHIM);
|
|
1397
1400
|
await esbuild.build({
|
|
1398
1401
|
entryPoints: [previewEntryPath],
|
|
1399
1402
|
bundle: true,
|
|
@@ -1428,17 +1431,19 @@ export function headers() { return new Headers(); }
|
|
|
1428
1431
|
}
|
|
1429
1432
|
});
|
|
1430
1433
|
try {
|
|
1431
|
-
await
|
|
1434
|
+
await fs11.unlink(shimPath);
|
|
1432
1435
|
} catch {
|
|
1433
1436
|
}
|
|
1434
1437
|
return outputPath;
|
|
1435
1438
|
}
|
|
1436
|
-
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin;
|
|
1439
|
+
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin, nextShimPlugin;
|
|
1437
1440
|
var init_compile_theme = __esm({
|
|
1438
1441
|
"src/utils/compile-theme.ts"() {
|
|
1439
1442
|
init_logger();
|
|
1440
1443
|
init_extract_schemas();
|
|
1441
1444
|
init_scan_theme_assets();
|
|
1445
|
+
init_detect_nextjs();
|
|
1446
|
+
init_nextjs_page_scanner();
|
|
1442
1447
|
PROCESS_SHIM = `
|
|
1443
1448
|
if (typeof process === "undefined") {
|
|
1444
1449
|
globalThis.process = {
|
|
@@ -1584,6 +1589,145 @@ export const {
|
|
|
1584
1589
|
}));
|
|
1585
1590
|
}
|
|
1586
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
|
+
};
|
|
1587
1731
|
}
|
|
1588
1732
|
});
|
|
1589
1733
|
|
|
@@ -1594,7 +1738,7 @@ __export(dev_server_exports, {
|
|
|
1594
1738
|
});
|
|
1595
1739
|
function createDevServer(options) {
|
|
1596
1740
|
const clients = /* @__PURE__ */ new Set();
|
|
1597
|
-
const themeDataPath =
|
|
1741
|
+
const themeDataPath = path13.join(options.distDir, "theme-data.json");
|
|
1598
1742
|
const server = http.createServer((req, res) => {
|
|
1599
1743
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1600
1744
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
@@ -1620,8 +1764,8 @@ function createDevServer(options) {
|
|
|
1620
1764
|
if (pathname.startsWith("/_assets/")) {
|
|
1621
1765
|
const parts = pathname.replace(/^\/_assets\//, "").split("/");
|
|
1622
1766
|
const assetSubpath = parts.slice(1).join("/");
|
|
1623
|
-
const assetPath =
|
|
1624
|
-
if (!assetPath.startsWith(
|
|
1767
|
+
const assetPath = path13.join(options.themePath, "assets", assetSubpath);
|
|
1768
|
+
if (!assetPath.startsWith(path13.join(options.themePath, "assets"))) {
|
|
1625
1769
|
res.writeHead(403);
|
|
1626
1770
|
res.end("Forbidden");
|
|
1627
1771
|
return;
|
|
@@ -1632,8 +1776,8 @@ function createDevServer(options) {
|
|
|
1632
1776
|
if (pathname.startsWith("/themes/")) {
|
|
1633
1777
|
const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
|
|
1634
1778
|
if (match) {
|
|
1635
|
-
const assetPath =
|
|
1636
|
-
if (!assetPath.startsWith(
|
|
1779
|
+
const assetPath = path13.join(options.themePath, "assets", match[1]);
|
|
1780
|
+
if (!assetPath.startsWith(path13.join(options.themePath, "assets"))) {
|
|
1637
1781
|
res.writeHead(403);
|
|
1638
1782
|
res.end("Forbidden");
|
|
1639
1783
|
return;
|
|
@@ -1645,26 +1789,26 @@ function createDevServer(options) {
|
|
|
1645
1789
|
if (pathname.startsWith("/assets/")) {
|
|
1646
1790
|
const subpath = pathname.replace(/^\/assets\//, "");
|
|
1647
1791
|
const segments = subpath.split("/");
|
|
1648
|
-
const assetsBase =
|
|
1792
|
+
const assetsBase = path13.join(options.themePath, "assets");
|
|
1649
1793
|
let assetPath;
|
|
1650
1794
|
if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
|
|
1651
|
-
assetPath =
|
|
1795
|
+
assetPath = path13.join(assetsBase, segments.slice(1).join("/"));
|
|
1652
1796
|
} else {
|
|
1653
|
-
assetPath =
|
|
1797
|
+
assetPath = path13.join(assetsBase, subpath);
|
|
1654
1798
|
}
|
|
1655
1799
|
if (assetPath.startsWith(assetsBase) && fs3.existsSync(assetPath)) {
|
|
1656
1800
|
serveFile(res, assetPath);
|
|
1657
1801
|
return;
|
|
1658
1802
|
}
|
|
1659
1803
|
if (segments.length > 1) {
|
|
1660
|
-
const fallbackPath =
|
|
1804
|
+
const fallbackPath = path13.join(assetsBase, segments.slice(1).join("/"));
|
|
1661
1805
|
if (fallbackPath.startsWith(assetsBase) && fs3.existsSync(fallbackPath)) {
|
|
1662
1806
|
serveFile(res, fallbackPath);
|
|
1663
1807
|
return;
|
|
1664
1808
|
}
|
|
1665
1809
|
}
|
|
1666
1810
|
}
|
|
1667
|
-
const filePath =
|
|
1811
|
+
const filePath = path13.join(options.distDir, pathname);
|
|
1668
1812
|
if (!filePath.startsWith(options.distDir)) {
|
|
1669
1813
|
res.writeHead(403);
|
|
1670
1814
|
res.end("Forbidden");
|
|
@@ -1707,7 +1851,7 @@ function serveFile(res, filePath) {
|
|
|
1707
1851
|
res.end("Not Found");
|
|
1708
1852
|
return;
|
|
1709
1853
|
}
|
|
1710
|
-
const ext =
|
|
1854
|
+
const ext = path13.extname(filePath);
|
|
1711
1855
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1712
1856
|
const content = fs3.readFileSync(filePath);
|
|
1713
1857
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -1824,26 +1968,26 @@ var init_dev_server = __esm({
|
|
|
1824
1968
|
// src/utils/file-helpers.ts
|
|
1825
1969
|
init_logger();
|
|
1826
1970
|
async function renderTemplate(templatePath, data) {
|
|
1827
|
-
const template = await
|
|
1971
|
+
const template = await fs8.readFile(templatePath, "utf-8");
|
|
1828
1972
|
return ejs.render(template, data);
|
|
1829
1973
|
}
|
|
1830
1974
|
async function writeFile(filePath, content) {
|
|
1831
|
-
await
|
|
1832
|
-
await
|
|
1975
|
+
await fs8.ensureDir(path13.dirname(filePath));
|
|
1976
|
+
await fs8.writeFile(filePath, content, "utf-8");
|
|
1833
1977
|
}
|
|
1834
1978
|
function getTemplatesDir() {
|
|
1835
1979
|
const locations = [
|
|
1836
|
-
|
|
1980
|
+
path13.join(__dirname, "../../templates"),
|
|
1837
1981
|
// Development
|
|
1838
|
-
|
|
1982
|
+
path13.join(__dirname, "../templates"),
|
|
1839
1983
|
// Production (dist/)
|
|
1840
|
-
|
|
1984
|
+
path13.join(process.cwd(), "templates"),
|
|
1841
1985
|
// Fallback
|
|
1842
|
-
|
|
1986
|
+
path13.join(process.cwd(), "packages/cli/templates")
|
|
1843
1987
|
// Monorepo
|
|
1844
1988
|
];
|
|
1845
1989
|
for (const location of locations) {
|
|
1846
|
-
if (
|
|
1990
|
+
if (fs8.existsSync(location)) {
|
|
1847
1991
|
return location;
|
|
1848
1992
|
}
|
|
1849
1993
|
}
|
|
@@ -1851,18 +1995,18 @@ function getTemplatesDir() {
|
|
|
1851
1995
|
}
|
|
1852
1996
|
async function copyTemplate(templateName, targetDir, data) {
|
|
1853
1997
|
const templatesDir = getTemplatesDir();
|
|
1854
|
-
const templateDir =
|
|
1855
|
-
if (!
|
|
1998
|
+
const templateDir = path13.join(templatesDir, templateName);
|
|
1999
|
+
if (!fs8.existsSync(templateDir)) {
|
|
1856
2000
|
throw new Error(
|
|
1857
|
-
`Template "${templateName}" not found at ${templateDir}. Available templates: ${
|
|
2001
|
+
`Template "${templateName}" not found at ${templateDir}. Available templates: ${fs8.readdirSync(templatesDir).join(", ")}`
|
|
1858
2002
|
);
|
|
1859
2003
|
}
|
|
1860
|
-
await
|
|
1861
|
-
const files = await
|
|
2004
|
+
await fs8.ensureDir(targetDir);
|
|
2005
|
+
const files = await fs8.readdir(templateDir);
|
|
1862
2006
|
for (const file of files) {
|
|
1863
|
-
const templatePath =
|
|
1864
|
-
const targetPath =
|
|
1865
|
-
const stat = await
|
|
2007
|
+
const templatePath = path13.join(templateDir, file);
|
|
2008
|
+
const targetPath = path13.join(targetDir, file);
|
|
2009
|
+
const stat = await fs8.stat(templatePath);
|
|
1866
2010
|
if (stat.isDirectory()) {
|
|
1867
2011
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
1868
2012
|
} else if (file.endsWith(".ejs")) {
|
|
@@ -1870,17 +2014,17 @@ async function copyTemplate(templateName, targetDir, data) {
|
|
|
1870
2014
|
const outputPath = targetPath.replace(/\.ejs$/, "");
|
|
1871
2015
|
await writeFile(outputPath, content);
|
|
1872
2016
|
} else {
|
|
1873
|
-
await
|
|
2017
|
+
await fs8.copy(templatePath, targetPath);
|
|
1874
2018
|
}
|
|
1875
2019
|
}
|
|
1876
2020
|
}
|
|
1877
2021
|
async function copyTemplateDir(templateDir, targetDir, data) {
|
|
1878
|
-
await
|
|
1879
|
-
const files = await
|
|
2022
|
+
await fs8.ensureDir(targetDir);
|
|
2023
|
+
const files = await fs8.readdir(templateDir);
|
|
1880
2024
|
for (const file of files) {
|
|
1881
|
-
const templatePath =
|
|
1882
|
-
const targetPath =
|
|
1883
|
-
const stat = await
|
|
2025
|
+
const templatePath = path13.join(templateDir, file);
|
|
2026
|
+
const targetPath = path13.join(targetDir, file);
|
|
2027
|
+
const stat = await fs8.stat(templatePath);
|
|
1884
2028
|
if (stat.isDirectory()) {
|
|
1885
2029
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
1886
2030
|
} else if (file.endsWith(".ejs")) {
|
|
@@ -1888,38 +2032,38 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1888
2032
|
const outputPath = targetPath.replace(/\.ejs$/, "");
|
|
1889
2033
|
await writeFile(outputPath, content);
|
|
1890
2034
|
} else {
|
|
1891
|
-
await
|
|
2035
|
+
await fs8.copy(templatePath, targetPath);
|
|
1892
2036
|
}
|
|
1893
2037
|
}
|
|
1894
2038
|
}
|
|
1895
2039
|
function getProjectRoot() {
|
|
1896
2040
|
let currentDir = process.cwd();
|
|
1897
|
-
while (currentDir !==
|
|
1898
|
-
const packageJsonPath =
|
|
1899
|
-
if (
|
|
1900
|
-
const packageJson =
|
|
1901
|
-
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"))) {
|
|
1902
2046
|
return currentDir;
|
|
1903
2047
|
}
|
|
1904
2048
|
}
|
|
1905
|
-
currentDir =
|
|
2049
|
+
currentDir = path13.dirname(currentDir);
|
|
1906
2050
|
}
|
|
1907
2051
|
return process.cwd();
|
|
1908
2052
|
}
|
|
1909
2053
|
function getThemesDir() {
|
|
1910
2054
|
const root = getProjectRoot();
|
|
1911
|
-
if (
|
|
1912
|
-
return
|
|
1913
|
-
if (
|
|
1914
|
-
return
|
|
1915
|
-
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);
|
|
1916
2060
|
}
|
|
1917
2061
|
function getFeaturesDir() {
|
|
1918
|
-
return
|
|
2062
|
+
return path13.join(getProjectRoot(), "src/features");
|
|
1919
2063
|
}
|
|
1920
2064
|
function isOneXProject() {
|
|
1921
2065
|
const root = getProjectRoot();
|
|
1922
|
-
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"));
|
|
1923
2067
|
}
|
|
1924
2068
|
function ensureOneXProject() {
|
|
1925
2069
|
if (!isOneXProject()) {
|
|
@@ -1931,17 +2075,17 @@ function ensureOneXProject() {
|
|
|
1931
2075
|
}
|
|
1932
2076
|
function listThemes() {
|
|
1933
2077
|
const themesDir = getThemesDir();
|
|
1934
|
-
if (!
|
|
2078
|
+
if (!fs8.existsSync(themesDir)) {
|
|
1935
2079
|
return [];
|
|
1936
2080
|
}
|
|
1937
|
-
return
|
|
1938
|
-
const themePath =
|
|
1939
|
-
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")));
|
|
1940
2084
|
});
|
|
1941
2085
|
}
|
|
1942
2086
|
function themeExists(themeName) {
|
|
1943
|
-
const themePath =
|
|
1944
|
-
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")));
|
|
1945
2089
|
}
|
|
1946
2090
|
function detectPackageManager() {
|
|
1947
2091
|
const userAgent = process.env.npm_config_user_agent || "";
|
|
@@ -1949,9 +2093,9 @@ function detectPackageManager() {
|
|
|
1949
2093
|
if (userAgent.includes("yarn")) return "yarn";
|
|
1950
2094
|
if (userAgent.includes("bun")) return "bun";
|
|
1951
2095
|
const cwd = process.cwd();
|
|
1952
|
-
if (
|
|
1953
|
-
if (
|
|
1954
|
-
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";
|
|
1955
2099
|
return "npm";
|
|
1956
2100
|
}
|
|
1957
2101
|
async function installDependencies(projectPath, packageManager = "npm") {
|
|
@@ -2000,23 +2144,23 @@ function getValidCategories() {
|
|
|
2000
2144
|
"contact"
|
|
2001
2145
|
];
|
|
2002
2146
|
}
|
|
2003
|
-
var AUTH_DIR =
|
|
2147
|
+
var AUTH_DIR = path13.join(os.homedir(), ".onexthm");
|
|
2004
2148
|
var ENV_URLS = {
|
|
2005
2149
|
dev: "https://platform-dev.onexeos.com",
|
|
2006
2150
|
staging: "https://platform-staging.onexeos.com",
|
|
2007
2151
|
prod: "https://platform-apis.onexeos.com"
|
|
2008
2152
|
};
|
|
2009
2153
|
function getAuthFile(env = "dev") {
|
|
2010
|
-
const newFile =
|
|
2154
|
+
const newFile = path13.join(AUTH_DIR, `auth-${env}.json`);
|
|
2011
2155
|
if (env === "dev") {
|
|
2012
|
-
const legacyFile =
|
|
2013
|
-
if (
|
|
2156
|
+
const legacyFile = path13.join(AUTH_DIR, "auth.json");
|
|
2157
|
+
if (fs8.existsSync(legacyFile) && !fs8.existsSync(newFile)) {
|
|
2014
2158
|
try {
|
|
2015
|
-
|
|
2159
|
+
fs8.moveSync(legacyFile, newFile);
|
|
2016
2160
|
} catch {
|
|
2017
2161
|
try {
|
|
2018
|
-
|
|
2019
|
-
|
|
2162
|
+
fs8.copySync(legacyFile, newFile);
|
|
2163
|
+
fs8.removeSync(legacyFile);
|
|
2020
2164
|
} catch {
|
|
2021
2165
|
}
|
|
2022
2166
|
}
|
|
@@ -2028,17 +2172,17 @@ function getApiUrl(env = "dev") {
|
|
|
2028
2172
|
return process.env.ONEXTHM_API_URL || ENV_URLS[env];
|
|
2029
2173
|
}
|
|
2030
2174
|
async function saveAuthTokens(tokens, env = "dev") {
|
|
2031
|
-
await
|
|
2175
|
+
await fs8.ensureDir(AUTH_DIR);
|
|
2032
2176
|
const key = getMachineKey();
|
|
2033
2177
|
const data = JSON.stringify(tokens);
|
|
2034
2178
|
const encrypted = encrypt(data, key);
|
|
2035
|
-
await
|
|
2179
|
+
await fs8.writeFile(getAuthFile(env), encrypted, "utf-8");
|
|
2036
2180
|
}
|
|
2037
2181
|
function loadAuthTokens(env = "dev") {
|
|
2038
2182
|
try {
|
|
2039
2183
|
const file = getAuthFile(env);
|
|
2040
|
-
if (!
|
|
2041
|
-
const encrypted =
|
|
2184
|
+
if (!fs8.existsSync(file)) return null;
|
|
2185
|
+
const encrypted = fs8.readFileSync(file, "utf-8");
|
|
2042
2186
|
const key = getMachineKey();
|
|
2043
2187
|
const data = decrypt(encrypted, key);
|
|
2044
2188
|
return JSON.parse(data);
|
|
@@ -2048,7 +2192,7 @@ function loadAuthTokens(env = "dev") {
|
|
|
2048
2192
|
}
|
|
2049
2193
|
async function clearAuthTokens(env = "dev") {
|
|
2050
2194
|
try {
|
|
2051
|
-
await
|
|
2195
|
+
await fs8.remove(getAuthFile(env));
|
|
2052
2196
|
} catch {
|
|
2053
2197
|
}
|
|
2054
2198
|
}
|
|
@@ -2107,7 +2251,7 @@ function getMachineKey() {
|
|
|
2107
2251
|
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
2108
2252
|
} else if (process.platform === "linux") {
|
|
2109
2253
|
try {
|
|
2110
|
-
seed = `onexthm:${
|
|
2254
|
+
seed = `onexthm:${fs8.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
2111
2255
|
} catch {
|
|
2112
2256
|
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
2113
2257
|
}
|
|
@@ -2145,7 +2289,7 @@ function parseJwtClaims(idToken) {
|
|
|
2145
2289
|
}
|
|
2146
2290
|
|
|
2147
2291
|
// src/commands/init.ts
|
|
2148
|
-
async function initCommand(projectName, options
|
|
2292
|
+
async function initCommand(projectName, options) {
|
|
2149
2293
|
logger.header("Create New OneX Theme Project");
|
|
2150
2294
|
let name;
|
|
2151
2295
|
if (!projectName) {
|
|
@@ -2160,7 +2304,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
2160
2304
|
if (!validateThemeName(kebabName)) {
|
|
2161
2305
|
return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
|
|
2162
2306
|
}
|
|
2163
|
-
if (fs3.existsSync(
|
|
2307
|
+
if (fs3.existsSync(path13.join(process.cwd(), kebabName))) {
|
|
2164
2308
|
return `Directory "${kebabName}" already exists`;
|
|
2165
2309
|
}
|
|
2166
2310
|
return true;
|
|
@@ -2171,14 +2315,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
2171
2315
|
} else {
|
|
2172
2316
|
name = toKebabCase(projectName);
|
|
2173
2317
|
}
|
|
2174
|
-
const projectPath =
|
|
2318
|
+
const projectPath = path13.join(process.cwd(), name);
|
|
2175
2319
|
if (fs3.existsSync(projectPath)) {
|
|
2176
2320
|
logger.error(`Directory "${name}" already exists.`);
|
|
2177
2321
|
process.exit(1);
|
|
2178
2322
|
}
|
|
2179
2323
|
if (!options.yes) {
|
|
2180
2324
|
try {
|
|
2181
|
-
const apiUrl = getApiUrl(options.env
|
|
2325
|
+
const apiUrl = getApiUrl(options.env);
|
|
2182
2326
|
const controller = new AbortController();
|
|
2183
2327
|
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
2184
2328
|
const response = await fetch(
|
|
@@ -2287,7 +2431,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
2287
2431
|
description,
|
|
2288
2432
|
author
|
|
2289
2433
|
);
|
|
2290
|
-
const mcpJsonPath =
|
|
2434
|
+
const mcpJsonPath = path13.join(projectPath, ".mcp.json");
|
|
2291
2435
|
if (fs3.existsSync(mcpJsonPath)) {
|
|
2292
2436
|
let mcpContent = fs3.readFileSync(mcpJsonPath, "utf-8");
|
|
2293
2437
|
if (figmaApiKey) {
|
|
@@ -2367,7 +2511,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
2367
2511
|
}
|
|
2368
2512
|
}
|
|
2369
2513
|
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
2370
|
-
const configPath =
|
|
2514
|
+
const configPath = path13.join(projectPath, "theme.config.ts");
|
|
2371
2515
|
if (fs3.existsSync(configPath)) {
|
|
2372
2516
|
let content = fs3.readFileSync(configPath, "utf-8");
|
|
2373
2517
|
content = content.replace(
|
|
@@ -2380,7 +2524,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
|
|
|
2380
2524
|
);
|
|
2381
2525
|
fs3.writeFileSync(configPath, content, "utf-8");
|
|
2382
2526
|
}
|
|
2383
|
-
const pkgPath =
|
|
2527
|
+
const pkgPath = path13.join(projectPath, "package.json");
|
|
2384
2528
|
if (fs3.existsSync(pkgPath)) {
|
|
2385
2529
|
let content = fs3.readFileSync(pkgPath, "utf-8");
|
|
2386
2530
|
content = content.replace(
|
|
@@ -2402,10 +2546,10 @@ async function createSectionCommand(name, options) {
|
|
|
2402
2546
|
ensureOneXProject();
|
|
2403
2547
|
if (!options.theme) {
|
|
2404
2548
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
2405
|
-
(f) =>
|
|
2549
|
+
(f) => fs8.existsSync(path13.join(process.cwd(), f))
|
|
2406
2550
|
);
|
|
2407
2551
|
if (isStandaloneTheme) {
|
|
2408
|
-
options.theme =
|
|
2552
|
+
options.theme = path13.basename(process.cwd());
|
|
2409
2553
|
}
|
|
2410
2554
|
}
|
|
2411
2555
|
const sectionName = toKebabCase(name);
|
|
@@ -2468,35 +2612,35 @@ async function createSectionCommand(name, options) {
|
|
|
2468
2612
|
};
|
|
2469
2613
|
logger.startSpinner("Creating section files...");
|
|
2470
2614
|
try {
|
|
2471
|
-
const themePath =
|
|
2472
|
-
const sectionPath =
|
|
2615
|
+
const themePath = path13.join(getThemesDir(), themeName);
|
|
2616
|
+
const sectionPath = path13.join(themePath, "sections", sectionName);
|
|
2473
2617
|
const schemaContent = generateSectionSchema(data);
|
|
2474
2618
|
await writeFile(
|
|
2475
|
-
|
|
2619
|
+
path13.join(sectionPath, `${sectionName}.schema.ts`),
|
|
2476
2620
|
schemaContent
|
|
2477
2621
|
);
|
|
2478
2622
|
if (createTemplate) {
|
|
2479
2623
|
const templateContent = generateSectionTemplate(data);
|
|
2480
2624
|
await writeFile(
|
|
2481
|
-
|
|
2625
|
+
path13.join(sectionPath, `${sectionName}-default.tsx`),
|
|
2482
2626
|
templateContent
|
|
2483
2627
|
);
|
|
2484
2628
|
}
|
|
2485
2629
|
const indexContent = generateSectionIndex(data, createTemplate);
|
|
2486
|
-
await writeFile(
|
|
2630
|
+
await writeFile(path13.join(sectionPath, "index.ts"), indexContent);
|
|
2487
2631
|
logger.stopSpinner(true, "Section files created successfully!");
|
|
2488
2632
|
logger.newLine();
|
|
2489
2633
|
logger.section("Next steps:");
|
|
2490
2634
|
logger.log(
|
|
2491
|
-
` 1. Edit schema: ${
|
|
2635
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(sectionPath, `${sectionName}.schema.ts`))}`
|
|
2492
2636
|
);
|
|
2493
2637
|
if (createTemplate) {
|
|
2494
2638
|
logger.log(
|
|
2495
|
-
` 2. Edit template: ${
|
|
2639
|
+
` 2. Edit template: ${path13.relative(process.cwd(), path13.join(sectionPath, `${sectionName}-default.tsx`))}`
|
|
2496
2640
|
);
|
|
2497
2641
|
}
|
|
2498
2642
|
logger.log(
|
|
2499
|
-
` 3. Add to theme manifest: ${
|
|
2643
|
+
` 3. Add to theme manifest: ${path13.relative(process.cwd(), path13.join(themePath, "manifest.ts"))}`
|
|
2500
2644
|
);
|
|
2501
2645
|
logger.newLine();
|
|
2502
2646
|
logger.success("Section created successfully!");
|
|
@@ -2644,10 +2788,10 @@ async function createBlockCommand(name, options) {
|
|
|
2644
2788
|
ensureOneXProject();
|
|
2645
2789
|
if (!options.theme) {
|
|
2646
2790
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
2647
|
-
(f) =>
|
|
2791
|
+
(f) => fs8.existsSync(path13.join(process.cwd(), f))
|
|
2648
2792
|
);
|
|
2649
2793
|
if (isStandaloneTheme) {
|
|
2650
|
-
options.theme =
|
|
2794
|
+
options.theme = path13.basename(process.cwd());
|
|
2651
2795
|
}
|
|
2652
2796
|
}
|
|
2653
2797
|
const blockName = toKebabCase(name);
|
|
@@ -2722,24 +2866,24 @@ async function createBlockCommand(name, options) {
|
|
|
2722
2866
|
};
|
|
2723
2867
|
logger.startSpinner("Creating block files...");
|
|
2724
2868
|
try {
|
|
2725
|
-
const blockPath = scope === "shared" ?
|
|
2869
|
+
const blockPath = scope === "shared" ? path13.join(getFeaturesDir(), "blocks", blockName) : path13.join(getThemesDir(), themeName, "blocks", blockName);
|
|
2726
2870
|
const schemaContent = generateBlockSchema(data);
|
|
2727
2871
|
await writeFile(
|
|
2728
|
-
|
|
2872
|
+
path13.join(blockPath, `${blockName}.schema.ts`),
|
|
2729
2873
|
schemaContent
|
|
2730
2874
|
);
|
|
2731
2875
|
const componentContent = generateBlockComponent(data);
|
|
2732
|
-
await writeFile(
|
|
2876
|
+
await writeFile(path13.join(blockPath, `${blockName}.tsx`), componentContent);
|
|
2733
2877
|
const indexContent = generateBlockIndex(data);
|
|
2734
|
-
await writeFile(
|
|
2878
|
+
await writeFile(path13.join(blockPath, "index.ts"), indexContent);
|
|
2735
2879
|
logger.stopSpinner(true, "Block files created successfully!");
|
|
2736
2880
|
logger.newLine();
|
|
2737
2881
|
logger.section("Next steps:");
|
|
2738
2882
|
logger.log(
|
|
2739
|
-
` 1. Edit schema: ${
|
|
2883
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(blockPath, `${blockName}.schema.ts`))}`
|
|
2740
2884
|
);
|
|
2741
2885
|
logger.log(
|
|
2742
|
-
` 2. Edit component: ${
|
|
2886
|
+
` 2. Edit component: ${path13.relative(process.cwd(), path13.join(blockPath, `${blockName}.tsx`))}`
|
|
2743
2887
|
);
|
|
2744
2888
|
logger.log(
|
|
2745
2889
|
` 3. Register in block registry: src/lib/registry/block-registry.ts`
|
|
@@ -2917,31 +3061,31 @@ async function createComponentCommand(name, options) {
|
|
|
2917
3061
|
};
|
|
2918
3062
|
logger.startSpinner("Creating component files...");
|
|
2919
3063
|
try {
|
|
2920
|
-
const componentPath =
|
|
3064
|
+
const componentPath = path13.join(
|
|
2921
3065
|
getFeaturesDir(),
|
|
2922
3066
|
"components",
|
|
2923
3067
|
componentName
|
|
2924
3068
|
);
|
|
2925
3069
|
const schemaContent = generateComponentSchema(data);
|
|
2926
3070
|
await writeFile(
|
|
2927
|
-
|
|
3071
|
+
path13.join(componentPath, `${componentName}.schema.ts`),
|
|
2928
3072
|
schemaContent
|
|
2929
3073
|
);
|
|
2930
3074
|
const componentContent = generateComponent(data);
|
|
2931
3075
|
await writeFile(
|
|
2932
|
-
|
|
3076
|
+
path13.join(componentPath, `${componentName}.tsx`),
|
|
2933
3077
|
componentContent
|
|
2934
3078
|
);
|
|
2935
3079
|
const indexContent = generateComponentIndex(data);
|
|
2936
|
-
await writeFile(
|
|
3080
|
+
await writeFile(path13.join(componentPath, "index.ts"), indexContent);
|
|
2937
3081
|
logger.stopSpinner(true, "Component files created successfully!");
|
|
2938
3082
|
logger.newLine();
|
|
2939
3083
|
logger.section("Next steps:");
|
|
2940
3084
|
logger.log(
|
|
2941
|
-
` 1. Edit schema: ${
|
|
3085
|
+
` 1. Edit schema: ${path13.relative(process.cwd(), path13.join(componentPath, `${componentName}.schema.ts`))}`
|
|
2942
3086
|
);
|
|
2943
3087
|
logger.log(
|
|
2944
|
-
` 2. Edit component: ${
|
|
3088
|
+
` 2. Edit component: ${path13.relative(process.cwd(), path13.join(componentPath, `${componentName}.tsx`))}`
|
|
2945
3089
|
);
|
|
2946
3090
|
logger.log(
|
|
2947
3091
|
` 3. Register in component registry: src/lib/registry/component-registry.ts`
|
|
@@ -3098,13 +3242,13 @@ async function listSections(themeFilter) {
|
|
|
3098
3242
|
return;
|
|
3099
3243
|
}
|
|
3100
3244
|
for (const theme of themes) {
|
|
3101
|
-
const sectionsDir =
|
|
3102
|
-
if (!
|
|
3245
|
+
const sectionsDir = path13.join(getThemesDir(), theme, "sections");
|
|
3246
|
+
if (!fs8.existsSync(sectionsDir)) {
|
|
3103
3247
|
continue;
|
|
3104
3248
|
}
|
|
3105
|
-
const sections =
|
|
3106
|
-
const sectionPath =
|
|
3107
|
-
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"));
|
|
3108
3252
|
});
|
|
3109
3253
|
if (sections.length > 0) {
|
|
3110
3254
|
logger.log(chalk4.cyan(`
|
|
@@ -3118,11 +3262,11 @@ async function listSections(themeFilter) {
|
|
|
3118
3262
|
}
|
|
3119
3263
|
async function listBlocks(themeFilter) {
|
|
3120
3264
|
logger.section("\u{1F9F1} Blocks");
|
|
3121
|
-
const sharedBlocksDir =
|
|
3122
|
-
if (
|
|
3123
|
-
const sharedBlocks =
|
|
3124
|
-
const blockPath =
|
|
3125
|
-
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"));
|
|
3126
3270
|
});
|
|
3127
3271
|
if (sharedBlocks.length > 0) {
|
|
3128
3272
|
logger.log(chalk4.cyan("\n Shared:"));
|
|
@@ -3133,13 +3277,13 @@ async function listBlocks(themeFilter) {
|
|
|
3133
3277
|
}
|
|
3134
3278
|
const themes = themeFilter ? [themeFilter] : listThemes();
|
|
3135
3279
|
for (const theme of themes) {
|
|
3136
|
-
const blocksDir =
|
|
3137
|
-
if (!
|
|
3280
|
+
const blocksDir = path13.join(getThemesDir(), theme, "blocks");
|
|
3281
|
+
if (!fs8.existsSync(blocksDir)) {
|
|
3138
3282
|
continue;
|
|
3139
3283
|
}
|
|
3140
|
-
const blocks =
|
|
3141
|
-
const blockPath =
|
|
3142
|
-
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"));
|
|
3143
3287
|
});
|
|
3144
3288
|
if (blocks.length > 0) {
|
|
3145
3289
|
logger.log(chalk4.cyan(`
|
|
@@ -3153,14 +3297,14 @@ async function listBlocks(themeFilter) {
|
|
|
3153
3297
|
}
|
|
3154
3298
|
async function listComponents() {
|
|
3155
3299
|
logger.section("\u2699\uFE0F Components");
|
|
3156
|
-
const componentsDir =
|
|
3157
|
-
if (!
|
|
3300
|
+
const componentsDir = path13.join(getFeaturesDir(), "components");
|
|
3301
|
+
if (!fs8.existsSync(componentsDir)) {
|
|
3158
3302
|
logger.warning("No components directory found");
|
|
3159
3303
|
return;
|
|
3160
3304
|
}
|
|
3161
|
-
const components =
|
|
3162
|
-
const componentPath =
|
|
3163
|
-
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"));
|
|
3164
3308
|
});
|
|
3165
3309
|
if (components.length === 0) {
|
|
3166
3310
|
logger.warning("No components found");
|
|
@@ -3181,13 +3325,13 @@ async function listThemesInfo() {
|
|
|
3181
3325
|
}
|
|
3182
3326
|
logger.log("");
|
|
3183
3327
|
for (const theme of themes) {
|
|
3184
|
-
const themeDir =
|
|
3328
|
+
const themeDir = path13.join(getThemesDir(), theme);
|
|
3185
3329
|
const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
|
|
3186
3330
|
let manifestContent = "";
|
|
3187
3331
|
for (const candidate of candidates) {
|
|
3188
|
-
const candidatePath =
|
|
3189
|
-
if (
|
|
3190
|
-
manifestContent =
|
|
3332
|
+
const candidatePath = path13.join(themeDir, candidate);
|
|
3333
|
+
if (fs8.existsSync(candidatePath)) {
|
|
3334
|
+
manifestContent = fs8.readFileSync(candidatePath, "utf-8");
|
|
3191
3335
|
break;
|
|
3192
3336
|
}
|
|
3193
3337
|
}
|
|
@@ -3207,6 +3351,7 @@ async function listThemesInfo() {
|
|
|
3207
3351
|
|
|
3208
3352
|
// src/commands/validate.ts
|
|
3209
3353
|
init_logger();
|
|
3354
|
+
init_detect_nextjs();
|
|
3210
3355
|
async function validateCommand(options) {
|
|
3211
3356
|
logger.header("Validate Theme");
|
|
3212
3357
|
ensureOneXProject();
|
|
@@ -3222,10 +3367,13 @@ async function validateCommand(options) {
|
|
|
3222
3367
|
const isThemeDir2 = [
|
|
3223
3368
|
"theme.config.ts",
|
|
3224
3369
|
"bundle-entry.ts",
|
|
3225
|
-
"manifest.ts"
|
|
3226
|
-
|
|
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)));
|
|
3227
3375
|
if (isThemeDir2) {
|
|
3228
|
-
themeToValidate =
|
|
3376
|
+
themeToValidate = path13.basename(process.cwd());
|
|
3229
3377
|
logger.info(`Validating current theme: ${themeToValidate}`);
|
|
3230
3378
|
} else {
|
|
3231
3379
|
logger.error(
|
|
@@ -3234,11 +3382,11 @@ async function validateCommand(options) {
|
|
|
3234
3382
|
process.exit(1);
|
|
3235
3383
|
}
|
|
3236
3384
|
}
|
|
3237
|
-
const themePath =
|
|
3385
|
+
const themePath = path13.join(getThemesDir(), themeToValidate);
|
|
3238
3386
|
logger.startSpinner("Running validation checks...");
|
|
3239
3387
|
const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
|
|
3240
3388
|
const foundEntry = entryFiles.find(
|
|
3241
|
-
(f) =>
|
|
3389
|
+
(f) => fs8.existsSync(path13.join(themePath, f))
|
|
3242
3390
|
);
|
|
3243
3391
|
if (!foundEntry) {
|
|
3244
3392
|
issues.push({
|
|
@@ -3247,8 +3395,8 @@ async function validateCommand(options) {
|
|
|
3247
3395
|
message: "No theme entry file found (need at least one of: manifest.ts, theme.config.ts, bundle-entry.ts)"
|
|
3248
3396
|
});
|
|
3249
3397
|
} else if (foundEntry === "manifest.ts") {
|
|
3250
|
-
const manifestContent =
|
|
3251
|
-
|
|
3398
|
+
const manifestContent = fs8.readFileSync(
|
|
3399
|
+
path13.join(themePath, foundEntry),
|
|
3252
3400
|
"utf-8"
|
|
3253
3401
|
);
|
|
3254
3402
|
if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
|
|
@@ -3259,56 +3407,56 @@ async function validateCommand(options) {
|
|
|
3259
3407
|
});
|
|
3260
3408
|
}
|
|
3261
3409
|
}
|
|
3262
|
-
const configPath =
|
|
3263
|
-
if (!
|
|
3410
|
+
const configPath = path13.join(themePath, "theme.config.ts");
|
|
3411
|
+
if (!fs8.existsSync(configPath)) {
|
|
3264
3412
|
issues.push({
|
|
3265
3413
|
type: "warning",
|
|
3266
3414
|
file: "theme.config.ts",
|
|
3267
3415
|
message: "Theme config file not found (recommended)"
|
|
3268
3416
|
});
|
|
3269
3417
|
}
|
|
3270
|
-
const indexPath =
|
|
3271
|
-
if (!
|
|
3418
|
+
const indexPath = path13.join(themePath, "index.ts");
|
|
3419
|
+
if (!fs8.existsSync(indexPath)) {
|
|
3272
3420
|
issues.push({
|
|
3273
3421
|
type: "warning",
|
|
3274
3422
|
file: "index.ts",
|
|
3275
3423
|
message: "Index file not found (recommended)"
|
|
3276
3424
|
});
|
|
3277
3425
|
}
|
|
3278
|
-
const sectionsDir =
|
|
3279
|
-
if (!
|
|
3426
|
+
const sectionsDir = path13.join(themePath, "sections");
|
|
3427
|
+
if (!fs8.existsSync(sectionsDir)) {
|
|
3280
3428
|
issues.push({
|
|
3281
3429
|
type: "warning",
|
|
3282
3430
|
file: "sections/",
|
|
3283
3431
|
message: "Sections directory not found"
|
|
3284
3432
|
});
|
|
3285
3433
|
} else {
|
|
3286
|
-
const sections =
|
|
3287
|
-
(name) =>
|
|
3434
|
+
const sections = fs8.readdirSync(sectionsDir).filter(
|
|
3435
|
+
(name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory()
|
|
3288
3436
|
);
|
|
3289
3437
|
for (const sectionName of sections) {
|
|
3290
|
-
const sectionPath =
|
|
3291
|
-
const schemaFile =
|
|
3292
|
-
const defaultTemplate =
|
|
3438
|
+
const sectionPath = path13.join(sectionsDir, sectionName);
|
|
3439
|
+
const schemaFile = path13.join(sectionPath, `${sectionName}.schema.ts`);
|
|
3440
|
+
const defaultTemplate = path13.join(
|
|
3293
3441
|
sectionPath,
|
|
3294
3442
|
`${sectionName}-default.tsx`
|
|
3295
3443
|
);
|
|
3296
|
-
const indexFile =
|
|
3297
|
-
if (!
|
|
3444
|
+
const indexFile = path13.join(sectionPath, "index.ts");
|
|
3445
|
+
if (!fs8.existsSync(schemaFile)) {
|
|
3298
3446
|
issues.push({
|
|
3299
3447
|
type: "error",
|
|
3300
3448
|
file: `sections/${sectionName}/${sectionName}.schema.ts`,
|
|
3301
3449
|
message: "Section schema file missing"
|
|
3302
3450
|
});
|
|
3303
3451
|
}
|
|
3304
|
-
if (!
|
|
3452
|
+
if (!fs8.existsSync(indexFile)) {
|
|
3305
3453
|
issues.push({
|
|
3306
3454
|
type: "error",
|
|
3307
3455
|
file: `sections/${sectionName}/index.ts`,
|
|
3308
3456
|
message: "Section index file missing"
|
|
3309
3457
|
});
|
|
3310
3458
|
}
|
|
3311
|
-
if (!
|
|
3459
|
+
if (!fs8.existsSync(defaultTemplate)) {
|
|
3312
3460
|
issues.push({
|
|
3313
3461
|
type: "warning",
|
|
3314
3462
|
file: `sections/${sectionName}/${sectionName}-default.tsx`,
|
|
@@ -3317,29 +3465,29 @@ async function validateCommand(options) {
|
|
|
3317
3465
|
}
|
|
3318
3466
|
}
|
|
3319
3467
|
}
|
|
3320
|
-
const blocksDir =
|
|
3321
|
-
if (
|
|
3322
|
-
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());
|
|
3323
3471
|
for (const blockName of blocks) {
|
|
3324
|
-
const blockPath =
|
|
3325
|
-
const schemaFile =
|
|
3326
|
-
const componentFile =
|
|
3327
|
-
const indexFile =
|
|
3328
|
-
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)) {
|
|
3329
3477
|
issues.push({
|
|
3330
3478
|
type: "error",
|
|
3331
3479
|
file: `blocks/${blockName}/${blockName}.schema.ts`,
|
|
3332
3480
|
message: "Block schema file missing"
|
|
3333
3481
|
});
|
|
3334
3482
|
}
|
|
3335
|
-
if (!
|
|
3483
|
+
if (!fs8.existsSync(componentFile)) {
|
|
3336
3484
|
issues.push({
|
|
3337
3485
|
type: "error",
|
|
3338
3486
|
file: `blocks/${blockName}/${blockName}.tsx`,
|
|
3339
3487
|
message: "Block component file missing"
|
|
3340
3488
|
});
|
|
3341
3489
|
}
|
|
3342
|
-
if (!
|
|
3490
|
+
if (!fs8.existsSync(indexFile)) {
|
|
3343
3491
|
issues.push({
|
|
3344
3492
|
type: "error",
|
|
3345
3493
|
file: `blocks/${blockName}/index.ts`,
|
|
@@ -3348,16 +3496,16 @@ async function validateCommand(options) {
|
|
|
3348
3496
|
}
|
|
3349
3497
|
}
|
|
3350
3498
|
}
|
|
3351
|
-
if (
|
|
3352
|
-
const sections =
|
|
3353
|
-
(name) =>
|
|
3499
|
+
if (fs8.existsSync(sectionsDir)) {
|
|
3500
|
+
const sections = fs8.readdirSync(sectionsDir).filter(
|
|
3501
|
+
(name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory()
|
|
3354
3502
|
);
|
|
3355
3503
|
for (const sectionName of sections) {
|
|
3356
|
-
const sectionPath =
|
|
3357
|
-
const tsxFiles =
|
|
3504
|
+
const sectionPath = path13.join(sectionsDir, sectionName);
|
|
3505
|
+
const tsxFiles = fs8.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
|
|
3358
3506
|
for (const tsxFile of tsxFiles) {
|
|
3359
|
-
const filePath =
|
|
3360
|
-
const content =
|
|
3507
|
+
const filePath = path13.join(sectionPath, tsxFile);
|
|
3508
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
3361
3509
|
const relPath = `sections/${sectionName}/${tsxFile}`;
|
|
3362
3510
|
if (!content.includes('"use client"') && !content.includes("'use client'")) {
|
|
3363
3511
|
issues.push({
|
|
@@ -3404,12 +3552,12 @@ async function validateCommand(options) {
|
|
|
3404
3552
|
}
|
|
3405
3553
|
}
|
|
3406
3554
|
}
|
|
3407
|
-
const registryPath =
|
|
3408
|
-
const bundleEntryPath =
|
|
3409
|
-
const registryContent =
|
|
3410
|
-
if (
|
|
3411
|
-
const sections =
|
|
3412
|
-
(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()
|
|
3413
3561
|
);
|
|
3414
3562
|
for (const sectionName of sections) {
|
|
3415
3563
|
if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
|
|
@@ -3421,7 +3569,7 @@ async function validateCommand(options) {
|
|
|
3421
3569
|
}
|
|
3422
3570
|
}
|
|
3423
3571
|
}
|
|
3424
|
-
if (
|
|
3572
|
+
if (fs8.existsSync(sectionsDir)) {
|
|
3425
3573
|
const schemaTypes = await loadSchemaTypes(themePath, sectionsDir);
|
|
3426
3574
|
for (const { folderName, schemaType } of schemaTypes) {
|
|
3427
3575
|
if (schemaType && schemaType !== folderName) {
|
|
@@ -3432,8 +3580,8 @@ async function validateCommand(options) {
|
|
|
3432
3580
|
});
|
|
3433
3581
|
}
|
|
3434
3582
|
}
|
|
3435
|
-
const pagesDir =
|
|
3436
|
-
if (
|
|
3583
|
+
const pagesDir = path13.join(themePath, "pages");
|
|
3584
|
+
if (fs8.existsSync(pagesDir)) {
|
|
3437
3585
|
const allSchemaTypeSet = new Set(
|
|
3438
3586
|
schemaTypes.map((s) => s.schemaType || s.folderName)
|
|
3439
3587
|
);
|
|
@@ -3444,6 +3592,10 @@ async function validateCommand(options) {
|
|
|
3444
3592
|
issues.push(...pageIssues);
|
|
3445
3593
|
}
|
|
3446
3594
|
}
|
|
3595
|
+
if (isNextjsProject(themePath)) {
|
|
3596
|
+
const nextjsIssues = await validateNextjsComponents(themePath);
|
|
3597
|
+
issues.push(...nextjsIssues);
|
|
3598
|
+
}
|
|
3447
3599
|
logger.stopSpinner(true, "Validation complete");
|
|
3448
3600
|
const errors = issues.filter((i) => i.type === "error");
|
|
3449
3601
|
const warnings = issues.filter((i) => i.type === "warning");
|
|
@@ -3484,18 +3636,18 @@ async function validateCommand(options) {
|
|
|
3484
3636
|
}
|
|
3485
3637
|
async function loadSchemaTypes(themePath, sectionsDir) {
|
|
3486
3638
|
const results = [];
|
|
3487
|
-
const sections =
|
|
3639
|
+
const sections = fs8.readdirSync(sectionsDir).filter((name) => fs8.statSync(path13.join(sectionsDir, name)).isDirectory());
|
|
3488
3640
|
for (const sectionName of sections) {
|
|
3489
|
-
const schemaFile =
|
|
3641
|
+
const schemaFile = path13.join(
|
|
3490
3642
|
sectionsDir,
|
|
3491
3643
|
sectionName,
|
|
3492
3644
|
`${sectionName}.schema.ts`
|
|
3493
3645
|
);
|
|
3494
|
-
if (!
|
|
3646
|
+
if (!fs8.existsSync(schemaFile)) {
|
|
3495
3647
|
results.push({ folderName: sectionName, schemaType: null });
|
|
3496
3648
|
continue;
|
|
3497
3649
|
}
|
|
3498
|
-
const content =
|
|
3650
|
+
const content = fs8.readFileSync(schemaFile, "utf-8");
|
|
3499
3651
|
let schemaType = null;
|
|
3500
3652
|
const schemaExportMatch = content.match(
|
|
3501
3653
|
/:\s*SectionSchema\s*=\s*\{[\s\S]*?\btype:\s*["']([^"']+)["']/
|
|
@@ -3522,9 +3674,9 @@ async function loadSchemaTypes(themePath, sectionsDir) {
|
|
|
3522
3674
|
}
|
|
3523
3675
|
async function validatePageSectionTypes(pagesDir, validTypes) {
|
|
3524
3676
|
const issues = [];
|
|
3525
|
-
const files =
|
|
3677
|
+
const files = fs8.readdirSync(pagesDir).filter((f) => f.match(/\.(ts|js)$/));
|
|
3526
3678
|
for (const file of files) {
|
|
3527
|
-
const content =
|
|
3679
|
+
const content = fs8.readFileSync(path13.join(pagesDir, file), "utf-8");
|
|
3528
3680
|
const pageName = file.replace(/\.(ts|js)$/, "");
|
|
3529
3681
|
const sectionsMatch = content.match(/\bsections:\s*\[/);
|
|
3530
3682
|
if (!sectionsMatch || sectionsMatch.index === void 0) continue;
|
|
@@ -3654,9 +3806,70 @@ var BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
3654
3806
|
"team-member",
|
|
3655
3807
|
"hero-content"
|
|
3656
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
|
+
}
|
|
3657
3869
|
|
|
3658
3870
|
// src/commands/build.ts
|
|
3659
3871
|
init_logger();
|
|
3872
|
+
init_detect_nextjs();
|
|
3660
3873
|
async function buildCommand(options) {
|
|
3661
3874
|
logger.header("Build Theme");
|
|
3662
3875
|
let themePath;
|
|
@@ -3664,16 +3877,16 @@ async function buildCommand(options) {
|
|
|
3664
3877
|
if (options.theme) {
|
|
3665
3878
|
themeName = options.theme;
|
|
3666
3879
|
try {
|
|
3667
|
-
const workspaceThemePath =
|
|
3668
|
-
if (
|
|
3880
|
+
const workspaceThemePath = path13.join(getThemesDir(), themeName);
|
|
3881
|
+
if (fs8.existsSync(workspaceThemePath)) {
|
|
3669
3882
|
themePath = workspaceThemePath;
|
|
3670
3883
|
} else {
|
|
3671
|
-
themePath =
|
|
3884
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
3672
3885
|
}
|
|
3673
3886
|
} catch {
|
|
3674
|
-
themePath =
|
|
3887
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
3675
3888
|
}
|
|
3676
|
-
if (!
|
|
3889
|
+
if (!fs8.existsSync(themePath)) {
|
|
3677
3890
|
logger.error(`Theme "${themeName}" not found.`);
|
|
3678
3891
|
process.exit(1);
|
|
3679
3892
|
}
|
|
@@ -3681,11 +3894,14 @@ async function buildCommand(options) {
|
|
|
3681
3894
|
const isThemeDir2 = [
|
|
3682
3895
|
"theme.config.ts",
|
|
3683
3896
|
"bundle-entry.ts",
|
|
3684
|
-
"manifest.ts"
|
|
3685
|
-
|
|
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)));
|
|
3686
3902
|
if (isThemeDir2) {
|
|
3687
3903
|
themePath = process.cwd();
|
|
3688
|
-
themeName =
|
|
3904
|
+
themeName = path13.basename(themePath);
|
|
3689
3905
|
logger.info(`Building current theme: ${themeName}`);
|
|
3690
3906
|
} else {
|
|
3691
3907
|
logger.error(
|
|
@@ -3694,8 +3910,8 @@ async function buildCommand(options) {
|
|
|
3694
3910
|
process.exit(1);
|
|
3695
3911
|
}
|
|
3696
3912
|
}
|
|
3697
|
-
const packageJsonPath =
|
|
3698
|
-
const hasPkgJson =
|
|
3913
|
+
const packageJsonPath = path13.join(themePath, "package.json");
|
|
3914
|
+
const hasPkgJson = fs8.existsSync(packageJsonPath);
|
|
3699
3915
|
if (!hasPkgJson) {
|
|
3700
3916
|
logger.warning(
|
|
3701
3917
|
"No package.json found in theme. Skipping build (themes in monorepo are built via turbo)."
|
|
@@ -3710,30 +3926,42 @@ async function buildCommand(options) {
|
|
|
3710
3926
|
}
|
|
3711
3927
|
logger.newLine();
|
|
3712
3928
|
logger.section("Build Steps");
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
logger.
|
|
3725
|
-
|
|
3726
|
-
|
|
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)");
|
|
3727
3956
|
}
|
|
3728
|
-
logger.stopSpinner(true, "Lint passed");
|
|
3729
|
-
const pkgJson = fs.readJsonSync(packageJsonPath);
|
|
3730
3957
|
const buildScript = pkgJson.scripts?.build || "";
|
|
3731
3958
|
const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex build") || buildScript.includes("onex-cli build");
|
|
3959
|
+
const isNextjs = isNextjsProject(themePath);
|
|
3732
3960
|
logger.startSpinner(
|
|
3733
3961
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
3734
3962
|
);
|
|
3735
3963
|
let buildSuccess;
|
|
3736
|
-
if (isRecursive) {
|
|
3964
|
+
if (isRecursive || isNextjs) {
|
|
3737
3965
|
const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
3738
3966
|
buildSuccess = await compileStandaloneTheme2(themePath, themeName);
|
|
3739
3967
|
} else {
|
|
@@ -3750,10 +3978,10 @@ async function buildCommand(options) {
|
|
|
3750
3978
|
logger.success("\u2713 Theme built successfully!");
|
|
3751
3979
|
logger.newLine();
|
|
3752
3980
|
logger.info(`Theme: ${themeName}`);
|
|
3753
|
-
const distPath =
|
|
3754
|
-
if (
|
|
3755
|
-
logger.log(`Output: ${
|
|
3756
|
-
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);
|
|
3757
3985
|
logger.log(`Files: ${files.length}`);
|
|
3758
3986
|
}
|
|
3759
3987
|
}
|
|
@@ -3808,8 +4036,8 @@ async function packageCommand(options) {
|
|
|
3808
4036
|
let themeName;
|
|
3809
4037
|
if (options.theme) {
|
|
3810
4038
|
themeName = options.theme;
|
|
3811
|
-
themePath =
|
|
3812
|
-
if (!
|
|
4039
|
+
themePath = path13.join(getThemesDir(), themeName);
|
|
4040
|
+
if (!fs8.existsSync(themePath)) {
|
|
3813
4041
|
logger.error(`Theme "${themeName}" not found.`);
|
|
3814
4042
|
process.exit(1);
|
|
3815
4043
|
}
|
|
@@ -3817,11 +4045,14 @@ async function packageCommand(options) {
|
|
|
3817
4045
|
const isThemeDir2 = [
|
|
3818
4046
|
"theme.config.ts",
|
|
3819
4047
|
"bundle-entry.ts",
|
|
3820
|
-
"manifest.ts"
|
|
3821
|
-
|
|
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)));
|
|
3822
4053
|
if (isThemeDir2) {
|
|
3823
4054
|
themePath = process.cwd();
|
|
3824
|
-
themeName =
|
|
4055
|
+
themeName = path13.basename(themePath);
|
|
3825
4056
|
logger.info(`Packaging current theme: ${themeName}`);
|
|
3826
4057
|
} else {
|
|
3827
4058
|
logger.error(
|
|
@@ -3830,22 +4061,19 @@ async function packageCommand(options) {
|
|
|
3830
4061
|
process.exit(1);
|
|
3831
4062
|
}
|
|
3832
4063
|
}
|
|
3833
|
-
const packageJsonPath =
|
|
4064
|
+
const packageJsonPath = path13.join(themePath, "package.json");
|
|
3834
4065
|
let version2 = "1.0.0";
|
|
3835
|
-
if (
|
|
3836
|
-
const packageJson = await
|
|
4066
|
+
if (fs8.existsSync(packageJsonPath)) {
|
|
4067
|
+
const packageJson = await fs8.readJson(packageJsonPath);
|
|
3837
4068
|
version2 = packageJson.version || "1.0.0";
|
|
3838
4069
|
}
|
|
3839
4070
|
logger.newLine();
|
|
3840
4071
|
logger.info(`Theme: ${themeName}`);
|
|
3841
4072
|
logger.info(`Version: ${version2}`);
|
|
3842
4073
|
logger.newLine();
|
|
3843
|
-
const
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
themeName,
|
|
3847
|
-
"dist"
|
|
3848
|
-
);
|
|
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;
|
|
3849
4077
|
if (!options.skipBuild) {
|
|
3850
4078
|
logger.section("Step 1: Compile Theme");
|
|
3851
4079
|
logger.startSpinner("Compiling theme with esbuild...");
|
|
@@ -3866,7 +4094,7 @@ async function packageCommand(options) {
|
|
|
3866
4094
|
} else {
|
|
3867
4095
|
logger.info("Skipping build (--skip-build flag)");
|
|
3868
4096
|
}
|
|
3869
|
-
if (!
|
|
4097
|
+
if (!fs8.existsSync(compiledThemePath)) {
|
|
3870
4098
|
logger.error(`Compiled theme not found at: ${compiledThemePath}`);
|
|
3871
4099
|
logger.info("Run without --skip-build to compile first.");
|
|
3872
4100
|
process.exit(1);
|
|
@@ -3874,25 +4102,25 @@ async function packageCommand(options) {
|
|
|
3874
4102
|
logger.newLine();
|
|
3875
4103
|
logger.section("Step 2: Create Package");
|
|
3876
4104
|
const packageName = options.name || `${themeName}-${version2}`;
|
|
3877
|
-
const outputDir = options.output ||
|
|
3878
|
-
const outputPath =
|
|
3879
|
-
await
|
|
4105
|
+
const outputDir = options.output || path13.join(process.cwd(), "dist");
|
|
4106
|
+
const outputPath = path13.join(outputDir, `${packageName}.zip`);
|
|
4107
|
+
await fs8.ensureDir(outputDir);
|
|
3880
4108
|
logger.startSpinner("Creating zip archive...");
|
|
3881
4109
|
try {
|
|
3882
4110
|
await createZipArchive(compiledThemePath, outputPath);
|
|
3883
4111
|
logger.stopSpinner(true, "Package created");
|
|
3884
|
-
const stats = await
|
|
4112
|
+
const stats = await fs8.stat(outputPath);
|
|
3885
4113
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
3886
4114
|
logger.newLine();
|
|
3887
4115
|
logger.success("\u2713 Theme packaged successfully!");
|
|
3888
4116
|
logger.newLine();
|
|
3889
4117
|
logger.info(`Package: ${packageName}.zip`);
|
|
3890
4118
|
logger.log(`Size: ${sizeMB} MB`);
|
|
3891
|
-
logger.log(`Location: ${
|
|
4119
|
+
logger.log(`Location: ${path13.relative(process.cwd(), outputPath)}`);
|
|
3892
4120
|
logger.newLine();
|
|
3893
4121
|
logger.section("Next steps:");
|
|
3894
4122
|
logger.log(
|
|
3895
|
-
` onexthm deploy --package ${
|
|
4123
|
+
` onexthm deploy --package ${path13.relative(process.cwd(), outputPath)}`
|
|
3896
4124
|
);
|
|
3897
4125
|
} catch (error) {
|
|
3898
4126
|
logger.stopSpinner(false, "Failed to create package");
|
|
@@ -3926,7 +4154,7 @@ function runCommand2(command, args) {
|
|
|
3926
4154
|
}
|
|
3927
4155
|
async function createZipArchive(compiledThemePath, outputPath) {
|
|
3928
4156
|
return new Promise((resolve, reject) => {
|
|
3929
|
-
const output =
|
|
4157
|
+
const output = fs8.createWriteStream(outputPath);
|
|
3930
4158
|
const archive = archiver("zip", {
|
|
3931
4159
|
zlib: { level: 9 }
|
|
3932
4160
|
// Maximum compression
|
|
@@ -3950,14 +4178,14 @@ async function deployCommand(options) {
|
|
|
3950
4178
|
ensureOneXProject();
|
|
3951
4179
|
let packagePath;
|
|
3952
4180
|
if (options.package) {
|
|
3953
|
-
packagePath =
|
|
4181
|
+
packagePath = path13.resolve(options.package);
|
|
3954
4182
|
} else if (options.theme) {
|
|
3955
|
-
const distDir =
|
|
3956
|
-
if (!
|
|
4183
|
+
const distDir = path13.join(process.cwd(), "dist");
|
|
4184
|
+
if (!fs8.existsSync(distDir)) {
|
|
3957
4185
|
logger.error("No dist/ directory found. Run 'onexthm package' first.");
|
|
3958
4186
|
process.exit(1);
|
|
3959
4187
|
}
|
|
3960
|
-
const files =
|
|
4188
|
+
const files = fs8.readdirSync(distDir);
|
|
3961
4189
|
const packageFiles = files.filter(
|
|
3962
4190
|
(f) => f.startsWith(options.theme) && f.endsWith(".zip")
|
|
3963
4191
|
);
|
|
@@ -3967,7 +4195,7 @@ async function deployCommand(options) {
|
|
|
3967
4195
|
process.exit(1);
|
|
3968
4196
|
}
|
|
3969
4197
|
packageFiles.sort().reverse();
|
|
3970
|
-
packagePath =
|
|
4198
|
+
packagePath = path13.join(distDir, packageFiles[0]);
|
|
3971
4199
|
} else {
|
|
3972
4200
|
logger.error("Either --package or --theme must be specified.");
|
|
3973
4201
|
logger.info("Examples:");
|
|
@@ -3975,17 +4203,17 @@ async function deployCommand(options) {
|
|
|
3975
4203
|
logger.log(" onexthm deploy --theme tinan");
|
|
3976
4204
|
process.exit(1);
|
|
3977
4205
|
}
|
|
3978
|
-
if (!
|
|
4206
|
+
if (!fs8.existsSync(packagePath)) {
|
|
3979
4207
|
logger.error(`Package not found: ${packagePath}`);
|
|
3980
4208
|
process.exit(1);
|
|
3981
4209
|
}
|
|
3982
|
-
const stats = await
|
|
4210
|
+
const stats = await fs8.stat(packagePath);
|
|
3983
4211
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
3984
|
-
const fileName =
|
|
4212
|
+
const fileName = path13.basename(packagePath);
|
|
3985
4213
|
logger.newLine();
|
|
3986
4214
|
logger.info(`Package: ${fileName}`);
|
|
3987
4215
|
logger.log(`Size: ${sizeMB} MB`);
|
|
3988
|
-
logger.log(`Path: ${
|
|
4216
|
+
logger.log(`Path: ${path13.relative(process.cwd(), packagePath)}`);
|
|
3989
4217
|
logger.newLine();
|
|
3990
4218
|
const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
|
|
3991
4219
|
const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
|
|
@@ -3995,7 +4223,7 @@ async function deployCommand(options) {
|
|
|
3995
4223
|
logger.startSpinner("Uploading theme package...");
|
|
3996
4224
|
try {
|
|
3997
4225
|
const formData = new FormData();
|
|
3998
|
-
formData.append("theme",
|
|
4226
|
+
formData.append("theme", fs8.createReadStream(packagePath), {
|
|
3999
4227
|
filename: fileName,
|
|
4000
4228
|
contentType: "application/zip"
|
|
4001
4229
|
});
|
|
@@ -4142,24 +4370,24 @@ async function downloadBundleZip(apiUrl, themeId, version2) {
|
|
|
4142
4370
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
4143
4371
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
4144
4372
|
if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
|
|
4145
|
-
const hashedPath =
|
|
4146
|
-
const stablePath =
|
|
4147
|
-
if (await
|
|
4148
|
-
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);
|
|
4149
4377
|
const mapPath = hashedPath + ".map";
|
|
4150
|
-
if (await
|
|
4151
|
-
await
|
|
4378
|
+
if (await fs8.pathExists(mapPath)) {
|
|
4379
|
+
await fs8.copy(mapPath, stablePath + ".map");
|
|
4152
4380
|
}
|
|
4153
4381
|
}
|
|
4154
4382
|
}
|
|
4155
|
-
const sectionsRegistryPath =
|
|
4383
|
+
const sectionsRegistryPath = path13.join(outputDir, "sections-registry.js");
|
|
4156
4384
|
const content = `// Re-export all sections from bundle-entry
|
|
4157
4385
|
// This file exists to maintain compatibility with the import path
|
|
4158
4386
|
export * from './bundle-entry.js';
|
|
4159
4387
|
`;
|
|
4160
|
-
await
|
|
4161
|
-
const pkgJsonPath =
|
|
4162
|
-
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");
|
|
4163
4391
|
}
|
|
4164
4392
|
function showDownloadFailureHelp(themeId, apiUrl) {
|
|
4165
4393
|
console.log();
|
|
@@ -4199,7 +4427,7 @@ function showDownloadFailureHelp(themeId, apiUrl) {
|
|
|
4199
4427
|
}
|
|
4200
4428
|
async function downloadCommand(options) {
|
|
4201
4429
|
logger.header("Download Theme");
|
|
4202
|
-
const env = options.env
|
|
4430
|
+
const env = options.env;
|
|
4203
4431
|
const apiUrl = getApiUrl(env);
|
|
4204
4432
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
4205
4433
|
const spinner = ora("Initializing download...").start();
|
|
@@ -4251,14 +4479,14 @@ async function downloadCommand(options) {
|
|
|
4251
4479
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
4252
4480
|
spinner.succeed(`Downloaded bundle.zip (${sizeMB} MB)`);
|
|
4253
4481
|
spinner.start("Extracting bundle...");
|
|
4254
|
-
await
|
|
4255
|
-
await
|
|
4482
|
+
await fs8.remove(outputDir);
|
|
4483
|
+
await fs8.ensureDir(outputDir);
|
|
4256
4484
|
const zip = new AdmZip(zipBuffer);
|
|
4257
4485
|
zip.extractAllTo(outputDir, true);
|
|
4258
4486
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
4259
4487
|
spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
|
|
4260
|
-
const manifestPath =
|
|
4261
|
-
const manifest = await
|
|
4488
|
+
const manifestPath = path13.join(outputDir, "manifest.json");
|
|
4489
|
+
const manifest = await fs8.readJson(manifestPath);
|
|
4262
4490
|
await createCompatibilityFiles(outputDir, manifest);
|
|
4263
4491
|
console.log();
|
|
4264
4492
|
logger.success(chalk4.green.bold("Theme downloaded successfully!"));
|
|
@@ -4378,9 +4606,9 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4378
4606
|
const oldPrefix = `${oldName}-`;
|
|
4379
4607
|
const newPrefix = `${newName}-`;
|
|
4380
4608
|
const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4381
|
-
const pkgPath =
|
|
4382
|
-
if (await
|
|
4383
|
-
const pkg = await
|
|
4609
|
+
const pkgPath = path13.join(themeDir, "package.json");
|
|
4610
|
+
if (await fs8.pathExists(pkgPath)) {
|
|
4611
|
+
const pkg = await fs8.readJson(pkgPath);
|
|
4384
4612
|
pkg.name = `@onex-themes/${newName}`;
|
|
4385
4613
|
if (pkg.description) {
|
|
4386
4614
|
pkg.description = pkg.description.replace(
|
|
@@ -4392,33 +4620,33 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4392
4620
|
if (pkg.devDependencies?.["@onexapis/cli"]) {
|
|
4393
4621
|
delete pkg.devDependencies["@onexapis/cli"];
|
|
4394
4622
|
}
|
|
4395
|
-
await
|
|
4623
|
+
await fs8.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
4396
4624
|
}
|
|
4397
|
-
const configPath =
|
|
4398
|
-
if (await
|
|
4399
|
-
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");
|
|
4400
4628
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
4401
4629
|
content = content.replace(
|
|
4402
4630
|
/name:\s*"[^"]*Theme"/,
|
|
4403
4631
|
`name: "${newDisplayName} Theme"`
|
|
4404
4632
|
);
|
|
4405
|
-
await
|
|
4633
|
+
await fs8.writeFile(configPath, content);
|
|
4406
4634
|
}
|
|
4407
|
-
const layoutPath =
|
|
4408
|
-
if (await
|
|
4409
|
-
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");
|
|
4410
4638
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
4411
4639
|
content = content.replace(
|
|
4412
4640
|
/name:\s*"[^"]*Theme"/,
|
|
4413
4641
|
`name: "${newDisplayName} Theme"`
|
|
4414
4642
|
);
|
|
4415
|
-
await
|
|
4643
|
+
await fs8.writeFile(layoutPath, content);
|
|
4416
4644
|
}
|
|
4417
4645
|
const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4418
4646
|
const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
|
|
4419
4647
|
for (const file of tsFiles) {
|
|
4420
|
-
const filePath =
|
|
4421
|
-
let content = await
|
|
4648
|
+
const filePath = path13.join(themeDir, file);
|
|
4649
|
+
let content = await fs8.readFile(filePath, "utf-8");
|
|
4422
4650
|
const original = content;
|
|
4423
4651
|
content = content.replace(
|
|
4424
4652
|
new RegExp(`"${oldPrefix}`, "g"),
|
|
@@ -4433,13 +4661,13 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
4433
4661
|
`${newDisplayName} Theme`
|
|
4434
4662
|
);
|
|
4435
4663
|
if (content !== original) {
|
|
4436
|
-
await
|
|
4664
|
+
await fs8.writeFile(filePath, content);
|
|
4437
4665
|
}
|
|
4438
4666
|
}
|
|
4439
4667
|
}
|
|
4440
4668
|
async function cloneCommand(themeName, options) {
|
|
4441
4669
|
logger.header("Clone Theme Source");
|
|
4442
|
-
const env = options.env
|
|
4670
|
+
const env = options.env;
|
|
4443
4671
|
const apiUrl = getApiUrl(env);
|
|
4444
4672
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
4445
4673
|
if (options.bucket) {
|
|
@@ -4460,8 +4688,8 @@ async function cloneCommand(themeName, options) {
|
|
|
4460
4688
|
}
|
|
4461
4689
|
const spinner = ora("Initializing clone...").start();
|
|
4462
4690
|
try {
|
|
4463
|
-
const outputDir = options.output ||
|
|
4464
|
-
if (await
|
|
4691
|
+
const outputDir = options.output || path13.resolve(process.cwd(), newName);
|
|
4692
|
+
if (await fs8.pathExists(outputDir)) {
|
|
4465
4693
|
spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
|
|
4466
4694
|
logger.info(
|
|
4467
4695
|
chalk4.gray(
|
|
@@ -4494,7 +4722,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4494
4722
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
4495
4723
|
spinner.succeed(`Downloaded source.zip (${sizeMB} MB)`);
|
|
4496
4724
|
spinner.start(`Extracting to ${outputDir}...`);
|
|
4497
|
-
await
|
|
4725
|
+
await fs8.ensureDir(outputDir);
|
|
4498
4726
|
const zip = new AdmZip(zipBuffer);
|
|
4499
4727
|
zip.extractAllTo(outputDir, true);
|
|
4500
4728
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
@@ -4506,9 +4734,9 @@ async function cloneCommand(themeName, options) {
|
|
|
4506
4734
|
spinner.succeed(
|
|
4507
4735
|
`Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
|
|
4508
4736
|
);
|
|
4509
|
-
const envExamplePath =
|
|
4510
|
-
if (!await
|
|
4511
|
-
await
|
|
4737
|
+
const envExamplePath = path13.join(outputDir, ".env.example");
|
|
4738
|
+
if (!await fs8.pathExists(envExamplePath)) {
|
|
4739
|
+
await fs8.writeFile(
|
|
4512
4740
|
envExamplePath,
|
|
4513
4741
|
[
|
|
4514
4742
|
"# API Configuration (enables real data in preview)",
|
|
@@ -4519,8 +4747,8 @@ async function cloneCommand(themeName, options) {
|
|
|
4519
4747
|
].join("\n")
|
|
4520
4748
|
);
|
|
4521
4749
|
}
|
|
4522
|
-
const mcpJsonPath =
|
|
4523
|
-
if (await
|
|
4750
|
+
const mcpJsonPath = path13.join(outputDir, ".mcp.json");
|
|
4751
|
+
if (await fs8.pathExists(mcpJsonPath)) {
|
|
4524
4752
|
const { default: inquirerMod } = await import('inquirer');
|
|
4525
4753
|
const { figmaApiKey } = await inquirerMod.prompt([
|
|
4526
4754
|
{
|
|
@@ -4529,7 +4757,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4529
4757
|
message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
|
|
4530
4758
|
}
|
|
4531
4759
|
]);
|
|
4532
|
-
let mcpContent = await
|
|
4760
|
+
let mcpContent = await fs8.readFile(mcpJsonPath, "utf-8");
|
|
4533
4761
|
if (figmaApiKey) {
|
|
4534
4762
|
mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
|
|
4535
4763
|
} else {
|
|
@@ -4540,11 +4768,11 @@ async function cloneCommand(themeName, options) {
|
|
|
4540
4768
|
} catch {
|
|
4541
4769
|
}
|
|
4542
4770
|
}
|
|
4543
|
-
await
|
|
4771
|
+
await fs8.writeFile(mcpJsonPath, mcpContent, "utf-8");
|
|
4544
4772
|
}
|
|
4545
4773
|
if (options.install !== false) {
|
|
4546
|
-
const hasPkgJson = await
|
|
4547
|
-
|
|
4774
|
+
const hasPkgJson = await fs8.pathExists(
|
|
4775
|
+
path13.join(outputDir, "package.json")
|
|
4548
4776
|
);
|
|
4549
4777
|
if (hasPkgJson) {
|
|
4550
4778
|
spinner.start("Installing dependencies...");
|
|
@@ -4572,7 +4800,7 @@ async function cloneCommand(themeName, options) {
|
|
|
4572
4800
|
console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
|
|
4573
4801
|
console.log();
|
|
4574
4802
|
console.log(chalk4.cyan("Next steps:"));
|
|
4575
|
-
console.log(chalk4.gray(` cd ${
|
|
4803
|
+
console.log(chalk4.gray(` cd ${path13.relative(process.cwd(), outputDir)}`));
|
|
4576
4804
|
console.log(
|
|
4577
4805
|
chalk4.gray(" cp .env.example .env # then add your Company ID")
|
|
4578
4806
|
);
|
|
@@ -4599,16 +4827,16 @@ async function devCommand(options) {
|
|
|
4599
4827
|
if (options.theme) {
|
|
4600
4828
|
themeName = options.theme;
|
|
4601
4829
|
try {
|
|
4602
|
-
const workspaceThemePath =
|
|
4603
|
-
if (
|
|
4830
|
+
const workspaceThemePath = path13.join(getThemesDir(), themeName);
|
|
4831
|
+
if (fs8.existsSync(workspaceThemePath)) {
|
|
4604
4832
|
themePath = workspaceThemePath;
|
|
4605
4833
|
} else {
|
|
4606
|
-
themePath =
|
|
4834
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
4607
4835
|
}
|
|
4608
4836
|
} catch {
|
|
4609
|
-
themePath =
|
|
4837
|
+
themePath = path13.join(process.cwd(), themeName);
|
|
4610
4838
|
}
|
|
4611
|
-
if (!
|
|
4839
|
+
if (!fs8.existsSync(themePath)) {
|
|
4612
4840
|
logger.error(`Theme "${themeName}" not found.`);
|
|
4613
4841
|
process.exit(1);
|
|
4614
4842
|
}
|
|
@@ -4616,11 +4844,14 @@ async function devCommand(options) {
|
|
|
4616
4844
|
const isThemeDir2 = [
|
|
4617
4845
|
"theme.config.ts",
|
|
4618
4846
|
"bundle-entry.ts",
|
|
4619
|
-
"manifest.ts"
|
|
4620
|
-
|
|
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)));
|
|
4621
4852
|
if (isThemeDir2) {
|
|
4622
4853
|
themePath = process.cwd();
|
|
4623
|
-
themeName =
|
|
4854
|
+
themeName = path13.basename(themePath);
|
|
4624
4855
|
} else {
|
|
4625
4856
|
logger.error(
|
|
4626
4857
|
"Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
|
|
@@ -4689,9 +4920,9 @@ async function devCommand(options) {
|
|
|
4689
4920
|
watcher.close();
|
|
4690
4921
|
await context2.dispose();
|
|
4691
4922
|
server.close();
|
|
4692
|
-
const shimPath =
|
|
4923
|
+
const shimPath = path13.join(outputDir, ".process-shim.js");
|
|
4693
4924
|
try {
|
|
4694
|
-
await
|
|
4925
|
+
await fs11.unlink(shimPath);
|
|
4695
4926
|
} catch {
|
|
4696
4927
|
}
|
|
4697
4928
|
process.exit(0);
|
|
@@ -4700,8 +4931,8 @@ async function devCommand(options) {
|
|
|
4700
4931
|
|
|
4701
4932
|
// src/commands/config.ts
|
|
4702
4933
|
init_logger();
|
|
4703
|
-
var CONFIG_DIR =
|
|
4704
|
-
var CONFIG_FILE =
|
|
4934
|
+
var CONFIG_DIR = path13.join(os.homedir(), ".onexthm");
|
|
4935
|
+
var CONFIG_FILE = path13.join(CONFIG_DIR, ".env");
|
|
4705
4936
|
var CONFIG_ENTRIES = [
|
|
4706
4937
|
{
|
|
4707
4938
|
key: "AWS_ACCESS_KEY_ID",
|
|
@@ -4787,7 +5018,7 @@ async function configCommand() {
|
|
|
4787
5018
|
logger.header("OneX CLI Configuration");
|
|
4788
5019
|
let existing = {};
|
|
4789
5020
|
try {
|
|
4790
|
-
const content = await
|
|
5021
|
+
const content = await fs8.readFile(CONFIG_FILE, "utf-8");
|
|
4791
5022
|
existing = parseEnvFile(content);
|
|
4792
5023
|
logger.info(`Existing config found at: ${CONFIG_FILE}`);
|
|
4793
5024
|
logger.newLine();
|
|
@@ -4823,8 +5054,8 @@ async function configCommand() {
|
|
|
4823
5054
|
for (const key of Object.keys(merged)) {
|
|
4824
5055
|
if (!merged[key]) delete merged[key];
|
|
4825
5056
|
}
|
|
4826
|
-
await
|
|
4827
|
-
await
|
|
5057
|
+
await fs8.ensureDir(CONFIG_DIR);
|
|
5058
|
+
await fs8.writeFile(CONFIG_FILE, serializeEnv(merged));
|
|
4828
5059
|
logger.newLine();
|
|
4829
5060
|
logger.success(`Config saved to: ${CONFIG_FILE}`);
|
|
4830
5061
|
logger.newLine();
|
|
@@ -4843,8 +5074,8 @@ async function configCommand() {
|
|
|
4843
5074
|
|
|
4844
5075
|
// src/commands/login.ts
|
|
4845
5076
|
init_logger();
|
|
4846
|
-
async function loginCommand(options
|
|
4847
|
-
const env = options.env
|
|
5077
|
+
async function loginCommand(options) {
|
|
5078
|
+
const env = options.env;
|
|
4848
5079
|
const apiUrl = getApiUrl(env);
|
|
4849
5080
|
logger.header("OneX Theme Developer Login");
|
|
4850
5081
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
@@ -4933,8 +5164,8 @@ async function loginCommand(options = {}) {
|
|
|
4933
5164
|
|
|
4934
5165
|
// src/commands/logout.ts
|
|
4935
5166
|
init_logger();
|
|
4936
|
-
async function logoutCommand(options
|
|
4937
|
-
const env = options.env
|
|
5167
|
+
async function logoutCommand(options) {
|
|
5168
|
+
const env = options.env;
|
|
4938
5169
|
const tokens = loadAuthTokens(env);
|
|
4939
5170
|
if (!tokens) {
|
|
4940
5171
|
logger.info(`Not logged in to ${env} environment.`);
|
|
@@ -4946,8 +5177,8 @@ async function logoutCommand(options = {}) {
|
|
|
4946
5177
|
|
|
4947
5178
|
// src/commands/whoami.ts
|
|
4948
5179
|
init_logger();
|
|
4949
|
-
async function whoamiCommand(options
|
|
4950
|
-
const env = options.env
|
|
5180
|
+
async function whoamiCommand(options) {
|
|
5181
|
+
const env = options.env;
|
|
4951
5182
|
const tokens = loadAuthTokens(env);
|
|
4952
5183
|
if (!tokens) {
|
|
4953
5184
|
logger.error(
|
|
@@ -5200,11 +5431,11 @@ function diffFieldList(prior, current, pathPrefix, out) {
|
|
|
5200
5431
|
}
|
|
5201
5432
|
}
|
|
5202
5433
|
}
|
|
5203
|
-
function diffFieldPair(p, c,
|
|
5434
|
+
function diffFieldPair(p, c, path25, out) {
|
|
5204
5435
|
if (p.type !== c.type) {
|
|
5205
5436
|
out.push({
|
|
5206
5437
|
kind: "breaking",
|
|
5207
|
-
path:
|
|
5438
|
+
path: path25,
|
|
5208
5439
|
detail: `Type changed (${p.type} \u2192 ${c.type}). Saved values may misrender.`
|
|
5209
5440
|
});
|
|
5210
5441
|
return;
|
|
@@ -5212,7 +5443,7 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5212
5443
|
if (p.required !== true && c.required === true) {
|
|
5213
5444
|
out.push({
|
|
5214
5445
|
kind: "breaking",
|
|
5215
|
-
path:
|
|
5446
|
+
path: path25,
|
|
5216
5447
|
detail: "Field became required. Existing empty instances now invalid."
|
|
5217
5448
|
});
|
|
5218
5449
|
}
|
|
@@ -5220,7 +5451,7 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5220
5451
|
if ((c.maxLength ?? Infinity) < (p.maxLength ?? Infinity)) {
|
|
5221
5452
|
out.push({
|
|
5222
5453
|
kind: "breaking",
|
|
5223
|
-
path:
|
|
5454
|
+
path: path25,
|
|
5224
5455
|
detail: `maxLength tightened (${p.maxLength ?? "\u221E"} \u2192 ${c.maxLength}).`
|
|
5225
5456
|
});
|
|
5226
5457
|
}
|
|
@@ -5229,7 +5460,7 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5229
5460
|
if ((c.min ?? -Infinity) > (p.min ?? -Infinity)) {
|
|
5230
5461
|
out.push({
|
|
5231
5462
|
kind: "breaking",
|
|
5232
|
-
path:
|
|
5463
|
+
path: path25,
|
|
5233
5464
|
detail: `min raised (${p.min ?? "-\u221E"} \u2192 ${c.min}).`
|
|
5234
5465
|
});
|
|
5235
5466
|
}
|
|
@@ -5238,7 +5469,7 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5238
5469
|
if ((c.max ?? Infinity) < (p.max ?? Infinity)) {
|
|
5239
5470
|
out.push({
|
|
5240
5471
|
kind: "breaking",
|
|
5241
|
-
path:
|
|
5472
|
+
path: path25,
|
|
5242
5473
|
detail: `max lowered (${p.max ?? "\u221E"} \u2192 ${c.max}).`
|
|
5243
5474
|
});
|
|
5244
5475
|
}
|
|
@@ -5251,14 +5482,14 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5251
5482
|
if (removed.length > 0) {
|
|
5252
5483
|
out.push({
|
|
5253
5484
|
kind: "breaking",
|
|
5254
|
-
path:
|
|
5485
|
+
path: path25,
|
|
5255
5486
|
detail: `Option(s) removed: ${removed.join(", ")}. Existing saved values may be orphaned.`
|
|
5256
5487
|
});
|
|
5257
5488
|
}
|
|
5258
5489
|
if (added.length > 0) {
|
|
5259
5490
|
out.push({
|
|
5260
5491
|
kind: "additive",
|
|
5261
|
-
path:
|
|
5492
|
+
path: path25,
|
|
5262
5493
|
detail: `Option(s) added: ${added.join(", ")}.`
|
|
5263
5494
|
});
|
|
5264
5495
|
}
|
|
@@ -5266,7 +5497,7 @@ function diffFieldPair(p, c, path23, out) {
|
|
|
5266
5497
|
if (!deepEqual(p.default, c.default)) {
|
|
5267
5498
|
out.push({
|
|
5268
5499
|
kind: "defaults-only",
|
|
5269
|
-
path:
|
|
5500
|
+
path: path25,
|
|
5270
5501
|
detail: `Default changed: ${JSON.stringify(p.default)} \u2192 ${JSON.stringify(c.default)}.`
|
|
5271
5502
|
});
|
|
5272
5503
|
}
|
|
@@ -5324,7 +5555,7 @@ function deepEqual(a, b) {
|
|
|
5324
5555
|
|
|
5325
5556
|
// src/commands/publish.ts
|
|
5326
5557
|
async function publishCommand(options) {
|
|
5327
|
-
const env = options.env
|
|
5558
|
+
const env = options.env;
|
|
5328
5559
|
logger.header("OneX Theme Publish");
|
|
5329
5560
|
logger.info(`Environment: ${env} (${getApiUrl(env)})`);
|
|
5330
5561
|
logger.newLine();
|
|
@@ -5338,13 +5569,13 @@ async function publishCommand(options) {
|
|
|
5338
5569
|
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
5339
5570
|
let themePath;
|
|
5340
5571
|
if (options.theme) {
|
|
5341
|
-
themePath =
|
|
5572
|
+
themePath = path13.resolve(options.theme);
|
|
5342
5573
|
} else {
|
|
5343
5574
|
const isThemeDir2 = [
|
|
5344
5575
|
"theme.config.ts",
|
|
5345
5576
|
"bundle-entry.ts",
|
|
5346
5577
|
"manifest.ts"
|
|
5347
|
-
].some((f) =>
|
|
5578
|
+
].some((f) => fs8.existsSync(path13.join(process.cwd(), f)));
|
|
5348
5579
|
if (isThemeDir2) {
|
|
5349
5580
|
themePath = process.cwd();
|
|
5350
5581
|
} else {
|
|
@@ -5354,13 +5585,13 @@ async function publishCommand(options) {
|
|
|
5354
5585
|
process.exit(1);
|
|
5355
5586
|
}
|
|
5356
5587
|
}
|
|
5357
|
-
const pkgPath =
|
|
5358
|
-
if (!
|
|
5588
|
+
const pkgPath = path13.join(themePath, "package.json");
|
|
5589
|
+
if (!fs8.existsSync(pkgPath)) {
|
|
5359
5590
|
logger.error("No package.json found in theme directory");
|
|
5360
5591
|
process.exit(1);
|
|
5361
5592
|
}
|
|
5362
|
-
const pkg =
|
|
5363
|
-
const themeId = pkg.name?.replace("@onex-themes/", "") ||
|
|
5593
|
+
const pkg = fs8.readJsonSync(pkgPath);
|
|
5594
|
+
const themeId = pkg.name?.replace("@onex-themes/", "") || path13.basename(themePath);
|
|
5364
5595
|
if (options.bump) {
|
|
5365
5596
|
const currentVersion = pkg.version || "1.0.0";
|
|
5366
5597
|
const newVersion = semver.inc(currentVersion, options.bump);
|
|
@@ -5369,7 +5600,7 @@ async function publishCommand(options) {
|
|
|
5369
5600
|
process.exit(1);
|
|
5370
5601
|
}
|
|
5371
5602
|
pkg.version = newVersion;
|
|
5372
|
-
|
|
5603
|
+
fs8.writeJsonSync(pkgPath, pkg, { spaces: 2 });
|
|
5373
5604
|
logger.info(`Bumped version: ${currentVersion} -> ${newVersion}`);
|
|
5374
5605
|
}
|
|
5375
5606
|
const version2 = pkg.version || "1.0.0";
|
|
@@ -5476,7 +5707,7 @@ Or use the --bump flag:
|
|
|
5476
5707
|
logger.error(error instanceof Error ? error.message : "Build error");
|
|
5477
5708
|
process.exit(1);
|
|
5478
5709
|
}
|
|
5479
|
-
const distDir =
|
|
5710
|
+
const distDir = path13.join(themePath, "dist");
|
|
5480
5711
|
const classification = await runSchemaDiffGate(
|
|
5481
5712
|
themeId,
|
|
5482
5713
|
distDir,
|
|
@@ -5525,8 +5756,8 @@ Or use the --bump flag:
|
|
|
5525
5756
|
for (const [originalPath, url] of Object.entries(videoUrls)) {
|
|
5526
5757
|
assetMap[originalPath] = url;
|
|
5527
5758
|
}
|
|
5528
|
-
const assetMapPath =
|
|
5529
|
-
await
|
|
5759
|
+
const assetMapPath = path13.join(distDir, "asset-map.json");
|
|
5760
|
+
await fs8.writeFile(assetMapPath, JSON.stringify(assetMap, null, 2));
|
|
5530
5761
|
} catch (error) {
|
|
5531
5762
|
logger.error(
|
|
5532
5763
|
`Failed to write asset-map.json: ${error instanceof Error ? error.message : "unknown"}`
|
|
@@ -5601,7 +5832,7 @@ Or use the --bump flag:
|
|
|
5601
5832
|
continue;
|
|
5602
5833
|
}
|
|
5603
5834
|
try {
|
|
5604
|
-
const buf = await
|
|
5835
|
+
const buf = await fs8.promises.readFile(entry.absPath);
|
|
5605
5836
|
const res = await fetch(item.upload_url, {
|
|
5606
5837
|
method: "PUT",
|
|
5607
5838
|
headers: {
|
|
@@ -5643,12 +5874,12 @@ Or use the --bump flag:
|
|
|
5643
5874
|
}
|
|
5644
5875
|
logger.startSpinner("Uploading bundle...");
|
|
5645
5876
|
try {
|
|
5646
|
-
if (!
|
|
5877
|
+
if (!fs8.existsSync(distDir)) {
|
|
5647
5878
|
logger.stopSpinner(false, "No dist/ directory");
|
|
5648
5879
|
logger.error("Build the theme first: onexthm build");
|
|
5649
5880
|
process.exit(1);
|
|
5650
5881
|
}
|
|
5651
|
-
const bundleZipPath =
|
|
5882
|
+
const bundleZipPath = path13.join(themePath, "dist", "bundle.zip");
|
|
5652
5883
|
await createZip(distDir, bundleZipPath, [
|
|
5653
5884
|
"bundle.zip",
|
|
5654
5885
|
"source.zip",
|
|
@@ -5657,7 +5888,7 @@ Or use the --bump flag:
|
|
|
5657
5888
|
"theme-assets",
|
|
5658
5889
|
"theme-assets/**"
|
|
5659
5890
|
]);
|
|
5660
|
-
const bundleBuffer =
|
|
5891
|
+
const bundleBuffer = fs8.readFileSync(bundleZipPath);
|
|
5661
5892
|
const bundleRes = await fetch(bundleUploadUrl, {
|
|
5662
5893
|
method: "PUT",
|
|
5663
5894
|
headers: { "Content-Type": "application/zip" },
|
|
@@ -5675,7 +5906,7 @@ Or use the --bump flag:
|
|
|
5675
5906
|
}
|
|
5676
5907
|
logger.startSpinner("Uploading source...");
|
|
5677
5908
|
try {
|
|
5678
|
-
const sourceZipPath =
|
|
5909
|
+
const sourceZipPath = path13.join(themePath, "dist", "source.zip");
|
|
5679
5910
|
await createZip(themePath, sourceZipPath, [
|
|
5680
5911
|
"node_modules",
|
|
5681
5912
|
"dist",
|
|
@@ -5683,7 +5914,7 @@ Or use the --bump flag:
|
|
|
5683
5914
|
".env",
|
|
5684
5915
|
".env.local"
|
|
5685
5916
|
]);
|
|
5686
|
-
const sourceBuffer =
|
|
5917
|
+
const sourceBuffer = fs8.readFileSync(sourceZipPath);
|
|
5687
5918
|
const sourceRes = await fetch(sourceUploadUrl, {
|
|
5688
5919
|
method: "PUT",
|
|
5689
5920
|
headers: { "Content-Type": "application/zip" },
|
|
@@ -5761,9 +5992,9 @@ async function uploadThumbnail(apiUrl, themeId, themePath, distDir, env = "dev")
|
|
|
5761
5992
|
let imageBase64 = null;
|
|
5762
5993
|
let mimeType = "image/png";
|
|
5763
5994
|
for (const { file, mime } of THUMBNAIL_CANDIDATES) {
|
|
5764
|
-
const candidate =
|
|
5765
|
-
if (
|
|
5766
|
-
const buf =
|
|
5995
|
+
const candidate = path13.join(themePath, file);
|
|
5996
|
+
if (fs8.existsSync(candidate)) {
|
|
5997
|
+
const buf = fs8.readFileSync(candidate);
|
|
5767
5998
|
imageBase64 = `data:${mime};base64,${buf.toString("base64")}`;
|
|
5768
5999
|
mimeType = mime;
|
|
5769
6000
|
logger.info(`Using local thumbnail: ${file}`);
|
|
@@ -5863,7 +6094,7 @@ async function screenshotHomePage(themePath, distDir) {
|
|
|
5863
6094
|
const { compilePreviewRuntime: compilePreviewRuntime2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
5864
6095
|
const { createDevServer: createDevServer2 } = await Promise.resolve().then(() => (init_dev_server(), dev_server_exports));
|
|
5865
6096
|
const previewRuntimePath = await compilePreviewRuntime2(themePath);
|
|
5866
|
-
const themeName =
|
|
6097
|
+
const themeName = path13.basename(themePath);
|
|
5867
6098
|
const port = await findFreePort(4500);
|
|
5868
6099
|
const server = createDevServer2({
|
|
5869
6100
|
port,
|
|
@@ -5920,7 +6151,7 @@ async function findFreePort(start) {
|
|
|
5920
6151
|
});
|
|
5921
6152
|
}
|
|
5922
6153
|
async function uploadVideoMultipart(apiUrl, themeId, video, env = "dev") {
|
|
5923
|
-
const fileName =
|
|
6154
|
+
const fileName = path13.basename(video.originalPath);
|
|
5924
6155
|
const videoInitUrl = `${apiUrl}/media/videos/multipart/init`;
|
|
5925
6156
|
const videoInitBody = {
|
|
5926
6157
|
file_name: fileName,
|
|
@@ -5956,7 +6187,7 @@ async function uploadVideoMultipart(apiUrl, themeId, video, env = "dev") {
|
|
|
5956
6187
|
);
|
|
5957
6188
|
}
|
|
5958
6189
|
const { upload_id, file_key, chunk_size, chunk_urls } = initBody;
|
|
5959
|
-
const fileBuffer = await
|
|
6190
|
+
const fileBuffer = await fs8.promises.readFile(video.absPath);
|
|
5960
6191
|
const CHUNK_CONCURRENCY = 4;
|
|
5961
6192
|
const queue = [...chunk_urls];
|
|
5962
6193
|
const parts = [];
|
|
@@ -6065,7 +6296,7 @@ async function runSchemaDiffGate(themeId, distDir, env, options) {
|
|
|
6065
6296
|
let currentAssets;
|
|
6066
6297
|
try {
|
|
6067
6298
|
currentSchemas = JSON.parse(
|
|
6068
|
-
await
|
|
6299
|
+
await fs8.readFile(path13.join(distDir, "schemas.json"), "utf-8")
|
|
6069
6300
|
);
|
|
6070
6301
|
} catch (err) {
|
|
6071
6302
|
logger.warning(
|
|
@@ -6075,7 +6306,7 @@ async function runSchemaDiffGate(themeId, distDir, env, options) {
|
|
|
6075
6306
|
}
|
|
6076
6307
|
try {
|
|
6077
6308
|
currentAssets = JSON.parse(
|
|
6078
|
-
await
|
|
6309
|
+
await fs8.readFile(path13.join(distDir, "asset-manifest.json"), "utf-8")
|
|
6079
6310
|
);
|
|
6080
6311
|
} catch {
|
|
6081
6312
|
currentAssets = { manifestVersion: 1, assets: [] };
|
|
@@ -6145,24 +6376,24 @@ var AI_CONTEXT_FILES = [
|
|
|
6145
6376
|
".mcp.json"
|
|
6146
6377
|
];
|
|
6147
6378
|
function resolveTargetDir(opts) {
|
|
6148
|
-
return
|
|
6379
|
+
return path13.resolve(opts.cwd ?? process.cwd());
|
|
6149
6380
|
}
|
|
6150
6381
|
function resolveDefaultTemplateDir() {
|
|
6151
|
-
return
|
|
6382
|
+
return path13.join(getTemplatesDir(), "default");
|
|
6152
6383
|
}
|
|
6153
6384
|
function isThemeDir(dir) {
|
|
6154
|
-
return
|
|
6385
|
+
return fs8.existsSync(path13.join(dir, "theme.config.ts")) || fs8.existsSync(path13.join(dir, "theme.config.js"));
|
|
6155
6386
|
}
|
|
6156
6387
|
function inspectFiles(templateDir, targetDir) {
|
|
6157
6388
|
return AI_CONTEXT_FILES.map((name) => {
|
|
6158
|
-
const templatePath =
|
|
6159
|
-
const targetPath =
|
|
6160
|
-
const exists =
|
|
6389
|
+
const templatePath = path13.join(templateDir, name);
|
|
6390
|
+
const targetPath = path13.join(targetDir, name);
|
|
6391
|
+
const exists = fs8.existsSync(targetPath);
|
|
6161
6392
|
let identical = false;
|
|
6162
|
-
if (exists &&
|
|
6393
|
+
if (exists && fs8.existsSync(templatePath)) {
|
|
6163
6394
|
try {
|
|
6164
|
-
const a =
|
|
6165
|
-
const b =
|
|
6395
|
+
const a = fs8.readFileSync(templatePath, "utf-8");
|
|
6396
|
+
const b = fs8.readFileSync(targetPath, "utf-8");
|
|
6166
6397
|
identical = a.replace(/\r\n/g, "\n") === b.replace(/\r\n/g, "\n");
|
|
6167
6398
|
} catch {
|
|
6168
6399
|
identical = false;
|
|
@@ -6212,11 +6443,11 @@ async function mcpSetupCommand(options = {}) {
|
|
|
6212
6443
|
}
|
|
6213
6444
|
}
|
|
6214
6445
|
for (const s of missing) {
|
|
6215
|
-
if (!
|
|
6446
|
+
if (!fs8.existsSync(s.templatePath)) {
|
|
6216
6447
|
logger.warning(` ! ${s.name} not in template \u2014 skipped`);
|
|
6217
6448
|
continue;
|
|
6218
6449
|
}
|
|
6219
|
-
await
|
|
6450
|
+
await fs8.copy(s.templatePath, s.targetPath);
|
|
6220
6451
|
logger.success(` \u2713 ${s.name}`);
|
|
6221
6452
|
}
|
|
6222
6453
|
logger.log("");
|
|
@@ -6277,11 +6508,11 @@ async function mcpUpgradeCommand(options = {}) {
|
|
|
6277
6508
|
}
|
|
6278
6509
|
}
|
|
6279
6510
|
for (const s of toUpgrade) {
|
|
6280
|
-
if (!
|
|
6511
|
+
if (!fs8.existsSync(s.templatePath)) {
|
|
6281
6512
|
logger.warning(` ! ${s.name} not in template \u2014 skipped`);
|
|
6282
6513
|
continue;
|
|
6283
6514
|
}
|
|
6284
|
-
await
|
|
6515
|
+
await fs8.copy(s.templatePath, s.targetPath, { overwrite: true });
|
|
6285
6516
|
logger.success(` \u2713 ${s.name}`);
|
|
6286
6517
|
}
|
|
6287
6518
|
logger.log("");
|
|
@@ -6299,12 +6530,12 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
6299
6530
|
return;
|
|
6300
6531
|
}
|
|
6301
6532
|
logger.success("theme.config.ts present");
|
|
6302
|
-
const mcpJsonPath =
|
|
6303
|
-
if (!
|
|
6533
|
+
const mcpJsonPath = path13.join(targetDir, ".mcp.json");
|
|
6534
|
+
if (!fs8.existsSync(mcpJsonPath)) {
|
|
6304
6535
|
logger.error(".mcp.json missing \u2014 run `onexthm mcp setup`");
|
|
6305
6536
|
} else {
|
|
6306
6537
|
try {
|
|
6307
|
-
const mcpJson = JSON.parse(
|
|
6538
|
+
const mcpJson = JSON.parse(fs8.readFileSync(mcpJsonPath, "utf-8"));
|
|
6308
6539
|
const servers = mcpJson?.mcpServers ?? {};
|
|
6309
6540
|
if (servers.onexthm) {
|
|
6310
6541
|
logger.success(".mcp.json registers `onexthm`");
|
|
@@ -6337,8 +6568,8 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
6337
6568
|
logger.success(`${s.name} up to date`);
|
|
6338
6569
|
}
|
|
6339
6570
|
}
|
|
6340
|
-
const registryPath =
|
|
6341
|
-
if (
|
|
6571
|
+
const registryPath = path13.join(targetDir, "sections-registry.ts");
|
|
6572
|
+
if (fs8.existsSync(registryPath)) {
|
|
6342
6573
|
logger.success("sections-registry.ts present");
|
|
6343
6574
|
} else {
|
|
6344
6575
|
logger.warning(
|
|
@@ -6349,24 +6580,27 @@ async function mcpDoctorCommand(options = {}) {
|
|
|
6349
6580
|
|
|
6350
6581
|
// src/cli.ts
|
|
6351
6582
|
dotenv.config({
|
|
6352
|
-
path:
|
|
6583
|
+
path: path13.join(process.cwd(), ".env.local"),
|
|
6353
6584
|
override: true
|
|
6354
6585
|
});
|
|
6355
|
-
dotenv.config({ path:
|
|
6586
|
+
dotenv.config({ path: path13.join(process.cwd(), ".env") });
|
|
6356
6587
|
try {
|
|
6357
6588
|
const projectRoot = getProjectRoot();
|
|
6358
|
-
if (
|
|
6589
|
+
if (path13.resolve(projectRoot) !== path13.resolve(process.cwd())) {
|
|
6359
6590
|
dotenv.config({
|
|
6360
|
-
path:
|
|
6591
|
+
path: path13.join(projectRoot, ".env.local")
|
|
6361
6592
|
});
|
|
6362
|
-
dotenv.config({ path:
|
|
6593
|
+
dotenv.config({ path: path13.join(projectRoot, ".env") });
|
|
6363
6594
|
}
|
|
6364
6595
|
} catch {
|
|
6365
6596
|
}
|
|
6366
6597
|
dotenv.config({
|
|
6367
|
-
path:
|
|
6598
|
+
path: path13.join(os.homedir(), ".onexthm", ".env"),
|
|
6368
6599
|
quiet: true
|
|
6369
6600
|
});
|
|
6601
|
+
function envOpt() {
|
|
6602
|
+
return new Option("--env <env>", "Target environment: dev, staging, or prod").choices(["dev", "staging", "prod"]).makeOptionMandatory();
|
|
6603
|
+
}
|
|
6370
6604
|
var require2 = createRequire(import.meta.url);
|
|
6371
6605
|
var { version } = require2("../package.json");
|
|
6372
6606
|
var program = new Command();
|
|
@@ -6375,11 +6609,7 @@ program.command("init").description("Create a new OneX theme project").argument(
|
|
|
6375
6609
|
"-t, --template <template>",
|
|
6376
6610
|
"Template to use (default, minimal)",
|
|
6377
6611
|
"default"
|
|
6378
|
-
).option("--no-install", "Skip installing dependencies").option("--git", "Initialize git repository").option("-y, --yes", "Skip prompts and use defaults").
|
|
6379
|
-
"--env <env>",
|
|
6380
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6381
|
-
"dev"
|
|
6382
|
-
).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);
|
|
6383
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(
|
|
6384
6614
|
"-c, --category <category>",
|
|
6385
6615
|
"Section category (headers, content, footers)"
|
|
@@ -6406,36 +6636,16 @@ program.command("download").description("Download a published theme via the webs
|
|
|
6406
6636
|
"-v, --version <version>",
|
|
6407
6637
|
"Theme version (default: latest)",
|
|
6408
6638
|
"latest"
|
|
6409
|
-
).option(
|
|
6410
|
-
"--env <env>",
|
|
6411
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6412
|
-
"dev"
|
|
6413
|
-
).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);
|
|
6414
6640
|
program.command("clone").description("Clone theme source code via the website-api").argument("<theme-name>", "Theme to clone").option(
|
|
6415
6641
|
"-v, --version <version>",
|
|
6416
6642
|
"Theme version (default: latest)",
|
|
6417
6643
|
"latest"
|
|
6418
|
-
).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option(
|
|
6419
|
-
"--env <env>",
|
|
6420
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6421
|
-
"dev"
|
|
6422
|
-
).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);
|
|
6423
6645
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
6424
|
-
program.command("login").description("Login to OneX platform").
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
"dev"
|
|
6428
|
-
).action(loginCommand);
|
|
6429
|
-
program.command("logout").description("Logout from OneX platform").option(
|
|
6430
|
-
"--env <env>",
|
|
6431
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6432
|
-
"dev"
|
|
6433
|
-
).action(logoutCommand);
|
|
6434
|
-
program.command("whoami").description("Show current logged-in developer").option(
|
|
6435
|
-
"--env <env>",
|
|
6436
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6437
|
-
"dev"
|
|
6438
|
-
).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);
|
|
6439
6649
|
var mcpCmd = program.command("mcp").description("Manage MCP server registration and AI-context files");
|
|
6440
6650
|
mcpCmd.command("setup").description(
|
|
6441
6651
|
"Install .mcp.json + CLAUDE.md + AGENTS.md + .cursorrules into the current theme"
|
|
@@ -6447,11 +6657,7 @@ mcpCmd.command("doctor").description("Diagnose MCP setup in the current theme di
|
|
|
6447
6657
|
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
|
|
6448
6658
|
"--bump <type>",
|
|
6449
6659
|
"Auto-bump version before publish (patch|minor|major)"
|
|
6450
|
-
).option(
|
|
6451
|
-
"--env <env>",
|
|
6452
|
-
"Target environment: dev, staging, or prod (default: dev)",
|
|
6453
|
-
"dev"
|
|
6454
|
-
).option(
|
|
6660
|
+
).addOption(envOpt()).option(
|
|
6455
6661
|
"--dry-run",
|
|
6456
6662
|
"Build locally and print the schema-diff classification without publishing"
|
|
6457
6663
|
).option(
|