@onexapis/cli 1.1.65 → 1.1.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +1688 -801
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1684 -797
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +784 -405
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +779 -400
- package/dist/index.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +89 -7
- package/package.json +2 -2
- package/templates/default/package.json +5 -0
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import chalk4 from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import fs7 from 'fs/promises';
|
|
6
|
-
import crypto from 'crypto';
|
|
3
|
+
import fs3 from 'fs';
|
|
4
|
+
import path12 from 'path';
|
|
7
5
|
import { glob } from 'glob';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import crypto from 'crypto';
|
|
8
|
+
import fs10 from 'fs/promises';
|
|
9
|
+
import * as esbuild from 'esbuild';
|
|
8
10
|
import { createRequire } from 'module';
|
|
9
|
-
import fs3 from 'fs';
|
|
10
11
|
import { execSync, spawn } from 'child_process';
|
|
11
12
|
import inquirer from 'inquirer';
|
|
12
|
-
import fs from 'fs-extra';
|
|
13
13
|
import ejs from 'ejs';
|
|
14
14
|
import os from 'os';
|
|
15
15
|
import AdmZip from 'adm-zip';
|
|
@@ -85,6 +85,289 @@ var init_logger = __esm({
|
|
|
85
85
|
logger = new Logger();
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
|
+
function isNextjsProject(dir) {
|
|
89
|
+
return fs3.existsSync(path12.join(dir, "next.config.ts")) || fs3.existsSync(path12.join(dir, "next.config.js")) || fs3.existsSync(path12.join(dir, "next.config.mjs"));
|
|
90
|
+
}
|
|
91
|
+
var init_detect_nextjs = __esm({
|
|
92
|
+
"src/utils/detect-nextjs.ts"() {
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
function sortedCopy(value) {
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.map((v) => sortedCopy(v));
|
|
98
|
+
}
|
|
99
|
+
if (value && typeof value === "object") {
|
|
100
|
+
const sorted = {};
|
|
101
|
+
for (const key of Object.keys(value).sort()) {
|
|
102
|
+
sorted[key] = sortedCopy(value[key]);
|
|
103
|
+
}
|
|
104
|
+
return sorted;
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
function normalizeField(raw) {
|
|
109
|
+
const out = {
|
|
110
|
+
id: String(raw.id),
|
|
111
|
+
type: String(raw.type)
|
|
112
|
+
};
|
|
113
|
+
if (raw.required === true) out.required = true;
|
|
114
|
+
if (raw.default !== void 0) out.default = raw.default;
|
|
115
|
+
if (Array.isArray(raw.aliases) && raw.aliases.length > 0) {
|
|
116
|
+
out.aliases = [...raw.aliases].map(String).sort();
|
|
117
|
+
}
|
|
118
|
+
if (typeof raw.maxLength === "number") out.maxLength = raw.maxLength;
|
|
119
|
+
if (typeof raw.min === "number") out.min = raw.min;
|
|
120
|
+
if (typeof raw.max === "number") out.max = raw.max;
|
|
121
|
+
if (typeof raw.step === "number") out.step = raw.step;
|
|
122
|
+
if (Array.isArray(raw.accept)) {
|
|
123
|
+
out.accept = [...raw.accept].map(String).sort();
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(raw.options)) {
|
|
126
|
+
out.options = raw.options.map((o) => String(o?.value ?? o)).sort();
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
function normalizeBlock(raw) {
|
|
131
|
+
return {
|
|
132
|
+
type: String(raw.type),
|
|
133
|
+
settings: Array.isArray(raw.settings) ? raw.settings.map(normalizeField).sort(sortFieldsById) : [],
|
|
134
|
+
defaults: raw.defaults && typeof raw.defaults === "object" ? sortedCopy(raw.defaults) : {},
|
|
135
|
+
...typeof raw.limit === "number" ? { limit: raw.limit } : {},
|
|
136
|
+
...typeof raw.min === "number" ? { min: raw.min } : {},
|
|
137
|
+
...raw.sortable === true ? { sortable: true } : {},
|
|
138
|
+
...raw.baseType ? { baseType: String(raw.baseType) } : {}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function normalizeTemplate(raw) {
|
|
142
|
+
const out = { id: String(raw.id) };
|
|
143
|
+
if (raw.isDefault === true) out.isDefault = true;
|
|
144
|
+
if (Array.isArray(raw.settings)) {
|
|
145
|
+
out.settings = raw.settings.map(normalizeField).sort(sortFieldsById);
|
|
146
|
+
}
|
|
147
|
+
if (raw.defaults && typeof raw.defaults === "object") {
|
|
148
|
+
out.defaults = sortedCopy(raw.defaults);
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
function sortFieldsById(a, b) {
|
|
153
|
+
return a.id.localeCompare(b.id);
|
|
154
|
+
}
|
|
155
|
+
function sortByType(a, b) {
|
|
156
|
+
return a.type.localeCompare(b.type);
|
|
157
|
+
}
|
|
158
|
+
function normalizeSection(raw) {
|
|
159
|
+
return {
|
|
160
|
+
type: String(raw.type),
|
|
161
|
+
settings: Array.isArray(raw.settings) ? raw.settings.map(normalizeField).sort(sortFieldsById) : [],
|
|
162
|
+
defaults: raw.defaults && typeof raw.defaults === "object" ? sortedCopy(raw.defaults) : {},
|
|
163
|
+
blocks: Array.isArray(raw.blocks) ? raw.blocks.map(normalizeBlock).sort(sortByType) : [],
|
|
164
|
+
templates: Array.isArray(raw.templates) ? raw.templates.map(normalizeTemplate).sort(sortFieldsById) : [],
|
|
165
|
+
dataRequirements: raw.dataRequirements && typeof raw.dataRequirements === "object" ? sortedCopy(raw.dataRequirements) : null,
|
|
166
|
+
...raw.global === true ? { global: true } : {},
|
|
167
|
+
...typeof raw.maxBlocks === "number" ? { maxBlocks: raw.maxBlocks } : {}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function extractSchemas(themePath) {
|
|
171
|
+
const { createJiti } = await import('jiti');
|
|
172
|
+
const jiti = createJiti(import.meta.url);
|
|
173
|
+
const schemaFiles = await glob("sections/**/*.schema.ts", { cwd: themePath });
|
|
174
|
+
const sections = {};
|
|
175
|
+
for (const file of schemaFiles) {
|
|
176
|
+
try {
|
|
177
|
+
const mod = await jiti.import(path12.join(themePath, file));
|
|
178
|
+
const exports$1 = mod;
|
|
179
|
+
for (const value of Object.values(exports$1)) {
|
|
180
|
+
if (value && typeof value === "object" && typeof value.type === "string" && Array.isArray(value.settings)) {
|
|
181
|
+
const section = normalizeSection(value);
|
|
182
|
+
sections[section.type] = section;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const manifest = {
|
|
189
|
+
manifestVersion: 1,
|
|
190
|
+
sections: {}
|
|
191
|
+
};
|
|
192
|
+
for (const type of Object.keys(sections).sort()) {
|
|
193
|
+
manifest.sections[type] = sections[type];
|
|
194
|
+
}
|
|
195
|
+
return manifest;
|
|
196
|
+
}
|
|
197
|
+
function serializeManifest(manifest) {
|
|
198
|
+
return JSON.stringify(sortedCopy(manifest), null, 2);
|
|
199
|
+
}
|
|
200
|
+
var init_extract_schemas = __esm({
|
|
201
|
+
"src/utils/extract-schemas.ts"() {
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
function mimeFor(filename) {
|
|
205
|
+
const ext = path12.extname(filename).toLowerCase();
|
|
206
|
+
return MIME_MAP[ext] || "application/octet-stream";
|
|
207
|
+
}
|
|
208
|
+
async function sha256Prefix(absPath, len) {
|
|
209
|
+
const buf = await fs.readFile(absPath);
|
|
210
|
+
return crypto.createHash("sha256").update(buf).digest("hex").slice(0, len);
|
|
211
|
+
}
|
|
212
|
+
function insertHashIntoName(relPath, hash) {
|
|
213
|
+
const dir = path12.posix.dirname(relPath);
|
|
214
|
+
const base = path12.posix.basename(relPath);
|
|
215
|
+
const ext = path12.posix.extname(base);
|
|
216
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
217
|
+
const hashed = `${stem}-${hash}${ext}`;
|
|
218
|
+
return dir === "." ? hashed : `${dir}/${hashed}`;
|
|
219
|
+
}
|
|
220
|
+
async function scanThemeAssets(distDir) {
|
|
221
|
+
const assetsDir = path12.join(distDir, "theme-assets");
|
|
222
|
+
if (!await fs.pathExists(assetsDir)) return [];
|
|
223
|
+
const files = await glob("**/*", {
|
|
224
|
+
cwd: assetsDir,
|
|
225
|
+
nodir: true,
|
|
226
|
+
dot: false
|
|
227
|
+
});
|
|
228
|
+
const results = [];
|
|
229
|
+
for (const rel of files) {
|
|
230
|
+
const absPath = path12.join(assetsDir, rel);
|
|
231
|
+
const stat = await fs.stat(absPath);
|
|
232
|
+
if (!stat.isFile()) continue;
|
|
233
|
+
const originalPath = rel.split(path12.sep).join("/");
|
|
234
|
+
const hash = await sha256Prefix(absPath, HASH_LEN);
|
|
235
|
+
const hashedPath = insertHashIntoName(originalPath, hash);
|
|
236
|
+
const contentType = mimeFor(rel);
|
|
237
|
+
results.push({
|
|
238
|
+
originalPath,
|
|
239
|
+
hashedPath,
|
|
240
|
+
hash,
|
|
241
|
+
size: stat.size,
|
|
242
|
+
contentType,
|
|
243
|
+
absPath
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
results.sort((a, b) => a.originalPath.localeCompare(b.originalPath));
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
var MIME_MAP, HASH_LEN;
|
|
250
|
+
var init_scan_theme_assets = __esm({
|
|
251
|
+
"src/utils/scan-theme-assets.ts"() {
|
|
252
|
+
MIME_MAP = {
|
|
253
|
+
".png": "image/png",
|
|
254
|
+
".jpg": "image/jpeg",
|
|
255
|
+
".jpeg": "image/jpeg",
|
|
256
|
+
".gif": "image/gif",
|
|
257
|
+
".webp": "image/webp",
|
|
258
|
+
".avif": "image/avif",
|
|
259
|
+
".svg": "image/svg+xml",
|
|
260
|
+
".ico": "image/x-icon",
|
|
261
|
+
".bmp": "image/bmp",
|
|
262
|
+
".woff": "font/woff",
|
|
263
|
+
".woff2": "font/woff2",
|
|
264
|
+
".ttf": "font/ttf",
|
|
265
|
+
".otf": "font/otf",
|
|
266
|
+
".eot": "application/vnd.ms-fontobject",
|
|
267
|
+
".mp4": "video/mp4",
|
|
268
|
+
".webm": "video/webm",
|
|
269
|
+
".mov": "video/quicktime",
|
|
270
|
+
".ogg": "video/ogg",
|
|
271
|
+
".json": "application/json"
|
|
272
|
+
};
|
|
273
|
+
HASH_LEN = 8;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
async function scanAppDirectory(themePath) {
|
|
277
|
+
const appDir = path12.join(themePath, "app");
|
|
278
|
+
let pageFiles;
|
|
279
|
+
try {
|
|
280
|
+
pageFiles = await glob("**/page.{tsx,ts,jsx,js}", { cwd: appDir });
|
|
281
|
+
} catch {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
if (pageFiles.length === 0) return [];
|
|
285
|
+
const pages = [];
|
|
286
|
+
for (const pageFile of pageFiles) {
|
|
287
|
+
const routePath = deriveRoutePath(pageFile);
|
|
288
|
+
const absPageFile = path12.join(appDir, pageFile);
|
|
289
|
+
let source;
|
|
290
|
+
try {
|
|
291
|
+
source = await fs10.readFile(absPageFile, "utf-8");
|
|
292
|
+
} catch {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const sections = await extractSectionsFromPage(source, themePath);
|
|
296
|
+
pages.push({
|
|
297
|
+
routePath,
|
|
298
|
+
sourceFile: path12.join("app", pageFile),
|
|
299
|
+
sections
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return pages;
|
|
303
|
+
}
|
|
304
|
+
function deriveRoutePath(pageFile) {
|
|
305
|
+
const dir = path12.dirname(pageFile);
|
|
306
|
+
if (dir === ".") return "/";
|
|
307
|
+
return "/" + dir;
|
|
308
|
+
}
|
|
309
|
+
async function extractSectionsFromPage(source, themePath) {
|
|
310
|
+
const importRegex = /import\s+\w+\s+from\s+["'](@\/|\.\.?\/)(components\/[^"']+)["']/g;
|
|
311
|
+
const sections = [];
|
|
312
|
+
const seen = /* @__PURE__ */ new Set();
|
|
313
|
+
for (const match of source.matchAll(importRegex)) {
|
|
314
|
+
const rawImportPath = match[2];
|
|
315
|
+
const componentDir = path12.dirname(rawImportPath);
|
|
316
|
+
const absComponentDir = path12.join(themePath, componentDir);
|
|
317
|
+
if (seen.has(componentDir)) continue;
|
|
318
|
+
seen.add(componentDir);
|
|
319
|
+
const sectionJsonPath = path12.join(absComponentDir, "section.json");
|
|
320
|
+
let sectionJson;
|
|
321
|
+
try {
|
|
322
|
+
const raw = await fs10.readFile(sectionJsonPath, "utf-8");
|
|
323
|
+
sectionJson = JSON.parse(raw);
|
|
324
|
+
} catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (sectionJson.type !== "opaque-react") continue;
|
|
328
|
+
if (!sectionJson.entry) continue;
|
|
329
|
+
sections.push({
|
|
330
|
+
type: "opaque-react",
|
|
331
|
+
name: sectionJson.name ?? path12.basename(componentDir),
|
|
332
|
+
entry: path12.join(componentDir, sectionJson.entry),
|
|
333
|
+
componentDir
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return sections;
|
|
337
|
+
}
|
|
338
|
+
function buildNextjsPagesMap(pages, themeId) {
|
|
339
|
+
const result = {};
|
|
340
|
+
for (const page of pages) {
|
|
341
|
+
const id = page.routePath === "/" ? "home" : page.routePath.replace(/\//g, "-").replace(/^-/, "");
|
|
342
|
+
const makeSectionType = (name) => `${themeId}-${name.toLowerCase().replace(/\s+/g, "-")}`;
|
|
343
|
+
result[id] = {
|
|
344
|
+
id,
|
|
345
|
+
name: id.charAt(0).toUpperCase() + id.slice(1),
|
|
346
|
+
path: page.routePath,
|
|
347
|
+
config: {
|
|
348
|
+
id,
|
|
349
|
+
path: page.routePath,
|
|
350
|
+
sections: page.sections.map((s, i) => ({
|
|
351
|
+
id: `${id}-section-${i}`,
|
|
352
|
+
type: makeSectionType(s.name),
|
|
353
|
+
sectionType: "opaque-react",
|
|
354
|
+
settings: {}
|
|
355
|
+
}))
|
|
356
|
+
},
|
|
357
|
+
sections: page.sections.map((s, i) => ({
|
|
358
|
+
id: `${id}-section-${i}`,
|
|
359
|
+
type: makeSectionType(s.name),
|
|
360
|
+
sectionType: "opaque-react",
|
|
361
|
+
settings: {}
|
|
362
|
+
}))
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
var init_nextjs_page_scanner = __esm({
|
|
368
|
+
"src/utils/nextjs-page-scanner.ts"() {
|
|
369
|
+
}
|
|
370
|
+
});
|
|
88
371
|
|
|
89
372
|
// src/utils/compile-theme.ts
|
|
90
373
|
var compile_theme_exports = {};
|
|
@@ -100,8 +383,8 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
100
383
|
const tailwindcss = (await import('tailwindcss')).default;
|
|
101
384
|
const tailwindConfig = {
|
|
102
385
|
content: [
|
|
103
|
-
|
|
104
|
-
|
|
386
|
+
path12.join(themePath, "sections/**/*.{ts,tsx}"),
|
|
387
|
+
path12.join(themePath, "components/**/*.{ts,tsx}")
|
|
105
388
|
],
|
|
106
389
|
theme: { extend: {} },
|
|
107
390
|
plugins: []
|
|
@@ -111,7 +394,7 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
111
394
|
inputCSS,
|
|
112
395
|
{ from: void 0 }
|
|
113
396
|
);
|
|
114
|
-
await
|
|
397
|
+
await fs10.writeFile(path12.join(outDir, "bundle.css"), result.css);
|
|
115
398
|
logger.info("Generated bundle.css");
|
|
116
399
|
} catch (err) {
|
|
117
400
|
logger.warning(
|
|
@@ -122,12 +405,12 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
122
405
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
123
406
|
let dir = startDir;
|
|
124
407
|
while (true) {
|
|
125
|
-
const candidate =
|
|
408
|
+
const candidate = path12.join(dir, "node_modules", relativePath);
|
|
126
409
|
try {
|
|
127
|
-
await
|
|
410
|
+
await fs10.access(candidate);
|
|
128
411
|
return candidate;
|
|
129
412
|
} catch {
|
|
130
|
-
const parent =
|
|
413
|
+
const parent = path12.dirname(dir);
|
|
131
414
|
if (parent === dir) break;
|
|
132
415
|
dir = parent;
|
|
133
416
|
}
|
|
@@ -151,7 +434,7 @@ async function scanImportsFromPackage(sourceDir, packageName) {
|
|
|
151
434
|
});
|
|
152
435
|
for (const file of sourceFiles) {
|
|
153
436
|
try {
|
|
154
|
-
const content = await
|
|
437
|
+
const content = await fs10.readFile(path12.join(sourceDir, file), "utf-8");
|
|
155
438
|
for (const match of content.matchAll(namespaceImportRegex)) {
|
|
156
439
|
const subpath = match[1] ? match[1].slice(1) : "";
|
|
157
440
|
if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
|
|
@@ -205,17 +488,17 @@ function createCoreGlobalPlugin(themePath) {
|
|
|
205
488
|
const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
|
|
206
489
|
let distPath = await resolveNodeModulesFile(
|
|
207
490
|
themePath,
|
|
208
|
-
|
|
491
|
+
path12.join("@onexapis", "core", "dist", distFileName)
|
|
209
492
|
);
|
|
210
493
|
if (!distPath) {
|
|
211
494
|
distPath = await resolveNodeModulesFile(
|
|
212
495
|
__dirname,
|
|
213
|
-
|
|
496
|
+
path12.join("@onexapis", "core", "dist", distFileName)
|
|
214
497
|
);
|
|
215
498
|
}
|
|
216
499
|
try {
|
|
217
500
|
if (!distPath) throw new Error("not found");
|
|
218
|
-
const distContent = await
|
|
501
|
+
const distContent = await fs10.readFile(distPath, "utf-8");
|
|
219
502
|
const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
|
|
220
503
|
for (const m of exportMatches) {
|
|
221
504
|
const names = m[1].split(",").map((n) => {
|
|
@@ -262,180 +545,6 @@ ${namedExportLines}
|
|
|
262
545
|
}
|
|
263
546
|
};
|
|
264
547
|
}
|
|
265
|
-
function createThemeDepsStubPlugin(themePath) {
|
|
266
|
-
return {
|
|
267
|
-
name: "theme-deps-stub",
|
|
268
|
-
setup(build2) {
|
|
269
|
-
const tryResolveOrStub = (filter, namespace) => {
|
|
270
|
-
build2.onResolve({ filter }, async (args) => {
|
|
271
|
-
if (args.pluginData?.skipStub) return void 0;
|
|
272
|
-
try {
|
|
273
|
-
const result = await build2.resolve(args.path, {
|
|
274
|
-
kind: args.kind,
|
|
275
|
-
resolveDir: args.resolveDir || themePath,
|
|
276
|
-
importer: args.importer,
|
|
277
|
-
namespace: "file",
|
|
278
|
-
pluginData: { skipStub: true }
|
|
279
|
-
});
|
|
280
|
-
if (!result.errors.length) return result;
|
|
281
|
-
} catch {
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
const req = createRequire(import.meta.url || __filename);
|
|
285
|
-
const resolved = req.resolve(args.path);
|
|
286
|
-
if (resolved) return { path: resolved, namespace: "file" };
|
|
287
|
-
} catch {
|
|
288
|
-
}
|
|
289
|
-
return { path: args.path, namespace };
|
|
290
|
-
});
|
|
291
|
-
};
|
|
292
|
-
tryResolveOrStub(/^next\//, "next-stub");
|
|
293
|
-
build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
|
|
294
|
-
const stubs = {
|
|
295
|
-
"next/image": `
|
|
296
|
-
import React from 'react';
|
|
297
|
-
const Image = React.forwardRef((props, ref) => {
|
|
298
|
-
const { src, alt, width, height, fill, priority, sizes, quality, placeholder, blurDataURL, onLoad, onError, style, className, ...rest } = props;
|
|
299
|
-
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
300
|
-
const fillStyle = fill ? { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: style?.objectFit || 'cover', display: 'block' } : {};
|
|
301
|
-
const mergedStyle = { ...fillStyle, ...style };
|
|
302
|
-
return React.createElement('img', {
|
|
303
|
-
ref, src: imgSrc, alt,
|
|
304
|
-
width: fill ? undefined : width, height: fill ? undefined : height,
|
|
305
|
-
loading: priority ? 'eager' : 'lazy',
|
|
306
|
-
style: Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined,
|
|
307
|
-
className, onLoad, onError, ...rest,
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
export default Image;
|
|
311
|
-
`,
|
|
312
|
-
"next/link": `
|
|
313
|
-
import React from 'react';
|
|
314
|
-
const Link = ({ href, children, ...rest }) => React.createElement('a', { href, ...rest }, children);
|
|
315
|
-
export default Link;
|
|
316
|
-
`,
|
|
317
|
-
"next/navigation": `
|
|
318
|
-
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(){} }; }
|
|
319
|
-
export function usePathname() { return window.location.pathname; }
|
|
320
|
-
export function useSearchParams() { return new URLSearchParams(window.location.search); }
|
|
321
|
-
export function useParams() { return {}; }
|
|
322
|
-
export function redirect(url) { window.location.href = url; }
|
|
323
|
-
export function notFound() { throw new Error('Not Found'); }
|
|
324
|
-
`,
|
|
325
|
-
"next/headers": `
|
|
326
|
-
export function cookies() { return { get(){}, getAll(){ return []; }, set(){}, delete(){}, has(){ return false; } }; }
|
|
327
|
-
export function headers() { return new Headers(); }
|
|
328
|
-
`
|
|
329
|
-
};
|
|
330
|
-
return {
|
|
331
|
-
contents: stubs[args.path] || "export default {};",
|
|
332
|
-
loader: "jsx",
|
|
333
|
-
resolveDir: themePath
|
|
334
|
-
};
|
|
335
|
-
});
|
|
336
|
-
const lucideImports = /* @__PURE__ */ new Set();
|
|
337
|
-
let lucideThemeScanned = false;
|
|
338
|
-
tryResolveOrStub(/^lucide-react/, "lucide-stub");
|
|
339
|
-
build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
|
|
340
|
-
if (!lucideThemeScanned) {
|
|
341
|
-
lucideThemeScanned = true;
|
|
342
|
-
try {
|
|
343
|
-
const scanned = await scanImportsFromPackage(
|
|
344
|
-
themePath,
|
|
345
|
-
"lucide-react"
|
|
346
|
-
);
|
|
347
|
-
for (const names of Object.values(scanned)) {
|
|
348
|
-
for (const name of names) lucideImports.add(name);
|
|
349
|
-
}
|
|
350
|
-
} catch {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const iconNames = [...lucideImports];
|
|
354
|
-
const exports$1 = iconNames.map((n) => `icon as ${n}`).join(", ");
|
|
355
|
-
return {
|
|
356
|
-
contents: `
|
|
357
|
-
const icon = (props) => null;
|
|
358
|
-
export { ${exports$1} };
|
|
359
|
-
export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
|
|
360
|
-
`.trim(),
|
|
361
|
-
loader: "jsx"
|
|
362
|
-
};
|
|
363
|
-
});
|
|
364
|
-
tryResolveOrStub(/^framer-motion/, "motion-stub");
|
|
365
|
-
build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
|
|
366
|
-
contents: `
|
|
367
|
-
import React from 'react';
|
|
368
|
-
const handler = { get: (_, name) => {
|
|
369
|
-
if (name === '__esModule') return true;
|
|
370
|
-
return React.forwardRef((props, ref) => React.createElement(name, { ...props, ref }));
|
|
371
|
-
}};
|
|
372
|
-
export const motion = new Proxy({}, handler);
|
|
373
|
-
export const AnimatePresence = ({ children }) => children || null;
|
|
374
|
-
export function useInView() { return true; }
|
|
375
|
-
export default { motion, AnimatePresence, useInView };
|
|
376
|
-
`.trim(),
|
|
377
|
-
loader: "jsx",
|
|
378
|
-
resolveDir: themePath
|
|
379
|
-
}));
|
|
380
|
-
tryResolveOrStub(/^sonner$/, "sonner-stub");
|
|
381
|
-
build2.onLoad({ filter: /.*/, namespace: "sonner-stub" }, () => ({
|
|
382
|
-
contents: `
|
|
383
|
-
export const toast = new Proxy(() => {}, { get: () => () => {} });
|
|
384
|
-
export const Toaster = () => null;
|
|
385
|
-
export default { toast, Toaster };
|
|
386
|
-
`.trim(),
|
|
387
|
-
loader: "jsx"
|
|
388
|
-
}));
|
|
389
|
-
tryResolveOrStub(/^react-hook-form$/, "rhf-stub");
|
|
390
|
-
build2.onLoad({ filter: /.*/, namespace: "rhf-stub" }, () => ({
|
|
391
|
-
contents: `
|
|
392
|
-
export function useForm() {
|
|
393
|
-
return {
|
|
394
|
-
register: () => ({}),
|
|
395
|
-
handleSubmit: (fn) => (e) => { e?.preventDefault?.(); fn({}); },
|
|
396
|
-
formState: { errors: {}, isSubmitting: false, isValid: true },
|
|
397
|
-
watch: () => undefined,
|
|
398
|
-
setValue: () => {},
|
|
399
|
-
reset: () => {},
|
|
400
|
-
control: {},
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
export function useController() { return { field: {}, fieldState: {} }; }
|
|
404
|
-
export function useFormContext() { return useForm(); }
|
|
405
|
-
`.trim(),
|
|
406
|
-
loader: "js"
|
|
407
|
-
}));
|
|
408
|
-
tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
|
|
409
|
-
build2.onLoad(
|
|
410
|
-
{ filter: /.*/, namespace: "hookform-resolvers-stub" },
|
|
411
|
-
() => ({
|
|
412
|
-
contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
|
|
413
|
-
loader: "js"
|
|
414
|
-
})
|
|
415
|
-
);
|
|
416
|
-
tryResolveOrStub(/^next-intl$/, "next-intl-stub");
|
|
417
|
-
build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
|
|
418
|
-
contents: `
|
|
419
|
-
export function useTranslations(ns) {
|
|
420
|
-
return (key) => ns ? ns + '.' + key : key;
|
|
421
|
-
}
|
|
422
|
-
export function useLocale() { return 'en'; }
|
|
423
|
-
export function useMessages() { return {}; }
|
|
424
|
-
`.trim(),
|
|
425
|
-
loader: "js"
|
|
426
|
-
}));
|
|
427
|
-
tryResolveOrStub(/^zod$/, "zod-stub");
|
|
428
|
-
build2.onLoad({ filter: /.*/, namespace: "zod-stub" }, () => ({
|
|
429
|
-
contents: `
|
|
430
|
-
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 });
|
|
431
|
-
export const z = { string: schema, number: schema, boolean: schema, object: (s) => ({ ...schema(), shape: s }), array: schema, enum: schema, union: schema, literal: schema, infer: undefined };
|
|
432
|
-
export default z;
|
|
433
|
-
`.trim(),
|
|
434
|
-
loader: "js"
|
|
435
|
-
}));
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
548
|
async function generateThemeData(themePath, outputDir, themeId) {
|
|
440
549
|
const { createJiti } = await import('jiti');
|
|
441
550
|
const jiti = createJiti(import.meta.url);
|
|
@@ -444,7 +553,7 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
444
553
|
const pages = {};
|
|
445
554
|
for (const ext of [".ts", ".js"]) {
|
|
446
555
|
try {
|
|
447
|
-
const mod = await jiti.import(
|
|
556
|
+
const mod = await jiti.import(path12.join(themePath, `theme.config${ext}`));
|
|
448
557
|
themeConfig = mod.default || mod;
|
|
449
558
|
break;
|
|
450
559
|
} catch {
|
|
@@ -452,20 +561,20 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
452
561
|
}
|
|
453
562
|
for (const ext of [".ts", ".js"]) {
|
|
454
563
|
try {
|
|
455
|
-
const mod = await jiti.import(
|
|
564
|
+
const mod = await jiti.import(path12.join(themePath, `theme.layout${ext}`));
|
|
456
565
|
layoutConfig = mod.default || mod;
|
|
457
566
|
break;
|
|
458
567
|
} catch {
|
|
459
568
|
}
|
|
460
569
|
}
|
|
461
570
|
const schemas = {};
|
|
462
|
-
const sectionsDir =
|
|
571
|
+
const sectionsDir = path12.join(themePath, "sections");
|
|
463
572
|
try {
|
|
464
|
-
const sectionDirs = await
|
|
573
|
+
const sectionDirs = await fs10.readdir(sectionsDir);
|
|
465
574
|
for (const dir of sectionDirs) {
|
|
466
|
-
const schemaFile =
|
|
575
|
+
const schemaFile = path12.join(sectionsDir, dir, `${dir}.schema.ts`);
|
|
467
576
|
try {
|
|
468
|
-
await
|
|
577
|
+
await fs10.access(schemaFile);
|
|
469
578
|
const mod = await jiti.import(schemaFile);
|
|
470
579
|
for (const [key, value] of Object.entries(mod)) {
|
|
471
580
|
if (key.endsWith("Schema") && value && typeof value === "object" && value.type) {
|
|
@@ -477,14 +586,14 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
477
586
|
}
|
|
478
587
|
} catch {
|
|
479
588
|
}
|
|
480
|
-
const pagesDir =
|
|
589
|
+
const pagesDir = path12.join(themePath, "pages");
|
|
481
590
|
try {
|
|
482
|
-
const files = await
|
|
591
|
+
const files = await fs10.readdir(pagesDir);
|
|
483
592
|
for (const file of files) {
|
|
484
593
|
if (!file.match(/\.(ts|js)$/)) continue;
|
|
485
594
|
const name = file.replace(/\.(ts|js)$/, "");
|
|
486
595
|
try {
|
|
487
|
-
const mod = await jiti.import(
|
|
596
|
+
const mod = await jiti.import(path12.join(pagesDir, file));
|
|
488
597
|
const config = mod.default || mod;
|
|
489
598
|
const sections = (config.sections || []).map((section) => {
|
|
490
599
|
const schema = schemas[section.type];
|
|
@@ -512,8 +621,16 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
512
621
|
}
|
|
513
622
|
} catch {
|
|
514
623
|
}
|
|
515
|
-
|
|
516
|
-
|
|
624
|
+
if (isNextjsProject(themePath)) {
|
|
625
|
+
const nextjsPages = await scanAppDirectory(themePath);
|
|
626
|
+
if (nextjsPages.length > 0) {
|
|
627
|
+
const nextjsPagesMap = buildNextjsPagesMap(nextjsPages, themeId);
|
|
628
|
+
Object.assign(pages, nextjsPagesMap);
|
|
629
|
+
logger.info(`Scanned ${nextjsPages.length} Next.js app/ pages`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
await fs10.writeFile(
|
|
633
|
+
path12.join(outputDir, "theme-data.json"),
|
|
517
634
|
JSON.stringify(
|
|
518
635
|
{
|
|
519
636
|
themeId,
|
|
@@ -536,22 +653,22 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
536
653
|
logger.info(`Generated theme-data.json (${Object.keys(pages).length} pages)`);
|
|
537
654
|
}
|
|
538
655
|
async function contentHashEntry(outputDir) {
|
|
539
|
-
const entryPath =
|
|
540
|
-
const mapPath =
|
|
656
|
+
const entryPath = path12.join(outputDir, "bundle-entry.js");
|
|
657
|
+
const mapPath = path12.join(outputDir, "bundle-entry.js.map");
|
|
541
658
|
let entryContent;
|
|
542
659
|
try {
|
|
543
|
-
entryContent = await
|
|
660
|
+
entryContent = await fs10.readFile(entryPath, "utf-8");
|
|
544
661
|
} catch {
|
|
545
|
-
const indexPath =
|
|
662
|
+
const indexPath = path12.join(outputDir, "index.js");
|
|
546
663
|
try {
|
|
547
|
-
entryContent = await
|
|
664
|
+
entryContent = await fs10.readFile(indexPath, "utf-8");
|
|
548
665
|
} catch {
|
|
549
666
|
logger.warning("No entry file found in output, skipping content hash");
|
|
550
667
|
return;
|
|
551
668
|
}
|
|
552
669
|
const hash2 = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
553
670
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
554
|
-
const indexMapPath =
|
|
671
|
+
const indexMapPath = path12.join(outputDir, "index.js.map");
|
|
555
672
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
556
673
|
entryContent = entryContent.replace(
|
|
557
674
|
/\/\/# sourceMappingURL=index\.js\.map/,
|
|
@@ -559,18 +676,18 @@ async function contentHashEntry(outputDir) {
|
|
|
559
676
|
);
|
|
560
677
|
const oldFiles2 = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
561
678
|
for (const f of oldFiles2) {
|
|
562
|
-
await
|
|
679
|
+
await fs10.unlink(path12.join(outputDir, f));
|
|
563
680
|
}
|
|
564
|
-
await
|
|
565
|
-
await
|
|
681
|
+
await fs10.writeFile(path12.join(outputDir, hashedName2), entryContent);
|
|
682
|
+
await fs10.unlink(indexPath);
|
|
566
683
|
try {
|
|
567
|
-
await
|
|
684
|
+
await fs10.unlink(entryPath);
|
|
568
685
|
} catch {
|
|
569
686
|
}
|
|
570
|
-
await
|
|
687
|
+
await fs10.writeFile(entryPath, entryContent);
|
|
571
688
|
try {
|
|
572
|
-
await
|
|
573
|
-
await
|
|
689
|
+
await fs10.access(indexMapPath);
|
|
690
|
+
await fs10.rename(indexMapPath, path12.join(outputDir, hashedMapName2));
|
|
574
691
|
} catch {
|
|
575
692
|
}
|
|
576
693
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
@@ -585,17 +702,17 @@ async function contentHashEntry(outputDir) {
|
|
|
585
702
|
);
|
|
586
703
|
const oldFiles = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
587
704
|
for (const f of oldFiles) {
|
|
588
|
-
await
|
|
705
|
+
await fs10.unlink(path12.join(outputDir, f));
|
|
589
706
|
}
|
|
590
|
-
await
|
|
707
|
+
await fs10.writeFile(path12.join(outputDir, hashedName), entryContent);
|
|
591
708
|
try {
|
|
592
|
-
await
|
|
709
|
+
await fs10.unlink(entryPath);
|
|
593
710
|
} catch {
|
|
594
711
|
}
|
|
595
|
-
await
|
|
712
|
+
await fs10.writeFile(entryPath, entryContent);
|
|
596
713
|
try {
|
|
597
|
-
await
|
|
598
|
-
await
|
|
714
|
+
await fs10.access(mapPath);
|
|
715
|
+
await fs10.rename(mapPath, path12.join(outputDir, hashedMapName));
|
|
599
716
|
} catch {
|
|
600
717
|
}
|
|
601
718
|
logger.info(`Entry hashed: ${hashedName}`);
|
|
@@ -607,7 +724,7 @@ async function extractDataRequirements(themePath) {
|
|
|
607
724
|
const requirements = {};
|
|
608
725
|
for (const file of schemaFiles) {
|
|
609
726
|
try {
|
|
610
|
-
const mod = await jiti.import(
|
|
727
|
+
const mod = await jiti.import(path12.join(themePath, file));
|
|
611
728
|
const exports$1 = mod;
|
|
612
729
|
for (const value of Object.values(exports$1)) {
|
|
613
730
|
if (value && typeof value === "object" && typeof value.type === "string" && value.dataRequirements && typeof value.dataRequirements === "object") {
|
|
@@ -622,12 +739,105 @@ async function extractDataRequirements(themePath) {
|
|
|
622
739
|
}
|
|
623
740
|
return requirements;
|
|
624
741
|
}
|
|
742
|
+
async function writeGateManifests(themePath, outputDir) {
|
|
743
|
+
try {
|
|
744
|
+
const schemas = await extractSchemas(themePath);
|
|
745
|
+
await fs10.writeFile(
|
|
746
|
+
path12.join(outputDir, "schemas.json"),
|
|
747
|
+
serializeManifest(schemas)
|
|
748
|
+
);
|
|
749
|
+
logger.info(
|
|
750
|
+
`Generated schemas.json (${Object.keys(schemas.sections).length} sections)`
|
|
751
|
+
);
|
|
752
|
+
} catch (err) {
|
|
753
|
+
logger.warning(
|
|
754
|
+
`schemas.json not written: ${err instanceof Error ? err.message : String(err)}`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
const entries = await scanThemeAssets(outputDir);
|
|
759
|
+
const assets = entries.map((e) => ({
|
|
760
|
+
path: e.originalPath,
|
|
761
|
+
hash: e.hash,
|
|
762
|
+
size: e.size,
|
|
763
|
+
contentType: e.contentType
|
|
764
|
+
}));
|
|
765
|
+
await fs10.writeFile(
|
|
766
|
+
path12.join(outputDir, "asset-manifest.json"),
|
|
767
|
+
JSON.stringify({ manifestVersion: 1, assets }, null, 2)
|
|
768
|
+
);
|
|
769
|
+
logger.info(`Generated asset-manifest.json (${assets.length} assets)`);
|
|
770
|
+
} catch (err) {
|
|
771
|
+
logger.warning(
|
|
772
|
+
`asset-manifest.json not written: ${err instanceof Error ? err.message : String(err)}`
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
async function compileSections(themePath, outputDir) {
|
|
777
|
+
const sectionsDir = path12.join(themePath, "sections");
|
|
778
|
+
let sectionDirs;
|
|
779
|
+
try {
|
|
780
|
+
sectionDirs = await fs10.readdir(sectionsDir);
|
|
781
|
+
} catch {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
for (const dirName of sectionDirs) {
|
|
785
|
+
const sectionSrc = path12.join(sectionsDir, dirName);
|
|
786
|
+
const sectionOut = path12.join(outputDir, "sections", dirName);
|
|
787
|
+
let section = null;
|
|
788
|
+
try {
|
|
789
|
+
const raw = await fs10.readFile(
|
|
790
|
+
path12.join(sectionSrc, "section.manifest.json"),
|
|
791
|
+
"utf-8"
|
|
792
|
+
);
|
|
793
|
+
section = JSON.parse(raw);
|
|
794
|
+
} catch {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
switch (section.type) {
|
|
798
|
+
case "editable":
|
|
799
|
+
case "opaque-react":
|
|
800
|
+
break;
|
|
801
|
+
case "html": {
|
|
802
|
+
await fs10.mkdir(sectionOut, { recursive: true });
|
|
803
|
+
const htmlSrc = path12.join(sectionSrc, section.html);
|
|
804
|
+
let htmlContent = await fs10.readFile(htmlSrc, "utf-8");
|
|
805
|
+
htmlContent = htmlContent.replace(
|
|
806
|
+
/<script[^>]+src=["']https?:\/\/[^"']*["'][^>]*><\/script>/gi,
|
|
807
|
+
""
|
|
808
|
+
);
|
|
809
|
+
await fs10.writeFile(path12.join(sectionOut, path12.basename(section.html)), htmlContent);
|
|
810
|
+
if (section.css) {
|
|
811
|
+
await fs10.copyFile(
|
|
812
|
+
path12.join(sectionSrc, section.css),
|
|
813
|
+
path12.join(sectionOut, path12.basename(section.css))
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
case "iframe":
|
|
819
|
+
break;
|
|
820
|
+
case "webcomponent": {
|
|
821
|
+
await fs10.mkdir(sectionOut, { recursive: true });
|
|
822
|
+
await fs10.copyFile(
|
|
823
|
+
path12.join(sectionSrc, section.bundle),
|
|
824
|
+
path12.join(sectionOut, path12.basename(section.bundle))
|
|
825
|
+
);
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
default:
|
|
829
|
+
throw new Error(
|
|
830
|
+
`Unknown section type. Valid types: editable, opaque-react, html, iframe, webcomponent`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
625
835
|
async function generateManifest(themeName, themePath, outputDir) {
|
|
626
836
|
let version = "1.0.0";
|
|
627
837
|
let themeId = themeName;
|
|
628
838
|
try {
|
|
629
|
-
const pkgContent = await
|
|
630
|
-
|
|
839
|
+
const pkgContent = await fs10.readFile(
|
|
840
|
+
path12.join(themePath, "package.json"),
|
|
631
841
|
"utf-8"
|
|
632
842
|
);
|
|
633
843
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -645,7 +855,7 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
645
855
|
const dataRequirements = await extractDataRequirements(themePath);
|
|
646
856
|
let hasThemeConfig = false;
|
|
647
857
|
try {
|
|
648
|
-
await
|
|
858
|
+
await fs10.access(path12.join(themePath, "theme.config.ts"));
|
|
649
859
|
hasThemeConfig = true;
|
|
650
860
|
} catch {
|
|
651
861
|
}
|
|
@@ -686,24 +896,34 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
686
896
|
// Section data requirements for server-side prefetching (keyed by section type)
|
|
687
897
|
dataRequirements
|
|
688
898
|
};
|
|
689
|
-
await
|
|
690
|
-
|
|
899
|
+
await fs10.writeFile(
|
|
900
|
+
path12.join(outputDir, "manifest.json"),
|
|
691
901
|
JSON.stringify(manifest, null, 2)
|
|
692
902
|
);
|
|
693
903
|
}
|
|
694
904
|
async function compileStandaloneTheme(themePath, themeName) {
|
|
695
|
-
const outputDir =
|
|
696
|
-
const
|
|
697
|
-
|
|
905
|
+
const outputDir = path12.join(themePath, "dist");
|
|
906
|
+
const isNextjs = isNextjsProject(themePath);
|
|
907
|
+
if (isNextjs) {
|
|
908
|
+
logger.info("Detected Next.js project \u2014 using next/* shims");
|
|
909
|
+
}
|
|
910
|
+
const bundleEntry = path12.join(themePath, "bundle-entry.ts");
|
|
911
|
+
const indexEntry = path12.join(themePath, "index.ts");
|
|
698
912
|
let entryPoint = indexEntry;
|
|
699
913
|
try {
|
|
700
|
-
await
|
|
914
|
+
await fs10.access(bundleEntry);
|
|
701
915
|
entryPoint = bundleEntry;
|
|
702
916
|
} catch {
|
|
703
917
|
}
|
|
704
|
-
const shimPath =
|
|
705
|
-
await
|
|
706
|
-
await
|
|
918
|
+
const shimPath = path12.join(outputDir, ".process-shim.js");
|
|
919
|
+
await fs10.mkdir(outputDir, { recursive: true });
|
|
920
|
+
await fs10.writeFile(shimPath, PROCESS_SHIM);
|
|
921
|
+
const plugins = [
|
|
922
|
+
reactGlobalPlugin,
|
|
923
|
+
reactQueryGlobalPlugin,
|
|
924
|
+
createCoreGlobalPlugin(themePath)
|
|
925
|
+
];
|
|
926
|
+
if (isNextjs) plugins.unshift(nextShimPlugin);
|
|
707
927
|
const buildOptions = {
|
|
708
928
|
entryPoints: [entryPoint],
|
|
709
929
|
bundle: true,
|
|
@@ -715,12 +935,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
715
935
|
banner: {
|
|
716
936
|
js: '"use client";'
|
|
717
937
|
},
|
|
718
|
-
plugins
|
|
719
|
-
reactGlobalPlugin,
|
|
720
|
-
reactQueryGlobalPlugin,
|
|
721
|
-
createCoreGlobalPlugin(themePath),
|
|
722
|
-
createThemeDepsStubPlugin(themePath)
|
|
723
|
-
],
|
|
938
|
+
plugins,
|
|
724
939
|
external: [],
|
|
725
940
|
alias: {
|
|
726
941
|
events: "events/",
|
|
@@ -754,19 +969,21 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
754
969
|
try {
|
|
755
970
|
const result = await esbuild.build(buildOptions);
|
|
756
971
|
try {
|
|
757
|
-
await
|
|
972
|
+
await fs10.unlink(shimPath);
|
|
758
973
|
} catch {
|
|
759
974
|
}
|
|
975
|
+
await compileSections(themePath, outputDir);
|
|
760
976
|
await contentHashEntry(outputDir);
|
|
761
|
-
const themeAssetsDir =
|
|
762
|
-
const distThemeAssets =
|
|
977
|
+
const themeAssetsDir = path12.join(themePath, "assets");
|
|
978
|
+
const distThemeAssets = path12.join(outputDir, "theme-assets");
|
|
763
979
|
try {
|
|
764
|
-
await
|
|
765
|
-
await
|
|
980
|
+
await fs10.access(themeAssetsDir);
|
|
981
|
+
await fs10.cp(themeAssetsDir, distThemeAssets, { recursive: true });
|
|
766
982
|
logger.info("Copied static assets to dist/theme-assets/");
|
|
767
983
|
} catch {
|
|
768
984
|
}
|
|
769
985
|
await generateManifest(themeName, themePath, outputDir);
|
|
986
|
+
await writeGateManifests(themePath, outputDir);
|
|
770
987
|
await generateThemeData(themePath, outputDir, themeName);
|
|
771
988
|
await generateThemeCSS(themePath, outputDir);
|
|
772
989
|
if (result.metafile) {
|
|
@@ -781,7 +998,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
781
998
|
return true;
|
|
782
999
|
} catch (error) {
|
|
783
1000
|
try {
|
|
784
|
-
await
|
|
1001
|
+
await fs10.unlink(shimPath);
|
|
785
1002
|
} catch {
|
|
786
1003
|
}
|
|
787
1004
|
logger.error(`esbuild compilation failed: ${error}`);
|
|
@@ -789,18 +1006,25 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
789
1006
|
}
|
|
790
1007
|
}
|
|
791
1008
|
async function compileStandaloneThemeDev(themePath, themeName) {
|
|
792
|
-
const outputDir =
|
|
793
|
-
const
|
|
794
|
-
const
|
|
1009
|
+
const outputDir = path12.join(themePath, "dist");
|
|
1010
|
+
const isNextjs = isNextjsProject(themePath);
|
|
1011
|
+
const bundleEntry = path12.join(themePath, "bundle-entry.ts");
|
|
1012
|
+
const indexEntry = path12.join(themePath, "index.ts");
|
|
795
1013
|
let entryPoint = indexEntry;
|
|
796
1014
|
try {
|
|
797
|
-
await
|
|
1015
|
+
await fs10.access(bundleEntry);
|
|
798
1016
|
entryPoint = bundleEntry;
|
|
799
1017
|
} catch {
|
|
800
1018
|
}
|
|
801
|
-
const shimPath =
|
|
802
|
-
await
|
|
803
|
-
await
|
|
1019
|
+
const shimPath = path12.join(outputDir, ".process-shim.js");
|
|
1020
|
+
await fs10.mkdir(outputDir, { recursive: true });
|
|
1021
|
+
await fs10.writeFile(shimPath, PROCESS_SHIM);
|
|
1022
|
+
const devPlugins = [
|
|
1023
|
+
reactGlobalPlugin,
|
|
1024
|
+
reactQueryGlobalPlugin,
|
|
1025
|
+
createCoreGlobalPlugin(themePath)
|
|
1026
|
+
];
|
|
1027
|
+
if (isNextjs) devPlugins.unshift(nextShimPlugin);
|
|
804
1028
|
const buildOptions = {
|
|
805
1029
|
entryPoints: [entryPoint],
|
|
806
1030
|
bundle: true,
|
|
@@ -811,12 +1035,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
811
1035
|
banner: {
|
|
812
1036
|
js: '"use client";'
|
|
813
1037
|
},
|
|
814
|
-
plugins:
|
|
815
|
-
reactGlobalPlugin,
|
|
816
|
-
reactQueryGlobalPlugin,
|
|
817
|
-
createCoreGlobalPlugin(themePath),
|
|
818
|
-
createThemeDepsStubPlugin(themePath)
|
|
819
|
-
],
|
|
1038
|
+
plugins: devPlugins,
|
|
820
1039
|
external: [],
|
|
821
1040
|
alias: {
|
|
822
1041
|
events: "events/",
|
|
@@ -854,18 +1073,18 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
854
1073
|
return { context: context2, outputDir };
|
|
855
1074
|
}
|
|
856
1075
|
async function compilePreviewRuntime(themePath) {
|
|
857
|
-
const outputDir =
|
|
858
|
-
await
|
|
859
|
-
const outputPath =
|
|
1076
|
+
const outputDir = path12.join(themePath, "dist");
|
|
1077
|
+
await fs10.mkdir(outputDir, { recursive: true });
|
|
1078
|
+
const outputPath = path12.join(outputDir, "preview-runtime.js");
|
|
860
1079
|
const locations = [
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1080
|
+
path12.join(__dirname, "..", "preview", "preview-app.tsx"),
|
|
1081
|
+
path12.join(__dirname, "preview", "preview-app.tsx"),
|
|
1082
|
+
path12.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
|
|
864
1083
|
];
|
|
865
1084
|
let previewEntryPath = null;
|
|
866
1085
|
for (const loc of locations) {
|
|
867
1086
|
try {
|
|
868
|
-
await
|
|
1087
|
+
await fs10.access(loc);
|
|
869
1088
|
previewEntryPath = loc;
|
|
870
1089
|
break;
|
|
871
1090
|
} catch {
|
|
@@ -948,10 +1167,10 @@ ${locations.join("\n")}`
|
|
|
948
1167
|
if (!lucideScanned) {
|
|
949
1168
|
lucideScanned = true;
|
|
950
1169
|
const coreSrcCandidates = [
|
|
951
|
-
|
|
952
|
-
|
|
1170
|
+
path12.join(themePath, "node_modules", "@onexapis", "core", "src"),
|
|
1171
|
+
path12.join(themePath, "..", "..", "packages", "core", "src"),
|
|
953
1172
|
// monorepo sibling
|
|
954
|
-
|
|
1173
|
+
path12.join(
|
|
955
1174
|
__dirname,
|
|
956
1175
|
"..",
|
|
957
1176
|
"..",
|
|
@@ -966,7 +1185,7 @@ ${locations.join("\n")}`
|
|
|
966
1185
|
let coreSourceDir = null;
|
|
967
1186
|
for (const candidate of coreSrcCandidates) {
|
|
968
1187
|
try {
|
|
969
|
-
await
|
|
1188
|
+
await fs10.access(candidate);
|
|
970
1189
|
coreSourceDir = candidate;
|
|
971
1190
|
break;
|
|
972
1191
|
} catch {
|
|
@@ -985,21 +1204,21 @@ ${locations.join("\n")}`
|
|
|
985
1204
|
}
|
|
986
1205
|
} else {
|
|
987
1206
|
const coreDistCandidates = [
|
|
988
|
-
|
|
1207
|
+
path12.join(themePath, "node_modules", "@onexapis", "core", "dist")
|
|
989
1208
|
];
|
|
990
1209
|
const resolvedDist = await resolveNodeModulesFile(
|
|
991
1210
|
__dirname,
|
|
992
|
-
|
|
1211
|
+
path12.join("@onexapis", "core", "dist")
|
|
993
1212
|
);
|
|
994
1213
|
if (resolvedDist) coreDistCandidates.push(resolvedDist);
|
|
995
1214
|
for (const candidate of coreDistCandidates) {
|
|
996
1215
|
try {
|
|
997
|
-
await
|
|
1216
|
+
await fs10.access(candidate);
|
|
998
1217
|
const mjsFiles = await glob("*.mjs", { cwd: candidate });
|
|
999
1218
|
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
1000
1219
|
for (const file of mjsFiles) {
|
|
1001
|
-
const content = await
|
|
1002
|
-
|
|
1220
|
+
const content = await fs10.readFile(
|
|
1221
|
+
path12.join(candidate, file),
|
|
1003
1222
|
"utf-8"
|
|
1004
1223
|
);
|
|
1005
1224
|
for (const match of content.matchAll(importRegex)) {
|
|
@@ -1054,7 +1273,7 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
|
|
|
1054
1273
|
const req = createRequire(import.meta.url || __filename);
|
|
1055
1274
|
const cjsPath = req.resolve("framer-motion");
|
|
1056
1275
|
const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
|
|
1057
|
-
const esmEntry =
|
|
1276
|
+
const esmEntry = path12.join(pkgDir, "dist", "es", "index.mjs");
|
|
1058
1277
|
const { existsSync } = await import('fs');
|
|
1059
1278
|
if (existsSync(esmEntry)) {
|
|
1060
1279
|
return { path: esmEntry, namespace: "file" };
|
|
@@ -1153,8 +1372,8 @@ export function headers() { return new Headers(); }
|
|
|
1153
1372
|
});
|
|
1154
1373
|
}
|
|
1155
1374
|
};
|
|
1156
|
-
const shimPath =
|
|
1157
|
-
await
|
|
1375
|
+
const shimPath = path12.join(outputDir, ".process-shim-preview.js");
|
|
1376
|
+
await fs10.writeFile(shimPath, PROCESS_SHIM);
|
|
1158
1377
|
await esbuild.build({
|
|
1159
1378
|
entryPoints: [previewEntryPath],
|
|
1160
1379
|
bundle: true,
|
|
@@ -1189,15 +1408,19 @@ export function headers() { return new Headers(); }
|
|
|
1189
1408
|
}
|
|
1190
1409
|
});
|
|
1191
1410
|
try {
|
|
1192
|
-
await
|
|
1411
|
+
await fs10.unlink(shimPath);
|
|
1193
1412
|
} catch {
|
|
1194
1413
|
}
|
|
1195
1414
|
return outputPath;
|
|
1196
1415
|
}
|
|
1197
|
-
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin;
|
|
1416
|
+
var PROCESS_SHIM, reactGlobalPlugin, reactQueryGlobalPlugin, nextShimPlugin;
|
|
1198
1417
|
var init_compile_theme = __esm({
|
|
1199
1418
|
"src/utils/compile-theme.ts"() {
|
|
1200
1419
|
init_logger();
|
|
1420
|
+
init_extract_schemas();
|
|
1421
|
+
init_scan_theme_assets();
|
|
1422
|
+
init_detect_nextjs();
|
|
1423
|
+
init_nextjs_page_scanner();
|
|
1201
1424
|
PROCESS_SHIM = `
|
|
1202
1425
|
if (typeof process === "undefined") {
|
|
1203
1426
|
globalThis.process = {
|
|
@@ -1343,6 +1566,145 @@ export const {
|
|
|
1343
1566
|
}));
|
|
1344
1567
|
}
|
|
1345
1568
|
};
|
|
1569
|
+
nextShimPlugin = {
|
|
1570
|
+
name: "next-shim",
|
|
1571
|
+
setup(build2) {
|
|
1572
|
+
for (const serverModule of ["next/headers", "next/server", "next/cache"]) {
|
|
1573
|
+
build2.onResolve({ filter: new RegExp(`^${serverModule.replace("/", "\\/")}`) }, (args) => ({
|
|
1574
|
+
path: args.path,
|
|
1575
|
+
namespace: "next-server-error"
|
|
1576
|
+
}));
|
|
1577
|
+
}
|
|
1578
|
+
build2.onLoad({ filter: /.*/, namespace: "next-server-error" }, (args) => ({
|
|
1579
|
+
errors: [
|
|
1580
|
+
{
|
|
1581
|
+
text: `"${args.path}" is server-only and cannot be used in a OneX theme bundle. Use client-side equivalents or remove the import.`
|
|
1582
|
+
}
|
|
1583
|
+
]
|
|
1584
|
+
}));
|
|
1585
|
+
build2.onResolve({ filter: /^next\/navigation$/ }, () => ({
|
|
1586
|
+
path: "next-navigation-shim",
|
|
1587
|
+
namespace: "next-shim"
|
|
1588
|
+
}));
|
|
1589
|
+
build2.onLoad({ filter: /^next-navigation-shim$/, namespace: "next-shim" }, () => ({
|
|
1590
|
+
contents: `
|
|
1591
|
+
export function usePathname() {
|
|
1592
|
+
if (typeof window === 'undefined') return '/';
|
|
1593
|
+
return window.location.pathname;
|
|
1594
|
+
}
|
|
1595
|
+
export function useSearchParams() {
|
|
1596
|
+
if (typeof window === 'undefined') return new URLSearchParams();
|
|
1597
|
+
return new URLSearchParams(window.location.search);
|
|
1598
|
+
}
|
|
1599
|
+
export function useParams() {
|
|
1600
|
+
if (typeof window === 'undefined') return {};
|
|
1601
|
+
return (globalThis.__ONEX_ROUTE_PARAMS__) ?? {};
|
|
1602
|
+
}
|
|
1603
|
+
export function useRouter() {
|
|
1604
|
+
return {
|
|
1605
|
+
push(url) { if (typeof window !== 'undefined') window.location.href = url; },
|
|
1606
|
+
replace(url) { if (typeof window !== 'undefined') window.location.replace(url); },
|
|
1607
|
+
back() { if (typeof window !== 'undefined') window.history.back(); },
|
|
1608
|
+
forward() { if (typeof window !== 'undefined') window.history.forward(); },
|
|
1609
|
+
refresh() { if (typeof window !== 'undefined') window.location.reload(); },
|
|
1610
|
+
prefetch() {},
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
export function redirect(url) {
|
|
1614
|
+
if (typeof window !== 'undefined') window.location.href = url;
|
|
1615
|
+
throw new Error('redirect');
|
|
1616
|
+
}
|
|
1617
|
+
export function notFound() { throw new Error('not-found'); }
|
|
1618
|
+
`.trim(),
|
|
1619
|
+
loader: "js"
|
|
1620
|
+
}));
|
|
1621
|
+
build2.onResolve({ filter: /^next\/font\// }, () => ({
|
|
1622
|
+
path: "next-font-shim",
|
|
1623
|
+
namespace: "next-shim"
|
|
1624
|
+
}));
|
|
1625
|
+
build2.onLoad({ filter: /^next-font-shim$/, namespace: "next-shim" }, () => ({
|
|
1626
|
+
contents: `
|
|
1627
|
+
function makeFont(family) {
|
|
1628
|
+
return function(_opts) {
|
|
1629
|
+
return {
|
|
1630
|
+
className: '',
|
|
1631
|
+
style: { fontFamily: family + ', system-ui, sans-serif' },
|
|
1632
|
+
variable: '--font-' + family.toLowerCase().replace(/\\s+/g, '-'),
|
|
1633
|
+
};
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
export const Inter = makeFont('Inter');
|
|
1637
|
+
export const Roboto = makeFont('Roboto');
|
|
1638
|
+
export const Open_Sans = makeFont('Open Sans');
|
|
1639
|
+
export const Lato = makeFont('Lato');
|
|
1640
|
+
export const Montserrat = makeFont('Montserrat');
|
|
1641
|
+
export const Poppins = makeFont('Poppins');
|
|
1642
|
+
export const Raleway = makeFont('Raleway');
|
|
1643
|
+
export const Nunito = makeFont('Nunito');
|
|
1644
|
+
export const Geist = makeFont('Geist');
|
|
1645
|
+
export const Geist_Mono = makeFont('Geist Mono');
|
|
1646
|
+
export const DM_Sans = makeFont('DM Sans');
|
|
1647
|
+
export const Plus_Jakarta_Sans = makeFont('Plus Jakarta Sans');
|
|
1648
|
+
export function localFont(_opts) {
|
|
1649
|
+
return { className: '', style: { fontFamily: 'system-ui, sans-serif' }, variable: '--font-local' };
|
|
1650
|
+
}
|
|
1651
|
+
`.trim(),
|
|
1652
|
+
loader: "js"
|
|
1653
|
+
}));
|
|
1654
|
+
build2.onResolve({ filter: /^next\/dynamic$/ }, () => ({
|
|
1655
|
+
path: "next-dynamic-shim",
|
|
1656
|
+
namespace: "next-shim"
|
|
1657
|
+
}));
|
|
1658
|
+
build2.onLoad({ filter: /^next-dynamic-shim$/, namespace: "next-shim" }, () => ({
|
|
1659
|
+
contents: `
|
|
1660
|
+
import { lazy, Suspense, createElement } from 'react';
|
|
1661
|
+
export default function dynamic(loader, opts) {
|
|
1662
|
+
const Lazy = lazy(loader);
|
|
1663
|
+
return function DynamicComponent(props) {
|
|
1664
|
+
return createElement(Suspense, { fallback: opts?.loading ? createElement(opts.loading) : null },
|
|
1665
|
+
createElement(Lazy, props));
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
`.trim(),
|
|
1669
|
+
loader: "js"
|
|
1670
|
+
}));
|
|
1671
|
+
build2.onResolve({ filter: /^next\/image$/ }, () => ({
|
|
1672
|
+
path: "next-image-shim",
|
|
1673
|
+
namespace: "next-shim"
|
|
1674
|
+
}));
|
|
1675
|
+
build2.onLoad({ filter: /^next-image-shim$/, namespace: "next-shim" }, () => ({
|
|
1676
|
+
contents: `
|
|
1677
|
+
import { createElement } from 'react';
|
|
1678
|
+
export default function Image({ src, alt, width, height, style, className, ...rest }) {
|
|
1679
|
+
return createElement('img', { src, alt, width, height, style, className, ...rest });
|
|
1680
|
+
}
|
|
1681
|
+
`.trim(),
|
|
1682
|
+
loader: "js"
|
|
1683
|
+
}));
|
|
1684
|
+
build2.onResolve({ filter: /^next\/link$/ }, () => ({
|
|
1685
|
+
path: "next-link-shim",
|
|
1686
|
+
namespace: "next-shim"
|
|
1687
|
+
}));
|
|
1688
|
+
build2.onLoad({ filter: /^next-link-shim$/, namespace: "next-shim" }, () => ({
|
|
1689
|
+
contents: `
|
|
1690
|
+
import { createElement } from 'react';
|
|
1691
|
+
export default function Link({ href, children, className, style, ...rest }) {
|
|
1692
|
+
return createElement('a', { href, className, style, ...rest }, children);
|
|
1693
|
+
}
|
|
1694
|
+
`.trim(),
|
|
1695
|
+
loader: "js"
|
|
1696
|
+
}));
|
|
1697
|
+
build2.onResolve({ filter: /^next\// }, () => ({
|
|
1698
|
+
path: "next-noop-shim",
|
|
1699
|
+
namespace: "next-shim"
|
|
1700
|
+
}));
|
|
1701
|
+
build2.onLoad({ filter: /^next-noop-shim$/, namespace: "next-shim" }, () => ({
|
|
1702
|
+
contents: `export default {};
|
|
1703
|
+
`,
|
|
1704
|
+
loader: "js"
|
|
1705
|
+
}));
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1346
1708
|
}
|
|
1347
1709
|
});
|
|
1348
1710
|
|
|
@@ -1367,8 +1729,8 @@ function validateThemeName(name) {
|
|
|
1367
1729
|
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
1368
1730
|
}
|
|
1369
1731
|
function pathExists(filePath) {
|
|
1370
|
-
const
|
|
1371
|
-
return
|
|
1732
|
+
const fs14 = __require("fs-extra");
|
|
1733
|
+
return fs14.existsSync(filePath);
|
|
1372
1734
|
}
|
|
1373
1735
|
function validateCategory(category) {
|
|
1374
1736
|
const validCategories = [
|
|
@@ -1409,18 +1771,18 @@ async function renderTemplate(templatePath, data) {
|
|
|
1409
1771
|
return ejs.render(template, data);
|
|
1410
1772
|
}
|
|
1411
1773
|
async function writeFile(filePath, content) {
|
|
1412
|
-
await fs.ensureDir(
|
|
1774
|
+
await fs.ensureDir(path12.dirname(filePath));
|
|
1413
1775
|
await fs.writeFile(filePath, content, "utf-8");
|
|
1414
1776
|
}
|
|
1415
1777
|
function getTemplatesDir() {
|
|
1416
1778
|
const locations = [
|
|
1417
|
-
|
|
1779
|
+
path12.join(__dirname, "../../templates"),
|
|
1418
1780
|
// Development
|
|
1419
|
-
|
|
1781
|
+
path12.join(__dirname, "../templates"),
|
|
1420
1782
|
// Production (dist/)
|
|
1421
|
-
|
|
1783
|
+
path12.join(process.cwd(), "templates"),
|
|
1422
1784
|
// Fallback
|
|
1423
|
-
|
|
1785
|
+
path12.join(process.cwd(), "packages/cli/templates")
|
|
1424
1786
|
// Monorepo
|
|
1425
1787
|
];
|
|
1426
1788
|
for (const location of locations) {
|
|
@@ -1432,7 +1794,7 @@ function getTemplatesDir() {
|
|
|
1432
1794
|
}
|
|
1433
1795
|
async function copyTemplate(templateName, targetDir, data) {
|
|
1434
1796
|
const templatesDir = getTemplatesDir();
|
|
1435
|
-
const templateDir =
|
|
1797
|
+
const templateDir = path12.join(templatesDir, templateName);
|
|
1436
1798
|
if (!fs.existsSync(templateDir)) {
|
|
1437
1799
|
throw new Error(
|
|
1438
1800
|
`Template "${templateName}" not found at ${templateDir}. Available templates: ${fs.readdirSync(templatesDir).join(", ")}`
|
|
@@ -1441,8 +1803,8 @@ async function copyTemplate(templateName, targetDir, data) {
|
|
|
1441
1803
|
await fs.ensureDir(targetDir);
|
|
1442
1804
|
const files = await fs.readdir(templateDir);
|
|
1443
1805
|
for (const file of files) {
|
|
1444
|
-
const templatePath =
|
|
1445
|
-
const targetPath =
|
|
1806
|
+
const templatePath = path12.join(templateDir, file);
|
|
1807
|
+
const targetPath = path12.join(targetDir, file);
|
|
1446
1808
|
const stat = await fs.stat(templatePath);
|
|
1447
1809
|
if (stat.isDirectory()) {
|
|
1448
1810
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
@@ -1459,8 +1821,8 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1459
1821
|
await fs.ensureDir(targetDir);
|
|
1460
1822
|
const files = await fs.readdir(templateDir);
|
|
1461
1823
|
for (const file of files) {
|
|
1462
|
-
const templatePath =
|
|
1463
|
-
const targetPath =
|
|
1824
|
+
const templatePath = path12.join(templateDir, file);
|
|
1825
|
+
const targetPath = path12.join(targetDir, file);
|
|
1464
1826
|
const stat = await fs.stat(templatePath);
|
|
1465
1827
|
if (stat.isDirectory()) {
|
|
1466
1828
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
@@ -1475,32 +1837,32 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1475
1837
|
}
|
|
1476
1838
|
function getProjectRoot() {
|
|
1477
1839
|
let currentDir = process.cwd();
|
|
1478
|
-
while (currentDir !==
|
|
1479
|
-
const packageJsonPath =
|
|
1840
|
+
while (currentDir !== path12.parse(currentDir).root) {
|
|
1841
|
+
const packageJsonPath = path12.join(currentDir, "package.json");
|
|
1480
1842
|
if (fs.existsSync(packageJsonPath)) {
|
|
1481
1843
|
const packageJson = fs.readJsonSync(packageJsonPath);
|
|
1482
|
-
if (packageJson.workspaces || fs.existsSync(
|
|
1844
|
+
if (packageJson.workspaces || fs.existsSync(path12.join(currentDir, "src/themes")) || fs.existsSync(path12.join(currentDir, "themes"))) {
|
|
1483
1845
|
return currentDir;
|
|
1484
1846
|
}
|
|
1485
1847
|
}
|
|
1486
|
-
currentDir =
|
|
1848
|
+
currentDir = path12.dirname(currentDir);
|
|
1487
1849
|
}
|
|
1488
1850
|
return process.cwd();
|
|
1489
1851
|
}
|
|
1490
1852
|
function getThemesDir() {
|
|
1491
1853
|
const root = getProjectRoot();
|
|
1492
|
-
if (fs.existsSync(
|
|
1493
|
-
return
|
|
1494
|
-
if (fs.existsSync(
|
|
1495
|
-
return
|
|
1496
|
-
return
|
|
1854
|
+
if (fs.existsSync(path12.join(root, "themes")))
|
|
1855
|
+
return path12.join(root, "themes");
|
|
1856
|
+
if (fs.existsSync(path12.join(root, "src/themes")))
|
|
1857
|
+
return path12.join(root, "src/themes");
|
|
1858
|
+
return path12.dirname(root);
|
|
1497
1859
|
}
|
|
1498
1860
|
function getFeaturesDir() {
|
|
1499
|
-
return
|
|
1861
|
+
return path12.join(getProjectRoot(), "src/features");
|
|
1500
1862
|
}
|
|
1501
1863
|
function isOneXProject() {
|
|
1502
1864
|
const root = getProjectRoot();
|
|
1503
|
-
return fs.existsSync(
|
|
1865
|
+
return fs.existsSync(path12.join(root, "themes")) || fs.existsSync(path12.join(root, "src/themes")) || fs.existsSync(path12.join(root, "theme.config.ts")) || fs.existsSync(path12.join(root, "bundle-entry.ts"));
|
|
1504
1866
|
}
|
|
1505
1867
|
function ensureOneXProject() {
|
|
1506
1868
|
if (!isOneXProject()) {
|
|
@@ -1516,13 +1878,13 @@ function listThemes() {
|
|
|
1516
1878
|
return [];
|
|
1517
1879
|
}
|
|
1518
1880
|
return fs.readdirSync(themesDir).filter((name) => {
|
|
1519
|
-
const themePath =
|
|
1520
|
-
return fs.statSync(themePath).isDirectory() && (fs.existsSync(
|
|
1881
|
+
const themePath = path12.join(themesDir, name);
|
|
1882
|
+
return fs.statSync(themePath).isDirectory() && (fs.existsSync(path12.join(themePath, "theme.config.ts")) || fs.existsSync(path12.join(themePath, "bundle-entry.ts")) || fs.existsSync(path12.join(themePath, "manifest.ts")));
|
|
1521
1883
|
});
|
|
1522
1884
|
}
|
|
1523
1885
|
function themeExists(themeName) {
|
|
1524
|
-
const themePath =
|
|
1525
|
-
return fs.existsSync(themePath) && (fs.existsSync(
|
|
1886
|
+
const themePath = path12.join(getThemesDir(), themeName);
|
|
1887
|
+
return fs.existsSync(themePath) && (fs.existsSync(path12.join(themePath, "theme.config.ts")) || fs.existsSync(path12.join(themePath, "bundle-entry.ts")) || fs.existsSync(path12.join(themePath, "manifest.ts")));
|
|
1526
1888
|
}
|
|
1527
1889
|
function detectPackageManager() {
|
|
1528
1890
|
const userAgent = process.env.npm_config_user_agent || "";
|
|
@@ -1530,9 +1892,9 @@ function detectPackageManager() {
|
|
|
1530
1892
|
if (userAgent.includes("yarn")) return "yarn";
|
|
1531
1893
|
if (userAgent.includes("bun")) return "bun";
|
|
1532
1894
|
const cwd = process.cwd();
|
|
1533
|
-
if (fs.existsSync(
|
|
1534
|
-
if (fs.existsSync(
|
|
1535
|
-
if (fs.existsSync(
|
|
1895
|
+
if (fs.existsSync(path12.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
1896
|
+
if (fs.existsSync(path12.join(cwd, "yarn.lock"))) return "yarn";
|
|
1897
|
+
if (fs.existsSync(path12.join(cwd, "bun.lockb"))) return "bun";
|
|
1536
1898
|
return "npm";
|
|
1537
1899
|
}
|
|
1538
1900
|
async function installDependencies(projectPath, packageManager = "npm") {
|
|
@@ -1549,15 +1911,16 @@ async function installDependencies(projectPath, packageManager = "npm") {
|
|
|
1549
1911
|
}
|
|
1550
1912
|
});
|
|
1551
1913
|
}
|
|
1552
|
-
var AUTH_DIR =
|
|
1914
|
+
var AUTH_DIR = path12.join(os.homedir(), ".onexthm");
|
|
1553
1915
|
var ENV_URLS = {
|
|
1554
1916
|
dev: "https://platform-dev.onexeos.com",
|
|
1555
|
-
|
|
1917
|
+
staging: "https://platform-staging.onexeos.com",
|
|
1918
|
+
prod: "https://platform-apis.onexeos.com"
|
|
1556
1919
|
};
|
|
1557
1920
|
function getAuthFile(env = "dev") {
|
|
1558
|
-
const newFile =
|
|
1921
|
+
const newFile = path12.join(AUTH_DIR, `auth-${env}.json`);
|
|
1559
1922
|
if (env === "dev") {
|
|
1560
|
-
const legacyFile =
|
|
1923
|
+
const legacyFile = path12.join(AUTH_DIR, "auth.json");
|
|
1561
1924
|
if (fs.existsSync(legacyFile) && !fs.existsSync(newFile)) {
|
|
1562
1925
|
try {
|
|
1563
1926
|
fs.moveSync(legacyFile, newFile);
|
|
@@ -1684,7 +2047,7 @@ function decrypt(text, key) {
|
|
|
1684
2047
|
}
|
|
1685
2048
|
|
|
1686
2049
|
// src/commands/init.ts
|
|
1687
|
-
async function initCommand(projectName, options
|
|
2050
|
+
async function initCommand(projectName, options) {
|
|
1688
2051
|
logger.header("Create New OneX Theme Project");
|
|
1689
2052
|
let name;
|
|
1690
2053
|
if (!projectName) {
|
|
@@ -1699,7 +2062,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1699
2062
|
if (!validateThemeName(kebabName)) {
|
|
1700
2063
|
return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
|
|
1701
2064
|
}
|
|
1702
|
-
if (fs3.existsSync(
|
|
2065
|
+
if (fs3.existsSync(path12.join(process.cwd(), kebabName))) {
|
|
1703
2066
|
return `Directory "${kebabName}" already exists`;
|
|
1704
2067
|
}
|
|
1705
2068
|
return true;
|
|
@@ -1710,14 +2073,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
1710
2073
|
} else {
|
|
1711
2074
|
name = toKebabCase(projectName);
|
|
1712
2075
|
}
|
|
1713
|
-
const projectPath =
|
|
2076
|
+
const projectPath = path12.join(process.cwd(), name);
|
|
1714
2077
|
if (fs3.existsSync(projectPath)) {
|
|
1715
2078
|
logger.error(`Directory "${name}" already exists.`);
|
|
1716
2079
|
process.exit(1);
|
|
1717
2080
|
}
|
|
1718
2081
|
if (!options.yes) {
|
|
1719
2082
|
try {
|
|
1720
|
-
const apiUrl = getApiUrl(options.env
|
|
2083
|
+
const apiUrl = getApiUrl(options.env);
|
|
1721
2084
|
const controller = new AbortController();
|
|
1722
2085
|
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
1723
2086
|
const response = await fetch(
|
|
@@ -1826,7 +2189,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1826
2189
|
description,
|
|
1827
2190
|
author
|
|
1828
2191
|
);
|
|
1829
|
-
const mcpJsonPath =
|
|
2192
|
+
const mcpJsonPath = path12.join(projectPath, ".mcp.json");
|
|
1830
2193
|
if (fs3.existsSync(mcpJsonPath)) {
|
|
1831
2194
|
let mcpContent = fs3.readFileSync(mcpJsonPath, "utf-8");
|
|
1832
2195
|
if (figmaApiKey) {
|
|
@@ -1906,7 +2269,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1906
2269
|
}
|
|
1907
2270
|
}
|
|
1908
2271
|
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
1909
|
-
const configPath =
|
|
2272
|
+
const configPath = path12.join(projectPath, "theme.config.ts");
|
|
1910
2273
|
if (fs3.existsSync(configPath)) {
|
|
1911
2274
|
let content = fs3.readFileSync(configPath, "utf-8");
|
|
1912
2275
|
content = content.replace(
|
|
@@ -1919,7 +2282,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
|
|
|
1919
2282
|
);
|
|
1920
2283
|
fs3.writeFileSync(configPath, content, "utf-8");
|
|
1921
2284
|
}
|
|
1922
|
-
const pkgPath =
|
|
2285
|
+
const pkgPath = path12.join(projectPath, "package.json");
|
|
1923
2286
|
if (fs3.existsSync(pkgPath)) {
|
|
1924
2287
|
let content = fs3.readFileSync(pkgPath, "utf-8");
|
|
1925
2288
|
content = content.replace(
|
|
@@ -1941,10 +2304,10 @@ async function createSectionCommand(name, options) {
|
|
|
1941
2304
|
ensureOneXProject();
|
|
1942
2305
|
if (!options.theme) {
|
|
1943
2306
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
1944
|
-
(f) => fs.existsSync(
|
|
2307
|
+
(f) => fs.existsSync(path12.join(process.cwd(), f))
|
|
1945
2308
|
);
|
|
1946
2309
|
if (isStandaloneTheme) {
|
|
1947
|
-
options.theme =
|
|
2310
|
+
options.theme = path12.basename(process.cwd());
|
|
1948
2311
|
}
|
|
1949
2312
|
}
|
|
1950
2313
|
const sectionName = toKebabCase(name);
|
|
@@ -2007,35 +2370,35 @@ async function createSectionCommand(name, options) {
|
|
|
2007
2370
|
};
|
|
2008
2371
|
logger.startSpinner("Creating section files...");
|
|
2009
2372
|
try {
|
|
2010
|
-
const themePath =
|
|
2011
|
-
const sectionPath =
|
|
2373
|
+
const themePath = path12.join(getThemesDir(), themeName);
|
|
2374
|
+
const sectionPath = path12.join(themePath, "sections", sectionName);
|
|
2012
2375
|
const schemaContent = generateSectionSchema(data);
|
|
2013
2376
|
await writeFile(
|
|
2014
|
-
|
|
2377
|
+
path12.join(sectionPath, `${sectionName}.schema.ts`),
|
|
2015
2378
|
schemaContent
|
|
2016
2379
|
);
|
|
2017
2380
|
if (createTemplate) {
|
|
2018
2381
|
const templateContent = generateSectionTemplate(data);
|
|
2019
2382
|
await writeFile(
|
|
2020
|
-
|
|
2383
|
+
path12.join(sectionPath, `${sectionName}-default.tsx`),
|
|
2021
2384
|
templateContent
|
|
2022
2385
|
);
|
|
2023
2386
|
}
|
|
2024
2387
|
const indexContent = generateSectionIndex(data, createTemplate);
|
|
2025
|
-
await writeFile(
|
|
2388
|
+
await writeFile(path12.join(sectionPath, "index.ts"), indexContent);
|
|
2026
2389
|
logger.stopSpinner(true, "Section files created successfully!");
|
|
2027
2390
|
logger.newLine();
|
|
2028
2391
|
logger.section("Next steps:");
|
|
2029
2392
|
logger.log(
|
|
2030
|
-
` 1. Edit schema: ${
|
|
2393
|
+
` 1. Edit schema: ${path12.relative(process.cwd(), path12.join(sectionPath, `${sectionName}.schema.ts`))}`
|
|
2031
2394
|
);
|
|
2032
2395
|
if (createTemplate) {
|
|
2033
2396
|
logger.log(
|
|
2034
|
-
` 2. Edit template: ${
|
|
2397
|
+
` 2. Edit template: ${path12.relative(process.cwd(), path12.join(sectionPath, `${sectionName}-default.tsx`))}`
|
|
2035
2398
|
);
|
|
2036
2399
|
}
|
|
2037
2400
|
logger.log(
|
|
2038
|
-
` 3. Add to theme manifest: ${
|
|
2401
|
+
` 3. Add to theme manifest: ${path12.relative(process.cwd(), path12.join(themePath, "manifest.ts"))}`
|
|
2039
2402
|
);
|
|
2040
2403
|
logger.newLine();
|
|
2041
2404
|
logger.success("Section created successfully!");
|
|
@@ -2183,10 +2546,10 @@ async function createBlockCommand(name, options) {
|
|
|
2183
2546
|
ensureOneXProject();
|
|
2184
2547
|
if (!options.theme) {
|
|
2185
2548
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
2186
|
-
(f) => fs.existsSync(
|
|
2549
|
+
(f) => fs.existsSync(path12.join(process.cwd(), f))
|
|
2187
2550
|
);
|
|
2188
2551
|
if (isStandaloneTheme) {
|
|
2189
|
-
options.theme =
|
|
2552
|
+
options.theme = path12.basename(process.cwd());
|
|
2190
2553
|
}
|
|
2191
2554
|
}
|
|
2192
2555
|
const blockName = toKebabCase(name);
|
|
@@ -2261,24 +2624,24 @@ async function createBlockCommand(name, options) {
|
|
|
2261
2624
|
};
|
|
2262
2625
|
logger.startSpinner("Creating block files...");
|
|
2263
2626
|
try {
|
|
2264
|
-
const blockPath = scope === "shared" ?
|
|
2627
|
+
const blockPath = scope === "shared" ? path12.join(getFeaturesDir(), "blocks", blockName) : path12.join(getThemesDir(), themeName, "blocks", blockName);
|
|
2265
2628
|
const schemaContent = generateBlockSchema(data);
|
|
2266
2629
|
await writeFile(
|
|
2267
|
-
|
|
2630
|
+
path12.join(blockPath, `${blockName}.schema.ts`),
|
|
2268
2631
|
schemaContent
|
|
2269
2632
|
);
|
|
2270
2633
|
const componentContent = generateBlockComponent(data);
|
|
2271
|
-
await writeFile(
|
|
2634
|
+
await writeFile(path12.join(blockPath, `${blockName}.tsx`), componentContent);
|
|
2272
2635
|
const indexContent = generateBlockIndex(data);
|
|
2273
|
-
await writeFile(
|
|
2636
|
+
await writeFile(path12.join(blockPath, "index.ts"), indexContent);
|
|
2274
2637
|
logger.stopSpinner(true, "Block files created successfully!");
|
|
2275
2638
|
logger.newLine();
|
|
2276
2639
|
logger.section("Next steps:");
|
|
2277
2640
|
logger.log(
|
|
2278
|
-
` 1. Edit schema: ${
|
|
2641
|
+
` 1. Edit schema: ${path12.relative(process.cwd(), path12.join(blockPath, `${blockName}.schema.ts`))}`
|
|
2279
2642
|
);
|
|
2280
2643
|
logger.log(
|
|
2281
|
-
` 2. Edit component: ${
|
|
2644
|
+
` 2. Edit component: ${path12.relative(process.cwd(), path12.join(blockPath, `${blockName}.tsx`))}`
|
|
2282
2645
|
);
|
|
2283
2646
|
logger.log(
|
|
2284
2647
|
` 3. Register in block registry: src/lib/registry/block-registry.ts`
|
|
@@ -2456,31 +2819,31 @@ async function createComponentCommand(name, options) {
|
|
|
2456
2819
|
};
|
|
2457
2820
|
logger.startSpinner("Creating component files...");
|
|
2458
2821
|
try {
|
|
2459
|
-
const componentPath =
|
|
2822
|
+
const componentPath = path12.join(
|
|
2460
2823
|
getFeaturesDir(),
|
|
2461
2824
|
"components",
|
|
2462
2825
|
componentName
|
|
2463
2826
|
);
|
|
2464
2827
|
const schemaContent = generateComponentSchema(data);
|
|
2465
2828
|
await writeFile(
|
|
2466
|
-
|
|
2829
|
+
path12.join(componentPath, `${componentName}.schema.ts`),
|
|
2467
2830
|
schemaContent
|
|
2468
2831
|
);
|
|
2469
2832
|
const componentContent = generateComponent(data);
|
|
2470
2833
|
await writeFile(
|
|
2471
|
-
|
|
2834
|
+
path12.join(componentPath, `${componentName}.tsx`),
|
|
2472
2835
|
componentContent
|
|
2473
2836
|
);
|
|
2474
2837
|
const indexContent = generateComponentIndex(data);
|
|
2475
|
-
await writeFile(
|
|
2838
|
+
await writeFile(path12.join(componentPath, "index.ts"), indexContent);
|
|
2476
2839
|
logger.stopSpinner(true, "Component files created successfully!");
|
|
2477
2840
|
logger.newLine();
|
|
2478
2841
|
logger.section("Next steps:");
|
|
2479
2842
|
logger.log(
|
|
2480
|
-
` 1. Edit schema: ${
|
|
2843
|
+
` 1. Edit schema: ${path12.relative(process.cwd(), path12.join(componentPath, `${componentName}.schema.ts`))}`
|
|
2481
2844
|
);
|
|
2482
2845
|
logger.log(
|
|
2483
|
-
` 2. Edit component: ${
|
|
2846
|
+
` 2. Edit component: ${path12.relative(process.cwd(), path12.join(componentPath, `${componentName}.tsx`))}`
|
|
2484
2847
|
);
|
|
2485
2848
|
logger.log(
|
|
2486
2849
|
` 3. Register in component registry: src/lib/registry/component-registry.ts`
|
|
@@ -2637,13 +3000,13 @@ async function listSections(themeFilter) {
|
|
|
2637
3000
|
return;
|
|
2638
3001
|
}
|
|
2639
3002
|
for (const theme of themes) {
|
|
2640
|
-
const sectionsDir =
|
|
3003
|
+
const sectionsDir = path12.join(getThemesDir(), theme, "sections");
|
|
2641
3004
|
if (!fs.existsSync(sectionsDir)) {
|
|
2642
3005
|
continue;
|
|
2643
3006
|
}
|
|
2644
3007
|
const sections = fs.readdirSync(sectionsDir).filter((name) => {
|
|
2645
|
-
const sectionPath =
|
|
2646
|
-
return fs.statSync(sectionPath).isDirectory() && fs.existsSync(
|
|
3008
|
+
const sectionPath = path12.join(sectionsDir, name);
|
|
3009
|
+
return fs.statSync(sectionPath).isDirectory() && fs.existsSync(path12.join(sectionPath, "index.ts"));
|
|
2647
3010
|
});
|
|
2648
3011
|
if (sections.length > 0) {
|
|
2649
3012
|
logger.log(chalk4.cyan(`
|
|
@@ -2657,11 +3020,11 @@ async function listSections(themeFilter) {
|
|
|
2657
3020
|
}
|
|
2658
3021
|
async function listBlocks(themeFilter) {
|
|
2659
3022
|
logger.section("\u{1F9F1} Blocks");
|
|
2660
|
-
const sharedBlocksDir =
|
|
3023
|
+
const sharedBlocksDir = path12.join(getFeaturesDir(), "blocks");
|
|
2661
3024
|
if (fs.existsSync(sharedBlocksDir)) {
|
|
2662
3025
|
const sharedBlocks = fs.readdirSync(sharedBlocksDir).filter((name) => {
|
|
2663
|
-
const blockPath =
|
|
2664
|
-
return fs.statSync(blockPath).isDirectory() && fs.existsSync(
|
|
3026
|
+
const blockPath = path12.join(sharedBlocksDir, name);
|
|
3027
|
+
return fs.statSync(blockPath).isDirectory() && fs.existsSync(path12.join(blockPath, "index.ts"));
|
|
2665
3028
|
});
|
|
2666
3029
|
if (sharedBlocks.length > 0) {
|
|
2667
3030
|
logger.log(chalk4.cyan("\n Shared:"));
|
|
@@ -2672,13 +3035,13 @@ async function listBlocks(themeFilter) {
|
|
|
2672
3035
|
}
|
|
2673
3036
|
const themes = themeFilter ? [themeFilter] : listThemes();
|
|
2674
3037
|
for (const theme of themes) {
|
|
2675
|
-
const blocksDir =
|
|
3038
|
+
const blocksDir = path12.join(getThemesDir(), theme, "blocks");
|
|
2676
3039
|
if (!fs.existsSync(blocksDir)) {
|
|
2677
3040
|
continue;
|
|
2678
3041
|
}
|
|
2679
3042
|
const blocks = fs.readdirSync(blocksDir).filter((name) => {
|
|
2680
|
-
const blockPath =
|
|
2681
|
-
return fs.statSync(blockPath).isDirectory() && fs.existsSync(
|
|
3043
|
+
const blockPath = path12.join(blocksDir, name);
|
|
3044
|
+
return fs.statSync(blockPath).isDirectory() && fs.existsSync(path12.join(blockPath, "index.ts"));
|
|
2682
3045
|
});
|
|
2683
3046
|
if (blocks.length > 0) {
|
|
2684
3047
|
logger.log(chalk4.cyan(`
|
|
@@ -2692,14 +3055,14 @@ async function listBlocks(themeFilter) {
|
|
|
2692
3055
|
}
|
|
2693
3056
|
async function listComponents() {
|
|
2694
3057
|
logger.section("\u2699\uFE0F Components");
|
|
2695
|
-
const componentsDir =
|
|
3058
|
+
const componentsDir = path12.join(getFeaturesDir(), "components");
|
|
2696
3059
|
if (!fs.existsSync(componentsDir)) {
|
|
2697
3060
|
logger.warning("No components directory found");
|
|
2698
3061
|
return;
|
|
2699
3062
|
}
|
|
2700
3063
|
const components = fs.readdirSync(componentsDir).filter((name) => {
|
|
2701
|
-
const componentPath =
|
|
2702
|
-
return fs.statSync(componentPath).isDirectory() && fs.existsSync(
|
|
3064
|
+
const componentPath = path12.join(componentsDir, name);
|
|
3065
|
+
return fs.statSync(componentPath).isDirectory() && fs.existsSync(path12.join(componentPath, "index.ts"));
|
|
2703
3066
|
});
|
|
2704
3067
|
if (components.length === 0) {
|
|
2705
3068
|
logger.warning("No components found");
|
|
@@ -2720,11 +3083,11 @@ async function listThemesInfo() {
|
|
|
2720
3083
|
}
|
|
2721
3084
|
logger.log("");
|
|
2722
3085
|
for (const theme of themes) {
|
|
2723
|
-
const themeDir =
|
|
3086
|
+
const themeDir = path12.join(getThemesDir(), theme);
|
|
2724
3087
|
const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
|
|
2725
3088
|
let manifestContent = "";
|
|
2726
3089
|
for (const candidate of candidates) {
|
|
2727
|
-
const candidatePath =
|
|
3090
|
+
const candidatePath = path12.join(themeDir, candidate);
|
|
2728
3091
|
if (fs.existsSync(candidatePath)) {
|
|
2729
3092
|
manifestContent = fs.readFileSync(candidatePath, "utf-8");
|
|
2730
3093
|
break;
|
|
@@ -2746,6 +3109,7 @@ async function listThemesInfo() {
|
|
|
2746
3109
|
|
|
2747
3110
|
// src/commands/build.ts
|
|
2748
3111
|
init_logger();
|
|
3112
|
+
init_detect_nextjs();
|
|
2749
3113
|
async function buildCommand(options) {
|
|
2750
3114
|
logger.header("Build Theme");
|
|
2751
3115
|
let themePath;
|
|
@@ -2753,14 +3117,14 @@ async function buildCommand(options) {
|
|
|
2753
3117
|
if (options.theme) {
|
|
2754
3118
|
themeName = options.theme;
|
|
2755
3119
|
try {
|
|
2756
|
-
const workspaceThemePath =
|
|
3120
|
+
const workspaceThemePath = path12.join(getThemesDir(), themeName);
|
|
2757
3121
|
if (fs.existsSync(workspaceThemePath)) {
|
|
2758
3122
|
themePath = workspaceThemePath;
|
|
2759
3123
|
} else {
|
|
2760
|
-
themePath =
|
|
3124
|
+
themePath = path12.join(process.cwd(), themeName);
|
|
2761
3125
|
}
|
|
2762
3126
|
} catch {
|
|
2763
|
-
themePath =
|
|
3127
|
+
themePath = path12.join(process.cwd(), themeName);
|
|
2764
3128
|
}
|
|
2765
3129
|
if (!fs.existsSync(themePath)) {
|
|
2766
3130
|
logger.error(`Theme "${themeName}" not found.`);
|
|
@@ -2770,11 +3134,14 @@ async function buildCommand(options) {
|
|
|
2770
3134
|
const isThemeDir = [
|
|
2771
3135
|
"theme.config.ts",
|
|
2772
3136
|
"bundle-entry.ts",
|
|
2773
|
-
"manifest.ts"
|
|
2774
|
-
|
|
3137
|
+
"manifest.ts",
|
|
3138
|
+
"next.config.ts",
|
|
3139
|
+
"next.config.js",
|
|
3140
|
+
"next.config.mjs"
|
|
3141
|
+
].some((f) => fs.existsSync(path12.join(process.cwd(), f)));
|
|
2775
3142
|
if (isThemeDir) {
|
|
2776
3143
|
themePath = process.cwd();
|
|
2777
|
-
themeName =
|
|
3144
|
+
themeName = path12.basename(themePath);
|
|
2778
3145
|
logger.info(`Building current theme: ${themeName}`);
|
|
2779
3146
|
} else {
|
|
2780
3147
|
logger.error(
|
|
@@ -2783,7 +3150,7 @@ async function buildCommand(options) {
|
|
|
2783
3150
|
process.exit(1);
|
|
2784
3151
|
}
|
|
2785
3152
|
}
|
|
2786
|
-
const packageJsonPath =
|
|
3153
|
+
const packageJsonPath = path12.join(themePath, "package.json");
|
|
2787
3154
|
const hasPkgJson = fs.existsSync(packageJsonPath);
|
|
2788
3155
|
if (!hasPkgJson) {
|
|
2789
3156
|
logger.warning(
|
|
@@ -2799,30 +3166,42 @@ async function buildCommand(options) {
|
|
|
2799
3166
|
}
|
|
2800
3167
|
logger.newLine();
|
|
2801
3168
|
logger.section("Build Steps");
|
|
2802
|
-
logger.startSpinner("Running type check...");
|
|
2803
|
-
const typeCheckSuccess = await runCommand("pnpm", ["type-check"], themePath);
|
|
2804
|
-
if (!typeCheckSuccess) {
|
|
2805
|
-
logger.stopSpinner(false, "Type check failed");
|
|
2806
|
-
logger.error("Fix type errors before building.");
|
|
2807
|
-
process.exit(1);
|
|
2808
|
-
}
|
|
2809
|
-
logger.stopSpinner(true, "Type check passed");
|
|
2810
|
-
logger.startSpinner("Running linter...");
|
|
2811
|
-
const lintSuccess = await runCommand("pnpm", ["lint"], themePath);
|
|
2812
|
-
if (!lintSuccess) {
|
|
2813
|
-
logger.stopSpinner(false, "Lint failed");
|
|
2814
|
-
logger.error("Fix lint errors before building.");
|
|
2815
|
-
process.exit(1);
|
|
2816
|
-
}
|
|
2817
|
-
logger.stopSpinner(true, "Lint passed");
|
|
2818
3169
|
const pkgJson = fs.readJsonSync(packageJsonPath);
|
|
3170
|
+
if (pkgJson.scripts?.["type-check"]) {
|
|
3171
|
+
logger.startSpinner("Running type check...");
|
|
3172
|
+
const typeCheckSuccess = await runCommand("pnpm", ["type-check"], themePath);
|
|
3173
|
+
if (!typeCheckSuccess) {
|
|
3174
|
+
logger.stopSpinner(false, "Type check failed");
|
|
3175
|
+
logger.error("Fix type errors before building.");
|
|
3176
|
+
process.exit(1);
|
|
3177
|
+
}
|
|
3178
|
+
logger.stopSpinner(true, "Type check passed");
|
|
3179
|
+
} else {
|
|
3180
|
+
logger.info("Skipping type check (no type-check script in package.json)");
|
|
3181
|
+
}
|
|
3182
|
+
const isNextjsForLint = isNextjsProject(themePath);
|
|
3183
|
+
if (!isNextjsForLint && pkgJson.scripts?.lint) {
|
|
3184
|
+
logger.startSpinner("Running linter...");
|
|
3185
|
+
const lintSuccess = await runCommand("pnpm", ["lint"], themePath);
|
|
3186
|
+
if (!lintSuccess) {
|
|
3187
|
+
logger.stopSpinner(false, "Lint failed");
|
|
3188
|
+
logger.error("Fix lint errors before building.");
|
|
3189
|
+
process.exit(1);
|
|
3190
|
+
}
|
|
3191
|
+
logger.stopSpinner(true, "Lint passed");
|
|
3192
|
+
} else if (isNextjsForLint) {
|
|
3193
|
+
logger.info("Skipping lint (Next.js project compiled via esbuild)");
|
|
3194
|
+
} else {
|
|
3195
|
+
logger.info("Skipping lint (no lint script in package.json)");
|
|
3196
|
+
}
|
|
2819
3197
|
const buildScript = pkgJson.scripts?.build || "";
|
|
2820
3198
|
const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex build") || buildScript.includes("onex-cli build");
|
|
3199
|
+
const isNextjs = isNextjsProject(themePath);
|
|
2821
3200
|
logger.startSpinner(
|
|
2822
3201
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
2823
3202
|
);
|
|
2824
3203
|
let buildSuccess;
|
|
2825
|
-
if (isRecursive) {
|
|
3204
|
+
if (isRecursive || isNextjs) {
|
|
2826
3205
|
const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
2827
3206
|
buildSuccess = await compileStandaloneTheme2(themePath, themeName);
|
|
2828
3207
|
} else {
|
|
@@ -2839,9 +3218,9 @@ async function buildCommand(options) {
|
|
|
2839
3218
|
logger.success("\u2713 Theme built successfully!");
|
|
2840
3219
|
logger.newLine();
|
|
2841
3220
|
logger.info(`Theme: ${themeName}`);
|
|
2842
|
-
const distPath =
|
|
3221
|
+
const distPath = path12.join(themePath, "dist");
|
|
2843
3222
|
if (fs.existsSync(distPath)) {
|
|
2844
|
-
logger.log(`Output: ${
|
|
3223
|
+
logger.log(`Output: ${path12.relative(process.cwd(), distPath)}`);
|
|
2845
3224
|
const files = fs.readdirSync(distPath);
|
|
2846
3225
|
logger.log(`Files: ${files.length}`);
|
|
2847
3226
|
}
|
|
@@ -2982,8 +3361,8 @@ async function downloadBundleZip(apiUrl, themeId, version) {
|
|
|
2982
3361
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
2983
3362
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
2984
3363
|
if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
|
|
2985
|
-
const hashedPath =
|
|
2986
|
-
const stablePath =
|
|
3364
|
+
const hashedPath = path12.join(outputDir, entryFile);
|
|
3365
|
+
const stablePath = path12.join(outputDir, "bundle-entry.js");
|
|
2987
3366
|
if (await fs.pathExists(hashedPath)) {
|
|
2988
3367
|
await fs.copy(hashedPath, stablePath);
|
|
2989
3368
|
const mapPath = hashedPath + ".map";
|
|
@@ -2992,13 +3371,13 @@ async function createCompatibilityFiles(outputDir, manifest) {
|
|
|
2992
3371
|
}
|
|
2993
3372
|
}
|
|
2994
3373
|
}
|
|
2995
|
-
const sectionsRegistryPath =
|
|
3374
|
+
const sectionsRegistryPath = path12.join(outputDir, "sections-registry.js");
|
|
2996
3375
|
const content = `// Re-export all sections from bundle-entry
|
|
2997
3376
|
// This file exists to maintain compatibility with the import path
|
|
2998
3377
|
export * from './bundle-entry.js';
|
|
2999
3378
|
`;
|
|
3000
3379
|
await fs.writeFile(sectionsRegistryPath, content, "utf-8");
|
|
3001
|
-
const pkgJsonPath =
|
|
3380
|
+
const pkgJsonPath = path12.join(outputDir, "package.json");
|
|
3002
3381
|
await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
|
|
3003
3382
|
}
|
|
3004
3383
|
function showDownloadFailureHelp(themeId, apiUrl) {
|
|
@@ -3039,7 +3418,7 @@ function showDownloadFailureHelp(themeId, apiUrl) {
|
|
|
3039
3418
|
}
|
|
3040
3419
|
async function downloadCommand(options) {
|
|
3041
3420
|
logger.header("Download Theme");
|
|
3042
|
-
const env = options.env
|
|
3421
|
+
const env = options.env;
|
|
3043
3422
|
const apiUrl = getApiUrl(env);
|
|
3044
3423
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
3045
3424
|
const spinner = ora("Initializing download...").start();
|
|
@@ -3097,7 +3476,7 @@ async function downloadCommand(options) {
|
|
|
3097
3476
|
zip.extractAllTo(outputDir, true);
|
|
3098
3477
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
3099
3478
|
spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
|
|
3100
|
-
const manifestPath =
|
|
3479
|
+
const manifestPath = path12.join(outputDir, "manifest.json");
|
|
3101
3480
|
const manifest = await fs.readJson(manifestPath);
|
|
3102
3481
|
await createCompatibilityFiles(outputDir, manifest);
|
|
3103
3482
|
console.log();
|
|
@@ -3218,7 +3597,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3218
3597
|
const oldPrefix = `${oldName}-`;
|
|
3219
3598
|
const newPrefix = `${newName}-`;
|
|
3220
3599
|
const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3221
|
-
const pkgPath =
|
|
3600
|
+
const pkgPath = path12.join(themeDir, "package.json");
|
|
3222
3601
|
if (await fs.pathExists(pkgPath)) {
|
|
3223
3602
|
const pkg = await fs.readJson(pkgPath);
|
|
3224
3603
|
pkg.name = `@onex-themes/${newName}`;
|
|
@@ -3234,7 +3613,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3234
3613
|
}
|
|
3235
3614
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3236
3615
|
}
|
|
3237
|
-
const configPath =
|
|
3616
|
+
const configPath = path12.join(themeDir, "theme.config.ts");
|
|
3238
3617
|
if (await fs.pathExists(configPath)) {
|
|
3239
3618
|
let content = await fs.readFile(configPath, "utf-8");
|
|
3240
3619
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
@@ -3244,7 +3623,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3244
3623
|
);
|
|
3245
3624
|
await fs.writeFile(configPath, content);
|
|
3246
3625
|
}
|
|
3247
|
-
const layoutPath =
|
|
3626
|
+
const layoutPath = path12.join(themeDir, "theme.layout.ts");
|
|
3248
3627
|
if (await fs.pathExists(layoutPath)) {
|
|
3249
3628
|
let content = await fs.readFile(layoutPath, "utf-8");
|
|
3250
3629
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
@@ -3257,7 +3636,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3257
3636
|
const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3258
3637
|
const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
|
|
3259
3638
|
for (const file of tsFiles) {
|
|
3260
|
-
const filePath =
|
|
3639
|
+
const filePath = path12.join(themeDir, file);
|
|
3261
3640
|
let content = await fs.readFile(filePath, "utf-8");
|
|
3262
3641
|
const original = content;
|
|
3263
3642
|
content = content.replace(
|
|
@@ -3279,7 +3658,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3279
3658
|
}
|
|
3280
3659
|
async function cloneCommand(themeName, options) {
|
|
3281
3660
|
logger.header("Clone Theme Source");
|
|
3282
|
-
const env = options.env
|
|
3661
|
+
const env = options.env;
|
|
3283
3662
|
const apiUrl = getApiUrl(env);
|
|
3284
3663
|
logger.info(`Environment: ${env} (${apiUrl})`);
|
|
3285
3664
|
if (options.bucket) {
|
|
@@ -3300,7 +3679,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3300
3679
|
}
|
|
3301
3680
|
const spinner = ora("Initializing clone...").start();
|
|
3302
3681
|
try {
|
|
3303
|
-
const outputDir = options.output ||
|
|
3682
|
+
const outputDir = options.output || path12.resolve(process.cwd(), newName);
|
|
3304
3683
|
if (await fs.pathExists(outputDir)) {
|
|
3305
3684
|
spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
|
|
3306
3685
|
logger.info(
|
|
@@ -3346,7 +3725,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3346
3725
|
spinner.succeed(
|
|
3347
3726
|
`Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
|
|
3348
3727
|
);
|
|
3349
|
-
const envExamplePath =
|
|
3728
|
+
const envExamplePath = path12.join(outputDir, ".env.example");
|
|
3350
3729
|
if (!await fs.pathExists(envExamplePath)) {
|
|
3351
3730
|
await fs.writeFile(
|
|
3352
3731
|
envExamplePath,
|
|
@@ -3359,7 +3738,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3359
3738
|
].join("\n")
|
|
3360
3739
|
);
|
|
3361
3740
|
}
|
|
3362
|
-
const mcpJsonPath =
|
|
3741
|
+
const mcpJsonPath = path12.join(outputDir, ".mcp.json");
|
|
3363
3742
|
if (await fs.pathExists(mcpJsonPath)) {
|
|
3364
3743
|
const { default: inquirerMod } = await import('inquirer');
|
|
3365
3744
|
const { figmaApiKey } = await inquirerMod.prompt([
|
|
@@ -3384,7 +3763,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3384
3763
|
}
|
|
3385
3764
|
if (options.install !== false) {
|
|
3386
3765
|
const hasPkgJson = await fs.pathExists(
|
|
3387
|
-
|
|
3766
|
+
path12.join(outputDir, "package.json")
|
|
3388
3767
|
);
|
|
3389
3768
|
if (hasPkgJson) {
|
|
3390
3769
|
spinner.start("Installing dependencies...");
|
|
@@ -3412,7 +3791,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3412
3791
|
console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
|
|
3413
3792
|
console.log();
|
|
3414
3793
|
console.log(chalk4.cyan("Next steps:"));
|
|
3415
|
-
console.log(chalk4.gray(` cd ${
|
|
3794
|
+
console.log(chalk4.gray(` cd ${path12.relative(process.cwd(), outputDir)}`));
|
|
3416
3795
|
console.log(
|
|
3417
3796
|
chalk4.gray(" cp .env.example .env # then add your Company ID")
|
|
3418
3797
|
);
|