@onexapis/cli 1.0.4 → 1.1.1
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 +65 -63
- package/dist/cli.js +1150 -105
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1125 -103
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1018 -237
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +852 -90
- package/dist/index.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +368 -0
- package/package.json +7 -2
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,749 @@ 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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
290
|
+
} catch (e) {
|
|
69
291
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
73
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) {
|
|
420
|
+
}
|
|
421
|
+
logger.error(`esbuild compilation failed: ${error}`);
|
|
422
|
+
return false;
|
|
74
423
|
}
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
502
|
+
if (!previewEntryPath) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
`Preview app source not found. Searched:
|
|
505
|
+
${locations.join("\n")}`
|
|
506
|
+
);
|
|
83
507
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
+
build2.onResolve({ filter: /^lucide-react/ }, async (args) => {
|
|
556
|
+
var _a;
|
|
557
|
+
if ((_a = args.pluginData) == null ? void 0 : _a.skipStub) return void 0;
|
|
558
|
+
try {
|
|
559
|
+
const result = await build2.resolve(args.path, {
|
|
560
|
+
kind: args.kind,
|
|
561
|
+
resolveDir: args.resolveDir,
|
|
562
|
+
importer: args.importer,
|
|
563
|
+
namespace: "file",
|
|
564
|
+
pluginData: { skipStub: true }
|
|
565
|
+
});
|
|
566
|
+
if (!result.errors.length) return result;
|
|
567
|
+
} catch (e) {
|
|
568
|
+
}
|
|
569
|
+
return { path: args.path, namespace: "lucide-stub" };
|
|
570
|
+
});
|
|
571
|
+
build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, () => ({
|
|
572
|
+
// Provide all icon names used by @onexapis/core as no-op SVG stub components
|
|
573
|
+
contents: `
|
|
574
|
+
const icon = (props) => null;
|
|
575
|
+
export { icon as Check, icon as ChevronDown, icon as XCircle, icon as AlertTriangle };
|
|
576
|
+
export { icon as CheckCircle, icon as Info, icon as X, icon as XIcon };
|
|
577
|
+
export { icon as CircleIcon, icon as Star, icon as ShoppingCart };
|
|
578
|
+
export { icon as ChevronRight, icon as ChevronLeft, icon as ChevronUp };
|
|
579
|
+
export { icon as Search, icon as Menu, icon as Heart, icon as User };
|
|
580
|
+
export { icon as Trash2, icon as Plus, icon as Minus, icon as Eye, icon as EyeOff };
|
|
581
|
+
export { icon as ArrowRight, icon as ArrowLeft, icon as ExternalLink, icon as Mail };
|
|
582
|
+
export { icon as Phone, icon as MapPin, icon as Calendar, icon as Clock };
|
|
583
|
+
export { icon as Facebook, icon as Twitter, icon as Instagram, icon as Linkedin, icon as Github };
|
|
584
|
+
export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
|
|
585
|
+
`.trim(),
|
|
586
|
+
loader: "jsx"
|
|
587
|
+
}));
|
|
588
|
+
build2.onResolve({ filter: /^framer-motion/ }, async (args) => {
|
|
589
|
+
var _a;
|
|
590
|
+
if ((_a = args.pluginData) == null ? void 0 : _a.skipStub) return void 0;
|
|
591
|
+
try {
|
|
592
|
+
const result = await build2.resolve(args.path, {
|
|
593
|
+
kind: args.kind,
|
|
594
|
+
resolveDir: args.resolveDir,
|
|
595
|
+
importer: args.importer,
|
|
596
|
+
namespace: "file",
|
|
597
|
+
pluginData: { skipStub: true }
|
|
598
|
+
});
|
|
599
|
+
if (!result.errors.length) return result;
|
|
600
|
+
} catch (e) {
|
|
601
|
+
}
|
|
602
|
+
return { path: args.path, namespace: "motion-stub" };
|
|
603
|
+
});
|
|
604
|
+
build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
|
|
605
|
+
contents: `
|
|
606
|
+
const stub = (props) => props.children || null;
|
|
607
|
+
const handler = { get: (_, name) => name === '__esModule' ? true : stub };
|
|
608
|
+
export const motion = new Proxy({}, handler);
|
|
609
|
+
export const AnimatePresence = stub;
|
|
610
|
+
export default { motion, AnimatePresence };
|
|
611
|
+
`.trim(),
|
|
612
|
+
loader: "jsx"
|
|
613
|
+
}));
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
await esbuild__namespace.build({
|
|
617
|
+
entryPoints: [previewEntryPath],
|
|
618
|
+
bundle: true,
|
|
619
|
+
platform: "browser",
|
|
620
|
+
format: "esm",
|
|
621
|
+
outfile: outputPath,
|
|
622
|
+
// Bundle React + core INTO the output (NOT externalized)
|
|
623
|
+
external: [],
|
|
624
|
+
plugins: [serverStubPlugin],
|
|
625
|
+
minify: false,
|
|
626
|
+
sourcemap: true,
|
|
627
|
+
target: "es2020",
|
|
628
|
+
jsx: "automatic",
|
|
629
|
+
jsxImportSource: "react",
|
|
630
|
+
define: {
|
|
631
|
+
"process.env.NODE_ENV": JSON.stringify("development"),
|
|
632
|
+
global: "globalThis"
|
|
633
|
+
},
|
|
634
|
+
loader: { ".tsx": "tsx", ".ts": "ts" },
|
|
635
|
+
// Force CJS resolution to avoid sideEffects:false dropping ESM chunk imports
|
|
636
|
+
conditions: ["require", "default"],
|
|
637
|
+
mainFields: ["main"],
|
|
638
|
+
logOverride: {
|
|
639
|
+
"ignored-bare-import": "silent"
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
return outputPath;
|
|
643
|
+
}
|
|
644
|
+
var PROCESS_SHIM, reactGlobalPlugin;
|
|
645
|
+
var init_compile_theme = __esm({
|
|
646
|
+
"src/utils/compile-theme.ts"() {
|
|
647
|
+
init_logger();
|
|
648
|
+
PROCESS_SHIM = `
|
|
649
|
+
if (typeof process === "undefined") {
|
|
650
|
+
globalThis.process = {
|
|
651
|
+
env: {},
|
|
652
|
+
browser: true,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
`;
|
|
656
|
+
reactGlobalPlugin = {
|
|
657
|
+
name: "react-global",
|
|
658
|
+
setup(build2) {
|
|
659
|
+
build2.onResolve({ filter: /^react$/ }, () => ({
|
|
660
|
+
path: "react-external",
|
|
661
|
+
namespace: "react-global"
|
|
662
|
+
}));
|
|
663
|
+
build2.onResolve({ filter: /^react-dom$/ }, () => ({
|
|
664
|
+
path: "react-dom-external",
|
|
665
|
+
namespace: "react-global"
|
|
666
|
+
}));
|
|
667
|
+
build2.onResolve({ filter: /^react\/jsx-runtime$/ }, () => ({
|
|
668
|
+
path: "react-jsx-runtime-external",
|
|
669
|
+
namespace: "react-global"
|
|
670
|
+
}));
|
|
671
|
+
build2.onLoad({ filter: /.*/, namespace: "react-global" }, (args) => {
|
|
672
|
+
if (args.path === "react-external") {
|
|
673
|
+
return {
|
|
674
|
+
contents: `
|
|
675
|
+
if (!globalThis.__ONEX_REACT__) {
|
|
676
|
+
throw new Error('[Theme Bundle] React not initialized. Ensure globalThis.__ONEX_REACT__ is set before loading theme.');
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const React = globalThis.__ONEX_REACT__;
|
|
680
|
+
export default React;
|
|
681
|
+
|
|
682
|
+
export const {
|
|
683
|
+
useState,
|
|
684
|
+
useEffect,
|
|
685
|
+
useContext,
|
|
686
|
+
useReducer,
|
|
687
|
+
useCallback,
|
|
688
|
+
useMemo,
|
|
689
|
+
useRef,
|
|
690
|
+
useImperativeHandle,
|
|
691
|
+
useLayoutEffect,
|
|
692
|
+
useDebugValue,
|
|
693
|
+
useDeferredValue,
|
|
694
|
+
useTransition,
|
|
695
|
+
useId,
|
|
696
|
+
useSyncExternalStore,
|
|
697
|
+
useInsertionEffect,
|
|
698
|
+
createContext,
|
|
699
|
+
forwardRef,
|
|
700
|
+
lazy,
|
|
701
|
+
memo,
|
|
702
|
+
startTransition,
|
|
703
|
+
createElement,
|
|
704
|
+
cloneElement,
|
|
705
|
+
isValidElement,
|
|
706
|
+
Children,
|
|
707
|
+
Fragment,
|
|
708
|
+
Profiler,
|
|
709
|
+
StrictMode,
|
|
710
|
+
Suspense,
|
|
711
|
+
Component,
|
|
712
|
+
PureComponent,
|
|
713
|
+
useActionState,
|
|
714
|
+
use,
|
|
715
|
+
} = React;
|
|
716
|
+
`.trim(),
|
|
717
|
+
loader: "js"
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
if (args.path === "react-dom-external") {
|
|
721
|
+
return {
|
|
722
|
+
contents: `
|
|
723
|
+
if (!globalThis.__ONEX_REACT_DOM__) {
|
|
724
|
+
throw new Error('[Theme Bundle] ReactDOM not initialized. Ensure globalThis.__ONEX_REACT_DOM__ is set before loading theme.');
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const ReactDOM = globalThis.__ONEX_REACT_DOM__;
|
|
728
|
+
export default ReactDOM;
|
|
729
|
+
|
|
730
|
+
export const {
|
|
731
|
+
createRoot,
|
|
732
|
+
hydrateRoot,
|
|
733
|
+
flushSync,
|
|
734
|
+
createPortal,
|
|
735
|
+
findDOMNode,
|
|
736
|
+
render,
|
|
737
|
+
hydrate,
|
|
738
|
+
unmountComponentAtNode,
|
|
739
|
+
} = ReactDOM;
|
|
740
|
+
`.trim(),
|
|
741
|
+
loader: "js"
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
if (args.path === "react-jsx-runtime-external") {
|
|
745
|
+
return {
|
|
746
|
+
contents: `
|
|
747
|
+
if (!globalThis.__ONEX_JSX_RUNTIME__) {
|
|
748
|
+
throw new Error('[Theme Bundle] React JSX runtime not initialized. Ensure globalThis.__ONEX_JSX_RUNTIME__ is set before loading theme.');
|
|
749
|
+
}
|
|
750
|
+
const _jsxRuntime = globalThis.__ONEX_JSX_RUNTIME__;
|
|
751
|
+
export const jsx = _jsxRuntime.jsx;
|
|
752
|
+
export const jsxs = _jsxRuntime.jsxs;
|
|
753
|
+
export const Fragment = _jsxRuntime.Fragment;
|
|
754
|
+
`.trim(),
|
|
755
|
+
loader: "js"
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
return null;
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
};
|
|
87
762
|
}
|
|
88
|
-
};
|
|
89
|
-
var logger = new Logger();
|
|
763
|
+
});
|
|
90
764
|
|
|
91
765
|
// src/utils/file-helpers.ts
|
|
766
|
+
init_logger();
|
|
92
767
|
async function renderTemplate(templatePath, data) {
|
|
93
768
|
const template = await fs__default.default.readFile(templatePath, "utf-8");
|
|
94
769
|
return ejs__default.default.render(template, data);
|
|
@@ -174,8 +849,10 @@ function getProjectRoot() {
|
|
|
174
849
|
}
|
|
175
850
|
function getThemesDir() {
|
|
176
851
|
const root = getProjectRoot();
|
|
177
|
-
if (fs__default.default.existsSync(path__default.default.join(root, "themes")))
|
|
178
|
-
|
|
852
|
+
if (fs__default.default.existsSync(path__default.default.join(root, "themes")))
|
|
853
|
+
return path__default.default.join(root, "themes");
|
|
854
|
+
if (fs__default.default.existsSync(path__default.default.join(root, "src/themes")))
|
|
855
|
+
return path__default.default.join(root, "src/themes");
|
|
179
856
|
return path__default.default.dirname(root);
|
|
180
857
|
}
|
|
181
858
|
function getFeaturesDir() {
|
|
@@ -233,6 +910,9 @@ async function installDependencies(projectPath, packageManager = "npm") {
|
|
|
233
910
|
});
|
|
234
911
|
}
|
|
235
912
|
|
|
913
|
+
// src/commands/init.ts
|
|
914
|
+
init_logger();
|
|
915
|
+
|
|
236
916
|
// src/utils/validators.ts
|
|
237
917
|
function validateName(name) {
|
|
238
918
|
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
@@ -674,6 +1354,9 @@ export const homePageConfig: PageConfig = {
|
|
|
674
1354
|
};
|
|
675
1355
|
`;
|
|
676
1356
|
}
|
|
1357
|
+
|
|
1358
|
+
// src/commands/create-section.ts
|
|
1359
|
+
init_logger();
|
|
677
1360
|
async function createSectionCommand(name, options) {
|
|
678
1361
|
logger.header("Create New Section");
|
|
679
1362
|
ensureOneXProject();
|
|
@@ -913,6 +1596,9 @@ export { ${data.sectionName}Schema } from "./${data.sectionName}.schema";
|
|
|
913
1596
|
${hasTemplate ? `export { ${data.sectionNamePascal}Default } from "./${data.sectionName}-default";` : ""}
|
|
914
1597
|
`;
|
|
915
1598
|
}
|
|
1599
|
+
|
|
1600
|
+
// src/commands/create-block.ts
|
|
1601
|
+
init_logger();
|
|
916
1602
|
async function createBlockCommand(name, options) {
|
|
917
1603
|
logger.header("Create New Block");
|
|
918
1604
|
ensureOneXProject();
|
|
@@ -1137,6 +1823,9 @@ export { ${data.blockName}Definition } from "./${data.blockName}.schema";
|
|
|
1137
1823
|
export { ${data.blockNamePascal} } from "./${data.blockName}";
|
|
1138
1824
|
`;
|
|
1139
1825
|
}
|
|
1826
|
+
|
|
1827
|
+
// src/commands/create-component.ts
|
|
1828
|
+
init_logger();
|
|
1140
1829
|
async function createComponentCommand(name, options) {
|
|
1141
1830
|
logger.header("Create New Component");
|
|
1142
1831
|
ensureOneXProject();
|
|
@@ -1341,6 +2030,9 @@ export { ${data.componentName}Definition } from "./${data.componentName}.schema"
|
|
|
1341
2030
|
export { ${data.componentNamePascal} } from "./${data.componentName}";
|
|
1342
2031
|
`;
|
|
1343
2032
|
}
|
|
2033
|
+
|
|
2034
|
+
// src/commands/list.ts
|
|
2035
|
+
init_logger();
|
|
1344
2036
|
async function listCommand(options) {
|
|
1345
2037
|
logger.header("OneX Project Inventory");
|
|
1346
2038
|
ensureOneXProject();
|
|
@@ -1472,6 +2164,9 @@ async function listThemesInfo() {
|
|
|
1472
2164
|
}
|
|
1473
2165
|
logger.newLine();
|
|
1474
2166
|
}
|
|
2167
|
+
|
|
2168
|
+
// src/commands/validate.ts
|
|
2169
|
+
init_logger();
|
|
1475
2170
|
async function validateCommand(options) {
|
|
1476
2171
|
logger.header("Validate Theme");
|
|
1477
2172
|
ensureOneXProject();
|
|
@@ -1484,7 +2179,11 @@ async function validateCommand(options) {
|
|
|
1484
2179
|
}
|
|
1485
2180
|
themeToValidate = options.theme;
|
|
1486
2181
|
} else {
|
|
1487
|
-
const isThemeDir = [
|
|
2182
|
+
const isThemeDir = [
|
|
2183
|
+
"theme.config.ts",
|
|
2184
|
+
"bundle-entry.ts",
|
|
2185
|
+
"manifest.ts"
|
|
2186
|
+
].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
|
|
1488
2187
|
if (isThemeDir) {
|
|
1489
2188
|
themeToValidate = path__default.default.basename(process.cwd());
|
|
1490
2189
|
logger.info(`Validating current theme: ${themeToValidate}`);
|
|
@@ -1497,15 +2196,21 @@ async function validateCommand(options) {
|
|
|
1497
2196
|
}
|
|
1498
2197
|
const themePath = path__default.default.join(getThemesDir(), themeToValidate);
|
|
1499
2198
|
logger.startSpinner("Running validation checks...");
|
|
1500
|
-
const
|
|
1501
|
-
|
|
2199
|
+
const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
|
|
2200
|
+
const foundEntry = entryFiles.find(
|
|
2201
|
+
(f) => fs__default.default.existsSync(path__default.default.join(themePath, f))
|
|
2202
|
+
);
|
|
2203
|
+
if (!foundEntry) {
|
|
1502
2204
|
issues.push({
|
|
1503
2205
|
type: "error",
|
|
1504
|
-
file: "manifest.ts",
|
|
1505
|
-
message: "
|
|
2206
|
+
file: "manifest.ts / theme.config.ts / bundle-entry.ts",
|
|
2207
|
+
message: "No theme entry file found (need at least one of: manifest.ts, theme.config.ts, bundle-entry.ts)"
|
|
1506
2208
|
});
|
|
1507
|
-
} else {
|
|
1508
|
-
const manifestContent = fs__default.default.readFileSync(
|
|
2209
|
+
} else if (foundEntry === "manifest.ts") {
|
|
2210
|
+
const manifestContent = fs__default.default.readFileSync(
|
|
2211
|
+
path__default.default.join(themePath, foundEntry),
|
|
2212
|
+
"utf-8"
|
|
2213
|
+
);
|
|
1509
2214
|
if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
|
|
1510
2215
|
issues.push({
|
|
1511
2216
|
type: "error",
|
|
@@ -1641,7 +2346,11 @@ async function validateCommand(options) {
|
|
|
1641
2346
|
}
|
|
1642
2347
|
}
|
|
1643
2348
|
}
|
|
2349
|
+
|
|
2350
|
+
// src/commands/build.ts
|
|
2351
|
+
init_logger();
|
|
1644
2352
|
async function buildCommand(options) {
|
|
2353
|
+
var _a;
|
|
1645
2354
|
logger.header("Build Theme");
|
|
1646
2355
|
let themePath;
|
|
1647
2356
|
let themeName;
|
|
@@ -1662,7 +2371,11 @@ async function buildCommand(options) {
|
|
|
1662
2371
|
process.exit(1);
|
|
1663
2372
|
}
|
|
1664
2373
|
} else {
|
|
1665
|
-
const isThemeDir = [
|
|
2374
|
+
const isThemeDir = [
|
|
2375
|
+
"theme.config.ts",
|
|
2376
|
+
"bundle-entry.ts",
|
|
2377
|
+
"manifest.ts"
|
|
2378
|
+
].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
|
|
1666
2379
|
if (isThemeDir) {
|
|
1667
2380
|
themePath = process.cwd();
|
|
1668
2381
|
themeName = path__default.default.basename(themePath);
|
|
@@ -1706,11 +2419,20 @@ async function buildCommand(options) {
|
|
|
1706
2419
|
process.exit(1);
|
|
1707
2420
|
}
|
|
1708
2421
|
logger.stopSpinner(true, "Lint passed");
|
|
1709
|
-
const
|
|
2422
|
+
const pkgJson = fs__default.default.readJsonSync(packageJsonPath);
|
|
2423
|
+
const buildScript = ((_a = pkgJson.scripts) == null ? void 0 : _a.build) || "";
|
|
2424
|
+
const isRecursive = buildScript.includes("onex build") || buildScript.includes("onex-cli build");
|
|
1710
2425
|
logger.startSpinner(
|
|
1711
2426
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
1712
2427
|
);
|
|
1713
|
-
|
|
2428
|
+
let buildSuccess;
|
|
2429
|
+
if (isRecursive) {
|
|
2430
|
+
const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
2431
|
+
buildSuccess = await compileStandaloneTheme2(themePath, themeName);
|
|
2432
|
+
} else {
|
|
2433
|
+
const buildArgs = options.watch ? ["build", "--watch"] : ["build"];
|
|
2434
|
+
buildSuccess = await runCommand("pnpm", buildArgs, themePath);
|
|
2435
|
+
}
|
|
1714
2436
|
if (!buildSuccess && !options.watch) {
|
|
1715
2437
|
logger.stopSpinner(false, "Build failed");
|
|
1716
2438
|
process.exit(1);
|
|
@@ -1751,6 +2473,9 @@ function runCommand(command, args, cwd) {
|
|
|
1751
2473
|
});
|
|
1752
2474
|
});
|
|
1753
2475
|
}
|
|
2476
|
+
|
|
2477
|
+
// src/commands/package.ts
|
|
2478
|
+
init_logger();
|
|
1754
2479
|
async function packageCommand(options) {
|
|
1755
2480
|
logger.header("Package Theme");
|
|
1756
2481
|
ensureOneXProject();
|
|
@@ -1764,7 +2489,11 @@ async function packageCommand(options) {
|
|
|
1764
2489
|
process.exit(1);
|
|
1765
2490
|
}
|
|
1766
2491
|
} else {
|
|
1767
|
-
const isThemeDir = [
|
|
2492
|
+
const isThemeDir = [
|
|
2493
|
+
"theme.config.ts",
|
|
2494
|
+
"bundle-entry.ts",
|
|
2495
|
+
"manifest.ts"
|
|
2496
|
+
].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
|
|
1768
2497
|
if (isThemeDir) {
|
|
1769
2498
|
themePath = process.cwd();
|
|
1770
2499
|
themeName = path__default.default.basename(themePath);
|
|
@@ -1788,10 +2517,9 @@ async function packageCommand(options) {
|
|
|
1788
2517
|
logger.newLine();
|
|
1789
2518
|
const compiledThemePath = path__default.default.join(
|
|
1790
2519
|
process.cwd(),
|
|
1791
|
-
"
|
|
1792
|
-
|
|
1793
|
-
"
|
|
1794
|
-
`${themeName}@${version}`
|
|
2520
|
+
"themes",
|
|
2521
|
+
themeName,
|
|
2522
|
+
"dist"
|
|
1795
2523
|
);
|
|
1796
2524
|
if (!options.skipBuild) {
|
|
1797
2525
|
logger.section("Step 1: Compile Theme");
|
|
@@ -1889,6 +2617,9 @@ async function createZipArchive(compiledThemePath, outputPath) {
|
|
|
1889
2617
|
archive.finalize();
|
|
1890
2618
|
});
|
|
1891
2619
|
}
|
|
2620
|
+
|
|
2621
|
+
// src/commands/deploy.ts
|
|
2622
|
+
init_logger();
|
|
1892
2623
|
async function deployCommand(options) {
|
|
1893
2624
|
logger.header("Deploy Theme");
|
|
1894
2625
|
ensureOneXProject();
|
|
@@ -1991,6 +2722,9 @@ async function deployCommand(options) {
|
|
|
1991
2722
|
process.exit(1);
|
|
1992
2723
|
}
|
|
1993
2724
|
}
|
|
2725
|
+
|
|
2726
|
+
// src/commands/upload.ts
|
|
2727
|
+
init_logger();
|
|
1994
2728
|
function getS3Client() {
|
|
1995
2729
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
1996
2730
|
if (adapterMode === "vps") {
|
|
@@ -2030,21 +2764,12 @@ function getBucketName(env) {
|
|
|
2030
2764
|
return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
|
|
2031
2765
|
}
|
|
2032
2766
|
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
|
-
];
|
|
2767
|
+
const searchPaths = [path__default.default.resolve(process.cwd(), "dist")];
|
|
2044
2768
|
for (const dir of searchPaths) {
|
|
2045
2769
|
if (await fs__default.default.pathExists(dir)) {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2770
|
+
const hasManifest = await fs__default.default.pathExists(path__default.default.join(dir, "manifest.json"));
|
|
2771
|
+
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"));
|
|
2772
|
+
if (hasManifest || hasThemeEntry) {
|
|
2048
2773
|
return dir;
|
|
2049
2774
|
}
|
|
2050
2775
|
}
|
|
@@ -2147,13 +2872,8 @@ async function uploadCommand(options) {
|
|
|
2147
2872
|
`Compiled theme not found for ${themeId}@${version}. Run 'onex build' first.`
|
|
2148
2873
|
)
|
|
2149
2874
|
);
|
|
2150
|
-
logger.info(
|
|
2151
|
-
|
|
2152
|
-
`Expected locations:
|
|
2153
|
-
- ./dist/
|
|
2154
|
-
- ../../apps/api-server/compiled-themes/${themeId}@${version}/`
|
|
2155
|
-
)
|
|
2156
|
-
);
|
|
2875
|
+
logger.info(chalk4__default.default.gray(`Expected location:
|
|
2876
|
+
- ./dist/`));
|
|
2157
2877
|
process.exit(1);
|
|
2158
2878
|
}
|
|
2159
2879
|
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
@@ -2260,9 +2980,7 @@ async function uploadCommand(options) {
|
|
|
2260
2980
|
);
|
|
2261
2981
|
console.log(chalk4__default.default.cyan(" Bucket: ") + chalk4__default.default.white(bucket));
|
|
2262
2982
|
console.log(
|
|
2263
|
-
chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(
|
|
2264
|
-
`bundle.zip${sourceUploaded ? " + source.zip" : ""}`
|
|
2265
|
-
)
|
|
2983
|
+
chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(`bundle.zip${sourceUploaded ? " + source.zip" : ""}`)
|
|
2266
2984
|
);
|
|
2267
2985
|
console.log(
|
|
2268
2986
|
chalk4__default.default.cyan(" Path: ") + chalk4__default.default.gray(`s3://${bucket}/themes/${themeId}/${version}/`)
|
|
@@ -2274,6 +2992,9 @@ async function uploadCommand(options) {
|
|
|
2274
2992
|
process.exit(1);
|
|
2275
2993
|
}
|
|
2276
2994
|
}
|
|
2995
|
+
|
|
2996
|
+
// src/commands/download.ts
|
|
2997
|
+
init_logger();
|
|
2277
2998
|
function getS3Client2() {
|
|
2278
2999
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
2279
3000
|
if (adapterMode === "vps") {
|
|
@@ -2421,11 +3142,7 @@ function showDownloadFailureHelp(themeId, bucket) {
|
|
|
2421
3142
|
);
|
|
2422
3143
|
console.log();
|
|
2423
3144
|
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
|
-
);
|
|
3145
|
+
console.log(chalk4__default.default.gray(` aws s3 ls s3://${bucket}/themes/${themeId}/`));
|
|
2429
3146
|
console.log();
|
|
2430
3147
|
}
|
|
2431
3148
|
async function downloadCommand(options) {
|
|
@@ -2501,6 +3218,9 @@ async function downloadCommand(options) {
|
|
|
2501
3218
|
process.exit(1);
|
|
2502
3219
|
}
|
|
2503
3220
|
}
|
|
3221
|
+
|
|
3222
|
+
// src/commands/clone.ts
|
|
3223
|
+
init_logger();
|
|
2504
3224
|
function getS3Client3() {
|
|
2505
3225
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
2506
3226
|
if (adapterMode === "vps") {
|
|
@@ -2604,17 +3324,100 @@ function runInstall(cwd) {
|
|
|
2604
3324
|
proc.on("error", () => resolve(false));
|
|
2605
3325
|
});
|
|
2606
3326
|
}
|
|
3327
|
+
async function promptThemeName(originalName) {
|
|
3328
|
+
const { default: inquirer5 } = await import('inquirer');
|
|
3329
|
+
const { themeName } = await inquirer5.prompt([
|
|
3330
|
+
{
|
|
3331
|
+
type: "input",
|
|
3332
|
+
name: "themeName",
|
|
3333
|
+
message: "New theme name (kebab-case):",
|
|
3334
|
+
default: `my-${originalName}`,
|
|
3335
|
+
validate: (input) => {
|
|
3336
|
+
if (!/^[a-z][a-z0-9-]*$/.test(input)) {
|
|
3337
|
+
return "Theme name must be kebab-case (lowercase letters, numbers, hyphens)";
|
|
3338
|
+
}
|
|
3339
|
+
if (input === originalName) {
|
|
3340
|
+
return `Name must differ from the original theme "${originalName}"`;
|
|
3341
|
+
}
|
|
3342
|
+
return true;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
]);
|
|
3346
|
+
return themeName;
|
|
3347
|
+
}
|
|
3348
|
+
async function renameTheme(themeDir, oldName, newName) {
|
|
3349
|
+
const oldPrefix = `${oldName}-`;
|
|
3350
|
+
const newPrefix = `${newName}-`;
|
|
3351
|
+
const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3352
|
+
const pkgPath = path__default.default.join(themeDir, "package.json");
|
|
3353
|
+
if (await fs__default.default.pathExists(pkgPath)) {
|
|
3354
|
+
const pkg = await fs__default.default.readJson(pkgPath);
|
|
3355
|
+
pkg.name = `@onex-themes/${newName}`;
|
|
3356
|
+
if (pkg.description) {
|
|
3357
|
+
pkg.description = pkg.description.replace(
|
|
3358
|
+
new RegExp(oldName, "gi"),
|
|
3359
|
+
newDisplayName
|
|
3360
|
+
);
|
|
3361
|
+
}
|
|
3362
|
+
pkg.version = "1.0.0";
|
|
3363
|
+
await fs__default.default.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3364
|
+
}
|
|
3365
|
+
const configPath = path__default.default.join(themeDir, "theme.config.ts");
|
|
3366
|
+
if (await fs__default.default.pathExists(configPath)) {
|
|
3367
|
+
let content = await fs__default.default.readFile(configPath, "utf-8");
|
|
3368
|
+
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
3369
|
+
content = content.replace(
|
|
3370
|
+
/name:\s*"[^"]*Theme"/,
|
|
3371
|
+
`name: "${newDisplayName} Theme"`
|
|
3372
|
+
);
|
|
3373
|
+
await fs__default.default.writeFile(configPath, content);
|
|
3374
|
+
}
|
|
3375
|
+
const layoutPath = path__default.default.join(themeDir, "theme.layout.ts");
|
|
3376
|
+
if (await fs__default.default.pathExists(layoutPath)) {
|
|
3377
|
+
let content = await fs__default.default.readFile(layoutPath, "utf-8");
|
|
3378
|
+
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
3379
|
+
content = content.replace(
|
|
3380
|
+
/name:\s*"[^"]*Theme"/,
|
|
3381
|
+
`name: "${newDisplayName} Theme"`
|
|
3382
|
+
);
|
|
3383
|
+
await fs__default.default.writeFile(layoutPath, content);
|
|
3384
|
+
}
|
|
3385
|
+
const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3386
|
+
const tsFiles = await glob.glob("**/*.ts", { cwd: themeDir, nodir: true });
|
|
3387
|
+
for (const file of tsFiles) {
|
|
3388
|
+
const filePath = path__default.default.join(themeDir, file);
|
|
3389
|
+
let content = await fs__default.default.readFile(filePath, "utf-8");
|
|
3390
|
+
const original = content;
|
|
3391
|
+
content = content.replace(
|
|
3392
|
+
new RegExp(`"${oldPrefix}`, "g"),
|
|
3393
|
+
`"${newPrefix}`
|
|
3394
|
+
);
|
|
3395
|
+
content = content.replace(
|
|
3396
|
+
new RegExp(`themeId:\\s*"${oldName}"`, "g"),
|
|
3397
|
+
`themeId: "${newName}"`
|
|
3398
|
+
);
|
|
3399
|
+
content = content.replace(
|
|
3400
|
+
new RegExp(`${oldDisplayName} Theme`, "g"),
|
|
3401
|
+
`${newDisplayName} Theme`
|
|
3402
|
+
);
|
|
3403
|
+
if (content !== original) {
|
|
3404
|
+
await fs__default.default.writeFile(filePath, content);
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
2607
3408
|
async function cloneCommand(themeName, options) {
|
|
2608
3409
|
logger.header("Clone Theme Source");
|
|
3410
|
+
let newName = options.name;
|
|
3411
|
+
if (!newName) {
|
|
3412
|
+
newName = await promptThemeName(themeName);
|
|
3413
|
+
}
|
|
2609
3414
|
const spinner = ora__default.default("Initializing clone...").start();
|
|
2610
3415
|
try {
|
|
2611
3416
|
const bucket = options.bucket || getBucketName3(options.environment);
|
|
2612
|
-
const outputDir = options.output || path__default.default.resolve(process.cwd(),
|
|
3417
|
+
const outputDir = options.output || path__default.default.resolve(process.cwd(), newName);
|
|
2613
3418
|
const s3Client = getS3Client3();
|
|
2614
3419
|
if (await fs__default.default.pathExists(outputDir)) {
|
|
2615
|
-
spinner.fail(
|
|
2616
|
-
chalk4__default.default.red(`Directory already exists: ${outputDir}`)
|
|
2617
|
-
);
|
|
3420
|
+
spinner.fail(chalk4__default.default.red(`Directory already exists: ${outputDir}`));
|
|
2618
3421
|
logger.info(
|
|
2619
3422
|
chalk4__default.default.gray(
|
|
2620
3423
|
"Use -o to specify a different output directory, or remove the existing directory."
|
|
@@ -2628,9 +3431,7 @@ async function cloneCommand(themeName, options) {
|
|
|
2628
3431
|
version = await resolveLatestVersion2(s3Client, bucket, themeName);
|
|
2629
3432
|
spinner.succeed(`Resolved latest version: ${chalk4__default.default.cyan(version)}`);
|
|
2630
3433
|
}
|
|
2631
|
-
spinner.start(
|
|
2632
|
-
`Downloading source.zip for ${themeName}@${version}...`
|
|
2633
|
-
);
|
|
3434
|
+
spinner.start(`Downloading source.zip for ${themeName}@${version}...`);
|
|
2634
3435
|
const s3Key = `themes/${themeName}/${version}/source.zip`;
|
|
2635
3436
|
let zipBuffer;
|
|
2636
3437
|
try {
|
|
@@ -2648,9 +3449,7 @@ async function cloneCommand(themeName, options) {
|
|
|
2648
3449
|
chalk4__default.default.yellow("The theme source may not have been uploaded yet.")
|
|
2649
3450
|
);
|
|
2650
3451
|
console.log(
|
|
2651
|
-
chalk4__default.default.gray(
|
|
2652
|
-
`Upload source with: onex upload --theme ${themeName}`
|
|
2653
|
-
)
|
|
3452
|
+
chalk4__default.default.gray(`Upload source with: onex upload --theme ${themeName}`)
|
|
2654
3453
|
);
|
|
2655
3454
|
console.log();
|
|
2656
3455
|
process.exit(1);
|
|
@@ -2663,6 +3462,13 @@ async function cloneCommand(themeName, options) {
|
|
|
2663
3462
|
zip.extractAllTo(outputDir, true);
|
|
2664
3463
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
2665
3464
|
spinner.succeed(`Extracted ${entries.length} files`);
|
|
3465
|
+
spinner.start(
|
|
3466
|
+
`Renaming theme: ${chalk4__default.default.gray(themeName)} \u2192 ${chalk4__default.default.cyan(newName)}...`
|
|
3467
|
+
);
|
|
3468
|
+
await renameTheme(outputDir, themeName, newName);
|
|
3469
|
+
spinner.succeed(
|
|
3470
|
+
`Renamed theme: ${chalk4__default.default.gray(themeName)} \u2192 ${chalk4__default.default.cyan(newName)}`
|
|
3471
|
+
);
|
|
2666
3472
|
if (options.install !== false) {
|
|
2667
3473
|
const hasPkgJson = await fs__default.default.pathExists(
|
|
2668
3474
|
path__default.default.join(outputDir, "package.json")
|
|
@@ -2685,15 +3491,14 @@ async function cloneCommand(themeName, options) {
|
|
|
2685
3491
|
logger.success(chalk4__default.default.green.bold("Theme cloned successfully!"));
|
|
2686
3492
|
console.log();
|
|
2687
3493
|
console.log(
|
|
2688
|
-
chalk4__default.default.cyan("
|
|
3494
|
+
chalk4__default.default.cyan(" Source: ") + chalk4__default.default.gray(`${themeName}@${version}`)
|
|
2689
3495
|
);
|
|
3496
|
+
console.log(chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(newName));
|
|
2690
3497
|
console.log(chalk4__default.default.cyan(" Location: ") + chalk4__default.default.white(outputDir));
|
|
2691
3498
|
console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
|
|
2692
3499
|
console.log();
|
|
2693
3500
|
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
|
-
);
|
|
3501
|
+
console.log(chalk4__default.default.gray(` cd ${path__default.default.relative(process.cwd(), outputDir)}`));
|
|
2697
3502
|
if (options.install === false) {
|
|
2698
3503
|
console.log(chalk4__default.default.gray(" pnpm install"));
|
|
2699
3504
|
}
|
|
@@ -2706,14 +3511,253 @@ async function cloneCommand(themeName, options) {
|
|
|
2706
3511
|
}
|
|
2707
3512
|
}
|
|
2708
3513
|
|
|
3514
|
+
// src/commands/dev.ts
|
|
3515
|
+
init_logger();
|
|
3516
|
+
init_compile_theme();
|
|
3517
|
+
var MIME_TYPES = {
|
|
3518
|
+
".js": "application/javascript",
|
|
3519
|
+
".mjs": "application/javascript",
|
|
3520
|
+
".css": "text/css",
|
|
3521
|
+
".json": "application/json",
|
|
3522
|
+
".html": "text/html",
|
|
3523
|
+
".svg": "image/svg+xml",
|
|
3524
|
+
".png": "image/png",
|
|
3525
|
+
".jpg": "image/jpeg",
|
|
3526
|
+
".jpeg": "image/jpeg",
|
|
3527
|
+
".webp": "image/webp",
|
|
3528
|
+
".gif": "image/gif",
|
|
3529
|
+
".map": "application/json"
|
|
3530
|
+
};
|
|
3531
|
+
function createDevServer(options) {
|
|
3532
|
+
const clients = /* @__PURE__ */ new Set();
|
|
3533
|
+
const server = http__default.default.createServer((req, res) => {
|
|
3534
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
3535
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
3536
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
3537
|
+
if (req.method === "OPTIONS") {
|
|
3538
|
+
res.writeHead(200);
|
|
3539
|
+
res.end();
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
const url = new URL(req.url || "/", `http://localhost:${options.port}`);
|
|
3543
|
+
const pathname = url.pathname;
|
|
3544
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
3545
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3546
|
+
res.end(generatePreviewHTML(options.themeName));
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
if (pathname === "/preview-runtime.js") {
|
|
3550
|
+
serveFile(res, options.previewRuntimePath);
|
|
3551
|
+
return;
|
|
3552
|
+
}
|
|
3553
|
+
const filePath = path__default.default.join(options.distDir, pathname);
|
|
3554
|
+
if (!filePath.startsWith(options.distDir)) {
|
|
3555
|
+
res.writeHead(403);
|
|
3556
|
+
res.end("Forbidden");
|
|
3557
|
+
return;
|
|
3558
|
+
}
|
|
3559
|
+
serveFile(res, filePath);
|
|
3560
|
+
});
|
|
3561
|
+
const wss = new ws.WebSocketServer({ server });
|
|
3562
|
+
wss.on("connection", (ws) => {
|
|
3563
|
+
clients.add(ws);
|
|
3564
|
+
ws.on("close", () => clients.delete(ws));
|
|
3565
|
+
});
|
|
3566
|
+
server.listen(options.port);
|
|
3567
|
+
return {
|
|
3568
|
+
broadcast(message) {
|
|
3569
|
+
const data = JSON.stringify(message);
|
|
3570
|
+
for (const client of clients) {
|
|
3571
|
+
if (client.readyState === ws.WebSocket.OPEN) {
|
|
3572
|
+
client.send(data);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
},
|
|
3576
|
+
close() {
|
|
3577
|
+
wss.close();
|
|
3578
|
+
server.close();
|
|
3579
|
+
}
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
function serveFile(res, filePath) {
|
|
3583
|
+
try {
|
|
3584
|
+
if (!fs2__default.default.existsSync(filePath)) {
|
|
3585
|
+
res.writeHead(404);
|
|
3586
|
+
res.end("Not Found");
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
const ext = path__default.default.extname(filePath);
|
|
3590
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
3591
|
+
const content = fs2__default.default.readFileSync(filePath);
|
|
3592
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
3593
|
+
res.end(content);
|
|
3594
|
+
} catch (e) {
|
|
3595
|
+
res.writeHead(500);
|
|
3596
|
+
res.end("Internal Server Error");
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
function generatePreviewHTML(themeName, port) {
|
|
3600
|
+
return `<!DOCTYPE html>
|
|
3601
|
+
<html lang="en">
|
|
3602
|
+
<head>
|
|
3603
|
+
<meta charset="UTF-8">
|
|
3604
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3605
|
+
<title>OneX Dev \u2014 ${themeName}</title>
|
|
3606
|
+
<!-- Tailwind CSS Play CDN \u2014 JIT compilation in browser for dev preview -->
|
|
3607
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
3608
|
+
<style>
|
|
3609
|
+
#onex-dev-toolbar {
|
|
3610
|
+
position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
|
|
3611
|
+
height: 40px; background: #1a1a2e; color: #e0e0e0;
|
|
3612
|
+
display: flex; align-items: center; padding: 0 16px; gap: 16px;
|
|
3613
|
+
font-family: system-ui; font-size: 13px;
|
|
3614
|
+
border-bottom: 2px solid #16213e;
|
|
3615
|
+
}
|
|
3616
|
+
#onex-dev-toolbar .status { width: 8px; height: 8px; border-radius: 50%; }
|
|
3617
|
+
#onex-dev-toolbar .status.connected { background: #00ff88; }
|
|
3618
|
+
#onex-dev-toolbar .status.disconnected { background: #ff4444; }
|
|
3619
|
+
#onex-dev-toolbar .status.rebuilding { background: #ffaa00; animation: pulse 0.5s infinite; }
|
|
3620
|
+
@keyframes pulse { 50% { opacity: 0.5; } }
|
|
3621
|
+
#onex-preview-root { margin-top: 40px; }
|
|
3622
|
+
</style>
|
|
3623
|
+
</head>
|
|
3624
|
+
<body>
|
|
3625
|
+
<div id="onex-dev-toolbar">
|
|
3626
|
+
<span style="font-weight:600;">OneX Dev</span>
|
|
3627
|
+
<span style="color:#888;">|</span>
|
|
3628
|
+
<span>${themeName}</span>
|
|
3629
|
+
<span style="color:#888;">|</span>
|
|
3630
|
+
<span id="page-indicator">Home</span>
|
|
3631
|
+
<span style="color:#888;">|</span>
|
|
3632
|
+
<span class="status connected" id="ws-status"></span>
|
|
3633
|
+
<span id="ws-label">Connected</span>
|
|
3634
|
+
</div>
|
|
3635
|
+
<div id="onex-preview-root"></div>
|
|
3636
|
+
<script type="module" src="/preview-runtime.js"></script>
|
|
3637
|
+
</body>
|
|
3638
|
+
</html>`;
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
// src/commands/dev.ts
|
|
3642
|
+
async function devCommand(options) {
|
|
3643
|
+
logger.header("OneX Dev Server");
|
|
3644
|
+
let themePath;
|
|
3645
|
+
let themeName;
|
|
3646
|
+
if (options.theme) {
|
|
3647
|
+
themeName = options.theme;
|
|
3648
|
+
try {
|
|
3649
|
+
const workspaceThemePath = path__default.default.join(getThemesDir(), themeName);
|
|
3650
|
+
if (fs__default.default.existsSync(workspaceThemePath)) {
|
|
3651
|
+
themePath = workspaceThemePath;
|
|
3652
|
+
} else {
|
|
3653
|
+
themePath = path__default.default.join(process.cwd(), themeName);
|
|
3654
|
+
}
|
|
3655
|
+
} catch (e) {
|
|
3656
|
+
themePath = path__default.default.join(process.cwd(), themeName);
|
|
3657
|
+
}
|
|
3658
|
+
if (!fs__default.default.existsSync(themePath)) {
|
|
3659
|
+
logger.error(`Theme "${themeName}" not found.`);
|
|
3660
|
+
process.exit(1);
|
|
3661
|
+
}
|
|
3662
|
+
} else {
|
|
3663
|
+
const isThemeDir = [
|
|
3664
|
+
"theme.config.ts",
|
|
3665
|
+
"bundle-entry.ts",
|
|
3666
|
+
"manifest.ts"
|
|
3667
|
+
].some((f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f)));
|
|
3668
|
+
if (isThemeDir) {
|
|
3669
|
+
themePath = process.cwd();
|
|
3670
|
+
themeName = path__default.default.basename(themePath);
|
|
3671
|
+
} else {
|
|
3672
|
+
logger.error(
|
|
3673
|
+
"Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
|
|
3674
|
+
);
|
|
3675
|
+
process.exit(1);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
logger.startSpinner("Compiling preview runtime...");
|
|
3679
|
+
const previewRuntimePath = await compilePreviewRuntime(themePath);
|
|
3680
|
+
logger.stopSpinner(true, "Preview runtime compiled");
|
|
3681
|
+
logger.startSpinner("Compiling theme...");
|
|
3682
|
+
const { context: context2, outputDir } = await compileStandaloneThemeDev(
|
|
3683
|
+
themePath,
|
|
3684
|
+
themeName
|
|
3685
|
+
);
|
|
3686
|
+
logger.stopSpinner(true, "Theme compiled");
|
|
3687
|
+
const port = Number(options.port) || 3456;
|
|
3688
|
+
const server = createDevServer({
|
|
3689
|
+
port,
|
|
3690
|
+
distDir: outputDir,
|
|
3691
|
+
previewRuntimePath,
|
|
3692
|
+
themeName
|
|
3693
|
+
});
|
|
3694
|
+
const watcher = chokidar__default.default.watch(
|
|
3695
|
+
[
|
|
3696
|
+
"sections",
|
|
3697
|
+
"blocks",
|
|
3698
|
+
"components",
|
|
3699
|
+
"pages",
|
|
3700
|
+
"theme.config.ts",
|
|
3701
|
+
"theme.layout.ts",
|
|
3702
|
+
"bundle-entry.ts",
|
|
3703
|
+
"sections-registry.ts"
|
|
3704
|
+
],
|
|
3705
|
+
{ cwd: themePath, ignoreInitial: true }
|
|
3706
|
+
);
|
|
3707
|
+
let debounceTimer;
|
|
3708
|
+
watcher.on("all", (_event, filePath) => {
|
|
3709
|
+
clearTimeout(debounceTimer);
|
|
3710
|
+
debounceTimer = setTimeout(async () => {
|
|
3711
|
+
logger.info(`File changed: ${filePath}`);
|
|
3712
|
+
try {
|
|
3713
|
+
await context2.rebuild();
|
|
3714
|
+
await generateManifest2(themeName, themePath, outputDir);
|
|
3715
|
+
server.broadcast({ type: "reload", timestamp: Date.now() });
|
|
3716
|
+
logger.success("Rebuilt successfully");
|
|
3717
|
+
} catch (error) {
|
|
3718
|
+
server.broadcast({ type: "error", message: String(error) });
|
|
3719
|
+
logger.error(`Rebuild failed: ${error}`);
|
|
3720
|
+
}
|
|
3721
|
+
}, 150);
|
|
3722
|
+
});
|
|
3723
|
+
logger.newLine();
|
|
3724
|
+
logger.success(`Theme compiled: ${themeName}`);
|
|
3725
|
+
logger.info(`Preview: http://localhost:${port}`);
|
|
3726
|
+
logger.info("Watching for changes...");
|
|
3727
|
+
logger.newLine();
|
|
3728
|
+
if (options.open !== false) {
|
|
3729
|
+
const open = await import('open');
|
|
3730
|
+
open.default(`http://localhost:${port}`);
|
|
3731
|
+
}
|
|
3732
|
+
process.on("SIGINT", async () => {
|
|
3733
|
+
logger.newLine();
|
|
3734
|
+
logger.info("Shutting down...");
|
|
3735
|
+
watcher.close();
|
|
3736
|
+
await context2.dispose();
|
|
3737
|
+
server.close();
|
|
3738
|
+
const shimPath = path__default.default.join(outputDir, ".process-shim.js");
|
|
3739
|
+
try {
|
|
3740
|
+
await fs7__default.default.unlink(shimPath);
|
|
3741
|
+
} catch (e) {
|
|
3742
|
+
}
|
|
3743
|
+
process.exit(0);
|
|
3744
|
+
});
|
|
3745
|
+
}
|
|
3746
|
+
|
|
2709
3747
|
// src/cli.ts
|
|
2710
3748
|
try {
|
|
2711
3749
|
const projectRoot = getProjectRoot();
|
|
2712
|
-
dotenv__default.default.config({
|
|
3750
|
+
dotenv__default.default.config({
|
|
3751
|
+
path: path__default.default.join(projectRoot, ".env.local"),
|
|
3752
|
+
quiet: true
|
|
3753
|
+
});
|
|
2713
3754
|
dotenv__default.default.config({ path: path__default.default.join(projectRoot, ".env"), quiet: true });
|
|
2714
3755
|
} catch (e) {
|
|
2715
3756
|
}
|
|
2716
|
-
dotenv__default.default.config({
|
|
3757
|
+
dotenv__default.default.config({
|
|
3758
|
+
path: path__default.default.join(os__default.default.homedir(), ".onex", ".env"),
|
|
3759
|
+
quiet: true
|
|
3760
|
+
});
|
|
2717
3761
|
var program = new commander.Command();
|
|
2718
3762
|
program.name("onex").description("CLI tool for OneX theme development").version("0.1.0");
|
|
2719
3763
|
program.command("init").description("Create a new OneX theme project").argument("[project-name]", "Name of the project").option(
|
|
@@ -2735,6 +3779,7 @@ program.command("create:block").alias("cb").description("Create a new block").ar
|
|
|
2735
3779
|
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
3780
|
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
3781
|
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);
|
|
3782
|
+
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
3783
|
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
3784
|
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
3785
|
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 +3804,7 @@ program.command("clone").description("Clone theme source code from S3").argument
|
|
|
2759
3804
|
"-v, --version <version>",
|
|
2760
3805
|
"Theme version (default: latest)",
|
|
2761
3806
|
"latest"
|
|
2762
|
-
).option("-o, --output <dir>", "Output directory").option("-b, --bucket <name>", "S3 bucket name").option(
|
|
3807
|
+
).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
3808
|
"-e, --environment <env>",
|
|
2764
3809
|
"Environment (staging|production)",
|
|
2765
3810
|
"staging"
|