@onexapis/cli 1.0.3 → 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/README.md +65 -63
- package/dist/cli.js +1117 -114
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1092 -112
- 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 +978 -242
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +812 -95
- 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,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
|
|
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
|
+
}
|
|
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,18 +790,18 @@ function getProjectRoot() {
|
|
|
174
790
|
}
|
|
175
791
|
function getThemesDir() {
|
|
176
792
|
const root = getProjectRoot();
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return path__default.default.
|
|
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");
|
|
797
|
+
return path__default.default.dirname(root);
|
|
182
798
|
}
|
|
183
799
|
function getFeaturesDir() {
|
|
184
800
|
return path__default.default.join(getProjectRoot(), "src/features");
|
|
185
801
|
}
|
|
186
802
|
function isOneXProject() {
|
|
187
803
|
const root = getProjectRoot();
|
|
188
|
-
return fs__default.default.existsSync(path__default.default.join(root, "themes")) || fs__default.default.existsSync(path__default.default.join(root, "src/themes"));
|
|
804
|
+
return fs__default.default.existsSync(path__default.default.join(root, "themes")) || fs__default.default.existsSync(path__default.default.join(root, "src/themes")) || fs__default.default.existsSync(path__default.default.join(root, "theme.config.ts")) || fs__default.default.existsSync(path__default.default.join(root, "bundle-entry.ts"));
|
|
189
805
|
}
|
|
190
806
|
function ensureOneXProject() {
|
|
191
807
|
if (!isOneXProject()) {
|
|
@@ -235,6 +851,9 @@ async function installDependencies(projectPath, packageManager = "npm") {
|
|
|
235
851
|
});
|
|
236
852
|
}
|
|
237
853
|
|
|
854
|
+
// src/commands/init.ts
|
|
855
|
+
init_logger();
|
|
856
|
+
|
|
238
857
|
// src/utils/validators.ts
|
|
239
858
|
function validateName(name) {
|
|
240
859
|
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
@@ -676,9 +1295,20 @@ export const homePageConfig: PageConfig = {
|
|
|
676
1295
|
};
|
|
677
1296
|
`;
|
|
678
1297
|
}
|
|
1298
|
+
|
|
1299
|
+
// src/commands/create-section.ts
|
|
1300
|
+
init_logger();
|
|
679
1301
|
async function createSectionCommand(name, options) {
|
|
680
1302
|
logger.header("Create New Section");
|
|
681
1303
|
ensureOneXProject();
|
|
1304
|
+
if (!options.theme) {
|
|
1305
|
+
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
1306
|
+
(f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f))
|
|
1307
|
+
);
|
|
1308
|
+
if (isStandaloneTheme) {
|
|
1309
|
+
options.theme = path__default.default.basename(process.cwd());
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
682
1312
|
const sectionName = toKebabCase(name);
|
|
683
1313
|
if (!validateName(sectionName)) {
|
|
684
1314
|
logger.error(
|
|
@@ -907,9 +1537,20 @@ export { ${data.sectionName}Schema } from "./${data.sectionName}.schema";
|
|
|
907
1537
|
${hasTemplate ? `export { ${data.sectionNamePascal}Default } from "./${data.sectionName}-default";` : ""}
|
|
908
1538
|
`;
|
|
909
1539
|
}
|
|
1540
|
+
|
|
1541
|
+
// src/commands/create-block.ts
|
|
1542
|
+
init_logger();
|
|
910
1543
|
async function createBlockCommand(name, options) {
|
|
911
1544
|
logger.header("Create New Block");
|
|
912
1545
|
ensureOneXProject();
|
|
1546
|
+
if (!options.theme) {
|
|
1547
|
+
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
1548
|
+
(f) => fs__default.default.existsSync(path__default.default.join(process.cwd(), f))
|
|
1549
|
+
);
|
|
1550
|
+
if (isStandaloneTheme) {
|
|
1551
|
+
options.theme = path__default.default.basename(process.cwd());
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
913
1554
|
const blockName = toKebabCase(name);
|
|
914
1555
|
if (!validateName(blockName)) {
|
|
915
1556
|
logger.error(
|
|
@@ -1123,6 +1764,9 @@ export { ${data.blockName}Definition } from "./${data.blockName}.schema";
|
|
|
1123
1764
|
export { ${data.blockNamePascal} } from "./${data.blockName}";
|
|
1124
1765
|
`;
|
|
1125
1766
|
}
|
|
1767
|
+
|
|
1768
|
+
// src/commands/create-component.ts
|
|
1769
|
+
init_logger();
|
|
1126
1770
|
async function createComponentCommand(name, options) {
|
|
1127
1771
|
logger.header("Create New Component");
|
|
1128
1772
|
ensureOneXProject();
|
|
@@ -1327,6 +1971,9 @@ export { ${data.componentName}Definition } from "./${data.componentName}.schema"
|
|
|
1327
1971
|
export { ${data.componentNamePascal} } from "./${data.componentName}";
|
|
1328
1972
|
`;
|
|
1329
1973
|
}
|
|
1974
|
+
|
|
1975
|
+
// src/commands/list.ts
|
|
1976
|
+
init_logger();
|
|
1330
1977
|
async function listCommand(options) {
|
|
1331
1978
|
logger.header("OneX Project Inventory");
|
|
1332
1979
|
ensureOneXProject();
|
|
@@ -1458,6 +2105,9 @@ async function listThemesInfo() {
|
|
|
1458
2105
|
}
|
|
1459
2106
|
logger.newLine();
|
|
1460
2107
|
}
|
|
2108
|
+
|
|
2109
|
+
// src/commands/validate.ts
|
|
2110
|
+
init_logger();
|
|
1461
2111
|
async function validateCommand(options) {
|
|
1462
2112
|
logger.header("Validate Theme");
|
|
1463
2113
|
ensureOneXProject();
|
|
@@ -1470,8 +2120,12 @@ async function validateCommand(options) {
|
|
|
1470
2120
|
}
|
|
1471
2121
|
themeToValidate = options.theme;
|
|
1472
2122
|
} else {
|
|
1473
|
-
const
|
|
1474
|
-
|
|
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)));
|
|
2128
|
+
if (isThemeDir) {
|
|
1475
2129
|
themeToValidate = path__default.default.basename(process.cwd());
|
|
1476
2130
|
logger.info(`Validating current theme: ${themeToValidate}`);
|
|
1477
2131
|
} else {
|
|
@@ -1483,15 +2137,21 @@ async function validateCommand(options) {
|
|
|
1483
2137
|
}
|
|
1484
2138
|
const themePath = path__default.default.join(getThemesDir(), themeToValidate);
|
|
1485
2139
|
logger.startSpinner("Running validation checks...");
|
|
1486
|
-
const
|
|
1487
|
-
|
|
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) {
|
|
1488
2145
|
issues.push({
|
|
1489
2146
|
type: "error",
|
|
1490
|
-
file: "manifest.ts",
|
|
1491
|
-
message: "
|
|
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)"
|
|
1492
2149
|
});
|
|
1493
|
-
} else {
|
|
1494
|
-
const manifestContent = fs__default.default.readFileSync(
|
|
2150
|
+
} else if (foundEntry === "manifest.ts") {
|
|
2151
|
+
const manifestContent = fs__default.default.readFileSync(
|
|
2152
|
+
path__default.default.join(themePath, foundEntry),
|
|
2153
|
+
"utf-8"
|
|
2154
|
+
);
|
|
1495
2155
|
if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
|
|
1496
2156
|
issues.push({
|
|
1497
2157
|
type: "error",
|
|
@@ -1627,7 +2287,11 @@ async function validateCommand(options) {
|
|
|
1627
2287
|
}
|
|
1628
2288
|
}
|
|
1629
2289
|
}
|
|
2290
|
+
|
|
2291
|
+
// src/commands/build.ts
|
|
2292
|
+
init_logger();
|
|
1630
2293
|
async function buildCommand(options) {
|
|
2294
|
+
var _a;
|
|
1631
2295
|
logger.header("Build Theme");
|
|
1632
2296
|
let themePath;
|
|
1633
2297
|
let themeName;
|
|
@@ -1648,8 +2312,12 @@ async function buildCommand(options) {
|
|
|
1648
2312
|
process.exit(1);
|
|
1649
2313
|
}
|
|
1650
2314
|
} else {
|
|
1651
|
-
const
|
|
1652
|
-
|
|
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)));
|
|
2320
|
+
if (isThemeDir) {
|
|
1653
2321
|
themePath = process.cwd();
|
|
1654
2322
|
themeName = path__default.default.basename(themePath);
|
|
1655
2323
|
logger.info(`Building current theme: ${themeName}`);
|
|
@@ -1692,11 +2360,20 @@ async function buildCommand(options) {
|
|
|
1692
2360
|
process.exit(1);
|
|
1693
2361
|
}
|
|
1694
2362
|
logger.stopSpinner(true, "Lint passed");
|
|
1695
|
-
const
|
|
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");
|
|
1696
2366
|
logger.startSpinner(
|
|
1697
2367
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
1698
2368
|
);
|
|
1699
|
-
|
|
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
|
+
}
|
|
1700
2377
|
if (!buildSuccess && !options.watch) {
|
|
1701
2378
|
logger.stopSpinner(false, "Build failed");
|
|
1702
2379
|
process.exit(1);
|
|
@@ -1737,6 +2414,9 @@ function runCommand(command, args, cwd) {
|
|
|
1737
2414
|
});
|
|
1738
2415
|
});
|
|
1739
2416
|
}
|
|
2417
|
+
|
|
2418
|
+
// src/commands/package.ts
|
|
2419
|
+
init_logger();
|
|
1740
2420
|
async function packageCommand(options) {
|
|
1741
2421
|
logger.header("Package Theme");
|
|
1742
2422
|
ensureOneXProject();
|
|
@@ -1750,8 +2430,12 @@ async function packageCommand(options) {
|
|
|
1750
2430
|
process.exit(1);
|
|
1751
2431
|
}
|
|
1752
2432
|
} else {
|
|
1753
|
-
const
|
|
1754
|
-
|
|
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)));
|
|
2438
|
+
if (isThemeDir) {
|
|
1755
2439
|
themePath = process.cwd();
|
|
1756
2440
|
themeName = path__default.default.basename(themePath);
|
|
1757
2441
|
logger.info(`Packaging current theme: ${themeName}`);
|
|
@@ -1774,10 +2458,9 @@ async function packageCommand(options) {
|
|
|
1774
2458
|
logger.newLine();
|
|
1775
2459
|
const compiledThemePath = path__default.default.join(
|
|
1776
2460
|
process.cwd(),
|
|
1777
|
-
"
|
|
1778
|
-
|
|
1779
|
-
"
|
|
1780
|
-
`${themeName}@${version}`
|
|
2461
|
+
"themes",
|
|
2462
|
+
themeName,
|
|
2463
|
+
"dist"
|
|
1781
2464
|
);
|
|
1782
2465
|
if (!options.skipBuild) {
|
|
1783
2466
|
logger.section("Step 1: Compile Theme");
|
|
@@ -1875,6 +2558,9 @@ async function createZipArchive(compiledThemePath, outputPath) {
|
|
|
1875
2558
|
archive.finalize();
|
|
1876
2559
|
});
|
|
1877
2560
|
}
|
|
2561
|
+
|
|
2562
|
+
// src/commands/deploy.ts
|
|
2563
|
+
init_logger();
|
|
1878
2564
|
async function deployCommand(options) {
|
|
1879
2565
|
logger.header("Deploy Theme");
|
|
1880
2566
|
ensureOneXProject();
|
|
@@ -1977,6 +2663,9 @@ async function deployCommand(options) {
|
|
|
1977
2663
|
process.exit(1);
|
|
1978
2664
|
}
|
|
1979
2665
|
}
|
|
2666
|
+
|
|
2667
|
+
// src/commands/upload.ts
|
|
2668
|
+
init_logger();
|
|
1980
2669
|
function getS3Client() {
|
|
1981
2670
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
1982
2671
|
if (adapterMode === "vps") {
|
|
@@ -2016,21 +2705,12 @@ function getBucketName(env) {
|
|
|
2016
2705
|
return environment === "production" ? "onex-themes-prod" : "onex-themes-staging";
|
|
2017
2706
|
}
|
|
2018
2707
|
async function findCompiledThemeDir(themeId, version) {
|
|
2019
|
-
const searchPaths = [
|
|
2020
|
-
path__default.default.resolve(process.cwd(), "dist"),
|
|
2021
|
-
path__default.default.resolve(
|
|
2022
|
-
process.cwd(),
|
|
2023
|
-
`../../apps/api-server/compiled-themes/${themeId}@${version}`
|
|
2024
|
-
),
|
|
2025
|
-
path__default.default.resolve(
|
|
2026
|
-
process.cwd(),
|
|
2027
|
-
`../api-server/compiled-themes/${themeId}@${version}`
|
|
2028
|
-
)
|
|
2029
|
-
];
|
|
2708
|
+
const searchPaths = [path__default.default.resolve(process.cwd(), "dist")];
|
|
2030
2709
|
for (const dir of searchPaths) {
|
|
2031
2710
|
if (await fs__default.default.pathExists(dir)) {
|
|
2032
|
-
const
|
|
2033
|
-
|
|
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) {
|
|
2034
2714
|
return dir;
|
|
2035
2715
|
}
|
|
2036
2716
|
}
|
|
@@ -2133,13 +2813,8 @@ async function uploadCommand(options) {
|
|
|
2133
2813
|
`Compiled theme not found for ${themeId}@${version}. Run 'onex build' first.`
|
|
2134
2814
|
)
|
|
2135
2815
|
);
|
|
2136
|
-
logger.info(
|
|
2137
|
-
|
|
2138
|
-
`Expected locations:
|
|
2139
|
-
- ./dist/
|
|
2140
|
-
- ../../apps/api-server/compiled-themes/${themeId}@${version}/`
|
|
2141
|
-
)
|
|
2142
|
-
);
|
|
2816
|
+
logger.info(chalk4__default.default.gray(`Expected location:
|
|
2817
|
+
- ./dist/`));
|
|
2143
2818
|
process.exit(1);
|
|
2144
2819
|
}
|
|
2145
2820
|
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
@@ -2246,9 +2921,7 @@ async function uploadCommand(options) {
|
|
|
2246
2921
|
);
|
|
2247
2922
|
console.log(chalk4__default.default.cyan(" Bucket: ") + chalk4__default.default.white(bucket));
|
|
2248
2923
|
console.log(
|
|
2249
|
-
chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(
|
|
2250
|
-
`bundle.zip${sourceUploaded ? " + source.zip" : ""}`
|
|
2251
|
-
)
|
|
2924
|
+
chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(`bundle.zip${sourceUploaded ? " + source.zip" : ""}`)
|
|
2252
2925
|
);
|
|
2253
2926
|
console.log(
|
|
2254
2927
|
chalk4__default.default.cyan(" Path: ") + chalk4__default.default.gray(`s3://${bucket}/themes/${themeId}/${version}/`)
|
|
@@ -2260,6 +2933,9 @@ async function uploadCommand(options) {
|
|
|
2260
2933
|
process.exit(1);
|
|
2261
2934
|
}
|
|
2262
2935
|
}
|
|
2936
|
+
|
|
2937
|
+
// src/commands/download.ts
|
|
2938
|
+
init_logger();
|
|
2263
2939
|
function getS3Client2() {
|
|
2264
2940
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
2265
2941
|
if (adapterMode === "vps") {
|
|
@@ -2407,11 +3083,7 @@ function showDownloadFailureHelp(themeId, bucket) {
|
|
|
2407
3083
|
);
|
|
2408
3084
|
console.log();
|
|
2409
3085
|
console.log(chalk4__default.default.white("4. Verify theme exists in S3:"));
|
|
2410
|
-
console.log(
|
|
2411
|
-
chalk4__default.default.gray(
|
|
2412
|
-
` aws s3 ls s3://${bucket}/themes/${themeId}/`
|
|
2413
|
-
)
|
|
2414
|
-
);
|
|
3086
|
+
console.log(chalk4__default.default.gray(` aws s3 ls s3://${bucket}/themes/${themeId}/`));
|
|
2415
3087
|
console.log();
|
|
2416
3088
|
}
|
|
2417
3089
|
async function downloadCommand(options) {
|
|
@@ -2487,6 +3159,9 @@ async function downloadCommand(options) {
|
|
|
2487
3159
|
process.exit(1);
|
|
2488
3160
|
}
|
|
2489
3161
|
}
|
|
3162
|
+
|
|
3163
|
+
// src/commands/clone.ts
|
|
3164
|
+
init_logger();
|
|
2490
3165
|
function getS3Client3() {
|
|
2491
3166
|
const adapterMode = (process.env.ADAPTER_MODE || "aws").trim().toLowerCase();
|
|
2492
3167
|
if (adapterMode === "vps") {
|
|
@@ -2590,17 +3265,100 @@ function runInstall(cwd) {
|
|
|
2590
3265
|
proc.on("error", () => resolve(false));
|
|
2591
3266
|
});
|
|
2592
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
|
+
}
|
|
2593
3349
|
async function cloneCommand(themeName, options) {
|
|
2594
3350
|
logger.header("Clone Theme Source");
|
|
3351
|
+
let newName = options.name;
|
|
3352
|
+
if (!newName) {
|
|
3353
|
+
newName = await promptThemeName(themeName);
|
|
3354
|
+
}
|
|
2595
3355
|
const spinner = ora__default.default("Initializing clone...").start();
|
|
2596
3356
|
try {
|
|
2597
3357
|
const bucket = options.bucket || getBucketName3(options.environment);
|
|
2598
|
-
const outputDir = options.output || path__default.default.resolve(process.cwd(),
|
|
3358
|
+
const outputDir = options.output || path__default.default.resolve(process.cwd(), newName);
|
|
2599
3359
|
const s3Client = getS3Client3();
|
|
2600
3360
|
if (await fs__default.default.pathExists(outputDir)) {
|
|
2601
|
-
spinner.fail(
|
|
2602
|
-
chalk4__default.default.red(`Directory already exists: ${outputDir}`)
|
|
2603
|
-
);
|
|
3361
|
+
spinner.fail(chalk4__default.default.red(`Directory already exists: ${outputDir}`));
|
|
2604
3362
|
logger.info(
|
|
2605
3363
|
chalk4__default.default.gray(
|
|
2606
3364
|
"Use -o to specify a different output directory, or remove the existing directory."
|
|
@@ -2614,9 +3372,7 @@ async function cloneCommand(themeName, options) {
|
|
|
2614
3372
|
version = await resolveLatestVersion2(s3Client, bucket, themeName);
|
|
2615
3373
|
spinner.succeed(`Resolved latest version: ${chalk4__default.default.cyan(version)}`);
|
|
2616
3374
|
}
|
|
2617
|
-
spinner.start(
|
|
2618
|
-
`Downloading source.zip for ${themeName}@${version}...`
|
|
2619
|
-
);
|
|
3375
|
+
spinner.start(`Downloading source.zip for ${themeName}@${version}...`);
|
|
2620
3376
|
const s3Key = `themes/${themeName}/${version}/source.zip`;
|
|
2621
3377
|
let zipBuffer;
|
|
2622
3378
|
try {
|
|
@@ -2634,9 +3390,7 @@ async function cloneCommand(themeName, options) {
|
|
|
2634
3390
|
chalk4__default.default.yellow("The theme source may not have been uploaded yet.")
|
|
2635
3391
|
);
|
|
2636
3392
|
console.log(
|
|
2637
|
-
chalk4__default.default.gray(
|
|
2638
|
-
`Upload source with: onex upload --theme ${themeName}`
|
|
2639
|
-
)
|
|
3393
|
+
chalk4__default.default.gray(`Upload source with: onex upload --theme ${themeName}`)
|
|
2640
3394
|
);
|
|
2641
3395
|
console.log();
|
|
2642
3396
|
process.exit(1);
|
|
@@ -2649,6 +3403,13 @@ async function cloneCommand(themeName, options) {
|
|
|
2649
3403
|
zip.extractAllTo(outputDir, true);
|
|
2650
3404
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
2651
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
|
+
);
|
|
2652
3413
|
if (options.install !== false) {
|
|
2653
3414
|
const hasPkgJson = await fs__default.default.pathExists(
|
|
2654
3415
|
path__default.default.join(outputDir, "package.json")
|
|
@@ -2671,15 +3432,14 @@ async function cloneCommand(themeName, options) {
|
|
|
2671
3432
|
logger.success(chalk4__default.default.green.bold("Theme cloned successfully!"));
|
|
2672
3433
|
console.log();
|
|
2673
3434
|
console.log(
|
|
2674
|
-
chalk4__default.default.cyan("
|
|
3435
|
+
chalk4__default.default.cyan(" Source: ") + chalk4__default.default.gray(`${themeName}@${version}`)
|
|
2675
3436
|
);
|
|
3437
|
+
console.log(chalk4__default.default.cyan(" Theme: ") + chalk4__default.default.white(newName));
|
|
2676
3438
|
console.log(chalk4__default.default.cyan(" Location: ") + chalk4__default.default.white(outputDir));
|
|
2677
3439
|
console.log(chalk4__default.default.cyan(" Files: ") + chalk4__default.default.white(entries.length));
|
|
2678
3440
|
console.log();
|
|
2679
3441
|
console.log(chalk4__default.default.cyan("Next steps:"));
|
|
2680
|
-
console.log(
|
|
2681
|
-
chalk4__default.default.gray(` cd ${path__default.default.relative(process.cwd(), outputDir)}`)
|
|
2682
|
-
);
|
|
3442
|
+
console.log(chalk4__default.default.gray(` cd ${path__default.default.relative(process.cwd(), outputDir)}`));
|
|
2683
3443
|
if (options.install === false) {
|
|
2684
3444
|
console.log(chalk4__default.default.gray(" pnpm install"));
|
|
2685
3445
|
}
|
|
@@ -2692,11 +3452,253 @@ async function cloneCommand(themeName, options) {
|
|
|
2692
3452
|
}
|
|
2693
3453
|
}
|
|
2694
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
|
+
|
|
2695
3688
|
// src/cli.ts
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
dotenv__default.default.config({
|
|
2699
|
-
|
|
3689
|
+
try {
|
|
3690
|
+
const projectRoot = getProjectRoot();
|
|
3691
|
+
dotenv__default.default.config({
|
|
3692
|
+
path: path__default.default.join(projectRoot, ".env.local"),
|
|
3693
|
+
quiet: true
|
|
3694
|
+
});
|
|
3695
|
+
dotenv__default.default.config({ path: path__default.default.join(projectRoot, ".env"), quiet: true });
|
|
3696
|
+
} catch (e) {
|
|
3697
|
+
}
|
|
3698
|
+
dotenv__default.default.config({
|
|
3699
|
+
path: path__default.default.join(os__default.default.homedir(), ".onex", ".env"),
|
|
3700
|
+
quiet: true
|
|
3701
|
+
});
|
|
2700
3702
|
var program = new commander.Command();
|
|
2701
3703
|
program.name("onex").description("CLI tool for OneX theme development").version("0.1.0");
|
|
2702
3704
|
program.command("init").description("Create a new OneX theme project").argument("[project-name]", "Name of the project").option(
|
|
@@ -2718,6 +3720,7 @@ program.command("create:block").alias("cb").description("Create a new block").ar
|
|
|
2718
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);
|
|
2719
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);
|
|
2720
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);
|
|
2721
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);
|
|
2722
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);
|
|
2723
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(
|
|
@@ -2742,7 +3745,7 @@ program.command("clone").description("Clone theme source code from S3").argument
|
|
|
2742
3745
|
"-v, --version <version>",
|
|
2743
3746
|
"Theme version (default: latest)",
|
|
2744
3747
|
"latest"
|
|
2745
|
-
).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(
|
|
2746
3749
|
"-e, --environment <env>",
|
|
2747
3750
|
"Environment (staging|production)",
|
|
2748
3751
|
"staging"
|