@onexapis/cli 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,14 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ var chalk4 = require('chalk');
5
+ var ora = require('ora');
6
+ var esbuild = require('esbuild');
4
7
  var path = require('path');
8
+ var fs7 = require('fs/promises');
9
+ var crypto = require('crypto');
10
+ var glob = require('glob');
5
11
  var os = require('os');
6
12
  var dotenv = require('dotenv');
7
13
  var fs = require('fs-extra');
8
14
  var ejs = require('ejs');
9
15
  var child_process = require('child_process');
10
- var chalk4 = require('chalk');
11
- var ora = require('ora');
12
16
  var commander = require('commander');
13
17
  var fs2 = require('fs');
14
18
  var inquirer = require('inquirer');
@@ -17,78 +21,690 @@ var FormData = require('form-data');
17
21
  var fetch = require('node-fetch');
18
22
  var clientS3 = require('@aws-sdk/client-s3');
19
23
  var AdmZip = require('adm-zip');
24
+ var chokidar = require('chokidar');
25
+ var http = require('http');
26
+ var ws = require('ws');
20
27
 
21
28
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
29
 
30
+ function _interopNamespace(e) {
31
+ if (e && e.__esModule) return e;
32
+ var n = Object.create(null);
33
+ if (e) {
34
+ Object.keys(e).forEach(function (k) {
35
+ if (k !== 'default') {
36
+ var d = Object.getOwnPropertyDescriptor(e, k);
37
+ Object.defineProperty(n, k, d.get ? d : {
38
+ enumerable: true,
39
+ get: function () { return e[k]; }
40
+ });
41
+ }
42
+ });
43
+ }
44
+ n.default = e;
45
+ return Object.freeze(n);
46
+ }
47
+
48
+ var chalk4__default = /*#__PURE__*/_interopDefault(chalk4);
49
+ var ora__default = /*#__PURE__*/_interopDefault(ora);
50
+ var esbuild__namespace = /*#__PURE__*/_interopNamespace(esbuild);
23
51
  var path__default = /*#__PURE__*/_interopDefault(path);
52
+ var fs7__default = /*#__PURE__*/_interopDefault(fs7);
53
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
24
54
  var os__default = /*#__PURE__*/_interopDefault(os);
25
55
  var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
26
56
  var fs__default = /*#__PURE__*/_interopDefault(fs);
27
57
  var ejs__default = /*#__PURE__*/_interopDefault(ejs);
28
- var chalk4__default = /*#__PURE__*/_interopDefault(chalk4);
29
- var ora__default = /*#__PURE__*/_interopDefault(ora);
30
58
  var fs2__default = /*#__PURE__*/_interopDefault(fs2);
31
59
  var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
32
60
  var archiver__default = /*#__PURE__*/_interopDefault(archiver);
33
61
  var FormData__default = /*#__PURE__*/_interopDefault(FormData);
34
62
  var fetch__default = /*#__PURE__*/_interopDefault(fetch);
35
63
  var AdmZip__default = /*#__PURE__*/_interopDefault(AdmZip);
64
+ var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
65
+ var http__default = /*#__PURE__*/_interopDefault(http);
36
66
 
67
+ var __defProp = Object.defineProperty;
68
+ var __getOwnPropNames = Object.getOwnPropertyNames;
37
69
  var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
70
+ var __esm = (fn, res) => function __init() {
71
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
72
+ };
73
+ var __export = (target, all) => {
74
+ for (var name in all)
75
+ __defProp(target, name, { get: all[name], enumerable: true });
76
+ };
38
77
  var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
39
- var Logger = class {
40
- constructor() {
41
- this.spinner = null;
42
- }
43
- success(message) {
44
- console.log(chalk4__default.default.green("\u2713"), message);
45
- }
46
- error(message) {
47
- console.log(chalk4__default.default.red("\u2717"), message);
78
+ var Logger, logger;
79
+ var init_logger = __esm({
80
+ "src/utils/logger.ts"() {
81
+ Logger = class {
82
+ constructor() {
83
+ this.spinner = null;
84
+ }
85
+ success(message) {
86
+ console.log(chalk4__default.default.green("\u2713"), message);
87
+ }
88
+ error(message) {
89
+ console.log(chalk4__default.default.red("\u2717"), message);
90
+ }
91
+ warning(message) {
92
+ console.log(chalk4__default.default.yellow("\u26A0"), message);
93
+ }
94
+ info(message) {
95
+ console.log(chalk4__default.default.blue("\u2139"), message);
96
+ }
97
+ log(message) {
98
+ console.log(message);
99
+ }
100
+ startSpinner(message) {
101
+ this.spinner = ora__default.default(message).start();
102
+ }
103
+ stopSpinner(success = true, message) {
104
+ if (!this.spinner) return;
105
+ if (success) {
106
+ this.spinner.succeed(message);
107
+ } else {
108
+ this.spinner.fail(message);
109
+ }
110
+ this.spinner = null;
111
+ }
112
+ updateSpinner(message) {
113
+ if (this.spinner) {
114
+ this.spinner.text = message;
115
+ }
116
+ }
117
+ newLine() {
118
+ console.log();
119
+ }
120
+ header(message) {
121
+ console.log();
122
+ console.log(chalk4__default.default.bold.cyan(message));
123
+ console.log(chalk4__default.default.cyan("=".repeat(message.length)));
124
+ console.log();
125
+ }
126
+ section(message) {
127
+ console.log();
128
+ console.log(chalk4__default.default.bold(message));
129
+ }
130
+ };
131
+ logger = new Logger();
48
132
  }
49
- warning(message) {
50
- console.log(chalk4__default.default.yellow("\u26A0"), message);
133
+ });
134
+
135
+ // src/utils/compile-theme.ts
136
+ var compile_theme_exports = {};
137
+ __export(compile_theme_exports, {
138
+ compilePreviewRuntime: () => compilePreviewRuntime,
139
+ compileStandaloneTheme: () => compileStandaloneTheme,
140
+ compileStandaloneThemeDev: () => compileStandaloneThemeDev,
141
+ generateManifest: () => generateManifest2
142
+ });
143
+ async function resolveNodeModulesFile(startDir, relativePath) {
144
+ let dir = startDir;
145
+ while (true) {
146
+ const candidate = path__default.default.join(dir, "node_modules", relativePath);
147
+ try {
148
+ await fs7__default.default.access(candidate);
149
+ return candidate;
150
+ } catch (e) {
151
+ const parent = path__default.default.dirname(dir);
152
+ if (parent === dir) break;
153
+ dir = parent;
154
+ }
51
155
  }
52
- info(message) {
53
- console.log(chalk4__default.default.blue("\u2139"), message);
156
+ return null;
157
+ }
158
+ function createCoreGlobalPlugin(themePath) {
159
+ const exportsBySubpath = {};
160
+ return {
161
+ name: "core-global",
162
+ setup(build2) {
163
+ build2.onResolve({ filter: /^@onexapis\/core(\/.*)?$/ }, (args) => ({
164
+ path: args.path,
165
+ namespace: "core-global"
166
+ }));
167
+ build2.onLoad({ filter: /.*/, namespace: "core-global" }, async (args) => {
168
+ const match = args.path.match(/^@onexapis\/core(\/(.+))?$/);
169
+ const subpath = (match == null ? void 0 : match[2]) || "";
170
+ const moduleAccess = subpath ? `['${subpath}']` : "";
171
+ let namedExports = [];
172
+ const cacheKey = subpath || "__root__";
173
+ if (exportsBySubpath[cacheKey]) {
174
+ namedExports = exportsBySubpath[cacheKey];
175
+ } else {
176
+ const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
177
+ const distPath = await resolveNodeModulesFile(
178
+ themePath,
179
+ path__default.default.join("@onexapis", "core", "dist", distFileName)
180
+ );
181
+ try {
182
+ if (!distPath) throw new Error("not found");
183
+ const distContent = await fs7__default.default.readFile(distPath, "utf-8");
184
+ const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
185
+ for (const m of exportMatches) {
186
+ const names = m[1].split(",").map((n) => {
187
+ const parts = n.trim().split(/\s+as\s+/);
188
+ return (parts[1] || parts[0]).trim();
189
+ }).filter((n) => n.length > 0);
190
+ namedExports.push(...names);
191
+ }
192
+ namedExports = [...new Set(namedExports)];
193
+ } catch (e) {
194
+ }
195
+ exportsBySubpath[cacheKey] = namedExports;
196
+ }
197
+ const namedExportLines = namedExports.length > 0 ? `
198
+ export const {
199
+ ${namedExports.join(",\n ")}
200
+ } = _module;
201
+ ` : "";
202
+ return {
203
+ contents: `
204
+ if (!globalThis.__ONEX_CORE__) {
205
+ throw new Error('[Theme Bundle] @onexapis/core not initialized. Ensure globalThis.__ONEX_CORE__ is set before loading theme.');
206
+ }
207
+
208
+ const _module = globalThis.__ONEX_CORE__${moduleAccess};
209
+ if (!_module) {
210
+ const subpath = ${subpath ? `'${subpath}'` : "null"};
211
+ const modulePath = subpath ? '/' + subpath : '';
212
+ const moduleKey = subpath ? '["' + subpath + '"]' : '';
213
+ throw new Error('[Theme Bundle] @onexapis/core' + modulePath + ' not available in globalThis.__ONEX_CORE__' + moduleKey);
214
+ }
215
+
216
+ export default _module;
217
+ ${namedExportLines}
218
+ `.trim(),
219
+ loader: "js"
220
+ };
221
+ });
222
+ }
223
+ };
224
+ }
225
+ async function contentHashEntry(outputDir) {
226
+ const entryPath = path__default.default.join(outputDir, "bundle-entry.js");
227
+ const mapPath = path__default.default.join(outputDir, "bundle-entry.js.map");
228
+ const oldFiles = await glob.glob("bundle-entry-*.js*", { cwd: outputDir });
229
+ for (const f of oldFiles) {
230
+ await fs7__default.default.unlink(path__default.default.join(outputDir, f));
54
231
  }
55
- log(message) {
56
- console.log(message);
232
+ let entryContent;
233
+ try {
234
+ entryContent = await fs7__default.default.readFile(entryPath, "utf-8");
235
+ } catch (e) {
236
+ const indexPath = path__default.default.join(outputDir, "index.js");
237
+ try {
238
+ entryContent = await fs7__default.default.readFile(indexPath, "utf-8");
239
+ } catch (e2) {
240
+ logger.warning("No entry file found in output, skipping content hash");
241
+ return;
242
+ }
243
+ const hash2 = crypto__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
244
+ const hashedName2 = `bundle-entry-${hash2}.js`;
245
+ const indexMapPath = path__default.default.join(outputDir, "index.js.map");
246
+ const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
247
+ entryContent = entryContent.replace(
248
+ /\/\/# sourceMappingURL=index\.js\.map/,
249
+ `//# sourceMappingURL=${hashedMapName2}`
250
+ );
251
+ await fs7__default.default.writeFile(path__default.default.join(outputDir, hashedName2), entryContent);
252
+ await fs7__default.default.unlink(indexPath);
253
+ try {
254
+ await fs7__default.default.access(indexMapPath);
255
+ await fs7__default.default.rename(indexMapPath, path__default.default.join(outputDir, hashedMapName2));
256
+ } catch (e2) {
257
+ }
258
+ logger.info(`Entry hashed: ${hashedName2}`);
259
+ return;
57
260
  }
58
- startSpinner(message) {
59
- this.spinner = ora__default.default(message).start();
261
+ const hash = crypto__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
262
+ const hashedName = `bundle-entry-${hash}.js`;
263
+ const hashedMapName = `bundle-entry-${hash}.js.map`;
264
+ entryContent = entryContent.replace(
265
+ /\/\/# sourceMappingURL=bundle-entry\.js\.map/,
266
+ `//# sourceMappingURL=${hashedMapName}`
267
+ );
268
+ await fs7__default.default.writeFile(path__default.default.join(outputDir, hashedName), entryContent);
269
+ await fs7__default.default.unlink(entryPath);
270
+ try {
271
+ await fs7__default.default.access(mapPath);
272
+ await fs7__default.default.rename(mapPath, path__default.default.join(outputDir, hashedMapName));
273
+ } catch (e) {
60
274
  }
61
- stopSpinner(success = true, message) {
62
- if (!this.spinner) return;
63
- if (success) {
64
- this.spinner.succeed(message);
65
- } else {
66
- this.spinner.fail(message);
275
+ logger.info(`Entry hashed: ${hashedName}`);
276
+ }
277
+ async function generateManifest2(themeName, themePath, outputDir) {
278
+ let version = "1.0.0";
279
+ let themeId = themeName;
280
+ try {
281
+ const pkgContent = await fs7__default.default.readFile(
282
+ path__default.default.join(themePath, "package.json"),
283
+ "utf-8"
284
+ );
285
+ const pkg = JSON.parse(pkgContent);
286
+ version = pkg.version || version;
287
+ if (pkg.name) {
288
+ themeId = pkg.name.replace(/^@onex-themes\//, "");
67
289
  }
68
- this.spinner = null;
290
+ } catch (e) {
69
291
  }
70
- updateSpinner(message) {
71
- if (this.spinner) {
72
- this.spinner.text = message;
292
+ const [sectionFiles, blockFiles, schemaFiles] = await Promise.all([
293
+ glob.glob("sections/**/index.ts", { cwd: themePath }),
294
+ glob.glob("blocks/**/index.ts", { cwd: themePath }),
295
+ glob.glob("**/*.schema.ts", { cwd: themePath })
296
+ ]);
297
+ let hasThemeConfig = false;
298
+ try {
299
+ await fs7__default.default.access(path__default.default.join(themePath, "theme.config.ts"));
300
+ hasThemeConfig = true;
301
+ } catch (e) {
302
+ }
303
+ const allFiles = await glob.glob("**/*", { cwd: outputDir, nodir: true });
304
+ const jsFiles = allFiles.filter((f) => f.endsWith(".js"));
305
+ const cssFiles = allFiles.filter((f) => f.endsWith(".css"));
306
+ const entryFile = jsFiles.find((f) => f.includes("bundle-entry")) || "bundle-entry.js";
307
+ const manifest = {
308
+ themeId,
309
+ version,
310
+ name: themeId.charAt(0).toUpperCase() + themeId.slice(1),
311
+ compiledAt: (/* @__PURE__ */ new Date()).toISOString(),
312
+ format: "esm",
313
+ platform: "browser",
314
+ target: "es2020",
315
+ counts: {
316
+ sections: sectionFiles.length,
317
+ blocks: blockFiles.length,
318
+ schemas: schemaFiles.length
319
+ },
320
+ output: {
321
+ entry: entryFile,
322
+ chunks: jsFiles.filter((f) => f !== entryFile && !f.endsWith(".map")),
323
+ assets: allFiles.filter(
324
+ (f) => [".png", ".jpg", ".jpeg", ".svg", ".gif", ".webp"].some(
325
+ (ext) => f.endsWith(ext)
326
+ )
327
+ ),
328
+ stylesheets: cssFiles
329
+ },
330
+ external: ["react", "react-dom", "@onexapis/core"],
331
+ source: {
332
+ sections: sectionFiles,
333
+ blocks: blockFiles,
334
+ schemas: schemaFiles,
335
+ hasThemeConfig
336
+ }
337
+ };
338
+ await fs7__default.default.writeFile(
339
+ path__default.default.join(outputDir, "manifest.json"),
340
+ JSON.stringify(manifest, null, 2)
341
+ );
342
+ }
343
+ async function compileStandaloneTheme(themePath, themeName) {
344
+ const outputDir = path__default.default.join(themePath, "dist");
345
+ const bundleEntry = path__default.default.join(themePath, "bundle-entry.ts");
346
+ const indexEntry = path__default.default.join(themePath, "index.ts");
347
+ let entryPoint = indexEntry;
348
+ try {
349
+ await fs7__default.default.access(bundleEntry);
350
+ entryPoint = bundleEntry;
351
+ } catch (e) {
352
+ }
353
+ const shimPath = path__default.default.join(outputDir, ".process-shim.js");
354
+ await fs7__default.default.mkdir(outputDir, { recursive: true });
355
+ await fs7__default.default.writeFile(shimPath, PROCESS_SHIM);
356
+ const buildOptions = {
357
+ entryPoints: [entryPoint],
358
+ bundle: true,
359
+ platform: "browser",
360
+ format: "esm",
361
+ outdir: outputDir,
362
+ splitting: false,
363
+ chunkNames: "[name]-[hash]",
364
+ banner: {
365
+ js: '"use client";'
366
+ },
367
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
368
+ external: [],
369
+ alias: {
370
+ events: "events/",
371
+ buffer: "buffer/"
372
+ },
373
+ inject: [shimPath],
374
+ define: {
375
+ "process.env.NODE_ENV": JSON.stringify("production"),
376
+ global: "globalThis"
377
+ },
378
+ minify: true,
379
+ sourcemap: true,
380
+ logLevel: "warning",
381
+ target: "es2020",
382
+ jsx: "automatic",
383
+ jsxImportSource: "react",
384
+ loader: {
385
+ ".tsx": "tsx",
386
+ ".ts": "ts",
387
+ ".jpg": "file",
388
+ ".jpeg": "file",
389
+ ".png": "file",
390
+ ".gif": "file",
391
+ ".svg": "file",
392
+ ".webp": "file"
393
+ },
394
+ assetNames: "assets/[name]-[hash]",
395
+ publicPath: "./",
396
+ metafile: true
397
+ };
398
+ try {
399
+ const result = await esbuild__namespace.build(buildOptions);
400
+ try {
401
+ await fs7__default.default.unlink(shimPath);
402
+ } catch (e) {
403
+ }
404
+ await contentHashEntry(outputDir);
405
+ await generateManifest2(themeName, themePath, outputDir);
406
+ if (result.metafile) {
407
+ const outputs = result.metafile.outputs;
408
+ let totalSize = 0;
409
+ for (const output of Object.values(outputs)) {
410
+ totalSize += output.bytes;
411
+ }
412
+ const totalKB = (totalSize / 1024).toFixed(2);
413
+ logger.info(`Bundle size: ${totalKB} KB`);
414
+ }
415
+ return true;
416
+ } catch (error) {
417
+ try {
418
+ await fs7__default.default.unlink(shimPath);
419
+ } catch (e) {
73
420
  }
421
+ logger.error(`esbuild compilation failed: ${error}`);
422
+ return false;
74
423
  }
75
- newLine() {
76
- console.log();
424
+ }
425
+ async function compileStandaloneThemeDev(themePath, themeName) {
426
+ const outputDir = path__default.default.join(themePath, "dist");
427
+ const bundleEntry = path__default.default.join(themePath, "bundle-entry.ts");
428
+ const indexEntry = path__default.default.join(themePath, "index.ts");
429
+ let entryPoint = indexEntry;
430
+ try {
431
+ await fs7__default.default.access(bundleEntry);
432
+ entryPoint = bundleEntry;
433
+ } catch (e) {
434
+ }
435
+ const shimPath = path__default.default.join(outputDir, ".process-shim.js");
436
+ await fs7__default.default.mkdir(outputDir, { recursive: true });
437
+ await fs7__default.default.writeFile(shimPath, PROCESS_SHIM);
438
+ const buildOptions = {
439
+ entryPoints: [entryPoint],
440
+ bundle: true,
441
+ platform: "browser",
442
+ format: "esm",
443
+ outdir: outputDir,
444
+ splitting: false,
445
+ banner: {
446
+ js: '"use client";'
447
+ },
448
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
449
+ external: [],
450
+ alias: {
451
+ events: "events/",
452
+ buffer: "buffer/"
453
+ },
454
+ inject: [shimPath],
455
+ define: {
456
+ "process.env.NODE_ENV": JSON.stringify("development"),
457
+ global: "globalThis"
458
+ },
459
+ minify: false,
460
+ sourcemap: true,
461
+ logLevel: "warning",
462
+ target: "es2020",
463
+ jsx: "automatic",
464
+ jsxImportSource: "react",
465
+ loader: {
466
+ ".tsx": "tsx",
467
+ ".ts": "ts",
468
+ ".jpg": "file",
469
+ ".jpeg": "file",
470
+ ".png": "file",
471
+ ".gif": "file",
472
+ ".svg": "file",
473
+ ".webp": "file"
474
+ },
475
+ assetNames: "assets/[name]-[hash]",
476
+ publicPath: "./",
477
+ metafile: true
478
+ };
479
+ const context2 = await esbuild__namespace.context(buildOptions);
480
+ await context2.rebuild();
481
+ await generateManifest2(themeName, themePath, outputDir);
482
+ return { context: context2, outputDir };
483
+ }
484
+ async function compilePreviewRuntime(themePath) {
485
+ const outputDir = path__default.default.join(themePath, "dist");
486
+ await fs7__default.default.mkdir(outputDir, { recursive: true });
487
+ const outputPath = path__default.default.join(outputDir, "preview-runtime.js");
488
+ const locations = [
489
+ path__default.default.join(__dirname, "..", "preview", "preview-app.tsx"),
490
+ path__default.default.join(__dirname, "preview", "preview-app.tsx"),
491
+ path__default.default.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
492
+ ];
493
+ let previewEntryPath = null;
494
+ for (const loc of locations) {
495
+ try {
496
+ await fs7__default.default.access(loc);
497
+ previewEntryPath = loc;
498
+ break;
499
+ } catch (e) {
500
+ }
77
501
  }
78
- header(message) {
79
- console.log();
80
- console.log(chalk4__default.default.bold.cyan(message));
81
- console.log(chalk4__default.default.cyan("=".repeat(message.length)));
82
- console.log();
502
+ if (!previewEntryPath) {
503
+ throw new Error(
504
+ `Preview app source not found. Searched:
505
+ ${locations.join("\n")}`
506
+ );
83
507
  }
84
- section(message) {
85
- console.log();
86
- console.log(chalk4__default.default.bold(message));
508
+ const serverStubPlugin = {
509
+ name: "server-stub",
510
+ setup(build2) {
511
+ build2.onResolve({ filter: /^server-only$/ }, () => ({
512
+ path: "server-only",
513
+ namespace: "server-stub"
514
+ }));
515
+ build2.onLoad({ filter: /.*/, namespace: "server-stub" }, () => ({
516
+ contents: "// server-only stub for browser",
517
+ loader: "js"
518
+ }));
519
+ const nodeBuiltins = [
520
+ "fs",
521
+ "fs/promises",
522
+ "path",
523
+ "os",
524
+ "crypto",
525
+ "stream",
526
+ "url",
527
+ "http",
528
+ "https",
529
+ "net",
530
+ "tls",
531
+ "child_process",
532
+ "util",
533
+ "events",
534
+ "buffer",
535
+ "querystring",
536
+ "zlib"
537
+ ];
538
+ for (const mod of nodeBuiltins) {
539
+ build2.onResolve({ filter: new RegExp(`^${mod.replace("/", "\\/")}$`) }, () => ({
540
+ path: mod,
541
+ namespace: "node-stub"
542
+ }));
543
+ }
544
+ build2.onLoad({ filter: /.*/, namespace: "node-stub" }, (args) => {
545
+ const stubs = {
546
+ events: "export class EventEmitter { on(){return this} off(){return this} emit(){return false} addListener(){return this} removeListener(){return this} } export default { EventEmitter };",
547
+ path: "export function join(){return ''} export function resolve(){return ''} export function dirname(){return ''} export function basename(){return ''} export function extname(){return ''} export default {};",
548
+ fs: "export const promises = {}; export function readFileSync(){return ''} export function existsSync(){return false} export default {};"
549
+ };
550
+ return {
551
+ contents: stubs[args.path] || "export default {};",
552
+ loader: "js"
553
+ };
554
+ });
555
+ }
556
+ };
557
+ await esbuild__namespace.build({
558
+ entryPoints: [previewEntryPath],
559
+ bundle: true,
560
+ platform: "browser",
561
+ format: "esm",
562
+ outfile: outputPath,
563
+ // Bundle React + core INTO the output (NOT externalized)
564
+ external: [],
565
+ plugins: [serverStubPlugin],
566
+ minify: false,
567
+ sourcemap: true,
568
+ target: "es2020",
569
+ jsx: "automatic",
570
+ jsxImportSource: "react",
571
+ define: {
572
+ "process.env.NODE_ENV": JSON.stringify("development"),
573
+ global: "globalThis"
574
+ },
575
+ loader: { ".tsx": "tsx", ".ts": "ts" },
576
+ // Force CJS resolution to avoid sideEffects:false dropping ESM chunk imports
577
+ conditions: ["require", "default"],
578
+ mainFields: ["main"],
579
+ logOverride: {
580
+ "ignored-bare-import": "silent"
581
+ }
582
+ });
583
+ return outputPath;
584
+ }
585
+ var PROCESS_SHIM, reactGlobalPlugin;
586
+ var init_compile_theme = __esm({
587
+ "src/utils/compile-theme.ts"() {
588
+ init_logger();
589
+ PROCESS_SHIM = `
590
+ if (typeof process === "undefined") {
591
+ globalThis.process = {
592
+ env: {},
593
+ browser: true,
594
+ };
595
+ }
596
+ `;
597
+ reactGlobalPlugin = {
598
+ name: "react-global",
599
+ setup(build2) {
600
+ build2.onResolve({ filter: /^react$/ }, () => ({
601
+ path: "react-external",
602
+ namespace: "react-global"
603
+ }));
604
+ build2.onResolve({ filter: /^react-dom$/ }, () => ({
605
+ path: "react-dom-external",
606
+ namespace: "react-global"
607
+ }));
608
+ build2.onResolve({ filter: /^react\/jsx-runtime$/ }, () => ({
609
+ path: "react-jsx-runtime-external",
610
+ namespace: "react-global"
611
+ }));
612
+ build2.onLoad({ filter: /.*/, namespace: "react-global" }, (args) => {
613
+ if (args.path === "react-external") {
614
+ return {
615
+ contents: `
616
+ if (!globalThis.__ONEX_REACT__) {
617
+ throw new Error('[Theme Bundle] React not initialized. Ensure globalThis.__ONEX_REACT__ is set before loading theme.');
618
+ }
619
+
620
+ const React = globalThis.__ONEX_REACT__;
621
+ export default React;
622
+
623
+ export const {
624
+ useState,
625
+ useEffect,
626
+ useContext,
627
+ useReducer,
628
+ useCallback,
629
+ useMemo,
630
+ useRef,
631
+ useImperativeHandle,
632
+ useLayoutEffect,
633
+ useDebugValue,
634
+ useDeferredValue,
635
+ useTransition,
636
+ useId,
637
+ useSyncExternalStore,
638
+ useInsertionEffect,
639
+ createContext,
640
+ forwardRef,
641
+ lazy,
642
+ memo,
643
+ startTransition,
644
+ createElement,
645
+ cloneElement,
646
+ isValidElement,
647
+ Children,
648
+ Fragment,
649
+ Profiler,
650
+ StrictMode,
651
+ Suspense,
652
+ Component,
653
+ PureComponent,
654
+ useActionState,
655
+ use,
656
+ } = React;
657
+ `.trim(),
658
+ loader: "js"
659
+ };
660
+ }
661
+ if (args.path === "react-dom-external") {
662
+ return {
663
+ contents: `
664
+ if (!globalThis.__ONEX_REACT_DOM__) {
665
+ throw new Error('[Theme Bundle] ReactDOM not initialized. Ensure globalThis.__ONEX_REACT_DOM__ is set before loading theme.');
666
+ }
667
+
668
+ const ReactDOM = globalThis.__ONEX_REACT_DOM__;
669
+ export default ReactDOM;
670
+
671
+ export const {
672
+ createRoot,
673
+ hydrateRoot,
674
+ flushSync,
675
+ createPortal,
676
+ findDOMNode,
677
+ render,
678
+ hydrate,
679
+ unmountComponentAtNode,
680
+ } = ReactDOM;
681
+ `.trim(),
682
+ loader: "js"
683
+ };
684
+ }
685
+ if (args.path === "react-jsx-runtime-external") {
686
+ return {
687
+ contents: `
688
+ if (!globalThis.__ONEX_JSX_RUNTIME__) {
689
+ throw new Error('[Theme Bundle] React JSX runtime not initialized. Ensure globalThis.__ONEX_JSX_RUNTIME__ is set before loading theme.');
690
+ }
691
+ const _jsxRuntime = globalThis.__ONEX_JSX_RUNTIME__;
692
+ export const jsx = _jsxRuntime.jsx;
693
+ export const jsxs = _jsxRuntime.jsxs;
694
+ export const Fragment = _jsxRuntime.Fragment;
695
+ `.trim(),
696
+ loader: "js"
697
+ };
698
+ }
699
+ return null;
700
+ });
701
+ }
702
+ };
87
703
  }
88
- };
89
- var logger = new Logger();
704
+ });
90
705
 
91
706
  // src/utils/file-helpers.ts
707
+ init_logger();
92
708
  async function renderTemplate(templatePath, data) {
93
709
  const template = await fs__default.default.readFile(templatePath, "utf-8");
94
710
  return ejs__default.default.render(template, data);
@@ -174,8 +790,10 @@ function getProjectRoot() {
174
790
  }
175
791
  function getThemesDir() {
176
792
  const root = getProjectRoot();
177
- if (fs__default.default.existsSync(path__default.default.join(root, "themes"))) return path__default.default.join(root, "themes");
178
- if (fs__default.default.existsSync(path__default.default.join(root, "src/themes"))) return path__default.default.join(root, "src/themes");
793
+ if (fs__default.default.existsSync(path__default.default.join(root, "themes")))
794
+ return path__default.default.join(root, "themes");
795
+ if (fs__default.default.existsSync(path__default.default.join(root, "src/themes")))
796
+ return path__default.default.join(root, "src/themes");
179
797
  return path__default.default.dirname(root);
180
798
  }
181
799
  function getFeaturesDir() {
@@ -233,6 +851,9 @@ async function installDependencies(projectPath, packageManager = "npm") {
233
851
  });
234
852
  }
235
853
 
854
+ // src/commands/init.ts
855
+ init_logger();
856
+
236
857
  // src/utils/validators.ts
237
858
  function validateName(name) {
238
859
  return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
@@ -674,6 +1295,9 @@ export const homePageConfig: PageConfig = {
674
1295
  };
675
1296
  `;
676
1297
  }
1298
+
1299
+ // src/commands/create-section.ts
1300
+ init_logger();
677
1301
  async function createSectionCommand(name, options) {
678
1302
  logger.header("Create New Section");
679
1303
  ensureOneXProject();
@@ -913,6 +1537,9 @@ export { ${data.sectionName}Schema } from "./${data.sectionName}.schema";
913
1537
  ${hasTemplate ? `export { ${data.sectionNamePascal}Default } from "./${data.sectionName}-default";` : ""}
914
1538
  `;
915
1539
  }
1540
+
1541
+ // src/commands/create-block.ts
1542
+ init_logger();
916
1543
  async function createBlockCommand(name, options) {
917
1544
  logger.header("Create New Block");
918
1545
  ensureOneXProject();
@@ -1137,6 +1764,9 @@ export { ${data.blockName}Definition } from "./${data.blockName}.schema";
1137
1764
  export { ${data.blockNamePascal} } from "./${data.blockName}";
1138
1765
  `;
1139
1766
  }
1767
+
1768
+ // src/commands/create-component.ts
1769
+ init_logger();
1140
1770
  async function createComponentCommand(name, options) {
1141
1771
  logger.header("Create New Component");
1142
1772
  ensureOneXProject();
@@ -1341,6 +1971,9 @@ export { ${data.componentName}Definition } from "./${data.componentName}.schema"
1341
1971
  export { ${data.componentNamePascal} } from "./${data.componentName}";
1342
1972
  `;
1343
1973
  }
1974
+
1975
+ // src/commands/list.ts
1976
+ init_logger();
1344
1977
  async function listCommand(options) {
1345
1978
  logger.header("OneX Project Inventory");
1346
1979
  ensureOneXProject();
@@ -1472,6 +2105,9 @@ async function listThemesInfo() {
1472
2105
  }
1473
2106
  logger.newLine();
1474
2107
  }
2108
+
2109
+ // src/commands/validate.ts
2110
+ init_logger();
1475
2111
  async function validateCommand(options) {
1476
2112
  logger.header("Validate Theme");
1477
2113
  ensureOneXProject();
@@ -1484,7 +2120,11 @@ async function validateCommand(options) {
1484
2120
  }
1485
2121
  themeToValidate = options.theme;
1486
2122
  } else {
1487
- const isThemeDir = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
2123
+ const isThemeDir = [
2124
+ "theme.config.ts",
2125
+ "bundle-entry.ts",
2126
+ "manifest.ts"
2127
+ ].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
1488
2128
  if (isThemeDir) {
1489
2129
  themeToValidate = path__default.default.basename(process.cwd());
1490
2130
  logger.info(`Validating current theme: ${themeToValidate}`);
@@ -1497,15 +2137,21 @@ async function validateCommand(options) {
1497
2137
  }
1498
2138
  const themePath = path__default.default.join(getThemesDir(), themeToValidate);
1499
2139
  logger.startSpinner("Running validation checks...");
1500
- const manifestPath = path__default.default.join(themePath, "manifest.ts");
1501
- if (!fs__default.default.existsSync(manifestPath)) {
2140
+ const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
2141
+ const foundEntry = entryFiles.find(
2142
+ (f) => fs__default.default.existsSync(path__default.default.join(themePath, f))
2143
+ );
2144
+ if (!foundEntry) {
1502
2145
  issues.push({
1503
2146
  type: "error",
1504
- file: "manifest.ts",
1505
- message: "Manifest file not found"
2147
+ file: "manifest.ts / theme.config.ts / bundle-entry.ts",
2148
+ message: "No theme entry file found (need at least one of: manifest.ts, theme.config.ts, bundle-entry.ts)"
1506
2149
  });
1507
- } else {
1508
- const manifestContent = fs__default.default.readFileSync(manifestPath, "utf-8");
2150
+ } else if (foundEntry === "manifest.ts") {
2151
+ const manifestContent = fs__default.default.readFileSync(
2152
+ path__default.default.join(themePath, foundEntry),
2153
+ "utf-8"
2154
+ );
1509
2155
  if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
1510
2156
  issues.push({
1511
2157
  type: "error",
@@ -1641,7 +2287,11 @@ async function validateCommand(options) {
1641
2287
  }
1642
2288
  }
1643
2289
  }
2290
+
2291
+ // src/commands/build.ts
2292
+ init_logger();
1644
2293
  async function buildCommand(options) {
2294
+ var _a;
1645
2295
  logger.header("Build Theme");
1646
2296
  let themePath;
1647
2297
  let themeName;
@@ -1662,7 +2312,11 @@ async function buildCommand(options) {
1662
2312
  process.exit(1);
1663
2313
  }
1664
2314
  } else {
1665
- const isThemeDir = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
2315
+ const isThemeDir = [
2316
+ "theme.config.ts",
2317
+ "bundle-entry.ts",
2318
+ "manifest.ts"
2319
+ ].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
1666
2320
  if (isThemeDir) {
1667
2321
  themePath = process.cwd();
1668
2322
  themeName = path__default.default.basename(themePath);
@@ -1706,11 +2360,20 @@ async function buildCommand(options) {
1706
2360
  process.exit(1);
1707
2361
  }
1708
2362
  logger.stopSpinner(true, "Lint passed");
1709
- const buildArgs = options.watch ? ["build", "--watch"] : ["build"];
2363
+ const pkgJson = fs__default.default.readJsonSync(packageJsonPath);
2364
+ const buildScript = ((_a = pkgJson.scripts) == null ? void 0 : _a.build) || "";
2365
+ const isRecursive = buildScript.includes("onex build") || buildScript.includes("onex-cli build");
1710
2366
  logger.startSpinner(
1711
2367
  options.watch ? "Building (watch mode)..." : "Building..."
1712
2368
  );
1713
- const buildSuccess = await runCommand("pnpm", buildArgs, themePath);
2369
+ let buildSuccess;
2370
+ if (isRecursive) {
2371
+ const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
2372
+ buildSuccess = await compileStandaloneTheme2(themePath, themeName);
2373
+ } else {
2374
+ const buildArgs = options.watch ? ["build", "--watch"] : ["build"];
2375
+ buildSuccess = await runCommand("pnpm", buildArgs, themePath);
2376
+ }
1714
2377
  if (!buildSuccess && !options.watch) {
1715
2378
  logger.stopSpinner(false, "Build failed");
1716
2379
  process.exit(1);
@@ -1751,6 +2414,9 @@ function runCommand(command, args, cwd) {
1751
2414
  });
1752
2415
  });
1753
2416
  }
2417
+
2418
+ // src/commands/package.ts
2419
+ init_logger();
1754
2420
  async function packageCommand(options) {
1755
2421
  logger.header("Package Theme");
1756
2422
  ensureOneXProject();
@@ -1764,7 +2430,11 @@ async function packageCommand(options) {
1764
2430
  process.exit(1);
1765
2431
  }
1766
2432
  } else {
1767
- const isThemeDir = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
2433
+ const isThemeDir = [
2434
+ "theme.config.ts",
2435
+ "bundle-entry.ts",
2436
+ "manifest.ts"
2437
+ ].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
1768
2438
  if (isThemeDir) {
1769
2439
  themePath = process.cwd();
1770
2440
  themeName = path__default.default.basename(themePath);
@@ -1788,10 +2458,9 @@ async function packageCommand(options) {
1788
2458
  logger.newLine();
1789
2459
  const compiledThemePath = path__default.default.join(
1790
2460
  process.cwd(),
1791
- "apps",
1792
- "api-server",
1793
- "compiled-themes",
1794
- `${themeName}@${version}`
2461
+ "themes",
2462
+ themeName,
2463
+ "dist"
1795
2464
  );
1796
2465
  if (!options.skipBuild) {
1797
2466
  logger.section("Step 1: Compile Theme");
@@ -1889,6 +2558,9 @@ async function createZipArchive(compiledThemePath, outputPath) {
1889
2558
  archive.finalize();
1890
2559
  });
1891
2560
  }
2561
+
2562
+ // src/commands/deploy.ts
2563
+ init_logger();
1892
2564
  async function deployCommand(options) {
1893
2565
  logger.header("Deploy Theme");
1894
2566
  ensureOneXProject();
@@ -1991,6 +2663,9 @@ async function deployCommand(options) {
1991
2663
  process.exit(1);
1992
2664
  }
1993
2665
  }
2666
+
2667
+ // src/commands/upload.ts
2668
+ init_logger();
1994
2669
  function getS3Client() {
1995
2670
  const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
1996
2671
  if (adapterMode === "vps") {
@@ -2030,21 +2705,12 @@ function getBucketName(env) {
2030
2705
  return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
2031
2706
  }
2032
2707
  async function findCompiledThemeDir(themeId, version) {
2033
- const searchPaths = [
2034
- path__default.default.resolve(process.cwd(), "dist"),
2035
- path__default.default.resolve(
2036
- process.cwd(),
2037
- `../../apps/api-server/compiled-themes/${themeId}@${version}`
2038
- ),
2039
- path__default.default.resolve(
2040
- process.cwd(),
2041
- `../api-server/compiled-themes/${themeId}@${version}`
2042
- )
2043
- ];
2708
+ const searchPaths = [path__default.default.resolve(process.cwd(), "dist")];
2044
2709
  for (const dir of searchPaths) {
2045
2710
  if (await fs__default.default.pathExists(dir)) {
2046
- const manifestPath = path__default.default.join(dir, "manifest.json");
2047
- if (await fs__default.default.pathExists(manifestPath)) {
2711
+ const hasManifest = await fs__default.default.pathExists(path__default.default.join(dir, "manifest.json"));
2712
+ const hasThemeEntry = await fs__default.default.pathExists(path__default.default.join(dir, "bundle-entry.js")) || await fs__default.default.pathExists(path__default.default.join(dir, "theme.config.js")) || await fs__default.default.pathExists(path__default.default.join(dir, "index.js"));
2713
+ if (hasManifest || hasThemeEntry) {
2048
2714
  return dir;
2049
2715
  }
2050
2716
  }
@@ -2147,13 +2813,8 @@ async function uploadCommand(options) {
2147
2813
  `Compiled theme not found for ${themeId}@${version}. Run 'onex build' first.`
2148
2814
  )
2149
2815
  );
2150
- logger.info(
2151
- chalk4__default.default.gray(
2152
- `Expected locations:
2153
- - ./dist/
2154
- - ../../apps/api-server/compiled-themes/${themeId}@${version}/`
2155
- )
2156
- );
2816
+ logger.info(chalk4__default.default.gray(`Expected location:
2817
+ - ./dist/`));
2157
2818
  process.exit(1);
2158
2819
  }
2159
2820
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
@@ -2260,9 +2921,7 @@ async function uploadCommand(options) {
2260
2921
  );
2261
2922
  console.log(chalk4__default.default.cyan(" Bucket: ") + chalk4__default.default.white(bucket));
2262
2923
  console.log(
2263
- chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(
2264
- `bundle.zip${sourceUploaded ? " + source.zip" : ""}`
2265
- )
2924
+ chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(`bundle.zip${sourceUploaded ? " + source.zip" : ""}`)
2266
2925
  );
2267
2926
  console.log(
2268
2927
  chalk4__default.default.cyan(" Path: ") + chalk4__default.default.gray(`s3://${bucket}/themes/${themeId}/${version}/`)
@@ -2274,6 +2933,9 @@ async function uploadCommand(options) {
2274
2933
  process.exit(1);
2275
2934
  }
2276
2935
  }
2936
+
2937
+ // src/commands/download.ts
2938
+ init_logger();
2277
2939
  function getS3Client2() {
2278
2940
  const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
2279
2941
  if (adapterMode === "vps") {
@@ -2421,11 +3083,7 @@ function showDownloadFailureHelp(themeId, bucket) {
2421
3083
  );
2422
3084
  console.log();
2423
3085
  console.log(chalk4__default.default.white("4. Verify theme exists in S3:"));
2424
- console.log(
2425
- chalk4__default.default.gray(
2426
- ` aws s3 ls s3://${bucket}/themes/${themeId}/`
2427
- )
2428
- );
3086
+ console.log(chalk4__default.default.gray(` aws s3 ls s3://${bucket}/themes/${themeId}/`));
2429
3087
  console.log();
2430
3088
  }
2431
3089
  async function downloadCommand(options) {
@@ -2501,6 +3159,9 @@ async function downloadCommand(options) {
2501
3159
  process.exit(1);
2502
3160
  }
2503
3161
  }
3162
+
3163
+ // src/commands/clone.ts
3164
+ init_logger();
2504
3165
  function getS3Client3() {
2505
3166
  const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
2506
3167
  if (adapterMode === "vps") {
@@ -2604,17 +3265,100 @@ function runInstall(cwd) {
2604
3265
  proc.on("error", () => resolve(false));
2605
3266
  });
2606
3267
  }
3268
+ async function promptThemeName(originalName) {
3269
+ const { default: inquirer5 } = await import('inquirer');
3270
+ const { themeName } = await inquirer5.prompt([
3271
+ {
3272
+ type: "input",
3273
+ name: "themeName",
3274
+ message: "New theme name (kebab-case):",
3275
+ default: `my-${originalName}`,
3276
+ validate: (input) => {
3277
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
3278
+ return "Theme name must be kebab-case (lowercase letters, numbers, hyphens)";
3279
+ }
3280
+ if (input === originalName) {
3281
+ return `Name must differ from the original theme "${originalName}"`;
3282
+ }
3283
+ return true;
3284
+ }
3285
+ }
3286
+ ]);
3287
+ return themeName;
3288
+ }
3289
+ async function renameTheme(themeDir, oldName, newName) {
3290
+ const oldPrefix = `${oldName}-`;
3291
+ const newPrefix = `${newName}-`;
3292
+ const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3293
+ const pkgPath = path__default.default.join(themeDir, "package.json");
3294
+ if (await fs__default.default.pathExists(pkgPath)) {
3295
+ const pkg = await fs__default.default.readJson(pkgPath);
3296
+ pkg.name = `@onex-themes/${newName}`;
3297
+ if (pkg.description) {
3298
+ pkg.description = pkg.description.replace(
3299
+ new RegExp(oldName, "gi"),
3300
+ newDisplayName
3301
+ );
3302
+ }
3303
+ pkg.version = "1.0.0";
3304
+ await fs__default.default.writeJson(pkgPath, pkg, { spaces: 2 });
3305
+ }
3306
+ const configPath = path__default.default.join(themeDir, "theme.config.ts");
3307
+ if (await fs__default.default.pathExists(configPath)) {
3308
+ let content = await fs__default.default.readFile(configPath, "utf-8");
3309
+ content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
3310
+ content = content.replace(
3311
+ /name:\s*"[^"]*Theme"/,
3312
+ `name: "${newDisplayName} Theme"`
3313
+ );
3314
+ await fs__default.default.writeFile(configPath, content);
3315
+ }
3316
+ const layoutPath = path__default.default.join(themeDir, "theme.layout.ts");
3317
+ if (await fs__default.default.pathExists(layoutPath)) {
3318
+ let content = await fs__default.default.readFile(layoutPath, "utf-8");
3319
+ content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
3320
+ content = content.replace(
3321
+ /name:\s*"[^"]*Theme"/,
3322
+ `name: "${newDisplayName} Theme"`
3323
+ );
3324
+ await fs__default.default.writeFile(layoutPath, content);
3325
+ }
3326
+ const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3327
+ const tsFiles = await glob.glob("**/*.ts", { cwd: themeDir, nodir: true });
3328
+ for (const file of tsFiles) {
3329
+ const filePath = path__default.default.join(themeDir, file);
3330
+ let content = await fs__default.default.readFile(filePath, "utf-8");
3331
+ const original = content;
3332
+ content = content.replace(
3333
+ new RegExp(`"${oldPrefix}`, "g"),
3334
+ `"${newPrefix}`
3335
+ );
3336
+ content = content.replace(
3337
+ new RegExp(`themeId:\\s*"${oldName}"`, "g"),
3338
+ `themeId: "${newName}"`
3339
+ );
3340
+ content = content.replace(
3341
+ new RegExp(`${oldDisplayName} Theme`, "g"),
3342
+ `${newDisplayName} Theme`
3343
+ );
3344
+ if (content !== original) {
3345
+ await fs__default.default.writeFile(filePath, content);
3346
+ }
3347
+ }
3348
+ }
2607
3349
  async function cloneCommand(themeName, options) {
2608
3350
  logger.header("Clone Theme Source");
3351
+ let newName = options.name;
3352
+ if (!newName) {
3353
+ newName = await promptThemeName(themeName);
3354
+ }
2609
3355
  const spinner = ora__default.default("Initializing clone...").start();
2610
3356
  try {
2611
3357
  const bucket = options.bucket || getBucketName3(options.environment);
2612
- const outputDir = options.output || path__default.default.resolve(process.cwd(), themeName);
3358
+ const outputDir = options.output || path__default.default.resolve(process.cwd(), newName);
2613
3359
  const s3Client = getS3Client3();
2614
3360
  if (await fs__default.default.pathExists(outputDir)) {
2615
- spinner.fail(
2616
- chalk4__default.default.red(`Directory already exists: ${outputDir}`)
2617
- );
3361
+ spinner.fail(chalk4__default.default.red(`Directory already exists: ${outputDir}`));
2618
3362
  logger.info(
2619
3363
  chalk4__default.default.gray(
2620
3364
  "Use -o to specify a different output directory, or remove the existing directory."
@@ -2628,9 +3372,7 @@ async function cloneCommand(themeName, options) {
2628
3372
  version = await resolveLatestVersion2(s3Client, bucket, themeName);
2629
3373
  spinner.succeed(`Resolved latest version: ${chalk4__default.default.cyan(version)}`);
2630
3374
  }
2631
- spinner.start(
2632
- `Downloading source.zip for ${themeName}@${version}...`
2633
- );
3375
+ spinner.start(`Downloading source.zip for ${themeName}@${version}...`);
2634
3376
  const s3Key = `themes/${themeName}/${version}/source.zip`;
2635
3377
  let zipBuffer;
2636
3378
  try {
@@ -2648,9 +3390,7 @@ async function cloneCommand(themeName, options) {
2648
3390
  chalk4__default.default.yellow("The theme source may not have been uploaded yet.")
2649
3391
  );
2650
3392
  console.log(
2651
- chalk4__default.default.gray(
2652
- `Upload source with: onex upload --theme ${themeName}`
2653
- )
3393
+ chalk4__default.default.gray(`Upload source with: onex upload --theme ${themeName}`)
2654
3394
  );
2655
3395
  console.log();
2656
3396
  process.exit(1);
@@ -2663,6 +3403,13 @@ async function cloneCommand(themeName, options) {
2663
3403
  zip.extractAllTo(outputDir, true);
2664
3404
  const entries = zip.getEntries().filter((e) => !e.isDirectory);
2665
3405
  spinner.succeed(`Extracted ${entries.length} files`);
3406
+ spinner.start(
3407
+ `Renaming theme: ${chalk4__default.default.gray(themeName)} \u2192 ${chalk4__default.default.cyan(newName)}...`
3408
+ );
3409
+ await renameTheme(outputDir, themeName, newName);
3410
+ spinner.succeed(
3411
+ `Renamed theme: ${chalk4__default.default.gray(themeName)} \u2192 ${chalk4__default.default.cyan(newName)}`
3412
+ );
2666
3413
  if (options.install !== false) {
2667
3414
  const hasPkgJson = await fs__default.default.pathExists(
2668
3415
  path__default.default.join(outputDir, "package.json")
@@ -2685,15 +3432,14 @@ async function cloneCommand(themeName, options) {
2685
3432
  logger.success(chalk4__default.default.green.bold("Theme cloned successfully!"));
2686
3433
  console.log();
2687
3434
  console.log(
2688
- chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(`${themeName}@${version}`)
3435
+ chalk4__default.default.cyan(" Source: ") + chalk4__default.default.gray(`${themeName}@${version}`)
2689
3436
  );
3437
+ console.log(chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(newName));
2690
3438
  console.log(chalk4__default.default.cyan(" Location: ") + chalk4__default.default.white(outputDir));
2691
3439
  console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
2692
3440
  console.log();
2693
3441
  console.log(chalk4__default.default.cyan("Next steps:"));
2694
- console.log(
2695
- chalk4__default.default.gray(` cd ${path__default.default.relative(process.cwd(), outputDir)}`)
2696
- );
3442
+ console.log(chalk4__default.default.gray(` cd ${path__default.default.relative(process.cwd(), outputDir)}`));
2697
3443
  if (options.install === false) {
2698
3444
  console.log(chalk4__default.default.gray(" pnpm install"));
2699
3445
  }
@@ -2706,14 +3452,253 @@ async function cloneCommand(themeName, options) {
2706
3452
  }
2707
3453
  }
2708
3454
 
3455
+ // src/commands/dev.ts
3456
+ init_logger();
3457
+ init_compile_theme();
3458
+ var MIME_TYPES = {
3459
+ ".js": "application/javascript",
3460
+ ".mjs": "application/javascript",
3461
+ ".css": "text/css",
3462
+ ".json": "application/json",
3463
+ ".html": "text/html",
3464
+ ".svg": "image/svg+xml",
3465
+ ".png": "image/png",
3466
+ ".jpg": "image/jpeg",
3467
+ ".jpeg": "image/jpeg",
3468
+ ".webp": "image/webp",
3469
+ ".gif": "image/gif",
3470
+ ".map": "application/json"
3471
+ };
3472
+ function createDevServer(options) {
3473
+ const clients = /* @__PURE__ */ new Set();
3474
+ const server = http__default.default.createServer((req, res) => {
3475
+ res.setHeader("Access-Control-Allow-Origin", "*");
3476
+ res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
3477
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
3478
+ if (req.method === "OPTIONS") {
3479
+ res.writeHead(200);
3480
+ res.end();
3481
+ return;
3482
+ }
3483
+ const url = new URL(req.url || "/", `http://localhost:${options.port}`);
3484
+ const pathname = url.pathname;
3485
+ if (pathname === "/" || pathname === "/index.html") {
3486
+ res.writeHead(200, { "Content-Type": "text/html" });
3487
+ res.end(generatePreviewHTML(options.themeName));
3488
+ return;
3489
+ }
3490
+ if (pathname === "/preview-runtime.js") {
3491
+ serveFile(res, options.previewRuntimePath);
3492
+ return;
3493
+ }
3494
+ const filePath = path__default.default.join(options.distDir, pathname);
3495
+ if (!filePath.startsWith(options.distDir)) {
3496
+ res.writeHead(403);
3497
+ res.end("Forbidden");
3498
+ return;
3499
+ }
3500
+ serveFile(res, filePath);
3501
+ });
3502
+ const wss = new ws.WebSocketServer({ server });
3503
+ wss.on("connection", (ws) => {
3504
+ clients.add(ws);
3505
+ ws.on("close", () => clients.delete(ws));
3506
+ });
3507
+ server.listen(options.port);
3508
+ return {
3509
+ broadcast(message) {
3510
+ const data = JSON.stringify(message);
3511
+ for (const client of clients) {
3512
+ if (client.readyState === ws.WebSocket.OPEN) {
3513
+ client.send(data);
3514
+ }
3515
+ }
3516
+ },
3517
+ close() {
3518
+ wss.close();
3519
+ server.close();
3520
+ }
3521
+ };
3522
+ }
3523
+ function serveFile(res, filePath) {
3524
+ try {
3525
+ if (!fs2__default.default.existsSync(filePath)) {
3526
+ res.writeHead(404);
3527
+ res.end("Not Found");
3528
+ return;
3529
+ }
3530
+ const ext = path__default.default.extname(filePath);
3531
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
3532
+ const content = fs2__default.default.readFileSync(filePath);
3533
+ res.writeHead(200, { "Content-Type": contentType });
3534
+ res.end(content);
3535
+ } catch (e) {
3536
+ res.writeHead(500);
3537
+ res.end("Internal Server Error");
3538
+ }
3539
+ }
3540
+ function generatePreviewHTML(themeName, port) {
3541
+ return `<!DOCTYPE html>
3542
+ <html lang="en">
3543
+ <head>
3544
+ <meta charset="UTF-8">
3545
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
3546
+ <title>OneX Dev \u2014 ${themeName}</title>
3547
+ <!-- Tailwind CSS Play CDN \u2014 JIT compilation in browser for dev preview -->
3548
+ <script src="https://cdn.tailwindcss.com"></script>
3549
+ <style>
3550
+ #onex-dev-toolbar {
3551
+ position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
3552
+ height: 40px; background: #1a1a2e; color: #e0e0e0;
3553
+ display: flex; align-items: center; padding: 0 16px; gap: 16px;
3554
+ font-family: system-ui; font-size: 13px;
3555
+ border-bottom: 2px solid #16213e;
3556
+ }
3557
+ #onex-dev-toolbar .status { width: 8px; height: 8px; border-radius: 50%; }
3558
+ #onex-dev-toolbar .status.connected { background: #00ff88; }
3559
+ #onex-dev-toolbar .status.disconnected { background: #ff4444; }
3560
+ #onex-dev-toolbar .status.rebuilding { background: #ffaa00; animation: pulse 0.5s infinite; }
3561
+ @keyframes pulse { 50% { opacity: 0.5; } }
3562
+ #onex-preview-root { margin-top: 40px; }
3563
+ </style>
3564
+ </head>
3565
+ <body>
3566
+ <div id="onex-dev-toolbar">
3567
+ <span style="font-weight:600;">OneX Dev</span>
3568
+ <span style="color:#888;">|</span>
3569
+ <span>${themeName}</span>
3570
+ <span style="color:#888;">|</span>
3571
+ <span id="page-indicator">Home</span>
3572
+ <span style="color:#888;">|</span>
3573
+ <span class="status connected" id="ws-status"></span>
3574
+ <span id="ws-label">Connected</span>
3575
+ </div>
3576
+ <div id="onex-preview-root"></div>
3577
+ <script type="module" src="/preview-runtime.js"></script>
3578
+ </body>
3579
+ </html>`;
3580
+ }
3581
+
3582
+ // src/commands/dev.ts
3583
+ async function devCommand(options) {
3584
+ logger.header("OneX Dev Server");
3585
+ let themePath;
3586
+ let themeName;
3587
+ if (options.theme) {
3588
+ themeName = options.theme;
3589
+ try {
3590
+ const workspaceThemePath = path__default.default.join(getThemesDir(), themeName);
3591
+ if (fs__default.default.existsSync(workspaceThemePath)) {
3592
+ themePath = workspaceThemePath;
3593
+ } else {
3594
+ themePath = path__default.default.join(process.cwd(), themeName);
3595
+ }
3596
+ } catch (e) {
3597
+ themePath = path__default.default.join(process.cwd(), themeName);
3598
+ }
3599
+ if (!fs__default.default.existsSync(themePath)) {
3600
+ logger.error(`Theme "${themeName}" not found.`);
3601
+ process.exit(1);
3602
+ }
3603
+ } else {
3604
+ const isThemeDir = [
3605
+ "theme.config.ts",
3606
+ "bundle-entry.ts",
3607
+ "manifest.ts"
3608
+ ].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
3609
+ if (isThemeDir) {
3610
+ themePath = process.cwd();
3611
+ themeName = path__default.default.basename(themePath);
3612
+ } else {
3613
+ logger.error(
3614
+ "Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
3615
+ );
3616
+ process.exit(1);
3617
+ }
3618
+ }
3619
+ logger.startSpinner("Compiling preview runtime...");
3620
+ const previewRuntimePath = await compilePreviewRuntime(themePath);
3621
+ logger.stopSpinner(true, "Preview runtime compiled");
3622
+ logger.startSpinner("Compiling theme...");
3623
+ const { context: context2, outputDir } = await compileStandaloneThemeDev(
3624
+ themePath,
3625
+ themeName
3626
+ );
3627
+ logger.stopSpinner(true, "Theme compiled");
3628
+ const port = Number(options.port) || 3456;
3629
+ const server = createDevServer({
3630
+ port,
3631
+ distDir: outputDir,
3632
+ previewRuntimePath,
3633
+ themeName
3634
+ });
3635
+ const watcher = chokidar__default.default.watch(
3636
+ [
3637
+ "sections",
3638
+ "blocks",
3639
+ "components",
3640
+ "pages",
3641
+ "theme.config.ts",
3642
+ "theme.layout.ts",
3643
+ "bundle-entry.ts",
3644
+ "sections-registry.ts"
3645
+ ],
3646
+ { cwd: themePath, ignoreInitial: true }
3647
+ );
3648
+ let debounceTimer;
3649
+ watcher.on("all", (_event, filePath) => {
3650
+ clearTimeout(debounceTimer);
3651
+ debounceTimer = setTimeout(async () => {
3652
+ logger.info(`File changed: ${filePath}`);
3653
+ try {
3654
+ await context2.rebuild();
3655
+ await generateManifest2(themeName, themePath, outputDir);
3656
+ server.broadcast({ type: "reload", timestamp: Date.now() });
3657
+ logger.success("Rebuilt successfully");
3658
+ } catch (error) {
3659
+ server.broadcast({ type: "error", message: String(error) });
3660
+ logger.error(`Rebuild failed: ${error}`);
3661
+ }
3662
+ }, 150);
3663
+ });
3664
+ logger.newLine();
3665
+ logger.success(`Theme compiled: ${themeName}`);
3666
+ logger.info(`Preview: http://localhost:${port}`);
3667
+ logger.info("Watching for changes...");
3668
+ logger.newLine();
3669
+ if (options.open !== false) {
3670
+ const open = await import('open');
3671
+ open.default(`http://localhost:${port}`);
3672
+ }
3673
+ process.on("SIGINT", async () => {
3674
+ logger.newLine();
3675
+ logger.info("Shutting down...");
3676
+ watcher.close();
3677
+ await context2.dispose();
3678
+ server.close();
3679
+ const shimPath = path__default.default.join(outputDir, ".process-shim.js");
3680
+ try {
3681
+ await fs7__default.default.unlink(shimPath);
3682
+ } catch (e) {
3683
+ }
3684
+ process.exit(0);
3685
+ });
3686
+ }
3687
+
2709
3688
  // src/cli.ts
2710
3689
  try {
2711
3690
  const projectRoot = getProjectRoot();
2712
- dotenv__default.default.config({ path: path__default.default.join(projectRoot, ".env.local"), quiet: true });
3691
+ dotenv__default.default.config({
3692
+ path: path__default.default.join(projectRoot, ".env.local"),
3693
+ quiet: true
3694
+ });
2713
3695
  dotenv__default.default.config({ path: path__default.default.join(projectRoot, ".env"), quiet: true });
2714
3696
  } catch (e) {
2715
3697
  }
2716
- dotenv__default.default.config({ path: path__default.default.join(os__default.default.homedir(), ".onex", ".env"), quiet: true });
3698
+ dotenv__default.default.config({
3699
+ path: path__default.default.join(os__default.default.homedir(), ".onex", ".env"),
3700
+ quiet: true
3701
+ });
2717
3702
  var program = new commander.Command();
2718
3703
  program.name("onex").description("CLI tool for OneX theme development").version("0.1.0");
2719
3704
  program.command("init").description("Create a new OneX theme project").argument("[project-name]", "Name of the project").option(
@@ -2735,6 +3720,7 @@ program.command("create:block").alias("cb").description("Create a new block").ar
2735
3720
  program.command("create:component").alias("cc").description("Create a new component").argument("<name>", "Name of the component (e.g., button, badge)").option("-t, --type <type>", "Component type (ui, layout, form)").action(createComponentCommand);
2736
3721
  program.command("list").description("List available themes, sections, blocks, and components").option("-s, --sections", "List sections only").option("-b, --blocks", "List blocks only").option("-c, --components", "List components only").option("-t, --theme <theme>", "Filter by theme").action(listCommand);
2737
3722
  program.command("validate").description("Validate theme structure and files").option("-t, --theme <theme>", "Theme to validate").option("-f, --fix", "Auto-fix issues if possible (not implemented yet)").action(validateCommand);
3723
+ program.command("dev").description("Start dev server with live preview and hot reload").option("-t, --theme <theme>", "Theme to develop").option("-p, --port <port>", "Dev server port", "3456").option("--no-open", "Don't open browser automatically").action(devCommand);
2738
3724
  program.command("build").description("Build theme for production").option("-t, --theme <theme>", "Theme to build").option("-p, --production", "Production build with optimizations").option("-w, --watch", "Watch mode for development").action(buildCommand);
2739
3725
  program.command("package").description("Compile and package theme as distributable zip file").option("-t, --theme <theme>", "Theme to package").option("-o, --output <dir>", "Output directory for package").option("-n, --name <name>", "Custom package name").option("-m, --minify", "Minify compiled output").option("--skip-build", "Skip compilation step (use existing compiled theme)").action(packageCommand);
2740
3726
  program.command("deploy").description("Upload theme package to API server").option("-t, --theme <theme>", "Theme to deploy (finds latest package)").option("-p, --package <file>", "Specific package file to upload").option("--api-url <url>", "API server URL (default: http://localhost:3001)").option("-k, --api-key <key>", "API key for authentication").option(
@@ -2759,7 +3745,7 @@ program.command("clone").description("Clone theme source code from S3").argument
2759
3745
  "-v, --version <version>",
2760
3746
  "Theme version (default: latest)",
2761
3747
  "latest"
2762
- ).option("-o, --output <dir>", "Output directory").option("-b, --bucket <name>", "S3 bucket name").option(
3748
+ ).option("-n, --name <name>", "New theme name (skips interactive prompt)").option("-o, --output <dir>", "Output directory").option("-b, --bucket <name>", "S3 bucket name").option(
2763
3749
  "-e, --environment <env>",
2764
3750
  "Environment (staging|production)",
2765
3751
  "staging"