@onexapis/cli 1.1.16 → 1.1.18
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 +117 -51
- package/bin/onexthm.js +4 -0
- package/dist/cli.js +750 -328
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +747 -325
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +162 -299
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -299
- package/dist/index.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +175 -53
- package/package.json +14 -12
- package/templates/default/.env.example +1 -1
- package/templates/default/.mcp.json +8 -0
- package/templates/default/CLAUDE.md +941 -0
- package/templates/default/bundle-entry.ts +18 -0
- package/templates/default/index.ts +26 -0
- package/templates/default/package.json +37 -0
- package/templates/default/pages/about.ts +66 -0
- package/templates/default/pages/home.ts +93 -0
- package/templates/default/pages/showcase.ts +146 -0
- package/templates/default/sections/about/about-default.tsx +237 -0
- package/templates/default/sections/about/about.schema.ts +259 -0
- package/templates/default/sections/about/index.ts +15 -0
- package/templates/default/sections/cta/cta-default.tsx +180 -0
- package/templates/default/sections/cta/cta.schema.ts +210 -0
- package/templates/default/sections/cta/index.ts +11 -0
- package/templates/default/sections/features/features-default.tsx +154 -0
- package/templates/default/sections/features/features.schema.ts +330 -0
- package/templates/default/sections/features/index.ts +11 -0
- package/templates/default/sections/gallery/gallery-default.tsx +134 -0
- package/templates/default/sections/gallery/gallery.schema.ts +397 -0
- package/templates/default/sections/gallery/index.ts +11 -0
- package/templates/default/sections/hero/hero-default.tsx +212 -0
- package/templates/default/sections/hero/hero.schema.ts +273 -0
- package/templates/default/sections/hero/index.ts +15 -0
- package/templates/default/sections/stats/index.ts +11 -0
- package/templates/default/sections/stats/stats-default.tsx +103 -0
- package/templates/default/sections/stats/stats.schema.ts +266 -0
- package/templates/default/sections/testimonials/index.ts +11 -0
- package/templates/default/sections/testimonials/testimonials-default.tsx +130 -0
- package/templates/default/sections/testimonials/testimonials.schema.ts +371 -0
- package/templates/default/sections-registry.ts +32 -0
- package/templates/default/theme.config.ts +107 -0
- package/templates/default/theme.layout.ts +21 -0
- package/templates/default/tsconfig.json +16 -7
- package/templates/default/README.md.ejs +0 -129
- package/templates/default/esbuild.config.js +0 -81
- package/templates/default/package.json.ejs +0 -31
- package/templates/default/src/config.ts.ejs +0 -98
- package/templates/default/src/index.ts.ejs +0 -11
- package/templates/default/src/layout.ts +0 -23
- package/templates/default/src/manifest.ts.ejs +0 -47
- package/templates/default/src/pages/home.ts.ejs +0 -37
- package/templates/default/src/sections/footer/footer-default.tsx +0 -28
- package/templates/default/src/sections/footer/footer.schema.ts +0 -45
- package/templates/default/src/sections/footer/index.ts +0 -2
- package/templates/default/src/sections/header/header-default.tsx +0 -61
- package/templates/default/src/sections/header/header.schema.ts +0 -46
- package/templates/default/src/sections/header/index.ts +0 -2
- package/templates/default/src/sections/hero/hero-default.tsx +0 -52
- package/templates/default/src/sections/hero/hero.schema.ts +0 -52
- package/templates/default/src/sections/hero/index.ts +0 -2
package/dist/cli.js
CHANGED
|
@@ -6,20 +6,20 @@ var ora = require('ora');
|
|
|
6
6
|
var esbuild = require('esbuild');
|
|
7
7
|
var path8 = require('path');
|
|
8
8
|
var fs7 = require('fs/promises');
|
|
9
|
-
var
|
|
9
|
+
var crypto2 = require('crypto');
|
|
10
10
|
var glob = require('glob');
|
|
11
|
-
var
|
|
11
|
+
var module$1 = require('module');
|
|
12
|
+
var os3 = require('os');
|
|
12
13
|
var dotenv = require('dotenv');
|
|
13
14
|
var fs = require('fs-extra');
|
|
14
15
|
var ejs = require('ejs');
|
|
15
16
|
var child_process = require('child_process');
|
|
16
17
|
var commander = require('commander');
|
|
17
|
-
var module$1 = require('module');
|
|
18
18
|
var fs2 = require('fs');
|
|
19
19
|
var inquirer = require('inquirer');
|
|
20
20
|
var archiver = require('archiver');
|
|
21
21
|
var FormData = require('form-data');
|
|
22
|
-
var
|
|
22
|
+
var fetch2 = require('node-fetch');
|
|
23
23
|
var clientS3 = require('@aws-sdk/client-s3');
|
|
24
24
|
var AdmZip = require('adm-zip');
|
|
25
25
|
var chokidar = require('chokidar');
|
|
@@ -52,8 +52,8 @@ var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
|
52
52
|
var esbuild__namespace = /*#__PURE__*/_interopNamespace(esbuild);
|
|
53
53
|
var path8__default = /*#__PURE__*/_interopDefault(path8);
|
|
54
54
|
var fs7__default = /*#__PURE__*/_interopDefault(fs7);
|
|
55
|
-
var
|
|
56
|
-
var
|
|
55
|
+
var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
|
|
56
|
+
var os3__default = /*#__PURE__*/_interopDefault(os3);
|
|
57
57
|
var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
|
|
58
58
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
59
59
|
var ejs__default = /*#__PURE__*/_interopDefault(ejs);
|
|
@@ -61,7 +61,7 @@ var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
|
61
61
|
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
62
62
|
var archiver__default = /*#__PURE__*/_interopDefault(archiver);
|
|
63
63
|
var FormData__default = /*#__PURE__*/_interopDefault(FormData);
|
|
64
|
-
var
|
|
64
|
+
var fetch2__default = /*#__PURE__*/_interopDefault(fetch2);
|
|
65
65
|
var AdmZip__default = /*#__PURE__*/_interopDefault(AdmZip);
|
|
66
66
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
67
67
|
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
@@ -138,7 +138,7 @@ __export(compile_theme_exports, {
|
|
|
138
138
|
compilePreviewRuntime: () => compilePreviewRuntime,
|
|
139
139
|
compileStandaloneTheme: () => compileStandaloneTheme,
|
|
140
140
|
compileStandaloneThemeDev: () => compileStandaloneThemeDev,
|
|
141
|
-
generateManifest: () =>
|
|
141
|
+
generateManifest: () => generateManifest
|
|
142
142
|
});
|
|
143
143
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
144
144
|
let dir = startDir;
|
|
@@ -285,6 +285,12 @@ function createThemeDepsStubPlugin(themePath) {
|
|
|
285
285
|
if (!result.errors.length) return result;
|
|
286
286
|
} catch {
|
|
287
287
|
}
|
|
288
|
+
try {
|
|
289
|
+
const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href)) || __filename);
|
|
290
|
+
const resolved = req.resolve(args.path);
|
|
291
|
+
if (resolved) return { path: resolved, namespace: "file" };
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
288
294
|
return { path: args.path, namespace };
|
|
289
295
|
});
|
|
290
296
|
};
|
|
@@ -293,11 +299,19 @@ function createThemeDepsStubPlugin(themePath) {
|
|
|
293
299
|
const stubs = {
|
|
294
300
|
"next/image": `
|
|
295
301
|
import React from 'react';
|
|
296
|
-
const Image = (props) => {
|
|
297
|
-
const { src, alt, width, height, fill, priority, ...rest } = props;
|
|
302
|
+
const Image = React.forwardRef((props, ref) => {
|
|
303
|
+
const { src, alt, width, height, fill, priority, sizes, quality, placeholder, blurDataURL, onLoad, onError, style, className, ...rest } = props;
|
|
298
304
|
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
299
|
-
|
|
300
|
-
};
|
|
305
|
+
const fillStyle = fill ? { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: style?.objectFit || 'cover', display: 'block' } : {};
|
|
306
|
+
const mergedStyle = { ...fillStyle, ...style };
|
|
307
|
+
return React.createElement('img', {
|
|
308
|
+
ref, src: imgSrc, alt,
|
|
309
|
+
width: fill ? undefined : width, height: fill ? undefined : height,
|
|
310
|
+
loading: priority ? 'eager' : 'lazy',
|
|
311
|
+
style: Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined,
|
|
312
|
+
className, onLoad, onError, ...rest,
|
|
313
|
+
});
|
|
314
|
+
});
|
|
301
315
|
export default Image;
|
|
302
316
|
`,
|
|
303
317
|
"next/link": `
|
|
@@ -331,7 +345,10 @@ export function headers() { return new Headers(); }
|
|
|
331
345
|
if (!lucideThemeScanned) {
|
|
332
346
|
lucideThemeScanned = true;
|
|
333
347
|
try {
|
|
334
|
-
const scanned = await scanImportsFromPackage(
|
|
348
|
+
const scanned = await scanImportsFromPackage(
|
|
349
|
+
themePath,
|
|
350
|
+
"lucide-react"
|
|
351
|
+
);
|
|
335
352
|
for (const names of Object.values(scanned)) {
|
|
336
353
|
for (const name of names) lucideImports.add(name);
|
|
337
354
|
}
|
|
@@ -394,10 +411,13 @@ export function useFormContext() { return useForm(); }
|
|
|
394
411
|
loader: "js"
|
|
395
412
|
}));
|
|
396
413
|
tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
|
|
397
|
-
build2.onLoad(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
414
|
+
build2.onLoad(
|
|
415
|
+
{ filter: /.*/, namespace: "hookform-resolvers-stub" },
|
|
416
|
+
() => ({
|
|
417
|
+
contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
|
|
418
|
+
loader: "js"
|
|
419
|
+
})
|
|
420
|
+
);
|
|
401
421
|
tryResolveOrStub(/^next-intl$/, "next-intl-stub");
|
|
402
422
|
build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
|
|
403
423
|
contents: `
|
|
@@ -506,7 +526,7 @@ async function contentHashEntry(outputDir) {
|
|
|
506
526
|
logger.warning("No entry file found in output, skipping content hash");
|
|
507
527
|
return;
|
|
508
528
|
}
|
|
509
|
-
const hash2 =
|
|
529
|
+
const hash2 = crypto2__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
510
530
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
511
531
|
const indexMapPath = path8__default.default.join(outputDir, "index.js.map");
|
|
512
532
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
@@ -524,7 +544,7 @@ async function contentHashEntry(outputDir) {
|
|
|
524
544
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
525
545
|
return;
|
|
526
546
|
}
|
|
527
|
-
const hash =
|
|
547
|
+
const hash = crypto2__default.default.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
528
548
|
const hashedName = `bundle-entry-${hash}.js`;
|
|
529
549
|
const hashedMapName = `bundle-entry-${hash}.js.map`;
|
|
530
550
|
entryContent = entryContent.replace(
|
|
@@ -562,7 +582,7 @@ async function extractDataRequirements(themePath) {
|
|
|
562
582
|
}
|
|
563
583
|
return requirements;
|
|
564
584
|
}
|
|
565
|
-
async function
|
|
585
|
+
async function generateManifest(themeName, themePath, outputDir) {
|
|
566
586
|
let version2 = "1.0.0";
|
|
567
587
|
let themeId = themeName;
|
|
568
588
|
try {
|
|
@@ -655,7 +675,11 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
655
675
|
banner: {
|
|
656
676
|
js: '"use client";'
|
|
657
677
|
},
|
|
658
|
-
plugins: [
|
|
678
|
+
plugins: [
|
|
679
|
+
reactGlobalPlugin,
|
|
680
|
+
createCoreGlobalPlugin(themePath),
|
|
681
|
+
createThemeDepsStubPlugin(themePath)
|
|
682
|
+
],
|
|
659
683
|
external: [],
|
|
660
684
|
alias: {
|
|
661
685
|
events: "events/",
|
|
@@ -693,7 +717,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
693
717
|
} catch {
|
|
694
718
|
}
|
|
695
719
|
await contentHashEntry(outputDir);
|
|
696
|
-
await
|
|
720
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
697
721
|
await generateThemeData(themePath, outputDir, themeName);
|
|
698
722
|
if (result.metafile) {
|
|
699
723
|
const outputs = result.metafile.outputs;
|
|
@@ -737,7 +761,11 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
737
761
|
banner: {
|
|
738
762
|
js: '"use client";'
|
|
739
763
|
},
|
|
740
|
-
plugins: [
|
|
764
|
+
plugins: [
|
|
765
|
+
reactGlobalPlugin,
|
|
766
|
+
createCoreGlobalPlugin(themePath),
|
|
767
|
+
createThemeDepsStubPlugin(themePath)
|
|
768
|
+
],
|
|
741
769
|
external: [],
|
|
742
770
|
alias: {
|
|
743
771
|
events: "events/",
|
|
@@ -770,7 +798,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
770
798
|
};
|
|
771
799
|
const context2 = await esbuild__namespace.context(buildOptions);
|
|
772
800
|
await context2.rebuild();
|
|
773
|
-
await
|
|
801
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
774
802
|
await generateThemeData(themePath, outputDir, themeName);
|
|
775
803
|
return { context: context2, outputDir };
|
|
776
804
|
}
|
|
@@ -872,7 +900,16 @@ ${locations.join("\n")}`
|
|
|
872
900
|
path8__default.default.join(themePath, "node_modules", "@onexapis", "core", "src"),
|
|
873
901
|
path8__default.default.join(themePath, "..", "..", "packages", "core", "src"),
|
|
874
902
|
// monorepo sibling
|
|
875
|
-
path8__default.default.join(
|
|
903
|
+
path8__default.default.join(
|
|
904
|
+
__dirname,
|
|
905
|
+
"..",
|
|
906
|
+
"..",
|
|
907
|
+
"..",
|
|
908
|
+
"..",
|
|
909
|
+
"packages",
|
|
910
|
+
"core",
|
|
911
|
+
"src"
|
|
912
|
+
)
|
|
876
913
|
// from CLI src
|
|
877
914
|
];
|
|
878
915
|
let coreSourceDir = null;
|
|
@@ -886,7 +923,10 @@ ${locations.join("\n")}`
|
|
|
886
923
|
}
|
|
887
924
|
if (coreSourceDir) {
|
|
888
925
|
try {
|
|
889
|
-
const scanned = await scanImportsFromPackage(
|
|
926
|
+
const scanned = await scanImportsFromPackage(
|
|
927
|
+
coreSourceDir,
|
|
928
|
+
"lucide-react"
|
|
929
|
+
);
|
|
890
930
|
for (const names of Object.values(scanned)) {
|
|
891
931
|
for (const name of names) lucideIconNames.add(name);
|
|
892
932
|
}
|
|
@@ -907,7 +947,10 @@ ${locations.join("\n")}`
|
|
|
907
947
|
const mjsFiles = await glob.glob("*.mjs", { cwd: candidate });
|
|
908
948
|
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
909
949
|
for (const file of mjsFiles) {
|
|
910
|
-
const content = await fs7__default.default.readFile(
|
|
950
|
+
const content = await fs7__default.default.readFile(
|
|
951
|
+
path8__default.default.join(candidate, file),
|
|
952
|
+
"utf-8"
|
|
953
|
+
);
|
|
911
954
|
for (const match of content.matchAll(importRegex)) {
|
|
912
955
|
for (const name of match[1].split(",")) {
|
|
913
956
|
const original = name.trim().split(/\s+as\s+/)[0].trim();
|
|
@@ -923,7 +966,10 @@ ${locations.join("\n")}`
|
|
|
923
966
|
}
|
|
924
967
|
}
|
|
925
968
|
try {
|
|
926
|
-
const scanned = await scanImportsFromPackage(
|
|
969
|
+
const scanned = await scanImportsFromPackage(
|
|
970
|
+
themePath,
|
|
971
|
+
"lucide-react"
|
|
972
|
+
);
|
|
927
973
|
for (const names of Object.values(scanned)) {
|
|
928
974
|
for (const name of names) lucideIconNames.add(name);
|
|
929
975
|
}
|
|
@@ -953,14 +999,39 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
|
|
|
953
999
|
if (!result.errors.length) return result;
|
|
954
1000
|
} catch {
|
|
955
1001
|
}
|
|
1002
|
+
try {
|
|
1003
|
+
const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href)) || __filename);
|
|
1004
|
+
const cjsPath = req.resolve("framer-motion");
|
|
1005
|
+
const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
|
|
1006
|
+
const esmEntry = path8__default.default.join(pkgDir, "dist", "es", "index.mjs");
|
|
1007
|
+
const { existsSync } = await import('fs');
|
|
1008
|
+
if (existsSync(esmEntry)) {
|
|
1009
|
+
return { path: esmEntry, namespace: "file" };
|
|
1010
|
+
}
|
|
1011
|
+
return { path: cjsPath, namespace: "file" };
|
|
1012
|
+
} catch {
|
|
1013
|
+
}
|
|
956
1014
|
return { path: args.path, namespace: "motion-stub" };
|
|
957
1015
|
});
|
|
958
1016
|
build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
|
|
959
1017
|
contents: `
|
|
960
|
-
|
|
961
|
-
const
|
|
1018
|
+
import React from 'react';
|
|
1019
|
+
const MotionComponent = React.forwardRef((props, ref) => {
|
|
1020
|
+
const { initial, animate, exit, variants, transition, whileInView, whileHover, whileTap, viewport, onAnimationComplete, layout, layoutId, style, className, ...rest } = props;
|
|
1021
|
+
return React.createElement(rest.as || 'div', { ref, style, className, ...rest }, rest.children);
|
|
1022
|
+
});
|
|
1023
|
+
const handler = { get: (_, name) => {
|
|
1024
|
+
if (name === '__esModule') return true;
|
|
1025
|
+
if (name === 'create') return () => new Proxy({}, handler);
|
|
1026
|
+
return MotionComponent;
|
|
1027
|
+
}};
|
|
962
1028
|
export const motion = new Proxy({}, handler);
|
|
963
|
-
export const AnimatePresence =
|
|
1029
|
+
export const AnimatePresence = (props) => props.children || null;
|
|
1030
|
+
export const useInView = () => true;
|
|
1031
|
+
export const useAnimation = () => ({ start: () => {}, stop: () => {}, set: () => {} });
|
|
1032
|
+
export const useMotionValue = (v) => ({ get: () => v, set: () => {}, onChange: () => () => {} });
|
|
1033
|
+
export const useTransform = (v) => v;
|
|
1034
|
+
export const useScroll = () => ({ scrollY: { get: () => 0, onChange: () => () => {} }, scrollYProgress: { get: () => 0, onChange: () => () => {} } });
|
|
964
1035
|
export default { motion, AnimatePresence };
|
|
965
1036
|
`.trim(),
|
|
966
1037
|
loader: "jsx"
|
|
@@ -983,12 +1054,26 @@ export default { motion, AnimatePresence };
|
|
|
983
1054
|
build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
|
|
984
1055
|
const stubs = {
|
|
985
1056
|
"next/image": `
|
|
986
|
-
const Image = (props) => {
|
|
987
|
-
const { src, alt, width, height, fill, priority, ...rest } = props;
|
|
988
|
-
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
989
|
-
return React.createElement('img', { src: imgSrc, alt, width: fill ? undefined : width, height: fill ? undefined : height, loading: priority ? 'eager' : 'lazy', ...rest });
|
|
990
|
-
};
|
|
991
1057
|
import React from 'react';
|
|
1058
|
+
const Image = React.forwardRef((props, ref) => {
|
|
1059
|
+
const { src, alt, width, height, fill, priority, sizes, quality, placeholder, blurDataURL, onLoad, onError, style, className, ...rest } = props;
|
|
1060
|
+
const imgSrc = typeof src === 'object' ? src.src : src;
|
|
1061
|
+
const fillStyle = fill ? { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: style?.objectFit || 'cover', display: 'block' } : {};
|
|
1062
|
+
const mergedStyle = { ...fillStyle, ...style };
|
|
1063
|
+
return React.createElement('img', {
|
|
1064
|
+
ref,
|
|
1065
|
+
src: imgSrc,
|
|
1066
|
+
alt,
|
|
1067
|
+
width: fill ? undefined : width,
|
|
1068
|
+
height: fill ? undefined : height,
|
|
1069
|
+
loading: priority ? 'eager' : 'lazy',
|
|
1070
|
+
style: Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined,
|
|
1071
|
+
className,
|
|
1072
|
+
onLoad,
|
|
1073
|
+
onError,
|
|
1074
|
+
...rest,
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
992
1077
|
export default Image;
|
|
993
1078
|
`,
|
|
994
1079
|
"next/link": `
|
|
@@ -1449,45 +1534,20 @@ async function initCommand(projectName, options = {}) {
|
|
|
1449
1534
|
try {
|
|
1450
1535
|
fs2__default.default.mkdirSync(projectPath, { recursive: true });
|
|
1451
1536
|
await copyTemplate(template, projectPath, data);
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
const layoutContent = generateThemeLayout(data);
|
|
1459
|
-
await writeFile(path8__default.default.join(srcPath, "layout.ts"), layoutContent);
|
|
1460
|
-
const indexContent = generateThemeIndex(data);
|
|
1461
|
-
await writeFile(path8__default.default.join(srcPath, "index.ts"), indexContent);
|
|
1462
|
-
const sectionsPath = path8__default.default.join(srcPath, "sections");
|
|
1463
|
-
fs2__default.default.mkdirSync(sectionsPath, { recursive: true });
|
|
1464
|
-
await writeFile(
|
|
1465
|
-
path8__default.default.join(sectionsPath, "README.md"),
|
|
1466
|
-
`# ${displayName} Sections
|
|
1467
|
-
|
|
1468
|
-
Add your theme-specific sections here.
|
|
1469
|
-
`
|
|
1537
|
+
await renameThemeInFiles(
|
|
1538
|
+
projectPath,
|
|
1539
|
+
name,
|
|
1540
|
+
displayName,
|
|
1541
|
+
description,
|
|
1542
|
+
author
|
|
1470
1543
|
);
|
|
1471
|
-
const blocksPath = path8__default.default.join(srcPath, "blocks");
|
|
1472
|
-
fs2__default.default.mkdirSync(blocksPath, { recursive: true });
|
|
1473
|
-
await writeFile(
|
|
1474
|
-
path8__default.default.join(blocksPath, "README.md"),
|
|
1475
|
-
`# ${displayName} Blocks
|
|
1476
|
-
|
|
1477
|
-
Add your theme-specific blocks here.
|
|
1478
|
-
`
|
|
1479
|
-
);
|
|
1480
|
-
const pagesPath = path8__default.default.join(srcPath, "pages");
|
|
1481
|
-
fs2__default.default.mkdirSync(pagesPath, { recursive: true });
|
|
1482
|
-
const homePageContent = generateHomePage(data);
|
|
1483
|
-
await writeFile(path8__default.default.join(pagesPath, "home.ts"), homePageContent);
|
|
1484
1544
|
logger.stopSpinner(true, "Project structure created!");
|
|
1485
1545
|
if (options.git) {
|
|
1486
1546
|
logger.startSpinner("Initializing git repository...");
|
|
1487
1547
|
try {
|
|
1488
1548
|
child_process.execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
1489
1549
|
child_process.execSync("git add .", { cwd: projectPath, stdio: "ignore" });
|
|
1490
|
-
child_process.execSync('git commit -m "Initial commit from
|
|
1550
|
+
child_process.execSync('git commit -m "Initial commit from onexthm init"', {
|
|
1491
1551
|
cwd: projectPath,
|
|
1492
1552
|
stdio: "ignore"
|
|
1493
1553
|
});
|
|
@@ -1524,14 +1584,14 @@ Add your theme-specific blocks here.
|
|
|
1524
1584
|
logger.log(` npm run dev # Start development mode`);
|
|
1525
1585
|
logger.newLine();
|
|
1526
1586
|
logger.section("Theme structure:");
|
|
1527
|
-
logger.log("
|
|
1587
|
+
logger.log(" bundle-entry.ts - Theme manifest and exports");
|
|
1528
1588
|
logger.log(
|
|
1529
|
-
"
|
|
1589
|
+
" theme.config.ts - Design tokens (colors, typography, etc.)"
|
|
1530
1590
|
);
|
|
1531
|
-
logger.log("
|
|
1532
|
-
logger.log("
|
|
1533
|
-
logger.log("
|
|
1534
|
-
logger.log("
|
|
1591
|
+
logger.log(" theme.layout.ts - Header and footer configuration");
|
|
1592
|
+
logger.log(" sections/ - Custom sections for your theme");
|
|
1593
|
+
logger.log(" pages/ - Page configurations");
|
|
1594
|
+
logger.log(" CLAUDE.md - AI assistant context");
|
|
1535
1595
|
logger.newLine();
|
|
1536
1596
|
logger.success(`Happy theming! \u{1F3A8}`);
|
|
1537
1597
|
} catch (error) {
|
|
@@ -1545,231 +1605,33 @@ Add your theme-specific blocks here.
|
|
|
1545
1605
|
process.exit(1);
|
|
1546
1606
|
}
|
|
1547
1607
|
}
|
|
1548
|
-
function
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
// Example: productCard: () => import("./blocks/product-card").then((m) => m.productCardDefinition),
|
|
1576
|
-
},
|
|
1577
|
-
|
|
1578
|
-
// Default pages
|
|
1579
|
-
pages: {
|
|
1580
|
-
home: () => import("./pages/home").then((m) => m.homePageConfig),
|
|
1581
|
-
},
|
|
1582
|
-
|
|
1583
|
-
// Supported page types
|
|
1584
|
-
supportedPageTypes: ["home", "about", "contact", "custom"],
|
|
1585
|
-
|
|
1586
|
-
// Preview image (optional)
|
|
1587
|
-
preview: undefined,
|
|
1588
|
-
|
|
1589
|
-
// Tags for categorization (optional)
|
|
1590
|
-
tags: ["custom"],
|
|
1591
|
-
};
|
|
1592
|
-
|
|
1593
|
-
export default manifest;
|
|
1594
|
-
`;
|
|
1595
|
-
}
|
|
1596
|
-
function generateThemeConfig(data) {
|
|
1597
|
-
return `import type { ThemeConfig } from "@onexapis/core";
|
|
1598
|
-
|
|
1599
|
-
/**
|
|
1600
|
-
* ${data.displayName} Theme Configuration
|
|
1601
|
-
* Design tokens: colors, typography, spacing, etc.
|
|
1602
|
-
*/
|
|
1603
|
-
export const themeConfig: ThemeConfig = {
|
|
1604
|
-
// Color palette
|
|
1605
|
-
colors: {
|
|
1606
|
-
primary: {
|
|
1607
|
-
50: "#eff6ff",
|
|
1608
|
-
100: "#dbeafe",
|
|
1609
|
-
200: "#bfdbfe",
|
|
1610
|
-
300: "#93c5fd",
|
|
1611
|
-
400: "#60a5fa",
|
|
1612
|
-
500: "#3b82f6",
|
|
1613
|
-
600: "#2563eb",
|
|
1614
|
-
700: "#1d4ed8",
|
|
1615
|
-
800: "#1e40af",
|
|
1616
|
-
900: "#1e3a8a",
|
|
1617
|
-
},
|
|
1618
|
-
secondary: {
|
|
1619
|
-
50: "#f8fafc",
|
|
1620
|
-
100: "#f1f5f9",
|
|
1621
|
-
200: "#e2e8f0",
|
|
1622
|
-
300: "#cbd5e1",
|
|
1623
|
-
400: "#94a3b8",
|
|
1624
|
-
500: "#64748b",
|
|
1625
|
-
600: "#475569",
|
|
1626
|
-
700: "#334155",
|
|
1627
|
-
800: "#1e293b",
|
|
1628
|
-
900: "#0f172a",
|
|
1629
|
-
},
|
|
1630
|
-
accent: {
|
|
1631
|
-
50: "#fdf4ff",
|
|
1632
|
-
100: "#fae8ff",
|
|
1633
|
-
200: "#f5d0fe",
|
|
1634
|
-
300: "#f0abfc",
|
|
1635
|
-
400: "#e879f9",
|
|
1636
|
-
500: "#d946ef",
|
|
1637
|
-
600: "#c026d3",
|
|
1638
|
-
700: "#a21caf",
|
|
1639
|
-
800: "#86198f",
|
|
1640
|
-
900: "#701a75",
|
|
1641
|
-
},
|
|
1642
|
-
},
|
|
1643
|
-
|
|
1644
|
-
// Typography
|
|
1645
|
-
typography: {
|
|
1646
|
-
fontFamily: {
|
|
1647
|
-
sans: ["Inter", "system-ui", "sans-serif"],
|
|
1648
|
-
serif: ["Georgia", "serif"],
|
|
1649
|
-
mono: ["Monaco", "monospace"],
|
|
1650
|
-
},
|
|
1651
|
-
fontSize: {
|
|
1652
|
-
xs: "0.75rem",
|
|
1653
|
-
sm: "0.875rem",
|
|
1654
|
-
base: "1rem",
|
|
1655
|
-
lg: "1.125rem",
|
|
1656
|
-
xl: "1.25rem",
|
|
1657
|
-
"2xl": "1.5rem",
|
|
1658
|
-
"3xl": "1.875rem",
|
|
1659
|
-
"4xl": "2.25rem",
|
|
1660
|
-
"5xl": "3rem",
|
|
1661
|
-
},
|
|
1662
|
-
},
|
|
1663
|
-
|
|
1664
|
-
// Spacing
|
|
1665
|
-
spacing: {
|
|
1666
|
-
xs: "0.5rem",
|
|
1667
|
-
sm: "1rem",
|
|
1668
|
-
md: "1.5rem",
|
|
1669
|
-
lg: "2rem",
|
|
1670
|
-
xl: "3rem",
|
|
1671
|
-
"2xl": "4rem",
|
|
1672
|
-
"3xl": "6rem",
|
|
1673
|
-
"4xl": "8rem",
|
|
1674
|
-
},
|
|
1675
|
-
|
|
1676
|
-
// Border radius
|
|
1677
|
-
borderRadius: {
|
|
1678
|
-
none: "0",
|
|
1679
|
-
sm: "0.125rem",
|
|
1680
|
-
md: "0.375rem",
|
|
1681
|
-
lg: "0.5rem",
|
|
1682
|
-
xl: "0.75rem",
|
|
1683
|
-
full: "9999px",
|
|
1684
|
-
},
|
|
1685
|
-
|
|
1686
|
-
// Breakpoints
|
|
1687
|
-
breakpoints: {
|
|
1688
|
-
sm: "640px",
|
|
1689
|
-
md: "768px",
|
|
1690
|
-
lg: "1024px",
|
|
1691
|
-
xl: "1280px",
|
|
1692
|
-
"2xl": "1536px",
|
|
1693
|
-
},
|
|
1694
|
-
};
|
|
1695
|
-
`;
|
|
1696
|
-
}
|
|
1697
|
-
function generateThemeLayout(data) {
|
|
1698
|
-
return `import type { ThemeLayoutConfig } from "@onexapis/core";
|
|
1699
|
-
|
|
1700
|
-
/**
|
|
1701
|
-
* ${data.themeName} Theme Layout
|
|
1702
|
-
* Define header and footer sections
|
|
1703
|
-
*/
|
|
1704
|
-
export const themeLayout: ThemeLayoutConfig = {
|
|
1705
|
-
// Header section configuration
|
|
1706
|
-
header: undefined,
|
|
1707
|
-
// Example:
|
|
1708
|
-
// header: {
|
|
1709
|
-
// type: "header",
|
|
1710
|
-
// template: "default",
|
|
1711
|
-
// enabled: true,
|
|
1712
|
-
// settings: {},
|
|
1713
|
-
// },
|
|
1714
|
-
|
|
1715
|
-
// Footer section configuration
|
|
1716
|
-
footer: undefined,
|
|
1717
|
-
// Example:
|
|
1718
|
-
// footer: {
|
|
1719
|
-
// type: "footer",
|
|
1720
|
-
// template: "default",
|
|
1721
|
-
// enabled: true,
|
|
1722
|
-
// settings: {},
|
|
1723
|
-
// },
|
|
1724
|
-
};
|
|
1725
|
-
`;
|
|
1726
|
-
}
|
|
1727
|
-
function generateThemeIndex(data) {
|
|
1728
|
-
return `/**
|
|
1729
|
-
* ${data.themeNamePascal} Theme
|
|
1730
|
-
*/
|
|
1731
|
-
|
|
1732
|
-
export { manifest as ${data.themeNamePascal}Manifest } from "./manifest";
|
|
1733
|
-
export { themeConfig as ${data.themeNamePascal}Config } from "./config";
|
|
1734
|
-
export { themeLayout as ${data.themeNamePascal}Layout } from "./layout";
|
|
1735
|
-
`;
|
|
1736
|
-
}
|
|
1737
|
-
function generateHomePage(data) {
|
|
1738
|
-
return `import type { PageConfig } from "@onexapis/core";
|
|
1739
|
-
|
|
1740
|
-
/**
|
|
1741
|
-
* Home Page Configuration
|
|
1742
|
-
*/
|
|
1743
|
-
export const homePageConfig: PageConfig = {
|
|
1744
|
-
type: "home",
|
|
1745
|
-
title: "${data.displayName}",
|
|
1746
|
-
description: "Welcome to ${data.displayName}",
|
|
1747
|
-
|
|
1748
|
-
// SEO metadata
|
|
1749
|
-
seo: {
|
|
1750
|
-
title: "${data.displayName} - Home",
|
|
1751
|
-
description: "Welcome to ${data.displayName}",
|
|
1752
|
-
keywords: [],
|
|
1753
|
-
ogImage: undefined,
|
|
1754
|
-
},
|
|
1755
|
-
|
|
1756
|
-
// Page sections
|
|
1757
|
-
sections: [
|
|
1758
|
-
// Add your sections here
|
|
1759
|
-
// Example:
|
|
1760
|
-
// {
|
|
1761
|
-
// id: "hero-1",
|
|
1762
|
-
// type: "hero",
|
|
1763
|
-
// template: "default",
|
|
1764
|
-
// order: 0,
|
|
1765
|
-
// enabled: true,
|
|
1766
|
-
// settings: {},
|
|
1767
|
-
// components: [],
|
|
1768
|
-
// blocks: [],
|
|
1769
|
-
// },
|
|
1770
|
-
],
|
|
1771
|
-
};
|
|
1772
|
-
`;
|
|
1608
|
+
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
1609
|
+
const configPath = path8__default.default.join(projectPath, "theme.config.ts");
|
|
1610
|
+
if (fs2__default.default.existsSync(configPath)) {
|
|
1611
|
+
let content = fs2__default.default.readFileSync(configPath, "utf-8");
|
|
1612
|
+
content = content.replace(
|
|
1613
|
+
/name: "My Simple Theme"/,
|
|
1614
|
+
`name: "${displayName}"`
|
|
1615
|
+
);
|
|
1616
|
+
content = content.replace(
|
|
1617
|
+
/description: ".*?"/,
|
|
1618
|
+
`description: "${description}"`
|
|
1619
|
+
);
|
|
1620
|
+
fs2__default.default.writeFileSync(configPath, content, "utf-8");
|
|
1621
|
+
}
|
|
1622
|
+
const pkgPath = path8__default.default.join(projectPath, "package.json");
|
|
1623
|
+
if (fs2__default.default.existsSync(pkgPath)) {
|
|
1624
|
+
let content = fs2__default.default.readFileSync(pkgPath, "utf-8");
|
|
1625
|
+
content = content.replace(
|
|
1626
|
+
/@onex-themes\/my-simple/g,
|
|
1627
|
+
`@onex-themes/${themeName}`
|
|
1628
|
+
);
|
|
1629
|
+
content = content.replace(
|
|
1630
|
+
/"description": ".*?"/,
|
|
1631
|
+
`"description": "${description}"`
|
|
1632
|
+
);
|
|
1633
|
+
fs2__default.default.writeFileSync(pkgPath, content, "utf-8");
|
|
1634
|
+
}
|
|
1773
1635
|
}
|
|
1774
1636
|
|
|
1775
1637
|
// src/commands/create-section.ts
|
|
@@ -2837,7 +2699,7 @@ async function buildCommand(options) {
|
|
|
2837
2699
|
logger.stopSpinner(true, "Lint passed");
|
|
2838
2700
|
const pkgJson = fs__default.default.readJsonSync(packageJsonPath);
|
|
2839
2701
|
const buildScript = pkgJson.scripts?.build || "";
|
|
2840
|
-
const isRecursive = buildScript.includes("
|
|
2702
|
+
const isRecursive = buildScript.includes("onexthm build") || buildScript.includes("onex-cli build");
|
|
2841
2703
|
logger.startSpinner(
|
|
2842
2704
|
options.watch ? "Building (watch mode)..." : "Building..."
|
|
2843
2705
|
);
|
|
@@ -2983,7 +2845,7 @@ async function packageCommand(options) {
|
|
|
2983
2845
|
logger.newLine();
|
|
2984
2846
|
logger.section("Next steps:");
|
|
2985
2847
|
logger.log(
|
|
2986
|
-
`
|
|
2848
|
+
` onexthm deploy --package ${path8__default.default.relative(process.cwd(), outputPath)}`
|
|
2987
2849
|
);
|
|
2988
2850
|
} catch (error) {
|
|
2989
2851
|
logger.stopSpinner(false, "Failed to create package");
|
|
@@ -3045,7 +2907,7 @@ async function deployCommand(options) {
|
|
|
3045
2907
|
} else if (options.theme) {
|
|
3046
2908
|
const distDir = path8__default.default.join(process.cwd(), "dist");
|
|
3047
2909
|
if (!fs__default.default.existsSync(distDir)) {
|
|
3048
|
-
logger.error("No dist/ directory found. Run '
|
|
2910
|
+
logger.error("No dist/ directory found. Run 'onexthm package' first.");
|
|
3049
2911
|
process.exit(1);
|
|
3050
2912
|
}
|
|
3051
2913
|
const files = fs__default.default.readdirSync(distDir);
|
|
@@ -3054,7 +2916,7 @@ async function deployCommand(options) {
|
|
|
3054
2916
|
);
|
|
3055
2917
|
if (packageFiles.length === 0) {
|
|
3056
2918
|
logger.error(`No package found for theme "${options.theme}".`);
|
|
3057
|
-
logger.info("Run:
|
|
2919
|
+
logger.info("Run: onexthm package --theme " + options.theme);
|
|
3058
2920
|
process.exit(1);
|
|
3059
2921
|
}
|
|
3060
2922
|
packageFiles.sort().reverse();
|
|
@@ -3062,8 +2924,8 @@ async function deployCommand(options) {
|
|
|
3062
2924
|
} else {
|
|
3063
2925
|
logger.error("Either --package or --theme must be specified.");
|
|
3064
2926
|
logger.info("Examples:");
|
|
3065
|
-
logger.log("
|
|
3066
|
-
logger.log("
|
|
2927
|
+
logger.log(" onexthm deploy --package dist/tinan-1.0.0.zip");
|
|
2928
|
+
logger.log(" onexthm deploy --theme tinan");
|
|
3067
2929
|
process.exit(1);
|
|
3068
2930
|
}
|
|
3069
2931
|
if (!fs__default.default.existsSync(packagePath)) {
|
|
@@ -3079,7 +2941,7 @@ async function deployCommand(options) {
|
|
|
3079
2941
|
logger.log(`Path: ${path8__default.default.relative(process.cwd(), packagePath)}`);
|
|
3080
2942
|
logger.newLine();
|
|
3081
2943
|
const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
|
|
3082
|
-
const uploadEndpoint = `${apiUrl}/api/themes/upload`;
|
|
2944
|
+
const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
|
|
3083
2945
|
logger.section("Uploading to API Server");
|
|
3084
2946
|
logger.info(`Endpoint: ${uploadEndpoint}`);
|
|
3085
2947
|
logger.newLine();
|
|
@@ -3096,7 +2958,7 @@ async function deployCommand(options) {
|
|
|
3096
2958
|
if (options.environment) {
|
|
3097
2959
|
formData.append("environment", options.environment);
|
|
3098
2960
|
}
|
|
3099
|
-
const response = await
|
|
2961
|
+
const response = await fetch2__default.default(uploadEndpoint, {
|
|
3100
2962
|
method: "POST",
|
|
3101
2963
|
body: formData,
|
|
3102
2964
|
headers: formData.getHeaders()
|
|
@@ -3177,7 +3039,7 @@ function getBucketName(env) {
|
|
|
3177
3039
|
return process.env.BUCKET_NAME;
|
|
3178
3040
|
}
|
|
3179
3041
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3180
|
-
return environment === "production" ? "
|
|
3042
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3181
3043
|
}
|
|
3182
3044
|
async function findCompiledThemeDir(themeId, version2) {
|
|
3183
3045
|
const searchPaths = [path8__default.default.resolve(process.cwd(), "dist")];
|
|
@@ -3284,7 +3146,7 @@ async function uploadCommand(options) {
|
|
|
3284
3146
|
if (!compiledDir) {
|
|
3285
3147
|
spinner.fail(
|
|
3286
3148
|
chalk4__default.default.red(
|
|
3287
|
-
`Compiled theme not found for ${themeId}@${version2}. Run '
|
|
3149
|
+
`Compiled theme not found for ${themeId}@${version2}. Run 'onexthm build' first.`
|
|
3288
3150
|
)
|
|
3289
3151
|
);
|
|
3290
3152
|
logger.info(chalk4__default.default.gray(`Expected location:
|
|
@@ -3293,7 +3155,7 @@ async function uploadCommand(options) {
|
|
|
3293
3155
|
}
|
|
3294
3156
|
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
3295
3157
|
spinner.start("Creating bundle.zip...");
|
|
3296
|
-
const tmpDir =
|
|
3158
|
+
const tmpDir = os3__default.default.tmpdir();
|
|
3297
3159
|
const bundleZipPath = path8__default.default.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
|
|
3298
3160
|
await createZipFromDir(compiledDir, bundleZipPath);
|
|
3299
3161
|
const bundleZipBuffer = await fs__default.default.readFile(bundleZipPath);
|
|
@@ -3446,7 +3308,7 @@ function getBucketName2(env) {
|
|
|
3446
3308
|
return process.env.BUCKET_NAME;
|
|
3447
3309
|
}
|
|
3448
3310
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3449
|
-
return environment === "production" ? "
|
|
3311
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3450
3312
|
}
|
|
3451
3313
|
async function streamToString(stream) {
|
|
3452
3314
|
const chunks = [];
|
|
@@ -3516,7 +3378,7 @@ function showDownloadFailureHelp(themeId, bucket) {
|
|
|
3516
3378
|
console.log(chalk4__default.default.white("1. Compile and upload the theme:"));
|
|
3517
3379
|
console.log(chalk4__default.default.gray(` cd themes/${themeId}`));
|
|
3518
3380
|
console.log(chalk4__default.default.gray(" pnpm build"));
|
|
3519
|
-
console.log(chalk4__default.default.gray("
|
|
3381
|
+
console.log(chalk4__default.default.gray(" onexthm upload"));
|
|
3520
3382
|
console.log();
|
|
3521
3383
|
console.log(chalk4__default.default.white("2. Verify AWS credentials are set:"));
|
|
3522
3384
|
console.log(
|
|
@@ -3647,7 +3509,7 @@ function getBucketName3(env) {
|
|
|
3647
3509
|
return process.env.BUCKET_NAME;
|
|
3648
3510
|
}
|
|
3649
3511
|
const environment = env || process.env.ENVIRONMENT || "staging";
|
|
3650
|
-
return environment === "production" ? "
|
|
3512
|
+
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3651
3513
|
}
|
|
3652
3514
|
async function streamToString2(stream) {
|
|
3653
3515
|
const chunks = [];
|
|
@@ -3691,8 +3553,8 @@ function runInstall(cwd) {
|
|
|
3691
3553
|
});
|
|
3692
3554
|
}
|
|
3693
3555
|
async function promptThemeName(originalName) {
|
|
3694
|
-
const { default:
|
|
3695
|
-
const { themeName } = await
|
|
3556
|
+
const { default: inquirer7 } = await import('inquirer');
|
|
3557
|
+
const { themeName } = await inquirer7.prompt([
|
|
3696
3558
|
{
|
|
3697
3559
|
type: "input",
|
|
3698
3560
|
name: "themeName",
|
|
@@ -3818,7 +3680,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3818
3680
|
chalk4__default.default.yellow("The theme source may not have been uploaded yet.")
|
|
3819
3681
|
);
|
|
3820
3682
|
console.log(
|
|
3821
|
-
chalk4__default.default.gray(`Upload source with:
|
|
3683
|
+
chalk4__default.default.gray(`Upload source with: onexthm upload --theme ${themeName}`)
|
|
3822
3684
|
);
|
|
3823
3685
|
console.log();
|
|
3824
3686
|
process.exit(1);
|
|
@@ -3845,7 +3707,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3845
3707
|
[
|
|
3846
3708
|
"# API Configuration (enables real data in preview)",
|
|
3847
3709
|
"# Get your Company ID from the OneX dashboard",
|
|
3848
|
-
"NEXT_PUBLIC_API_URL=https://
|
|
3710
|
+
"NEXT_PUBLIC_API_URL=https://platform-dev.onexeos.com",
|
|
3849
3711
|
"NEXT_PUBLIC_COMPANY_ID=",
|
|
3850
3712
|
""
|
|
3851
3713
|
].join("\n")
|
|
@@ -3887,7 +3749,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3887
3749
|
if (options.install === false) {
|
|
3888
3750
|
console.log(chalk4__default.default.gray(" pnpm install"));
|
|
3889
3751
|
}
|
|
3890
|
-
console.log(chalk4__default.default.gray("
|
|
3752
|
+
console.log(chalk4__default.default.gray(" onexthm build"));
|
|
3891
3753
|
console.log();
|
|
3892
3754
|
} catch (error) {
|
|
3893
3755
|
spinner.fail(chalk4__default.default.red(`Clone failed: ${error.message}`));
|
|
@@ -3915,6 +3777,7 @@ var MIME_TYPES = {
|
|
|
3915
3777
|
};
|
|
3916
3778
|
function createDevServer(options) {
|
|
3917
3779
|
const clients = /* @__PURE__ */ new Set();
|
|
3780
|
+
const themeDataPath = path8__default.default.join(options.distDir, "theme-data.json");
|
|
3918
3781
|
const server = http__default.default.createServer((req, res) => {
|
|
3919
3782
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
3920
3783
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
@@ -3928,7 +3791,9 @@ function createDevServer(options) {
|
|
|
3928
3791
|
const pathname = url.pathname;
|
|
3929
3792
|
if (pathname === "/" || pathname === "/index.html") {
|
|
3930
3793
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3931
|
-
res.end(
|
|
3794
|
+
res.end(
|
|
3795
|
+
generatePreviewHTML(options.themeName, options.port, themeDataPath)
|
|
3796
|
+
);
|
|
3932
3797
|
return;
|
|
3933
3798
|
}
|
|
3934
3799
|
if (pathname === "/preview-runtime.js") {
|
|
@@ -3947,6 +3812,37 @@ function createDevServer(options) {
|
|
|
3947
3812
|
serveFile(res, assetPath);
|
|
3948
3813
|
return;
|
|
3949
3814
|
}
|
|
3815
|
+
if (pathname.startsWith("/themes/")) {
|
|
3816
|
+
const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
|
|
3817
|
+
if (match) {
|
|
3818
|
+
const assetPath = path8__default.default.join(options.themePath, "assets", match[1]);
|
|
3819
|
+
if (!assetPath.startsWith(path8__default.default.join(options.themePath, "assets"))) {
|
|
3820
|
+
res.writeHead(403);
|
|
3821
|
+
res.end("Forbidden");
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3824
|
+
serveFile(res, assetPath);
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
if (pathname.startsWith("/assets/")) {
|
|
3829
|
+
const subpath = pathname.replace(/^\/assets\//, "");
|
|
3830
|
+
const segments = subpath.split("/");
|
|
3831
|
+
let assetPath;
|
|
3832
|
+
if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
|
|
3833
|
+
assetPath = path8__default.default.join(
|
|
3834
|
+
options.themePath,
|
|
3835
|
+
"assets",
|
|
3836
|
+
segments.slice(1).join("/")
|
|
3837
|
+
);
|
|
3838
|
+
} else {
|
|
3839
|
+
assetPath = path8__default.default.join(options.themePath, "assets", subpath);
|
|
3840
|
+
}
|
|
3841
|
+
if (assetPath.startsWith(path8__default.default.join(options.themePath, "assets")) && fs2__default.default.existsSync(assetPath)) {
|
|
3842
|
+
serveFile(res, assetPath);
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3950
3846
|
const filePath = path8__default.default.join(options.distDir, pathname);
|
|
3951
3847
|
if (!filePath.startsWith(options.distDir)) {
|
|
3952
3848
|
res.writeHead(403);
|
|
@@ -3957,7 +3853,9 @@ function createDevServer(options) {
|
|
|
3957
3853
|
serveFile(res, filePath);
|
|
3958
3854
|
} else {
|
|
3959
3855
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3960
|
-
res.end(
|
|
3856
|
+
res.end(
|
|
3857
|
+
generatePreviewHTML(options.themeName, options.port, themeDataPath)
|
|
3858
|
+
);
|
|
3961
3859
|
}
|
|
3962
3860
|
});
|
|
3963
3861
|
const wss = new ws.WebSocketServer({ server });
|
|
@@ -3998,17 +3896,82 @@ function serveFile(res, filePath) {
|
|
|
3998
3896
|
res.end("Internal Server Error");
|
|
3999
3897
|
}
|
|
4000
3898
|
}
|
|
4001
|
-
function generatePreviewHTML(themeName, port) {
|
|
3899
|
+
function generatePreviewHTML(themeName, port, themeDataPath) {
|
|
3900
|
+
let fontLinks = "";
|
|
3901
|
+
let fontVarsCSS = "";
|
|
3902
|
+
if (themeDataPath) {
|
|
3903
|
+
try {
|
|
3904
|
+
const themeData = JSON.parse(fs2__default.default.readFileSync(themeDataPath, "utf-8"));
|
|
3905
|
+
const typography = (themeData?.themeConfig || themeData?.theme?.config)?.typography?.fontFamily;
|
|
3906
|
+
if (typography) {
|
|
3907
|
+
const fontFamilies = /* @__PURE__ */ new Set();
|
|
3908
|
+
for (const value of Object.values(typography)) {
|
|
3909
|
+
const primary = value.split(",")[0].trim();
|
|
3910
|
+
if (primary && ![
|
|
3911
|
+
"serif",
|
|
3912
|
+
"sans-serif",
|
|
3913
|
+
"monospace",
|
|
3914
|
+
"system-ui",
|
|
3915
|
+
"Georgia",
|
|
3916
|
+
"Inter",
|
|
3917
|
+
"Consolas"
|
|
3918
|
+
].includes(primary)) {
|
|
3919
|
+
fontFamilies.add(primary);
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
if (fontFamilies.size > 0) {
|
|
3923
|
+
const families = Array.from(fontFamilies).map(
|
|
3924
|
+
(f) => `family=${f.replace(/\s+/g, "+")}:wght@300;400;500;600;700;800;900`
|
|
3925
|
+
).join("&");
|
|
3926
|
+
fontLinks = `<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
3927
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
3928
|
+
<link href="https://fonts.googleapis.com/css2?${families}&display=swap" rel="stylesheet">`;
|
|
3929
|
+
}
|
|
3930
|
+
const heading = typography.heading || typography.body || "system-ui";
|
|
3931
|
+
const body = typography.body || "system-ui";
|
|
3932
|
+
fontVarsCSS = `
|
|
3933
|
+
:root {
|
|
3934
|
+
--font-heading: ${heading};
|
|
3935
|
+
--font-body: ${body};
|
|
3936
|
+
}
|
|
3937
|
+
body { font-family: var(--font-body); }
|
|
3938
|
+
h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); }`;
|
|
3939
|
+
}
|
|
3940
|
+
} catch {
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
4002
3943
|
return `<!DOCTYPE html>
|
|
4003
3944
|
<html lang="en">
|
|
4004
3945
|
<head>
|
|
4005
3946
|
<meta charset="UTF-8">
|
|
4006
3947
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4007
3948
|
<title>OneX Dev \u2014 ${themeName}</title>
|
|
3949
|
+
${fontLinks}
|
|
4008
3950
|
<!-- Tailwind CSS Play CDN \u2014 JIT compilation in browser for dev preview -->
|
|
4009
3951
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
3952
|
+
<script>
|
|
3953
|
+
tailwind.config = {
|
|
3954
|
+
theme: {
|
|
3955
|
+
extend: {
|
|
3956
|
+
aspectRatio: {
|
|
3957
|
+
'2/1': '2 / 1',
|
|
3958
|
+
'3/2': '3 / 2',
|
|
3959
|
+
'4/3': '4 / 3',
|
|
3960
|
+
'3/4': '3 / 4',
|
|
3961
|
+
'5/4': '5 / 4',
|
|
3962
|
+
'16/9': '16 / 9',
|
|
3963
|
+
'21/9': '21 / 9',
|
|
3964
|
+
},
|
|
3965
|
+
fontFamily: {
|
|
3966
|
+
playfair: ['Playfair Display', 'Georgia', 'serif'],
|
|
3967
|
+
roboto: ['Roboto', 'system-ui', 'sans-serif'],
|
|
3968
|
+
},
|
|
3969
|
+
},
|
|
3970
|
+
},
|
|
3971
|
+
}
|
|
3972
|
+
</script>
|
|
4010
3973
|
<style>
|
|
4011
|
-
#onex-preview-root { margin-top: 0; }
|
|
3974
|
+
#onex-preview-root { margin-top: 0; }${fontVarsCSS}
|
|
4012
3975
|
</style>
|
|
4013
3976
|
</head>
|
|
4014
3977
|
<body>
|
|
@@ -4092,7 +4055,7 @@ async function devCommand(options) {
|
|
|
4092
4055
|
logger.info(`File changed: ${filePath}`);
|
|
4093
4056
|
try {
|
|
4094
4057
|
await context2.rebuild();
|
|
4095
|
-
await
|
|
4058
|
+
await generateManifest(themeName, themePath, outputDir);
|
|
4096
4059
|
server.broadcast({ type: "reload", timestamp: Date.now() });
|
|
4097
4060
|
logger.success("Rebuilt successfully");
|
|
4098
4061
|
} catch (error) {
|
|
@@ -4127,7 +4090,7 @@ async function devCommand(options) {
|
|
|
4127
4090
|
|
|
4128
4091
|
// src/commands/config.ts
|
|
4129
4092
|
init_logger();
|
|
4130
|
-
var CONFIG_DIR = path8__default.default.join(
|
|
4093
|
+
var CONFIG_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
|
|
4131
4094
|
var CONFIG_FILE = path8__default.default.join(CONFIG_DIR, ".env");
|
|
4132
4095
|
var CONFIG_ENTRIES = [
|
|
4133
4096
|
{
|
|
@@ -4157,7 +4120,7 @@ var CONFIG_ENTRIES = [
|
|
|
4157
4120
|
key: "NEXT_PUBLIC_API_URL",
|
|
4158
4121
|
label: "API URL",
|
|
4159
4122
|
required: false,
|
|
4160
|
-
defaultValue: "https://
|
|
4123
|
+
defaultValue: "https://platform-dev.onexeos.com"
|
|
4161
4124
|
},
|
|
4162
4125
|
{
|
|
4163
4126
|
key: "NEXT_PUBLIC_COMPANY_ID",
|
|
@@ -4181,7 +4144,7 @@ function parseEnvFile(content) {
|
|
|
4181
4144
|
function serializeEnv(values) {
|
|
4182
4145
|
const lines = [
|
|
4183
4146
|
"# OneX CLI Configuration",
|
|
4184
|
-
"# Generated by:
|
|
4147
|
+
"# Generated by: onexthm config",
|
|
4185
4148
|
""
|
|
4186
4149
|
];
|
|
4187
4150
|
lines.push("# AWS / S3 Configuration");
|
|
@@ -4268,6 +4231,461 @@ async function configCommand() {
|
|
|
4268
4231
|
);
|
|
4269
4232
|
}
|
|
4270
4233
|
|
|
4234
|
+
// src/commands/login.ts
|
|
4235
|
+
init_logger();
|
|
4236
|
+
var AUTH_DIR = path8__default.default.join(os3__default.default.homedir(), ".onexthm");
|
|
4237
|
+
var AUTH_FILE = path8__default.default.join(AUTH_DIR, "auth.json");
|
|
4238
|
+
function getApiUrl() {
|
|
4239
|
+
return process.env.NEXT_PUBLIC_API_URL || process.env.ONEXTHM_API_URL || "https://platform-dev.onexeos.com";
|
|
4240
|
+
}
|
|
4241
|
+
async function saveAuthTokens(tokens) {
|
|
4242
|
+
await fs__default.default.ensureDir(AUTH_DIR);
|
|
4243
|
+
const key = getMachineKey();
|
|
4244
|
+
const data = JSON.stringify(tokens);
|
|
4245
|
+
const encrypted = encrypt(data, key);
|
|
4246
|
+
await fs__default.default.writeFile(AUTH_FILE, encrypted, "utf-8");
|
|
4247
|
+
}
|
|
4248
|
+
function loadAuthTokens() {
|
|
4249
|
+
try {
|
|
4250
|
+
if (!fs__default.default.existsSync(AUTH_FILE)) return null;
|
|
4251
|
+
const encrypted = fs__default.default.readFileSync(AUTH_FILE, "utf-8");
|
|
4252
|
+
const key = getMachineKey();
|
|
4253
|
+
const data = decrypt(encrypted, key);
|
|
4254
|
+
return JSON.parse(data);
|
|
4255
|
+
} catch {
|
|
4256
|
+
return null;
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
async function clearAuthTokens() {
|
|
4260
|
+
try {
|
|
4261
|
+
await fs__default.default.remove(AUTH_FILE);
|
|
4262
|
+
} catch {
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
function isTokenExpired(tokens) {
|
|
4266
|
+
return Date.now() / 1e3 > tokens.expiresAt - 60;
|
|
4267
|
+
}
|
|
4268
|
+
async function getValidTokens() {
|
|
4269
|
+
const tokens = loadAuthTokens();
|
|
4270
|
+
if (!tokens) return null;
|
|
4271
|
+
if (!isTokenExpired(tokens)) return tokens;
|
|
4272
|
+
try {
|
|
4273
|
+
const apiUrl = getApiUrl();
|
|
4274
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
4275
|
+
method: "POST",
|
|
4276
|
+
headers: { "Content-Type": "application/json" },
|
|
4277
|
+
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
4278
|
+
});
|
|
4279
|
+
if (!response.ok) {
|
|
4280
|
+
await clearAuthTokens();
|
|
4281
|
+
return null;
|
|
4282
|
+
}
|
|
4283
|
+
const data = await response.json();
|
|
4284
|
+
const body = data.statusCode ? data.body : data;
|
|
4285
|
+
const refreshed = {
|
|
4286
|
+
...tokens,
|
|
4287
|
+
accessToken: body.AccessToken || tokens.accessToken,
|
|
4288
|
+
idToken: body.IdToken || tokens.idToken,
|
|
4289
|
+
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
4290
|
+
};
|
|
4291
|
+
await saveAuthTokens(refreshed);
|
|
4292
|
+
return refreshed;
|
|
4293
|
+
} catch {
|
|
4294
|
+
await clearAuthTokens();
|
|
4295
|
+
return null;
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
async function authenticatedFetch(url, init) {
|
|
4299
|
+
const tokens = await getValidTokens();
|
|
4300
|
+
if (!tokens) {
|
|
4301
|
+
throw new Error("Not logged in. Run: onexthm login");
|
|
4302
|
+
}
|
|
4303
|
+
const headers = new Headers(init?.headers);
|
|
4304
|
+
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
4305
|
+
headers.set("Content-Type", "application/json");
|
|
4306
|
+
return fetch(url, { ...init, headers });
|
|
4307
|
+
}
|
|
4308
|
+
function getMachineKey() {
|
|
4309
|
+
let seed;
|
|
4310
|
+
if (process.platform === "darwin") {
|
|
4311
|
+
seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
|
|
4312
|
+
} else if (process.platform === "linux") {
|
|
4313
|
+
try {
|
|
4314
|
+
seed = `onexthm:${fs__default.default.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
4315
|
+
} catch {
|
|
4316
|
+
seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
|
|
4317
|
+
}
|
|
4318
|
+
} else {
|
|
4319
|
+
seed = `onexthm:${os3__default.default.hostname()}:${os3__default.default.userInfo().username}`;
|
|
4320
|
+
}
|
|
4321
|
+
return crypto2__default.default.createHash("sha256").update(seed).digest();
|
|
4322
|
+
}
|
|
4323
|
+
function encrypt(text, key) {
|
|
4324
|
+
const iv = crypto2__default.default.randomBytes(16);
|
|
4325
|
+
const cipher = crypto2__default.default.createCipheriv("aes-256-gcm", key, iv);
|
|
4326
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
4327
|
+
encrypted += cipher.final("hex");
|
|
4328
|
+
const tag = cipher.getAuthTag();
|
|
4329
|
+
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
|
|
4330
|
+
}
|
|
4331
|
+
function decrypt(text, key) {
|
|
4332
|
+
const [ivHex, tagHex, encrypted] = text.split(":");
|
|
4333
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
4334
|
+
const tag = Buffer.from(tagHex, "hex");
|
|
4335
|
+
const decipher = crypto2__default.default.createDecipheriv("aes-256-gcm", key, iv);
|
|
4336
|
+
decipher.setAuthTag(tag);
|
|
4337
|
+
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
4338
|
+
decrypted += decipher.final("utf-8");
|
|
4339
|
+
return decrypted;
|
|
4340
|
+
}
|
|
4341
|
+
function parseJwtClaims(idToken) {
|
|
4342
|
+
try {
|
|
4343
|
+
const payload = idToken.split(".")[1];
|
|
4344
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
4345
|
+
return JSON.parse(decoded);
|
|
4346
|
+
} catch {
|
|
4347
|
+
return {};
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
// src/commands/login.ts
|
|
4352
|
+
async function loginCommand() {
|
|
4353
|
+
logger.header("OneX Theme Developer Login");
|
|
4354
|
+
const existing = loadAuthTokens();
|
|
4355
|
+
if (existing) {
|
|
4356
|
+
logger.info(`Already logged in as: ${existing.user.email}`);
|
|
4357
|
+
const { relogin } = await inquirer__default.default.prompt([
|
|
4358
|
+
{
|
|
4359
|
+
type: "confirm",
|
|
4360
|
+
name: "relogin",
|
|
4361
|
+
message: "Re-login with different account?",
|
|
4362
|
+
default: false
|
|
4363
|
+
}
|
|
4364
|
+
]);
|
|
4365
|
+
if (!relogin) return;
|
|
4366
|
+
}
|
|
4367
|
+
const { email, password } = await inquirer__default.default.prompt([
|
|
4368
|
+
{
|
|
4369
|
+
type: "input",
|
|
4370
|
+
name: "email",
|
|
4371
|
+
message: "Email:",
|
|
4372
|
+
validate: (input) => input.includes("@") ? true : "Enter a valid email"
|
|
4373
|
+
},
|
|
4374
|
+
{
|
|
4375
|
+
type: "password",
|
|
4376
|
+
name: "password",
|
|
4377
|
+
message: "Password:",
|
|
4378
|
+
validate: (input) => input.length >= 6 ? true : "Password too short"
|
|
4379
|
+
}
|
|
4380
|
+
]);
|
|
4381
|
+
logger.startSpinner("Logging in...");
|
|
4382
|
+
try {
|
|
4383
|
+
const apiUrl = getApiUrl();
|
|
4384
|
+
const response = await fetch(`${apiUrl}/auth/login`, {
|
|
4385
|
+
method: "POST",
|
|
4386
|
+
headers: { "Content-Type": "application/json" },
|
|
4387
|
+
body: JSON.stringify({ username: email, password })
|
|
4388
|
+
});
|
|
4389
|
+
const raw = await response.json();
|
|
4390
|
+
const data = raw.statusCode ? raw.body : raw;
|
|
4391
|
+
if (!response.ok || data.error) {
|
|
4392
|
+
logger.stopSpinner(false, "Login failed");
|
|
4393
|
+
logger.error(data.message || data.error || "Invalid credentials");
|
|
4394
|
+
process.exit(1);
|
|
4395
|
+
}
|
|
4396
|
+
const idToken = data.IdToken;
|
|
4397
|
+
const accessToken = data.AccessToken;
|
|
4398
|
+
const refreshToken = data.RefreshToken;
|
|
4399
|
+
const expiresIn = data.ExpiresIn || 3600;
|
|
4400
|
+
if (!idToken) {
|
|
4401
|
+
logger.stopSpinner(false, "Login failed");
|
|
4402
|
+
logger.error("No token received from server");
|
|
4403
|
+
process.exit(1);
|
|
4404
|
+
}
|
|
4405
|
+
const claims = parseJwtClaims(idToken);
|
|
4406
|
+
const tokens = {
|
|
4407
|
+
accessToken,
|
|
4408
|
+
idToken,
|
|
4409
|
+
refreshToken,
|
|
4410
|
+
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
4411
|
+
user: {
|
|
4412
|
+
email: claims.email || email,
|
|
4413
|
+
name: claims.name,
|
|
4414
|
+
companyId: claims["custom:company_id"],
|
|
4415
|
+
userId: claims.sub
|
|
4416
|
+
}
|
|
4417
|
+
};
|
|
4418
|
+
await saveAuthTokens(tokens);
|
|
4419
|
+
logger.stopSpinner(true, "Logged in!");
|
|
4420
|
+
logger.newLine();
|
|
4421
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4422
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4423
|
+
if (tokens.user.companyId)
|
|
4424
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4425
|
+
logger.newLine();
|
|
4426
|
+
logger.success("Token stored securely in ~/.onexthm/auth.json (encrypted)");
|
|
4427
|
+
} catch (error) {
|
|
4428
|
+
logger.stopSpinner(false, "Login failed");
|
|
4429
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4430
|
+
process.exit(1);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
// src/commands/logout.ts
|
|
4435
|
+
init_logger();
|
|
4436
|
+
async function logoutCommand() {
|
|
4437
|
+
const tokens = loadAuthTokens();
|
|
4438
|
+
if (!tokens) {
|
|
4439
|
+
logger.info("Not logged in.");
|
|
4440
|
+
return;
|
|
4441
|
+
}
|
|
4442
|
+
await clearAuthTokens();
|
|
4443
|
+
logger.success(`Logged out (was: ${tokens.user.email})`);
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
// src/commands/whoami.ts
|
|
4447
|
+
init_logger();
|
|
4448
|
+
async function whoamiCommand() {
|
|
4449
|
+
const tokens = loadAuthTokens();
|
|
4450
|
+
if (!tokens) {
|
|
4451
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
4452
|
+
process.exit(1);
|
|
4453
|
+
}
|
|
4454
|
+
const expired = isTokenExpired(tokens);
|
|
4455
|
+
logger.header("OneX Theme Developer");
|
|
4456
|
+
logger.info(` Email: ${tokens.user.email}`);
|
|
4457
|
+
if (tokens.user.name) logger.info(` Name: ${tokens.user.name}`);
|
|
4458
|
+
if (tokens.user.companyId)
|
|
4459
|
+
logger.info(` Company: ${tokens.user.companyId}`);
|
|
4460
|
+
logger.info(
|
|
4461
|
+
` Status: ${expired ? "\u26A0 Token expired (will auto-refresh)" : "\u2713 Active"}`
|
|
4462
|
+
);
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
// src/commands/publish.ts
|
|
4466
|
+
init_logger();
|
|
4467
|
+
async function publishCommand(options) {
|
|
4468
|
+
logger.header("OneX Theme Publish");
|
|
4469
|
+
const tokens = await getValidTokens();
|
|
4470
|
+
if (!tokens) {
|
|
4471
|
+
logger.error("Not logged in. Run: onexthm login");
|
|
4472
|
+
process.exit(1);
|
|
4473
|
+
}
|
|
4474
|
+
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
4475
|
+
let themePath;
|
|
4476
|
+
if (options.theme) {
|
|
4477
|
+
themePath = path8__default.default.resolve(options.theme);
|
|
4478
|
+
} else {
|
|
4479
|
+
const isThemeDir = [
|
|
4480
|
+
"theme.config.ts",
|
|
4481
|
+
"bundle-entry.ts",
|
|
4482
|
+
"manifest.ts"
|
|
4483
|
+
].some((f) => fs__default.default.existsSync(path8__default.default.join(process.cwd(), f)));
|
|
4484
|
+
if (isThemeDir) {
|
|
4485
|
+
themePath = process.cwd();
|
|
4486
|
+
} else {
|
|
4487
|
+
logger.error(
|
|
4488
|
+
"Not in a theme directory. Run from theme root or use --theme flag."
|
|
4489
|
+
);
|
|
4490
|
+
process.exit(1);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
const pkgPath = path8__default.default.join(themePath, "package.json");
|
|
4494
|
+
if (!fs__default.default.existsSync(pkgPath)) {
|
|
4495
|
+
logger.error("No package.json found in theme directory");
|
|
4496
|
+
process.exit(1);
|
|
4497
|
+
}
|
|
4498
|
+
const pkg = fs__default.default.readJsonSync(pkgPath);
|
|
4499
|
+
const themeId = pkg.name?.replace("@onex-themes/", "") || path8__default.default.basename(themePath);
|
|
4500
|
+
const version2 = pkg.version || "1.0.0";
|
|
4501
|
+
logger.newLine();
|
|
4502
|
+
logger.info(`Theme: ${themeId}`);
|
|
4503
|
+
logger.info(`Version: ${version2}`);
|
|
4504
|
+
logger.newLine();
|
|
4505
|
+
const apiUrl = getApiUrl();
|
|
4506
|
+
logger.startSpinner("Registering theme...");
|
|
4507
|
+
try {
|
|
4508
|
+
const regResponse = await authenticatedFetch(
|
|
4509
|
+
`${apiUrl}/website-api/themes/register`,
|
|
4510
|
+
{
|
|
4511
|
+
method: "POST",
|
|
4512
|
+
body: JSON.stringify({
|
|
4513
|
+
themeId,
|
|
4514
|
+
name: pkg.displayName || themeId,
|
|
4515
|
+
description: pkg.description || "",
|
|
4516
|
+
email: tokens.user.email
|
|
4517
|
+
})
|
|
4518
|
+
}
|
|
4519
|
+
);
|
|
4520
|
+
const regData = await regResponse.json();
|
|
4521
|
+
const regBody = regData.statusCode ? regData.body : regData;
|
|
4522
|
+
if (!regResponse.ok && regBody.error && !regBody.error.includes("already registered")) {
|
|
4523
|
+
logger.stopSpinner(false, "Registration failed");
|
|
4524
|
+
logger.error(regBody.error);
|
|
4525
|
+
process.exit(1);
|
|
4526
|
+
}
|
|
4527
|
+
logger.stopSpinner(true, regBody.message || "Theme registered");
|
|
4528
|
+
} catch (error) {
|
|
4529
|
+
logger.stopSpinner(false, "Registration failed");
|
|
4530
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4531
|
+
process.exit(1);
|
|
4532
|
+
}
|
|
4533
|
+
logger.startSpinner("Building theme...");
|
|
4534
|
+
try {
|
|
4535
|
+
const { execSync: execSync3 } = await import('child_process');
|
|
4536
|
+
execSync3(
|
|
4537
|
+
"npx tsup bundle-entry.ts --format esm --target es2020 --outDir dist",
|
|
4538
|
+
{
|
|
4539
|
+
cwd: themePath,
|
|
4540
|
+
stdio: "ignore"
|
|
4541
|
+
}
|
|
4542
|
+
);
|
|
4543
|
+
logger.stopSpinner(true, "Theme compiled");
|
|
4544
|
+
} catch {
|
|
4545
|
+
logger.stopSpinner(false, "Build failed");
|
|
4546
|
+
logger.error("Run 'onexthm build' to see build errors");
|
|
4547
|
+
process.exit(1);
|
|
4548
|
+
}
|
|
4549
|
+
logger.startSpinner("Getting upload URL...");
|
|
4550
|
+
let bundleUploadUrl;
|
|
4551
|
+
let sourceUploadUrl;
|
|
4552
|
+
try {
|
|
4553
|
+
const pubResponse = await authenticatedFetch(
|
|
4554
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions`,
|
|
4555
|
+
{
|
|
4556
|
+
method: "POST",
|
|
4557
|
+
body: JSON.stringify({ version: version2 })
|
|
4558
|
+
}
|
|
4559
|
+
);
|
|
4560
|
+
const pubData = await pubResponse.json();
|
|
4561
|
+
const pubBody = pubData.statusCode ? pubData.body : pubData;
|
|
4562
|
+
if (!pubResponse.ok || !pubBody.bundleUploadUrl) {
|
|
4563
|
+
logger.stopSpinner(false, "Failed to get upload URL");
|
|
4564
|
+
logger.error(pubBody.error || "Server error");
|
|
4565
|
+
process.exit(1);
|
|
4566
|
+
}
|
|
4567
|
+
bundleUploadUrl = pubBody.bundleUploadUrl;
|
|
4568
|
+
sourceUploadUrl = pubBody.sourceUploadUrl;
|
|
4569
|
+
logger.stopSpinner(true, "Upload URL obtained");
|
|
4570
|
+
} catch (error) {
|
|
4571
|
+
logger.stopSpinner(false, "Failed");
|
|
4572
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4573
|
+
process.exit(1);
|
|
4574
|
+
}
|
|
4575
|
+
logger.startSpinner("Uploading bundle...");
|
|
4576
|
+
try {
|
|
4577
|
+
const archiver3 = await import('archiver');
|
|
4578
|
+
const { createWriteStream } = await import('fs');
|
|
4579
|
+
const distDir = path8__default.default.join(themePath, "dist");
|
|
4580
|
+
if (!fs__default.default.existsSync(distDir)) {
|
|
4581
|
+
logger.stopSpinner(false, "No dist/ directory");
|
|
4582
|
+
logger.error("Build the theme first: onexthm build");
|
|
4583
|
+
process.exit(1);
|
|
4584
|
+
}
|
|
4585
|
+
const bundleZipPath = path8__default.default.join(themePath, "dist", "bundle.zip");
|
|
4586
|
+
await createZip(distDir, bundleZipPath, ["bundle.zip"]);
|
|
4587
|
+
const bundleBuffer = fs__default.default.readFileSync(bundleZipPath);
|
|
4588
|
+
const bundleRes = await fetch(bundleUploadUrl, {
|
|
4589
|
+
method: "PUT",
|
|
4590
|
+
headers: { "Content-Type": "application/zip" },
|
|
4591
|
+
body: bundleBuffer
|
|
4592
|
+
});
|
|
4593
|
+
if (!bundleRes.ok) {
|
|
4594
|
+
throw new Error(`Upload failed: ${bundleRes.status}`);
|
|
4595
|
+
}
|
|
4596
|
+
const sizeMB = (bundleBuffer.length / 1024 / 1024).toFixed(2);
|
|
4597
|
+
logger.stopSpinner(true, `Bundle uploaded (${sizeMB} MB)`);
|
|
4598
|
+
} catch (error) {
|
|
4599
|
+
logger.stopSpinner(false, "Upload failed");
|
|
4600
|
+
logger.error(error instanceof Error ? error.message : "Upload error");
|
|
4601
|
+
process.exit(1);
|
|
4602
|
+
}
|
|
4603
|
+
logger.startSpinner("Uploading source...");
|
|
4604
|
+
try {
|
|
4605
|
+
const sourceZipPath = path8__default.default.join(themePath, "dist", "source.zip");
|
|
4606
|
+
await createZip(themePath, sourceZipPath, [
|
|
4607
|
+
"node_modules",
|
|
4608
|
+
"dist",
|
|
4609
|
+
".git",
|
|
4610
|
+
".env",
|
|
4611
|
+
".env.local"
|
|
4612
|
+
]);
|
|
4613
|
+
const sourceBuffer = fs__default.default.readFileSync(sourceZipPath);
|
|
4614
|
+
const sourceRes = await fetch(sourceUploadUrl, {
|
|
4615
|
+
method: "PUT",
|
|
4616
|
+
headers: { "Content-Type": "application/zip" },
|
|
4617
|
+
body: sourceBuffer
|
|
4618
|
+
});
|
|
4619
|
+
if (!sourceRes.ok) {
|
|
4620
|
+
throw new Error(`Source upload failed: ${sourceRes.status}`);
|
|
4621
|
+
}
|
|
4622
|
+
const sizeMB = (sourceBuffer.length / 1024 / 1024).toFixed(2);
|
|
4623
|
+
logger.stopSpinner(true, `Source uploaded (${sizeMB} MB)`);
|
|
4624
|
+
} catch (error) {
|
|
4625
|
+
logger.stopSpinner(false, "Source upload failed");
|
|
4626
|
+
logger.info("Source upload skipped (bundle was uploaded successfully)");
|
|
4627
|
+
}
|
|
4628
|
+
logger.startSpinner("Scanning and publishing...");
|
|
4629
|
+
try {
|
|
4630
|
+
const confirmResponse = await authenticatedFetch(
|
|
4631
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/confirm`,
|
|
4632
|
+
{ method: "POST" }
|
|
4633
|
+
);
|
|
4634
|
+
const confirmData = await confirmResponse.json();
|
|
4635
|
+
const confirmBody = confirmData.statusCode ? confirmData.body : confirmData;
|
|
4636
|
+
if (!confirmResponse.ok || !confirmBody.success) {
|
|
4637
|
+
logger.stopSpinner(false, "Publishing failed");
|
|
4638
|
+
if (confirmBody.violations) {
|
|
4639
|
+
logger.error("Theme rejected \u2014 security violations found:");
|
|
4640
|
+
for (const v of confirmBody.violations) {
|
|
4641
|
+
logger.log(` \u274C ${v.file}: ${v.violation}`);
|
|
4642
|
+
}
|
|
4643
|
+
} else {
|
|
4644
|
+
logger.error(confirmBody.error || "Unknown error");
|
|
4645
|
+
}
|
|
4646
|
+
if (confirmBody.warnings?.length) {
|
|
4647
|
+
logger.newLine();
|
|
4648
|
+
logger.info("Warnings:");
|
|
4649
|
+
for (const w of confirmBody.warnings) {
|
|
4650
|
+
logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
process.exit(1);
|
|
4654
|
+
}
|
|
4655
|
+
logger.stopSpinner(true, confirmBody.message || "Published!");
|
|
4656
|
+
if (confirmBody.warnings?.length) {
|
|
4657
|
+
logger.newLine();
|
|
4658
|
+
logger.info("Warnings (non-blocking):");
|
|
4659
|
+
for (const w of confirmBody.warnings) {
|
|
4660
|
+
logger.log(` \u26A0\uFE0F ${w.file}: ${w.warning}`);
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
logger.stopSpinner(false, "Publishing failed");
|
|
4665
|
+
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4666
|
+
process.exit(1);
|
|
4667
|
+
}
|
|
4668
|
+
logger.newLine();
|
|
4669
|
+
logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
|
|
4670
|
+
}
|
|
4671
|
+
async function createZip(sourceDir, outputPath, exclude) {
|
|
4672
|
+
const archiver3 = (await import('archiver')).default;
|
|
4673
|
+
const { createWriteStream } = await import('fs');
|
|
4674
|
+
return new Promise((resolve, reject) => {
|
|
4675
|
+
const output = createWriteStream(outputPath);
|
|
4676
|
+
const archive = archiver3("zip", { zlib: { level: 9 } });
|
|
4677
|
+
output.on("close", resolve);
|
|
4678
|
+
archive.on("error", reject);
|
|
4679
|
+
archive.pipe(output);
|
|
4680
|
+
archive.glob("**/*", {
|
|
4681
|
+
cwd: sourceDir,
|
|
4682
|
+
ignore: exclude.map((e) => `${e}/**`),
|
|
4683
|
+
dot: false
|
|
4684
|
+
});
|
|
4685
|
+
archive.finalize();
|
|
4686
|
+
});
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4271
4689
|
// src/cli.ts
|
|
4272
4690
|
try {
|
|
4273
4691
|
const projectRoot = getProjectRoot();
|
|
@@ -4279,13 +4697,13 @@ try {
|
|
|
4279
4697
|
} catch {
|
|
4280
4698
|
}
|
|
4281
4699
|
dotenv__default.default.config({
|
|
4282
|
-
path: path8__default.default.join(
|
|
4700
|
+
path: path8__default.default.join(os3__default.default.homedir(), ".onexthm", ".env"),
|
|
4283
4701
|
quiet: true
|
|
4284
4702
|
});
|
|
4285
4703
|
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href)));
|
|
4286
4704
|
var { version } = require2("../package.json");
|
|
4287
4705
|
var program = new commander.Command();
|
|
4288
|
-
program.name("
|
|
4706
|
+
program.name("onexthm").description("CLI tool for OneX theme development").version(version);
|
|
4289
4707
|
program.command("init").description("Create a new OneX theme project").argument("[project-name]", "Name of the project").option(
|
|
4290
4708
|
"-t, --template <template>",
|
|
4291
4709
|
"Template to use (default, minimal)",
|
|
@@ -4336,6 +4754,10 @@ program.command("clone").description("Clone theme source code from S3").argument
|
|
|
4336
4754
|
"staging"
|
|
4337
4755
|
).option("--no-install", "Skip running pnpm install after clone").action(cloneCommand);
|
|
4338
4756
|
program.command("config").description("Configure OneX CLI credentials (AWS, API keys)").action(configCommand);
|
|
4757
|
+
program.command("login").description("Login to OneX platform").action(loginCommand);
|
|
4758
|
+
program.command("logout").description("Logout from OneX platform").action(logoutCommand);
|
|
4759
|
+
program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
|
|
4760
|
+
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
|
|
4339
4761
|
program.configureOutput({
|
|
4340
4762
|
writeErr: (str) => process.stderr.write(chalk4__default.default.red(str))
|
|
4341
4763
|
});
|