@onexapis/cli 1.1.36 → 1.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +693 -455
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +686 -450
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +343 -256
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +339 -253
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
package/dist/cli.mjs
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import chalk4 from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import * as esbuild from 'esbuild';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import path10 from 'path';
|
|
6
|
+
import fs9 from 'fs/promises';
|
|
7
|
+
import crypto from 'crypto';
|
|
8
8
|
import { glob } from 'glob';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
|
-
import
|
|
10
|
+
import os from 'os';
|
|
11
11
|
import dotenv from 'dotenv';
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
13
|
import ejs from 'ejs';
|
|
14
|
-
import
|
|
14
|
+
import spawn2 from 'cross-spawn';
|
|
15
15
|
import { Command } from 'commander';
|
|
16
|
-
import
|
|
16
|
+
import fs4 from 'fs';
|
|
17
17
|
import inquirer from 'inquirer';
|
|
18
18
|
import archiver from 'archiver';
|
|
19
19
|
import FormData from 'form-data';
|
|
@@ -23,6 +23,7 @@ import AdmZip from 'adm-zip';
|
|
|
23
23
|
import chokidar from 'chokidar';
|
|
24
24
|
import http from 'http';
|
|
25
25
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
26
|
+
import semver from 'semver';
|
|
26
27
|
|
|
27
28
|
var __defProp = Object.defineProperty;
|
|
28
29
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -104,8 +105,8 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
104
105
|
const tailwindcss = (await import('tailwindcss')).default;
|
|
105
106
|
const tailwindConfig = {
|
|
106
107
|
content: [
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
path10.join(themePath, "sections/**/*.{ts,tsx}"),
|
|
109
|
+
path10.join(themePath, "components/**/*.{ts,tsx}")
|
|
109
110
|
],
|
|
110
111
|
theme: { extend: {} },
|
|
111
112
|
plugins: []
|
|
@@ -115,7 +116,7 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
115
116
|
inputCSS,
|
|
116
117
|
{ from: void 0 }
|
|
117
118
|
);
|
|
118
|
-
await
|
|
119
|
+
await fs9.writeFile(path10.join(outDir, "bundle.css"), result.css);
|
|
119
120
|
logger.info("Generated bundle.css");
|
|
120
121
|
} catch (err) {
|
|
121
122
|
logger.warning(
|
|
@@ -126,12 +127,12 @@ async function generateThemeCSS(themePath, outDir) {
|
|
|
126
127
|
async function resolveNodeModulesFile(startDir, relativePath) {
|
|
127
128
|
let dir = startDir;
|
|
128
129
|
while (true) {
|
|
129
|
-
const candidate =
|
|
130
|
+
const candidate = path10.join(dir, "node_modules", relativePath);
|
|
130
131
|
try {
|
|
131
|
-
await
|
|
132
|
+
await fs9.access(candidate);
|
|
132
133
|
return candidate;
|
|
133
134
|
} catch {
|
|
134
|
-
const parent =
|
|
135
|
+
const parent = path10.dirname(dir);
|
|
135
136
|
if (parent === dir) break;
|
|
136
137
|
dir = parent;
|
|
137
138
|
}
|
|
@@ -155,7 +156,7 @@ async function scanImportsFromPackage(sourceDir, packageName) {
|
|
|
155
156
|
});
|
|
156
157
|
for (const file of sourceFiles) {
|
|
157
158
|
try {
|
|
158
|
-
const content = await
|
|
159
|
+
const content = await fs9.readFile(path10.join(sourceDir, file), "utf-8");
|
|
159
160
|
for (const match of content.matchAll(namespaceImportRegex)) {
|
|
160
161
|
const subpath = match[1] ? match[1].slice(1) : "";
|
|
161
162
|
if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
|
|
@@ -209,17 +210,17 @@ function createCoreGlobalPlugin(themePath) {
|
|
|
209
210
|
const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
|
|
210
211
|
let distPath = await resolveNodeModulesFile(
|
|
211
212
|
themePath,
|
|
212
|
-
|
|
213
|
+
path10.join("@onexapis", "core", "dist", distFileName)
|
|
213
214
|
);
|
|
214
215
|
if (!distPath) {
|
|
215
216
|
distPath = await resolveNodeModulesFile(
|
|
216
217
|
__dirname,
|
|
217
|
-
|
|
218
|
+
path10.join("@onexapis", "core", "dist", distFileName)
|
|
218
219
|
);
|
|
219
220
|
}
|
|
220
221
|
try {
|
|
221
222
|
if (!distPath) throw new Error("not found");
|
|
222
|
-
const distContent = await
|
|
223
|
+
const distContent = await fs9.readFile(distPath, "utf-8");
|
|
223
224
|
const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
|
|
224
225
|
for (const m of exportMatches) {
|
|
225
226
|
const names = m[1].split(",").map((n) => {
|
|
@@ -448,7 +449,7 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
448
449
|
const pages = {};
|
|
449
450
|
for (const ext of [".ts", ".js"]) {
|
|
450
451
|
try {
|
|
451
|
-
const mod = await jiti.import(
|
|
452
|
+
const mod = await jiti.import(path10.join(themePath, `theme.config${ext}`));
|
|
452
453
|
themeConfig = mod.default || mod;
|
|
453
454
|
break;
|
|
454
455
|
} catch {
|
|
@@ -456,20 +457,20 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
456
457
|
}
|
|
457
458
|
for (const ext of [".ts", ".js"]) {
|
|
458
459
|
try {
|
|
459
|
-
const mod = await jiti.import(
|
|
460
|
+
const mod = await jiti.import(path10.join(themePath, `theme.layout${ext}`));
|
|
460
461
|
layoutConfig = mod.default || mod;
|
|
461
462
|
break;
|
|
462
463
|
} catch {
|
|
463
464
|
}
|
|
464
465
|
}
|
|
465
466
|
const schemas = {};
|
|
466
|
-
const sectionsDir =
|
|
467
|
+
const sectionsDir = path10.join(themePath, "sections");
|
|
467
468
|
try {
|
|
468
|
-
const sectionDirs = await
|
|
469
|
+
const sectionDirs = await fs9.readdir(sectionsDir);
|
|
469
470
|
for (const dir of sectionDirs) {
|
|
470
|
-
const schemaFile =
|
|
471
|
+
const schemaFile = path10.join(sectionsDir, dir, `${dir}.schema.ts`);
|
|
471
472
|
try {
|
|
472
|
-
await
|
|
473
|
+
await fs9.access(schemaFile);
|
|
473
474
|
const mod = await jiti.import(schemaFile);
|
|
474
475
|
for (const [key, value] of Object.entries(mod)) {
|
|
475
476
|
if (key.endsWith("Schema") && value && typeof value === "object" && value.type) {
|
|
@@ -481,14 +482,14 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
481
482
|
}
|
|
482
483
|
} catch {
|
|
483
484
|
}
|
|
484
|
-
const pagesDir =
|
|
485
|
+
const pagesDir = path10.join(themePath, "pages");
|
|
485
486
|
try {
|
|
486
|
-
const files = await
|
|
487
|
+
const files = await fs9.readdir(pagesDir);
|
|
487
488
|
for (const file of files) {
|
|
488
489
|
if (!file.match(/\.(ts|js)$/)) continue;
|
|
489
490
|
const name = file.replace(/\.(ts|js)$/, "");
|
|
490
491
|
try {
|
|
491
|
-
const mod = await jiti.import(
|
|
492
|
+
const mod = await jiti.import(path10.join(pagesDir, file));
|
|
492
493
|
const config = mod.default || mod;
|
|
493
494
|
const sections = (config.sections || []).map((section) => {
|
|
494
495
|
const schema = schemas[section.type];
|
|
@@ -516,8 +517,8 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
516
517
|
}
|
|
517
518
|
} catch {
|
|
518
519
|
}
|
|
519
|
-
await
|
|
520
|
-
|
|
520
|
+
await fs9.writeFile(
|
|
521
|
+
path10.join(outputDir, "theme-data.json"),
|
|
521
522
|
JSON.stringify(
|
|
522
523
|
{
|
|
523
524
|
themeId,
|
|
@@ -540,53 +541,53 @@ async function generateThemeData(themePath, outputDir, themeId) {
|
|
|
540
541
|
logger.info(`Generated theme-data.json (${Object.keys(pages).length} pages)`);
|
|
541
542
|
}
|
|
542
543
|
async function contentHashEntry(outputDir) {
|
|
543
|
-
const entryPath =
|
|
544
|
-
const mapPath =
|
|
544
|
+
const entryPath = path10.join(outputDir, "bundle-entry.js");
|
|
545
|
+
const mapPath = path10.join(outputDir, "bundle-entry.js.map");
|
|
545
546
|
const oldFiles = await glob("bundle-entry-*.js*", { cwd: outputDir });
|
|
546
547
|
for (const f of oldFiles) {
|
|
547
|
-
await
|
|
548
|
+
await fs9.unlink(path10.join(outputDir, f));
|
|
548
549
|
}
|
|
549
550
|
let entryContent;
|
|
550
551
|
try {
|
|
551
|
-
entryContent = await
|
|
552
|
+
entryContent = await fs9.readFile(entryPath, "utf-8");
|
|
552
553
|
} catch {
|
|
553
|
-
const indexPath =
|
|
554
|
+
const indexPath = path10.join(outputDir, "index.js");
|
|
554
555
|
try {
|
|
555
|
-
entryContent = await
|
|
556
|
+
entryContent = await fs9.readFile(indexPath, "utf-8");
|
|
556
557
|
} catch {
|
|
557
558
|
logger.warning("No entry file found in output, skipping content hash");
|
|
558
559
|
return;
|
|
559
560
|
}
|
|
560
|
-
const hash2 =
|
|
561
|
+
const hash2 = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
561
562
|
const hashedName2 = `bundle-entry-${hash2}.js`;
|
|
562
|
-
const indexMapPath =
|
|
563
|
+
const indexMapPath = path10.join(outputDir, "index.js.map");
|
|
563
564
|
const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
|
|
564
565
|
entryContent = entryContent.replace(
|
|
565
566
|
/\/\/# sourceMappingURL=index\.js\.map/,
|
|
566
567
|
`//# sourceMappingURL=${hashedMapName2}`
|
|
567
568
|
);
|
|
568
|
-
await
|
|
569
|
-
await
|
|
569
|
+
await fs9.writeFile(path10.join(outputDir, hashedName2), entryContent);
|
|
570
|
+
await fs9.unlink(indexPath);
|
|
570
571
|
try {
|
|
571
|
-
await
|
|
572
|
-
await
|
|
572
|
+
await fs9.access(indexMapPath);
|
|
573
|
+
await fs9.rename(indexMapPath, path10.join(outputDir, hashedMapName2));
|
|
573
574
|
} catch {
|
|
574
575
|
}
|
|
575
576
|
logger.info(`Entry hashed: ${hashedName2}`);
|
|
576
577
|
return;
|
|
577
578
|
}
|
|
578
|
-
const hash =
|
|
579
|
+
const hash = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
|
|
579
580
|
const hashedName = `bundle-entry-${hash}.js`;
|
|
580
581
|
const hashedMapName = `bundle-entry-${hash}.js.map`;
|
|
581
582
|
entryContent = entryContent.replace(
|
|
582
583
|
/\/\/# sourceMappingURL=bundle-entry\.js\.map/,
|
|
583
584
|
`//# sourceMappingURL=${hashedMapName}`
|
|
584
585
|
);
|
|
585
|
-
await
|
|
586
|
-
await
|
|
586
|
+
await fs9.writeFile(path10.join(outputDir, hashedName), entryContent);
|
|
587
|
+
await fs9.unlink(entryPath);
|
|
587
588
|
try {
|
|
588
|
-
await
|
|
589
|
-
await
|
|
589
|
+
await fs9.access(mapPath);
|
|
590
|
+
await fs9.rename(mapPath, path10.join(outputDir, hashedMapName));
|
|
590
591
|
} catch {
|
|
591
592
|
}
|
|
592
593
|
logger.info(`Entry hashed: ${hashedName}`);
|
|
@@ -598,7 +599,7 @@ async function extractDataRequirements(themePath) {
|
|
|
598
599
|
const requirements = {};
|
|
599
600
|
for (const file of schemaFiles) {
|
|
600
601
|
try {
|
|
601
|
-
const mod = await jiti.import(
|
|
602
|
+
const mod = await jiti.import(path10.join(themePath, file));
|
|
602
603
|
const exports$1 = mod;
|
|
603
604
|
for (const value of Object.values(exports$1)) {
|
|
604
605
|
if (value && typeof value === "object" && typeof value.type === "string" && value.dataRequirements && typeof value.dataRequirements === "object") {
|
|
@@ -617,8 +618,8 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
617
618
|
let version2 = "1.0.0";
|
|
618
619
|
let themeId = themeName;
|
|
619
620
|
try {
|
|
620
|
-
const pkgContent = await
|
|
621
|
-
|
|
621
|
+
const pkgContent = await fs9.readFile(
|
|
622
|
+
path10.join(themePath, "package.json"),
|
|
622
623
|
"utf-8"
|
|
623
624
|
);
|
|
624
625
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -636,7 +637,7 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
636
637
|
const dataRequirements = await extractDataRequirements(themePath);
|
|
637
638
|
let hasThemeConfig = false;
|
|
638
639
|
try {
|
|
639
|
-
await
|
|
640
|
+
await fs9.access(path10.join(themePath, "theme.config.ts"));
|
|
640
641
|
hasThemeConfig = true;
|
|
641
642
|
} catch {
|
|
642
643
|
}
|
|
@@ -677,24 +678,24 @@ async function generateManifest(themeName, themePath, outputDir) {
|
|
|
677
678
|
// Section data requirements for server-side prefetching (keyed by section type)
|
|
678
679
|
dataRequirements
|
|
679
680
|
};
|
|
680
|
-
await
|
|
681
|
-
|
|
681
|
+
await fs9.writeFile(
|
|
682
|
+
path10.join(outputDir, "manifest.json"),
|
|
682
683
|
JSON.stringify(manifest, null, 2)
|
|
683
684
|
);
|
|
684
685
|
}
|
|
685
686
|
async function compileStandaloneTheme(themePath, themeName) {
|
|
686
|
-
const outputDir =
|
|
687
|
-
const bundleEntry =
|
|
688
|
-
const indexEntry =
|
|
687
|
+
const outputDir = path10.join(themePath, "dist");
|
|
688
|
+
const bundleEntry = path10.join(themePath, "bundle-entry.ts");
|
|
689
|
+
const indexEntry = path10.join(themePath, "index.ts");
|
|
689
690
|
let entryPoint = indexEntry;
|
|
690
691
|
try {
|
|
691
|
-
await
|
|
692
|
+
await fs9.access(bundleEntry);
|
|
692
693
|
entryPoint = bundleEntry;
|
|
693
694
|
} catch {
|
|
694
695
|
}
|
|
695
|
-
const shimPath =
|
|
696
|
-
await
|
|
697
|
-
await
|
|
696
|
+
const shimPath = path10.join(outputDir, ".process-shim.js");
|
|
697
|
+
await fs9.mkdir(outputDir, { recursive: true });
|
|
698
|
+
await fs9.writeFile(shimPath, PROCESS_SHIM);
|
|
698
699
|
const buildOptions = {
|
|
699
700
|
entryPoints: [entryPoint],
|
|
700
701
|
bundle: true,
|
|
@@ -744,7 +745,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
744
745
|
try {
|
|
745
746
|
const result = await esbuild.build(buildOptions);
|
|
746
747
|
try {
|
|
747
|
-
await
|
|
748
|
+
await fs9.unlink(shimPath);
|
|
748
749
|
} catch {
|
|
749
750
|
}
|
|
750
751
|
await contentHashEntry(outputDir);
|
|
@@ -763,7 +764,7 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
763
764
|
return true;
|
|
764
765
|
} catch (error) {
|
|
765
766
|
try {
|
|
766
|
-
await
|
|
767
|
+
await fs9.unlink(shimPath);
|
|
767
768
|
} catch {
|
|
768
769
|
}
|
|
769
770
|
logger.error(`esbuild compilation failed: ${error}`);
|
|
@@ -771,18 +772,18 @@ async function compileStandaloneTheme(themePath, themeName) {
|
|
|
771
772
|
}
|
|
772
773
|
}
|
|
773
774
|
async function compileStandaloneThemeDev(themePath, themeName) {
|
|
774
|
-
const outputDir =
|
|
775
|
-
const bundleEntry =
|
|
776
|
-
const indexEntry =
|
|
775
|
+
const outputDir = path10.join(themePath, "dist");
|
|
776
|
+
const bundleEntry = path10.join(themePath, "bundle-entry.ts");
|
|
777
|
+
const indexEntry = path10.join(themePath, "index.ts");
|
|
777
778
|
let entryPoint = indexEntry;
|
|
778
779
|
try {
|
|
779
|
-
await
|
|
780
|
+
await fs9.access(bundleEntry);
|
|
780
781
|
entryPoint = bundleEntry;
|
|
781
782
|
} catch {
|
|
782
783
|
}
|
|
783
|
-
const shimPath =
|
|
784
|
-
await
|
|
785
|
-
await
|
|
784
|
+
const shimPath = path10.join(outputDir, ".process-shim.js");
|
|
785
|
+
await fs9.mkdir(outputDir, { recursive: true });
|
|
786
|
+
await fs9.writeFile(shimPath, PROCESS_SHIM);
|
|
786
787
|
const buildOptions = {
|
|
787
788
|
entryPoints: [entryPoint],
|
|
788
789
|
bundle: true,
|
|
@@ -835,18 +836,18 @@ async function compileStandaloneThemeDev(themePath, themeName) {
|
|
|
835
836
|
return { context: context2, outputDir };
|
|
836
837
|
}
|
|
837
838
|
async function compilePreviewRuntime(themePath) {
|
|
838
|
-
const outputDir =
|
|
839
|
-
await
|
|
840
|
-
const outputPath =
|
|
839
|
+
const outputDir = path10.join(themePath, "dist");
|
|
840
|
+
await fs9.mkdir(outputDir, { recursive: true });
|
|
841
|
+
const outputPath = path10.join(outputDir, "preview-runtime.js");
|
|
841
842
|
const locations = [
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
843
|
+
path10.join(__dirname, "..", "preview", "preview-app.tsx"),
|
|
844
|
+
path10.join(__dirname, "preview", "preview-app.tsx"),
|
|
845
|
+
path10.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
|
|
845
846
|
];
|
|
846
847
|
let previewEntryPath = null;
|
|
847
848
|
for (const loc of locations) {
|
|
848
849
|
try {
|
|
849
|
-
await
|
|
850
|
+
await fs9.access(loc);
|
|
850
851
|
previewEntryPath = loc;
|
|
851
852
|
break;
|
|
852
853
|
} catch {
|
|
@@ -929,10 +930,10 @@ ${locations.join("\n")}`
|
|
|
929
930
|
if (!lucideScanned) {
|
|
930
931
|
lucideScanned = true;
|
|
931
932
|
const coreSrcCandidates = [
|
|
932
|
-
|
|
933
|
-
|
|
933
|
+
path10.join(themePath, "node_modules", "@onexapis", "core", "src"),
|
|
934
|
+
path10.join(themePath, "..", "..", "packages", "core", "src"),
|
|
934
935
|
// monorepo sibling
|
|
935
|
-
|
|
936
|
+
path10.join(
|
|
936
937
|
__dirname,
|
|
937
938
|
"..",
|
|
938
939
|
"..",
|
|
@@ -947,7 +948,7 @@ ${locations.join("\n")}`
|
|
|
947
948
|
let coreSourceDir = null;
|
|
948
949
|
for (const candidate of coreSrcCandidates) {
|
|
949
950
|
try {
|
|
950
|
-
await
|
|
951
|
+
await fs9.access(candidate);
|
|
951
952
|
coreSourceDir = candidate;
|
|
952
953
|
break;
|
|
953
954
|
} catch {
|
|
@@ -966,21 +967,21 @@ ${locations.join("\n")}`
|
|
|
966
967
|
}
|
|
967
968
|
} else {
|
|
968
969
|
const coreDistCandidates = [
|
|
969
|
-
|
|
970
|
+
path10.join(themePath, "node_modules", "@onexapis", "core", "dist")
|
|
970
971
|
];
|
|
971
972
|
const resolvedDist = await resolveNodeModulesFile(
|
|
972
973
|
__dirname,
|
|
973
|
-
|
|
974
|
+
path10.join("@onexapis", "core", "dist")
|
|
974
975
|
);
|
|
975
976
|
if (resolvedDist) coreDistCandidates.push(resolvedDist);
|
|
976
977
|
for (const candidate of coreDistCandidates) {
|
|
977
978
|
try {
|
|
978
|
-
await
|
|
979
|
+
await fs9.access(candidate);
|
|
979
980
|
const mjsFiles = await glob("*.mjs", { cwd: candidate });
|
|
980
981
|
const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
|
|
981
982
|
for (const file of mjsFiles) {
|
|
982
|
-
const content = await
|
|
983
|
-
|
|
983
|
+
const content = await fs9.readFile(
|
|
984
|
+
path10.join(candidate, file),
|
|
984
985
|
"utf-8"
|
|
985
986
|
);
|
|
986
987
|
for (const match of content.matchAll(importRegex)) {
|
|
@@ -1035,7 +1036,7 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
|
|
|
1035
1036
|
const req = createRequire(import.meta.url || __filename);
|
|
1036
1037
|
const cjsPath = req.resolve("framer-motion");
|
|
1037
1038
|
const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
|
|
1038
|
-
const esmEntry =
|
|
1039
|
+
const esmEntry = path10.join(pkgDir, "dist", "es", "index.mjs");
|
|
1039
1040
|
const { existsSync } = await import('fs');
|
|
1040
1041
|
if (existsSync(esmEntry)) {
|
|
1041
1042
|
return { path: esmEntry, namespace: "file" };
|
|
@@ -1134,8 +1135,8 @@ export function headers() { return new Headers(); }
|
|
|
1134
1135
|
});
|
|
1135
1136
|
}
|
|
1136
1137
|
};
|
|
1137
|
-
const shimPath =
|
|
1138
|
-
await
|
|
1138
|
+
const shimPath = path10.join(outputDir, ".process-shim-preview.js");
|
|
1139
|
+
await fs9.writeFile(shimPath, PROCESS_SHIM);
|
|
1139
1140
|
await esbuild.build({
|
|
1140
1141
|
entryPoints: [previewEntryPath],
|
|
1141
1142
|
bundle: true,
|
|
@@ -1170,7 +1171,7 @@ export function headers() { return new Headers(); }
|
|
|
1170
1171
|
}
|
|
1171
1172
|
});
|
|
1172
1173
|
try {
|
|
1173
|
-
await
|
|
1174
|
+
await fs9.unlink(shimPath);
|
|
1174
1175
|
} catch {
|
|
1175
1176
|
}
|
|
1176
1177
|
return outputPath;
|
|
@@ -1303,18 +1304,18 @@ async function renderTemplate(templatePath, data) {
|
|
|
1303
1304
|
return ejs.render(template, data);
|
|
1304
1305
|
}
|
|
1305
1306
|
async function writeFile(filePath, content) {
|
|
1306
|
-
await fs.ensureDir(
|
|
1307
|
+
await fs.ensureDir(path10.dirname(filePath));
|
|
1307
1308
|
await fs.writeFile(filePath, content, "utf-8");
|
|
1308
1309
|
}
|
|
1309
1310
|
function getTemplatesDir() {
|
|
1310
1311
|
const locations = [
|
|
1311
|
-
|
|
1312
|
+
path10.join(__dirname, "../../templates"),
|
|
1312
1313
|
// Development
|
|
1313
|
-
|
|
1314
|
+
path10.join(__dirname, "../templates"),
|
|
1314
1315
|
// Production (dist/)
|
|
1315
|
-
|
|
1316
|
+
path10.join(process.cwd(), "templates"),
|
|
1316
1317
|
// Fallback
|
|
1317
|
-
|
|
1318
|
+
path10.join(process.cwd(), "packages/cli/templates")
|
|
1318
1319
|
// Monorepo
|
|
1319
1320
|
];
|
|
1320
1321
|
for (const location of locations) {
|
|
@@ -1326,7 +1327,7 @@ function getTemplatesDir() {
|
|
|
1326
1327
|
}
|
|
1327
1328
|
async function copyTemplate(templateName, targetDir, data) {
|
|
1328
1329
|
const templatesDir = getTemplatesDir();
|
|
1329
|
-
const templateDir =
|
|
1330
|
+
const templateDir = path10.join(templatesDir, templateName);
|
|
1330
1331
|
if (!fs.existsSync(templateDir)) {
|
|
1331
1332
|
throw new Error(
|
|
1332
1333
|
`Template "${templateName}" not found at ${templateDir}. Available templates: ${fs.readdirSync(templatesDir).join(", ")}`
|
|
@@ -1335,8 +1336,8 @@ async function copyTemplate(templateName, targetDir, data) {
|
|
|
1335
1336
|
await fs.ensureDir(targetDir);
|
|
1336
1337
|
const files = await fs.readdir(templateDir);
|
|
1337
1338
|
for (const file of files) {
|
|
1338
|
-
const templatePath =
|
|
1339
|
-
const targetPath =
|
|
1339
|
+
const templatePath = path10.join(templateDir, file);
|
|
1340
|
+
const targetPath = path10.join(targetDir, file);
|
|
1340
1341
|
const stat = await fs.stat(templatePath);
|
|
1341
1342
|
if (stat.isDirectory()) {
|
|
1342
1343
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
@@ -1353,8 +1354,8 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1353
1354
|
await fs.ensureDir(targetDir);
|
|
1354
1355
|
const files = await fs.readdir(templateDir);
|
|
1355
1356
|
for (const file of files) {
|
|
1356
|
-
const templatePath =
|
|
1357
|
-
const targetPath =
|
|
1357
|
+
const templatePath = path10.join(templateDir, file);
|
|
1358
|
+
const targetPath = path10.join(targetDir, file);
|
|
1358
1359
|
const stat = await fs.stat(templatePath);
|
|
1359
1360
|
if (stat.isDirectory()) {
|
|
1360
1361
|
await copyTemplateDir(templatePath, targetPath, data);
|
|
@@ -1369,32 +1370,32 @@ async function copyTemplateDir(templateDir, targetDir, data) {
|
|
|
1369
1370
|
}
|
|
1370
1371
|
function getProjectRoot() {
|
|
1371
1372
|
let currentDir = process.cwd();
|
|
1372
|
-
while (currentDir !==
|
|
1373
|
-
const packageJsonPath =
|
|
1373
|
+
while (currentDir !== path10.parse(currentDir).root) {
|
|
1374
|
+
const packageJsonPath = path10.join(currentDir, "package.json");
|
|
1374
1375
|
if (fs.existsSync(packageJsonPath)) {
|
|
1375
1376
|
const packageJson = fs.readJsonSync(packageJsonPath);
|
|
1376
|
-
if (packageJson.workspaces || fs.existsSync(
|
|
1377
|
+
if (packageJson.workspaces || fs.existsSync(path10.join(currentDir, "src/themes")) || fs.existsSync(path10.join(currentDir, "themes"))) {
|
|
1377
1378
|
return currentDir;
|
|
1378
1379
|
}
|
|
1379
1380
|
}
|
|
1380
|
-
currentDir =
|
|
1381
|
+
currentDir = path10.dirname(currentDir);
|
|
1381
1382
|
}
|
|
1382
1383
|
return process.cwd();
|
|
1383
1384
|
}
|
|
1384
1385
|
function getThemesDir() {
|
|
1385
1386
|
const root = getProjectRoot();
|
|
1386
|
-
if (fs.existsSync(
|
|
1387
|
-
return
|
|
1388
|
-
if (fs.existsSync(
|
|
1389
|
-
return
|
|
1390
|
-
return
|
|
1387
|
+
if (fs.existsSync(path10.join(root, "themes")))
|
|
1388
|
+
return path10.join(root, "themes");
|
|
1389
|
+
if (fs.existsSync(path10.join(root, "src/themes")))
|
|
1390
|
+
return path10.join(root, "src/themes");
|
|
1391
|
+
return path10.dirname(root);
|
|
1391
1392
|
}
|
|
1392
1393
|
function getFeaturesDir() {
|
|
1393
|
-
return
|
|
1394
|
+
return path10.join(getProjectRoot(), "src/features");
|
|
1394
1395
|
}
|
|
1395
1396
|
function isOneXProject() {
|
|
1396
1397
|
const root = getProjectRoot();
|
|
1397
|
-
return fs.existsSync(
|
|
1398
|
+
return fs.existsSync(path10.join(root, "themes")) || fs.existsSync(path10.join(root, "src/themes")) || fs.existsSync(path10.join(root, "theme.config.ts")) || fs.existsSync(path10.join(root, "bundle-entry.ts"));
|
|
1398
1399
|
}
|
|
1399
1400
|
function ensureOneXProject() {
|
|
1400
1401
|
if (!isOneXProject()) {
|
|
@@ -1410,13 +1411,13 @@ function listThemes() {
|
|
|
1410
1411
|
return [];
|
|
1411
1412
|
}
|
|
1412
1413
|
return fs.readdirSync(themesDir).filter((name) => {
|
|
1413
|
-
const themePath =
|
|
1414
|
-
return fs.statSync(themePath).isDirectory() && (fs.existsSync(
|
|
1414
|
+
const themePath = path10.join(themesDir, name);
|
|
1415
|
+
return fs.statSync(themePath).isDirectory() && (fs.existsSync(path10.join(themePath, "theme.config.ts")) || fs.existsSync(path10.join(themePath, "bundle-entry.ts")) || fs.existsSync(path10.join(themePath, "manifest.ts")));
|
|
1415
1416
|
});
|
|
1416
1417
|
}
|
|
1417
1418
|
function themeExists(themeName) {
|
|
1418
|
-
const themePath =
|
|
1419
|
-
return fs.existsSync(themePath) && (fs.existsSync(
|
|
1419
|
+
const themePath = path10.join(getThemesDir(), themeName);
|
|
1420
|
+
return fs.existsSync(themePath) && (fs.existsSync(path10.join(themePath, "theme.config.ts")) || fs.existsSync(path10.join(themePath, "bundle-entry.ts")) || fs.existsSync(path10.join(themePath, "manifest.ts")));
|
|
1420
1421
|
}
|
|
1421
1422
|
function detectPackageManager() {
|
|
1422
1423
|
const userAgent = process.env.npm_config_user_agent || "";
|
|
@@ -1424,24 +1425,22 @@ function detectPackageManager() {
|
|
|
1424
1425
|
if (userAgent.includes("yarn")) return "yarn";
|
|
1425
1426
|
if (userAgent.includes("bun")) return "bun";
|
|
1426
1427
|
const cwd = process.cwd();
|
|
1427
|
-
if (fs.existsSync(
|
|
1428
|
-
if (fs.existsSync(
|
|
1429
|
-
if (fs.existsSync(
|
|
1428
|
+
if (fs.existsSync(path10.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
1429
|
+
if (fs.existsSync(path10.join(cwd, "yarn.lock"))) return "yarn";
|
|
1430
|
+
if (fs.existsSync(path10.join(cwd, "bun.lockb"))) return "bun";
|
|
1430
1431
|
return "npm";
|
|
1431
1432
|
}
|
|
1432
1433
|
async function installDependencies(projectPath, packageManager = "npm") {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
cwd: projectPath,
|
|
1438
|
-
stdio: "inherit"
|
|
1439
|
-
});
|
|
1440
|
-
resolve();
|
|
1441
|
-
} catch (error) {
|
|
1442
|
-
reject(error);
|
|
1443
|
-
}
|
|
1434
|
+
const args = packageManager === "yarn" ? [] : ["install"];
|
|
1435
|
+
const result = spawn2.sync(packageManager, args, {
|
|
1436
|
+
cwd: projectPath,
|
|
1437
|
+
stdio: "inherit"
|
|
1444
1438
|
});
|
|
1439
|
+
if (result.status !== 0) {
|
|
1440
|
+
throw new Error(
|
|
1441
|
+
`${packageManager} install failed with exit code ${result.status}`
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1445
1444
|
}
|
|
1446
1445
|
|
|
1447
1446
|
// src/commands/init.ts
|
|
@@ -1475,6 +1474,184 @@ function getValidCategories() {
|
|
|
1475
1474
|
"contact"
|
|
1476
1475
|
];
|
|
1477
1476
|
}
|
|
1477
|
+
var AUTH_DIR = path10.join(os.homedir(), ".onexthm");
|
|
1478
|
+
var AUTH_FILE = path10.join(AUTH_DIR, "auth.json");
|
|
1479
|
+
function getApiUrl() {
|
|
1480
|
+
return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
|
|
1481
|
+
}
|
|
1482
|
+
async function saveAuthTokens(tokens) {
|
|
1483
|
+
await fs.ensureDir(AUTH_DIR);
|
|
1484
|
+
const key = getMachineKey();
|
|
1485
|
+
const data = JSON.stringify(tokens);
|
|
1486
|
+
const encrypted = encrypt(data, key);
|
|
1487
|
+
await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
|
|
1488
|
+
}
|
|
1489
|
+
function loadAuthTokens() {
|
|
1490
|
+
try {
|
|
1491
|
+
if (!fs.existsSync(AUTH_FILE)) return null;
|
|
1492
|
+
const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
|
|
1493
|
+
const key = getMachineKey();
|
|
1494
|
+
const data = decrypt(encrypted, key);
|
|
1495
|
+
return JSON.parse(data);
|
|
1496
|
+
} catch {
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
async function clearAuthTokens() {
|
|
1501
|
+
try {
|
|
1502
|
+
await fs.remove(AUTH_FILE);
|
|
1503
|
+
} catch {
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
function isTokenExpired(tokens) {
|
|
1507
|
+
return Date.now() / 1e3 > tokens.expiresAt - 60;
|
|
1508
|
+
}
|
|
1509
|
+
async function getValidTokens() {
|
|
1510
|
+
const tokens = loadAuthTokens();
|
|
1511
|
+
if (!tokens) return null;
|
|
1512
|
+
if (!isTokenExpired(tokens)) return tokens;
|
|
1513
|
+
try {
|
|
1514
|
+
const apiUrl = getApiUrl();
|
|
1515
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
1516
|
+
method: "POST",
|
|
1517
|
+
headers: { "Content-Type": "application/json" },
|
|
1518
|
+
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
1519
|
+
});
|
|
1520
|
+
if (!response.ok) {
|
|
1521
|
+
await clearAuthTokens();
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
const data = await response.json();
|
|
1525
|
+
const body = data.statusCode ? data.body : data;
|
|
1526
|
+
const refreshed = {
|
|
1527
|
+
...tokens,
|
|
1528
|
+
accessToken: body.AccessToken || tokens.accessToken,
|
|
1529
|
+
idToken: body.IdToken || tokens.idToken,
|
|
1530
|
+
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
1531
|
+
};
|
|
1532
|
+
await saveAuthTokens(refreshed);
|
|
1533
|
+
return refreshed;
|
|
1534
|
+
} catch {
|
|
1535
|
+
await clearAuthTokens();
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
async function authenticatedFetch(url, init) {
|
|
1540
|
+
const tokens = await getValidTokens();
|
|
1541
|
+
if (!tokens) {
|
|
1542
|
+
throw new Error("Not logged in. Run: onexthm login");
|
|
1543
|
+
}
|
|
1544
|
+
const headers = new Headers(init?.headers);
|
|
1545
|
+
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
1546
|
+
headers.set("Content-Type", "application/json");
|
|
1547
|
+
return fetch(url, { ...init, headers });
|
|
1548
|
+
}
|
|
1549
|
+
function getMachineKey() {
|
|
1550
|
+
let seed;
|
|
1551
|
+
if (process.platform === "darwin") {
|
|
1552
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1553
|
+
} else if (process.platform === "linux") {
|
|
1554
|
+
try {
|
|
1555
|
+
seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
1556
|
+
} catch {
|
|
1557
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1558
|
+
}
|
|
1559
|
+
} else {
|
|
1560
|
+
seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
|
|
1561
|
+
}
|
|
1562
|
+
return crypto.createHash("sha256").update(seed).digest();
|
|
1563
|
+
}
|
|
1564
|
+
function encrypt(text, key) {
|
|
1565
|
+
const iv = crypto.randomBytes(16);
|
|
1566
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
1567
|
+
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
1568
|
+
encrypted += cipher.final("hex");
|
|
1569
|
+
const tag = cipher.getAuthTag();
|
|
1570
|
+
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
|
|
1571
|
+
}
|
|
1572
|
+
function decrypt(text, key) {
|
|
1573
|
+
const [ivHex, tagHex, encrypted] = text.split(":");
|
|
1574
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
1575
|
+
const tag = Buffer.from(tagHex, "hex");
|
|
1576
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
1577
|
+
decipher.setAuthTag(tag);
|
|
1578
|
+
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
1579
|
+
decrypted += decipher.final("utf-8");
|
|
1580
|
+
return decrypted;
|
|
1581
|
+
}
|
|
1582
|
+
function parseJwtClaims(idToken) {
|
|
1583
|
+
try {
|
|
1584
|
+
const payload = idToken.split(".")[1];
|
|
1585
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
1586
|
+
return JSON.parse(decoded);
|
|
1587
|
+
} catch {
|
|
1588
|
+
return {};
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
function getNpxCommand() {
|
|
1592
|
+
return process.platform === "win32" ? "npx.cmd" : "npx";
|
|
1593
|
+
}
|
|
1594
|
+
function getDefaultMcpConfig() {
|
|
1595
|
+
return {
|
|
1596
|
+
mcpServers: {
|
|
1597
|
+
onexthm: {
|
|
1598
|
+
command: getNpxCommand(),
|
|
1599
|
+
args: ["-y", "@onexapis/theme-mcp"]
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function ensureMcpJson(projectPath, figmaApiKey) {
|
|
1605
|
+
const mcpJsonPath = path10.join(projectPath, ".mcp.json");
|
|
1606
|
+
const npx = getNpxCommand();
|
|
1607
|
+
let mcpConfig;
|
|
1608
|
+
if (fs4.existsSync(mcpJsonPath)) {
|
|
1609
|
+
try {
|
|
1610
|
+
mcpConfig = JSON.parse(fs4.readFileSync(mcpJsonPath, "utf-8"));
|
|
1611
|
+
if (!mcpConfig.mcpServers) {
|
|
1612
|
+
mcpConfig.mcpServers = {};
|
|
1613
|
+
}
|
|
1614
|
+
} catch {
|
|
1615
|
+
mcpConfig = getDefaultMcpConfig();
|
|
1616
|
+
}
|
|
1617
|
+
} else {
|
|
1618
|
+
mcpConfig = getDefaultMcpConfig();
|
|
1619
|
+
}
|
|
1620
|
+
mcpConfig.mcpServers.onexthm = {
|
|
1621
|
+
command: npx,
|
|
1622
|
+
args: ["-y", "@onexapis/theme-mcp"]
|
|
1623
|
+
};
|
|
1624
|
+
if (figmaApiKey) {
|
|
1625
|
+
mcpConfig.mcpServers.figma = {
|
|
1626
|
+
command: npx,
|
|
1627
|
+
args: [
|
|
1628
|
+
"-y",
|
|
1629
|
+
"figma-developer-mcp",
|
|
1630
|
+
`--figma-api-key=${figmaApiKey}`,
|
|
1631
|
+
"--stdio"
|
|
1632
|
+
]
|
|
1633
|
+
};
|
|
1634
|
+
} else {
|
|
1635
|
+
delete mcpConfig.mcpServers.figma;
|
|
1636
|
+
}
|
|
1637
|
+
fs4.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
1638
|
+
ensureThemeMcpDependency(projectPath);
|
|
1639
|
+
}
|
|
1640
|
+
function ensureThemeMcpDependency(projectPath) {
|
|
1641
|
+
const pkgJsonPath = path10.join(projectPath, "package.json");
|
|
1642
|
+
if (!fs4.existsSync(pkgJsonPath)) return;
|
|
1643
|
+
try {
|
|
1644
|
+
const pkg = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
|
|
1645
|
+
if (!pkg.devDependencies) {
|
|
1646
|
+
pkg.devDependencies = {};
|
|
1647
|
+
}
|
|
1648
|
+
if (!pkg.devDependencies["@onexapis/theme-mcp"]) {
|
|
1649
|
+
pkg.devDependencies["@onexapis/theme-mcp"] = "^0.1.0";
|
|
1650
|
+
fs4.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1651
|
+
}
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1478
1655
|
|
|
1479
1656
|
// src/commands/init.ts
|
|
1480
1657
|
async function initCommand(projectName, options = {}) {
|
|
@@ -1492,7 +1669,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1492
1669
|
if (!validateThemeName(kebabName)) {
|
|
1493
1670
|
return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
|
|
1494
1671
|
}
|
|
1495
|
-
if (
|
|
1672
|
+
if (fs4.existsSync(path10.join(process.cwd(), kebabName))) {
|
|
1496
1673
|
return `Directory "${kebabName}" already exists`;
|
|
1497
1674
|
}
|
|
1498
1675
|
return true;
|
|
@@ -1503,11 +1680,46 @@ async function initCommand(projectName, options = {}) {
|
|
|
1503
1680
|
} else {
|
|
1504
1681
|
name = toKebabCase(projectName);
|
|
1505
1682
|
}
|
|
1506
|
-
const projectPath =
|
|
1507
|
-
if (
|
|
1683
|
+
const projectPath = path10.join(process.cwd(), name);
|
|
1684
|
+
if (fs4.existsSync(projectPath)) {
|
|
1508
1685
|
logger.error(`Directory "${name}" already exists.`);
|
|
1509
1686
|
process.exit(1);
|
|
1510
1687
|
}
|
|
1688
|
+
if (!options.yes) {
|
|
1689
|
+
try {
|
|
1690
|
+
const apiUrl = getApiUrl();
|
|
1691
|
+
const controller = new AbortController();
|
|
1692
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
1693
|
+
const response = await fetch(
|
|
1694
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(name)}/exists`,
|
|
1695
|
+
{ signal: controller.signal }
|
|
1696
|
+
);
|
|
1697
|
+
clearTimeout(timeout);
|
|
1698
|
+
if (response.ok) {
|
|
1699
|
+
const data2 = await response.json();
|
|
1700
|
+
const body = data2.statusCode ? data2.body : data2;
|
|
1701
|
+
if (body.exists && body.owner === "other") {
|
|
1702
|
+
logger.warning(
|
|
1703
|
+
`Theme ID "${name}" is already registered by another developer.
|
|
1704
|
+
You can still create this locally, but publishing will fail.
|
|
1705
|
+
Consider using a different name.`
|
|
1706
|
+
);
|
|
1707
|
+
const { proceed } = await inquirer.prompt([
|
|
1708
|
+
{
|
|
1709
|
+
type: "confirm",
|
|
1710
|
+
name: "proceed",
|
|
1711
|
+
message: "Continue anyway?",
|
|
1712
|
+
default: false
|
|
1713
|
+
}
|
|
1714
|
+
]);
|
|
1715
|
+
if (!proceed) {
|
|
1716
|
+
process.exit(0);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
} catch {
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1511
1723
|
let displayName;
|
|
1512
1724
|
let description;
|
|
1513
1725
|
let author;
|
|
@@ -1575,7 +1787,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
1575
1787
|
}
|
|
1576
1788
|
logger.startSpinner("Creating project structure...");
|
|
1577
1789
|
try {
|
|
1578
|
-
|
|
1790
|
+
fs4.mkdirSync(projectPath, { recursive: true });
|
|
1579
1791
|
await copyTemplate(template, projectPath, data);
|
|
1580
1792
|
await renameThemeInFiles(
|
|
1581
1793
|
projectPath,
|
|
@@ -1584,28 +1796,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
1584
1796
|
description,
|
|
1585
1797
|
author
|
|
1586
1798
|
);
|
|
1587
|
-
|
|
1588
|
-
if (fs2.existsSync(mcpJsonPath)) {
|
|
1589
|
-
let mcpContent = fs2.readFileSync(mcpJsonPath, "utf-8");
|
|
1590
|
-
if (figmaApiKey) {
|
|
1591
|
-
mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
|
|
1592
|
-
} else {
|
|
1593
|
-
try {
|
|
1594
|
-
const mcpJson = JSON.parse(mcpContent);
|
|
1595
|
-
delete mcpJson.mcpServers.figma;
|
|
1596
|
-
mcpContent = JSON.stringify(mcpJson, null, 2) + "\n";
|
|
1597
|
-
} catch {
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
fs2.writeFileSync(mcpJsonPath, mcpContent, "utf-8");
|
|
1601
|
-
}
|
|
1799
|
+
ensureMcpJson(projectPath, figmaApiKey || void 0);
|
|
1602
1800
|
logger.stopSpinner(true, "Project structure created!");
|
|
1603
1801
|
if (options.git) {
|
|
1604
1802
|
logger.startSpinner("Initializing git repository...");
|
|
1605
1803
|
try {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1804
|
+
spawn2.sync("git", ["init"], { cwd: projectPath, stdio: "ignore" });
|
|
1805
|
+
spawn2.sync("git", ["add", "."], { cwd: projectPath, stdio: "ignore" });
|
|
1806
|
+
spawn2.sync("git", ["commit", "-m", "Initial commit from onexthm init"], {
|
|
1609
1807
|
cwd: projectPath,
|
|
1610
1808
|
stdio: "ignore"
|
|
1611
1809
|
});
|
|
@@ -1657,16 +1855,16 @@ async function initCommand(projectName, options = {}) {
|
|
|
1657
1855
|
logger.error(
|
|
1658
1856
|
error instanceof Error ? error.message : "Unknown error occurred"
|
|
1659
1857
|
);
|
|
1660
|
-
if (
|
|
1661
|
-
|
|
1858
|
+
if (fs4.existsSync(projectPath)) {
|
|
1859
|
+
fs4.rmSync(projectPath, { recursive: true, force: true });
|
|
1662
1860
|
}
|
|
1663
1861
|
process.exit(1);
|
|
1664
1862
|
}
|
|
1665
1863
|
}
|
|
1666
1864
|
async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
|
|
1667
|
-
const configPath =
|
|
1668
|
-
if (
|
|
1669
|
-
let content =
|
|
1865
|
+
const configPath = path10.join(projectPath, "theme.config.ts");
|
|
1866
|
+
if (fs4.existsSync(configPath)) {
|
|
1867
|
+
let content = fs4.readFileSync(configPath, "utf-8");
|
|
1670
1868
|
content = content.replace(
|
|
1671
1869
|
/name: "My Simple Theme"/,
|
|
1672
1870
|
`name: "${displayName}"`
|
|
@@ -1675,11 +1873,11 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
|
|
|
1675
1873
|
/description: ".*?"/,
|
|
1676
1874
|
`description: "${description}"`
|
|
1677
1875
|
);
|
|
1678
|
-
|
|
1876
|
+
fs4.writeFileSync(configPath, content, "utf-8");
|
|
1679
1877
|
}
|
|
1680
|
-
const pkgPath =
|
|
1681
|
-
if (
|
|
1682
|
-
let content =
|
|
1878
|
+
const pkgPath = path10.join(projectPath, "package.json");
|
|
1879
|
+
if (fs4.existsSync(pkgPath)) {
|
|
1880
|
+
let content = fs4.readFileSync(pkgPath, "utf-8");
|
|
1683
1881
|
content = content.replace(
|
|
1684
1882
|
/@onex-themes\/my-simple/g,
|
|
1685
1883
|
`@onex-themes/${themeName}`
|
|
@@ -1688,7 +1886,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
|
|
|
1688
1886
|
/"description": ".*?"/,
|
|
1689
1887
|
`"description": "${description}"`
|
|
1690
1888
|
);
|
|
1691
|
-
|
|
1889
|
+
fs4.writeFileSync(pkgPath, content, "utf-8");
|
|
1692
1890
|
}
|
|
1693
1891
|
}
|
|
1694
1892
|
|
|
@@ -1699,10 +1897,10 @@ async function createSectionCommand(name, options) {
|
|
|
1699
1897
|
ensureOneXProject();
|
|
1700
1898
|
if (!options.theme) {
|
|
1701
1899
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
1702
|
-
(f) => fs.existsSync(
|
|
1900
|
+
(f) => fs.existsSync(path10.join(process.cwd(), f))
|
|
1703
1901
|
);
|
|
1704
1902
|
if (isStandaloneTheme) {
|
|
1705
|
-
options.theme =
|
|
1903
|
+
options.theme = path10.basename(process.cwd());
|
|
1706
1904
|
}
|
|
1707
1905
|
}
|
|
1708
1906
|
const sectionName = toKebabCase(name);
|
|
@@ -1765,35 +1963,35 @@ async function createSectionCommand(name, options) {
|
|
|
1765
1963
|
};
|
|
1766
1964
|
logger.startSpinner("Creating section files...");
|
|
1767
1965
|
try {
|
|
1768
|
-
const themePath =
|
|
1769
|
-
const sectionPath =
|
|
1966
|
+
const themePath = path10.join(getThemesDir(), themeName);
|
|
1967
|
+
const sectionPath = path10.join(themePath, "sections", sectionName);
|
|
1770
1968
|
const schemaContent = generateSectionSchema(data);
|
|
1771
1969
|
await writeFile(
|
|
1772
|
-
|
|
1970
|
+
path10.join(sectionPath, `${sectionName}.schema.ts`),
|
|
1773
1971
|
schemaContent
|
|
1774
1972
|
);
|
|
1775
1973
|
if (createTemplate) {
|
|
1776
1974
|
const templateContent = generateSectionTemplate(data);
|
|
1777
1975
|
await writeFile(
|
|
1778
|
-
|
|
1976
|
+
path10.join(sectionPath, `${sectionName}-default.tsx`),
|
|
1779
1977
|
templateContent
|
|
1780
1978
|
);
|
|
1781
1979
|
}
|
|
1782
1980
|
const indexContent = generateSectionIndex(data, createTemplate);
|
|
1783
|
-
await writeFile(
|
|
1981
|
+
await writeFile(path10.join(sectionPath, "index.ts"), indexContent);
|
|
1784
1982
|
logger.stopSpinner(true, "Section files created successfully!");
|
|
1785
1983
|
logger.newLine();
|
|
1786
1984
|
logger.section("Next steps:");
|
|
1787
1985
|
logger.log(
|
|
1788
|
-
` 1. Edit schema: ${
|
|
1986
|
+
` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(sectionPath, `${sectionName}.schema.ts`))}`
|
|
1789
1987
|
);
|
|
1790
1988
|
if (createTemplate) {
|
|
1791
1989
|
logger.log(
|
|
1792
|
-
` 2. Edit template: ${
|
|
1990
|
+
` 2. Edit template: ${path10.relative(process.cwd(), path10.join(sectionPath, `${sectionName}-default.tsx`))}`
|
|
1793
1991
|
);
|
|
1794
1992
|
}
|
|
1795
1993
|
logger.log(
|
|
1796
|
-
` 3. Add to theme manifest: ${
|
|
1994
|
+
` 3. Add to theme manifest: ${path10.relative(process.cwd(), path10.join(themePath, "manifest.ts"))}`
|
|
1797
1995
|
);
|
|
1798
1996
|
logger.newLine();
|
|
1799
1997
|
logger.success("Section created successfully!");
|
|
@@ -1941,10 +2139,10 @@ async function createBlockCommand(name, options) {
|
|
|
1941
2139
|
ensureOneXProject();
|
|
1942
2140
|
if (!options.theme) {
|
|
1943
2141
|
const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
|
|
1944
|
-
(f) => fs.existsSync(
|
|
2142
|
+
(f) => fs.existsSync(path10.join(process.cwd(), f))
|
|
1945
2143
|
);
|
|
1946
2144
|
if (isStandaloneTheme) {
|
|
1947
|
-
options.theme =
|
|
2145
|
+
options.theme = path10.basename(process.cwd());
|
|
1948
2146
|
}
|
|
1949
2147
|
}
|
|
1950
2148
|
const blockName = toKebabCase(name);
|
|
@@ -2019,24 +2217,24 @@ async function createBlockCommand(name, options) {
|
|
|
2019
2217
|
};
|
|
2020
2218
|
logger.startSpinner("Creating block files...");
|
|
2021
2219
|
try {
|
|
2022
|
-
const blockPath = scope === "shared" ?
|
|
2220
|
+
const blockPath = scope === "shared" ? path10.join(getFeaturesDir(), "blocks", blockName) : path10.join(getThemesDir(), themeName, "blocks", blockName);
|
|
2023
2221
|
const schemaContent = generateBlockSchema(data);
|
|
2024
2222
|
await writeFile(
|
|
2025
|
-
|
|
2223
|
+
path10.join(blockPath, `${blockName}.schema.ts`),
|
|
2026
2224
|
schemaContent
|
|
2027
2225
|
);
|
|
2028
2226
|
const componentContent = generateBlockComponent(data);
|
|
2029
|
-
await writeFile(
|
|
2227
|
+
await writeFile(path10.join(blockPath, `${blockName}.tsx`), componentContent);
|
|
2030
2228
|
const indexContent = generateBlockIndex(data);
|
|
2031
|
-
await writeFile(
|
|
2229
|
+
await writeFile(path10.join(blockPath, "index.ts"), indexContent);
|
|
2032
2230
|
logger.stopSpinner(true, "Block files created successfully!");
|
|
2033
2231
|
logger.newLine();
|
|
2034
2232
|
logger.section("Next steps:");
|
|
2035
2233
|
logger.log(
|
|
2036
|
-
` 1. Edit schema: ${
|
|
2234
|
+
` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(blockPath, `${blockName}.schema.ts`))}`
|
|
2037
2235
|
);
|
|
2038
2236
|
logger.log(
|
|
2039
|
-
` 2. Edit component: ${
|
|
2237
|
+
` 2. Edit component: ${path10.relative(process.cwd(), path10.join(blockPath, `${blockName}.tsx`))}`
|
|
2040
2238
|
);
|
|
2041
2239
|
logger.log(
|
|
2042
2240
|
` 3. Register in block registry: src/lib/registry/block-registry.ts`
|
|
@@ -2214,31 +2412,31 @@ async function createComponentCommand(name, options) {
|
|
|
2214
2412
|
};
|
|
2215
2413
|
logger.startSpinner("Creating component files...");
|
|
2216
2414
|
try {
|
|
2217
|
-
const componentPath =
|
|
2415
|
+
const componentPath = path10.join(
|
|
2218
2416
|
getFeaturesDir(),
|
|
2219
2417
|
"components",
|
|
2220
2418
|
componentName
|
|
2221
2419
|
);
|
|
2222
2420
|
const schemaContent = generateComponentSchema(data);
|
|
2223
2421
|
await writeFile(
|
|
2224
|
-
|
|
2422
|
+
path10.join(componentPath, `${componentName}.schema.ts`),
|
|
2225
2423
|
schemaContent
|
|
2226
2424
|
);
|
|
2227
2425
|
const componentContent = generateComponent(data);
|
|
2228
2426
|
await writeFile(
|
|
2229
|
-
|
|
2427
|
+
path10.join(componentPath, `${componentName}.tsx`),
|
|
2230
2428
|
componentContent
|
|
2231
2429
|
);
|
|
2232
2430
|
const indexContent = generateComponentIndex(data);
|
|
2233
|
-
await writeFile(
|
|
2431
|
+
await writeFile(path10.join(componentPath, "index.ts"), indexContent);
|
|
2234
2432
|
logger.stopSpinner(true, "Component files created successfully!");
|
|
2235
2433
|
logger.newLine();
|
|
2236
2434
|
logger.section("Next steps:");
|
|
2237
2435
|
logger.log(
|
|
2238
|
-
` 1. Edit schema: ${
|
|
2436
|
+
` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(componentPath, `${componentName}.schema.ts`))}`
|
|
2239
2437
|
);
|
|
2240
2438
|
logger.log(
|
|
2241
|
-
` 2. Edit component: ${
|
|
2439
|
+
` 2. Edit component: ${path10.relative(process.cwd(), path10.join(componentPath, `${componentName}.tsx`))}`
|
|
2242
2440
|
);
|
|
2243
2441
|
logger.log(
|
|
2244
2442
|
` 3. Register in component registry: src/lib/registry/component-registry.ts`
|
|
@@ -2395,13 +2593,13 @@ async function listSections(themeFilter) {
|
|
|
2395
2593
|
return;
|
|
2396
2594
|
}
|
|
2397
2595
|
for (const theme of themes) {
|
|
2398
|
-
const sectionsDir =
|
|
2596
|
+
const sectionsDir = path10.join(getThemesDir(), theme, "sections");
|
|
2399
2597
|
if (!fs.existsSync(sectionsDir)) {
|
|
2400
2598
|
continue;
|
|
2401
2599
|
}
|
|
2402
2600
|
const sections = fs.readdirSync(sectionsDir).filter((name) => {
|
|
2403
|
-
const sectionPath =
|
|
2404
|
-
return fs.statSync(sectionPath).isDirectory() && fs.existsSync(
|
|
2601
|
+
const sectionPath = path10.join(sectionsDir, name);
|
|
2602
|
+
return fs.statSync(sectionPath).isDirectory() && fs.existsSync(path10.join(sectionPath, "index.ts"));
|
|
2405
2603
|
});
|
|
2406
2604
|
if (sections.length > 0) {
|
|
2407
2605
|
logger.log(chalk4.cyan(`
|
|
@@ -2415,11 +2613,11 @@ async function listSections(themeFilter) {
|
|
|
2415
2613
|
}
|
|
2416
2614
|
async function listBlocks(themeFilter) {
|
|
2417
2615
|
logger.section("\u{1F9F1} Blocks");
|
|
2418
|
-
const sharedBlocksDir =
|
|
2616
|
+
const sharedBlocksDir = path10.join(getFeaturesDir(), "blocks");
|
|
2419
2617
|
if (fs.existsSync(sharedBlocksDir)) {
|
|
2420
2618
|
const sharedBlocks = fs.readdirSync(sharedBlocksDir).filter((name) => {
|
|
2421
|
-
const blockPath =
|
|
2422
|
-
return fs.statSync(blockPath).isDirectory() && fs.existsSync(
|
|
2619
|
+
const blockPath = path10.join(sharedBlocksDir, name);
|
|
2620
|
+
return fs.statSync(blockPath).isDirectory() && fs.existsSync(path10.join(blockPath, "index.ts"));
|
|
2423
2621
|
});
|
|
2424
2622
|
if (sharedBlocks.length > 0) {
|
|
2425
2623
|
logger.log(chalk4.cyan("\n Shared:"));
|
|
@@ -2430,13 +2628,13 @@ async function listBlocks(themeFilter) {
|
|
|
2430
2628
|
}
|
|
2431
2629
|
const themes = themeFilter ? [themeFilter] : listThemes();
|
|
2432
2630
|
for (const theme of themes) {
|
|
2433
|
-
const blocksDir =
|
|
2631
|
+
const blocksDir = path10.join(getThemesDir(), theme, "blocks");
|
|
2434
2632
|
if (!fs.existsSync(blocksDir)) {
|
|
2435
2633
|
continue;
|
|
2436
2634
|
}
|
|
2437
2635
|
const blocks = fs.readdirSync(blocksDir).filter((name) => {
|
|
2438
|
-
const blockPath =
|
|
2439
|
-
return fs.statSync(blockPath).isDirectory() && fs.existsSync(
|
|
2636
|
+
const blockPath = path10.join(blocksDir, name);
|
|
2637
|
+
return fs.statSync(blockPath).isDirectory() && fs.existsSync(path10.join(blockPath, "index.ts"));
|
|
2440
2638
|
});
|
|
2441
2639
|
if (blocks.length > 0) {
|
|
2442
2640
|
logger.log(chalk4.cyan(`
|
|
@@ -2450,14 +2648,14 @@ async function listBlocks(themeFilter) {
|
|
|
2450
2648
|
}
|
|
2451
2649
|
async function listComponents() {
|
|
2452
2650
|
logger.section("\u2699\uFE0F Components");
|
|
2453
|
-
const componentsDir =
|
|
2651
|
+
const componentsDir = path10.join(getFeaturesDir(), "components");
|
|
2454
2652
|
if (!fs.existsSync(componentsDir)) {
|
|
2455
2653
|
logger.warning("No components directory found");
|
|
2456
2654
|
return;
|
|
2457
2655
|
}
|
|
2458
2656
|
const components = fs.readdirSync(componentsDir).filter((name) => {
|
|
2459
|
-
const componentPath =
|
|
2460
|
-
return fs.statSync(componentPath).isDirectory() && fs.existsSync(
|
|
2657
|
+
const componentPath = path10.join(componentsDir, name);
|
|
2658
|
+
return fs.statSync(componentPath).isDirectory() && fs.existsSync(path10.join(componentPath, "index.ts"));
|
|
2461
2659
|
});
|
|
2462
2660
|
if (components.length === 0) {
|
|
2463
2661
|
logger.warning("No components found");
|
|
@@ -2478,11 +2676,11 @@ async function listThemesInfo() {
|
|
|
2478
2676
|
}
|
|
2479
2677
|
logger.log("");
|
|
2480
2678
|
for (const theme of themes) {
|
|
2481
|
-
const themeDir =
|
|
2679
|
+
const themeDir = path10.join(getThemesDir(), theme);
|
|
2482
2680
|
const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
|
|
2483
2681
|
let manifestContent = "";
|
|
2484
2682
|
for (const candidate of candidates) {
|
|
2485
|
-
const candidatePath =
|
|
2683
|
+
const candidatePath = path10.join(themeDir, candidate);
|
|
2486
2684
|
if (fs.existsSync(candidatePath)) {
|
|
2487
2685
|
manifestContent = fs.readFileSync(candidatePath, "utf-8");
|
|
2488
2686
|
break;
|
|
@@ -2520,9 +2718,9 @@ async function validateCommand(options) {
|
|
|
2520
2718
|
"theme.config.ts",
|
|
2521
2719
|
"bundle-entry.ts",
|
|
2522
2720
|
"manifest.ts"
|
|
2523
|
-
].some((f) => fs.existsSync(
|
|
2721
|
+
].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
|
|
2524
2722
|
if (isThemeDir) {
|
|
2525
|
-
themeToValidate =
|
|
2723
|
+
themeToValidate = path10.basename(process.cwd());
|
|
2526
2724
|
logger.info(`Validating current theme: ${themeToValidate}`);
|
|
2527
2725
|
} else {
|
|
2528
2726
|
logger.error(
|
|
@@ -2531,11 +2729,11 @@ async function validateCommand(options) {
|
|
|
2531
2729
|
process.exit(1);
|
|
2532
2730
|
}
|
|
2533
2731
|
}
|
|
2534
|
-
const themePath =
|
|
2732
|
+
const themePath = path10.join(getThemesDir(), themeToValidate);
|
|
2535
2733
|
logger.startSpinner("Running validation checks...");
|
|
2536
2734
|
const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
|
|
2537
2735
|
const foundEntry = entryFiles.find(
|
|
2538
|
-
(f) => fs.existsSync(
|
|
2736
|
+
(f) => fs.existsSync(path10.join(themePath, f))
|
|
2539
2737
|
);
|
|
2540
2738
|
if (!foundEntry) {
|
|
2541
2739
|
issues.push({
|
|
@@ -2545,7 +2743,7 @@ async function validateCommand(options) {
|
|
|
2545
2743
|
});
|
|
2546
2744
|
} else if (foundEntry === "manifest.ts") {
|
|
2547
2745
|
const manifestContent = fs.readFileSync(
|
|
2548
|
-
|
|
2746
|
+
path10.join(themePath, foundEntry),
|
|
2549
2747
|
"utf-8"
|
|
2550
2748
|
);
|
|
2551
2749
|
if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
|
|
@@ -2556,7 +2754,7 @@ async function validateCommand(options) {
|
|
|
2556
2754
|
});
|
|
2557
2755
|
}
|
|
2558
2756
|
}
|
|
2559
|
-
const configPath =
|
|
2757
|
+
const configPath = path10.join(themePath, "theme.config.ts");
|
|
2560
2758
|
if (!fs.existsSync(configPath)) {
|
|
2561
2759
|
issues.push({
|
|
2562
2760
|
type: "warning",
|
|
@@ -2564,7 +2762,7 @@ async function validateCommand(options) {
|
|
|
2564
2762
|
message: "Theme config file not found (recommended)"
|
|
2565
2763
|
});
|
|
2566
2764
|
}
|
|
2567
|
-
const indexPath =
|
|
2765
|
+
const indexPath = path10.join(themePath, "index.ts");
|
|
2568
2766
|
if (!fs.existsSync(indexPath)) {
|
|
2569
2767
|
issues.push({
|
|
2570
2768
|
type: "warning",
|
|
@@ -2572,7 +2770,7 @@ async function validateCommand(options) {
|
|
|
2572
2770
|
message: "Index file not found (recommended)"
|
|
2573
2771
|
});
|
|
2574
2772
|
}
|
|
2575
|
-
const sectionsDir =
|
|
2773
|
+
const sectionsDir = path10.join(themePath, "sections");
|
|
2576
2774
|
if (!fs.existsSync(sectionsDir)) {
|
|
2577
2775
|
issues.push({
|
|
2578
2776
|
type: "warning",
|
|
@@ -2581,16 +2779,16 @@ async function validateCommand(options) {
|
|
|
2581
2779
|
});
|
|
2582
2780
|
} else {
|
|
2583
2781
|
const sections = fs.readdirSync(sectionsDir).filter(
|
|
2584
|
-
(name) => fs.statSync(
|
|
2782
|
+
(name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
|
|
2585
2783
|
);
|
|
2586
2784
|
for (const sectionName of sections) {
|
|
2587
|
-
const sectionPath =
|
|
2588
|
-
const schemaFile =
|
|
2589
|
-
const defaultTemplate =
|
|
2785
|
+
const sectionPath = path10.join(sectionsDir, sectionName);
|
|
2786
|
+
const schemaFile = path10.join(sectionPath, `${sectionName}.schema.ts`);
|
|
2787
|
+
const defaultTemplate = path10.join(
|
|
2590
2788
|
sectionPath,
|
|
2591
2789
|
`${sectionName}-default.tsx`
|
|
2592
2790
|
);
|
|
2593
|
-
const indexFile =
|
|
2791
|
+
const indexFile = path10.join(sectionPath, "index.ts");
|
|
2594
2792
|
if (!fs.existsSync(schemaFile)) {
|
|
2595
2793
|
issues.push({
|
|
2596
2794
|
type: "error",
|
|
@@ -2614,14 +2812,14 @@ async function validateCommand(options) {
|
|
|
2614
2812
|
}
|
|
2615
2813
|
}
|
|
2616
2814
|
}
|
|
2617
|
-
const blocksDir =
|
|
2815
|
+
const blocksDir = path10.join(themePath, "blocks");
|
|
2618
2816
|
if (fs.existsSync(blocksDir)) {
|
|
2619
|
-
const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(
|
|
2817
|
+
const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(path10.join(blocksDir, name)).isDirectory());
|
|
2620
2818
|
for (const blockName of blocks) {
|
|
2621
|
-
const blockPath =
|
|
2622
|
-
const schemaFile =
|
|
2623
|
-
const componentFile =
|
|
2624
|
-
const indexFile =
|
|
2819
|
+
const blockPath = path10.join(blocksDir, blockName);
|
|
2820
|
+
const schemaFile = path10.join(blockPath, `${blockName}.schema.ts`);
|
|
2821
|
+
const componentFile = path10.join(blockPath, `${blockName}.tsx`);
|
|
2822
|
+
const indexFile = path10.join(blockPath, "index.ts");
|
|
2625
2823
|
if (!fs.existsSync(schemaFile)) {
|
|
2626
2824
|
issues.push({
|
|
2627
2825
|
type: "error",
|
|
@@ -2647,13 +2845,13 @@ async function validateCommand(options) {
|
|
|
2647
2845
|
}
|
|
2648
2846
|
if (fs.existsSync(sectionsDir)) {
|
|
2649
2847
|
const sections = fs.readdirSync(sectionsDir).filter(
|
|
2650
|
-
(name) => fs.statSync(
|
|
2848
|
+
(name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
|
|
2651
2849
|
);
|
|
2652
2850
|
for (const sectionName of sections) {
|
|
2653
|
-
const sectionPath =
|
|
2851
|
+
const sectionPath = path10.join(sectionsDir, sectionName);
|
|
2654
2852
|
const tsxFiles = fs.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
|
|
2655
2853
|
for (const tsxFile of tsxFiles) {
|
|
2656
|
-
const filePath =
|
|
2854
|
+
const filePath = path10.join(sectionPath, tsxFile);
|
|
2657
2855
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
2658
2856
|
const relPath = `sections/${sectionName}/${tsxFile}`;
|
|
2659
2857
|
if (!content.includes('"use client"') && !content.includes("'use client'")) {
|
|
@@ -2701,23 +2899,46 @@ async function validateCommand(options) {
|
|
|
2701
2899
|
}
|
|
2702
2900
|
}
|
|
2703
2901
|
}
|
|
2704
|
-
const registryPath =
|
|
2705
|
-
const bundleEntryPath =
|
|
2902
|
+
const registryPath = path10.join(themePath, "sections-registry.ts");
|
|
2903
|
+
const bundleEntryPath = path10.join(themePath, "bundle-entry.ts");
|
|
2706
2904
|
const registryContent = fs.existsSync(registryPath) ? fs.readFileSync(registryPath, "utf-8") : fs.existsSync(bundleEntryPath) ? fs.readFileSync(bundleEntryPath, "utf-8") : "";
|
|
2707
2905
|
if (fs.existsSync(sectionsDir) && registryContent) {
|
|
2708
2906
|
const sections = fs.readdirSync(sectionsDir).filter(
|
|
2709
|
-
(name) => fs.statSync(
|
|
2907
|
+
(name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
|
|
2710
2908
|
);
|
|
2711
2909
|
for (const sectionName of sections) {
|
|
2712
2910
|
if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
|
|
2713
2911
|
issues.push({
|
|
2714
|
-
type: "
|
|
2912
|
+
type: "error",
|
|
2715
2913
|
file: `sections/${sectionName}/`,
|
|
2716
|
-
message: "Section not
|
|
2914
|
+
message: "Section not exported in sections-registry.ts or bundle-entry.ts \u2014 will not be included in build"
|
|
2717
2915
|
});
|
|
2718
2916
|
}
|
|
2719
2917
|
}
|
|
2720
2918
|
}
|
|
2919
|
+
if (fs.existsSync(sectionsDir)) {
|
|
2920
|
+
const schemaTypes = await loadSchemaTypes(themePath, sectionsDir);
|
|
2921
|
+
for (const { folderName, schemaType } of schemaTypes) {
|
|
2922
|
+
if (schemaType && schemaType !== folderName) {
|
|
2923
|
+
issues.push({
|
|
2924
|
+
type: "error",
|
|
2925
|
+
file: `sections/${folderName}/${folderName}.schema.ts`,
|
|
2926
|
+
message: `Schema type "${schemaType}" doesn't match folder name "${folderName}". Rename folder to "${schemaType}/" or change schema type to "${folderName}".`
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
const pagesDir = path10.join(themePath, "pages");
|
|
2931
|
+
if (fs.existsSync(pagesDir)) {
|
|
2932
|
+
const allSchemaTypeSet = new Set(
|
|
2933
|
+
schemaTypes.map((s) => s.schemaType || s.folderName)
|
|
2934
|
+
);
|
|
2935
|
+
const pageIssues = await validatePageSectionTypes(
|
|
2936
|
+
pagesDir,
|
|
2937
|
+
allSchemaTypeSet
|
|
2938
|
+
);
|
|
2939
|
+
issues.push(...pageIssues);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2721
2942
|
logger.stopSpinner(true, "Validation complete");
|
|
2722
2943
|
const errors = issues.filter((i) => i.type === "error");
|
|
2723
2944
|
const warnings = issues.filter((i) => i.type === "warning");
|
|
@@ -2756,6 +2977,83 @@ async function validateCommand(options) {
|
|
|
2756
2977
|
}
|
|
2757
2978
|
}
|
|
2758
2979
|
}
|
|
2980
|
+
async function loadSchemaTypes(themePath, sectionsDir) {
|
|
2981
|
+
const results = [];
|
|
2982
|
+
const sections = fs.readdirSync(sectionsDir).filter((name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory());
|
|
2983
|
+
for (const sectionName of sections) {
|
|
2984
|
+
const schemaFile = path10.join(
|
|
2985
|
+
sectionsDir,
|
|
2986
|
+
sectionName,
|
|
2987
|
+
`${sectionName}.schema.ts`
|
|
2988
|
+
);
|
|
2989
|
+
if (!fs.existsSync(schemaFile)) {
|
|
2990
|
+
results.push({ folderName: sectionName, schemaType: null });
|
|
2991
|
+
continue;
|
|
2992
|
+
}
|
|
2993
|
+
const content = fs.readFileSync(schemaFile, "utf-8");
|
|
2994
|
+
const typeMatch = content.match(/\btype:\s*["']([^"']+)["']/);
|
|
2995
|
+
results.push({
|
|
2996
|
+
folderName: sectionName,
|
|
2997
|
+
schemaType: typeMatch ? typeMatch[1] : null
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
return results;
|
|
3001
|
+
}
|
|
3002
|
+
async function validatePageSectionTypes(pagesDir, validTypes) {
|
|
3003
|
+
const issues = [];
|
|
3004
|
+
const files = fs.readdirSync(pagesDir).filter((f) => f.match(/\.(ts|js)$/));
|
|
3005
|
+
for (const file of files) {
|
|
3006
|
+
const content = fs.readFileSync(path10.join(pagesDir, file), "utf-8");
|
|
3007
|
+
const pageName = file.replace(/\.(ts|js)$/, "");
|
|
3008
|
+
const typeMatches = content.matchAll(/\btype:\s*["']([^"']+)["']/g);
|
|
3009
|
+
for (const match of typeMatches) {
|
|
3010
|
+
const sectionType = match[1];
|
|
3011
|
+
if (isFieldType(sectionType)) continue;
|
|
3012
|
+
if (!validTypes.has(sectionType)) {
|
|
3013
|
+
issues.push({
|
|
3014
|
+
type: "error",
|
|
3015
|
+
file: `pages/${file}`,
|
|
3016
|
+
message: `Page "${pageName}" uses section type "${sectionType}" which doesn't exist in sections/`
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return issues;
|
|
3022
|
+
}
|
|
3023
|
+
var FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
3024
|
+
"text",
|
|
3025
|
+
"textarea",
|
|
3026
|
+
"richtext",
|
|
3027
|
+
"number",
|
|
3028
|
+
"range",
|
|
3029
|
+
"toggle",
|
|
3030
|
+
"switch",
|
|
3031
|
+
"select",
|
|
3032
|
+
"radio",
|
|
3033
|
+
"checkbox",
|
|
3034
|
+
"color",
|
|
3035
|
+
"image",
|
|
3036
|
+
"video",
|
|
3037
|
+
"url",
|
|
3038
|
+
"link",
|
|
3039
|
+
"icon",
|
|
3040
|
+
"date",
|
|
3041
|
+
"datetime",
|
|
3042
|
+
"collection",
|
|
3043
|
+
"product",
|
|
3044
|
+
"blog",
|
|
3045
|
+
"page",
|
|
3046
|
+
"html",
|
|
3047
|
+
"code",
|
|
3048
|
+
"json",
|
|
3049
|
+
"array",
|
|
3050
|
+
"object",
|
|
3051
|
+
"group",
|
|
3052
|
+
"section"
|
|
3053
|
+
]);
|
|
3054
|
+
function isFieldType(type) {
|
|
3055
|
+
return FIELD_TYPES.has(type);
|
|
3056
|
+
}
|
|
2759
3057
|
|
|
2760
3058
|
// src/commands/build.ts
|
|
2761
3059
|
init_logger();
|
|
@@ -2766,14 +3064,14 @@ async function buildCommand(options) {
|
|
|
2766
3064
|
if (options.theme) {
|
|
2767
3065
|
themeName = options.theme;
|
|
2768
3066
|
try {
|
|
2769
|
-
const workspaceThemePath =
|
|
3067
|
+
const workspaceThemePath = path10.join(getThemesDir(), themeName);
|
|
2770
3068
|
if (fs.existsSync(workspaceThemePath)) {
|
|
2771
3069
|
themePath = workspaceThemePath;
|
|
2772
3070
|
} else {
|
|
2773
|
-
themePath =
|
|
3071
|
+
themePath = path10.join(process.cwd(), themeName);
|
|
2774
3072
|
}
|
|
2775
3073
|
} catch {
|
|
2776
|
-
themePath =
|
|
3074
|
+
themePath = path10.join(process.cwd(), themeName);
|
|
2777
3075
|
}
|
|
2778
3076
|
if (!fs.existsSync(themePath)) {
|
|
2779
3077
|
logger.error(`Theme "${themeName}" not found.`);
|
|
@@ -2784,10 +3082,10 @@ async function buildCommand(options) {
|
|
|
2784
3082
|
"theme.config.ts",
|
|
2785
3083
|
"bundle-entry.ts",
|
|
2786
3084
|
"manifest.ts"
|
|
2787
|
-
].some((f) => fs.existsSync(
|
|
3085
|
+
].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
|
|
2788
3086
|
if (isThemeDir) {
|
|
2789
3087
|
themePath = process.cwd();
|
|
2790
|
-
themeName =
|
|
3088
|
+
themeName = path10.basename(themePath);
|
|
2791
3089
|
logger.info(`Building current theme: ${themeName}`);
|
|
2792
3090
|
} else {
|
|
2793
3091
|
logger.error(
|
|
@@ -2796,7 +3094,7 @@ async function buildCommand(options) {
|
|
|
2796
3094
|
process.exit(1);
|
|
2797
3095
|
}
|
|
2798
3096
|
}
|
|
2799
|
-
const packageJsonPath =
|
|
3097
|
+
const packageJsonPath = path10.join(themePath, "package.json");
|
|
2800
3098
|
const hasPkgJson = fs.existsSync(packageJsonPath);
|
|
2801
3099
|
if (!hasPkgJson) {
|
|
2802
3100
|
logger.warning(
|
|
@@ -2852,9 +3150,9 @@ async function buildCommand(options) {
|
|
|
2852
3150
|
logger.success("\u2713 Theme built successfully!");
|
|
2853
3151
|
logger.newLine();
|
|
2854
3152
|
logger.info(`Theme: ${themeName}`);
|
|
2855
|
-
const distPath =
|
|
3153
|
+
const distPath = path10.join(themePath, "dist");
|
|
2856
3154
|
if (fs.existsSync(distPath)) {
|
|
2857
|
-
logger.log(`Output: ${
|
|
3155
|
+
logger.log(`Output: ${path10.relative(process.cwd(), distPath)}`);
|
|
2858
3156
|
const files = fs.readdirSync(distPath);
|
|
2859
3157
|
logger.log(`Files: ${files.length}`);
|
|
2860
3158
|
}
|
|
@@ -2862,17 +3160,17 @@ async function buildCommand(options) {
|
|
|
2862
3160
|
}
|
|
2863
3161
|
function runCommand(command, args, cwd) {
|
|
2864
3162
|
return new Promise((resolve) => {
|
|
2865
|
-
const proc =
|
|
3163
|
+
const proc = spawn2(command, args, {
|
|
2866
3164
|
cwd,
|
|
2867
3165
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2868
3166
|
shell: true
|
|
2869
3167
|
});
|
|
2870
3168
|
let stdout = "";
|
|
2871
3169
|
let stderr = "";
|
|
2872
|
-
proc.stdout
|
|
3170
|
+
proc.stdout?.on("data", (data) => {
|
|
2873
3171
|
stdout += data.toString();
|
|
2874
3172
|
});
|
|
2875
|
-
proc.stderr
|
|
3173
|
+
proc.stderr?.on("data", (data) => {
|
|
2876
3174
|
stderr += data.toString();
|
|
2877
3175
|
});
|
|
2878
3176
|
proc.on("close", (code) => {
|
|
@@ -2910,7 +3208,7 @@ async function packageCommand(options) {
|
|
|
2910
3208
|
let themeName;
|
|
2911
3209
|
if (options.theme) {
|
|
2912
3210
|
themeName = options.theme;
|
|
2913
|
-
themePath =
|
|
3211
|
+
themePath = path10.join(getThemesDir(), themeName);
|
|
2914
3212
|
if (!fs.existsSync(themePath)) {
|
|
2915
3213
|
logger.error(`Theme "${themeName}" not found.`);
|
|
2916
3214
|
process.exit(1);
|
|
@@ -2920,10 +3218,10 @@ async function packageCommand(options) {
|
|
|
2920
3218
|
"theme.config.ts",
|
|
2921
3219
|
"bundle-entry.ts",
|
|
2922
3220
|
"manifest.ts"
|
|
2923
|
-
].some((f) => fs.existsSync(
|
|
3221
|
+
].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
|
|
2924
3222
|
if (isThemeDir) {
|
|
2925
3223
|
themePath = process.cwd();
|
|
2926
|
-
themeName =
|
|
3224
|
+
themeName = path10.basename(themePath);
|
|
2927
3225
|
logger.info(`Packaging current theme: ${themeName}`);
|
|
2928
3226
|
} else {
|
|
2929
3227
|
logger.error(
|
|
@@ -2932,7 +3230,7 @@ async function packageCommand(options) {
|
|
|
2932
3230
|
process.exit(1);
|
|
2933
3231
|
}
|
|
2934
3232
|
}
|
|
2935
|
-
const packageJsonPath =
|
|
3233
|
+
const packageJsonPath = path10.join(themePath, "package.json");
|
|
2936
3234
|
let version2 = "1.0.0";
|
|
2937
3235
|
if (fs.existsSync(packageJsonPath)) {
|
|
2938
3236
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
@@ -2942,7 +3240,7 @@ async function packageCommand(options) {
|
|
|
2942
3240
|
logger.info(`Theme: ${themeName}`);
|
|
2943
3241
|
logger.info(`Version: ${version2}`);
|
|
2944
3242
|
logger.newLine();
|
|
2945
|
-
const compiledThemePath =
|
|
3243
|
+
const compiledThemePath = path10.join(
|
|
2946
3244
|
process.cwd(),
|
|
2947
3245
|
"themes",
|
|
2948
3246
|
themeName,
|
|
@@ -2976,8 +3274,8 @@ async function packageCommand(options) {
|
|
|
2976
3274
|
logger.newLine();
|
|
2977
3275
|
logger.section("Step 2: Create Package");
|
|
2978
3276
|
const packageName = options.name || `${themeName}-${version2}`;
|
|
2979
|
-
const outputDir = options.output ||
|
|
2980
|
-
const outputPath =
|
|
3277
|
+
const outputDir = options.output || path10.join(process.cwd(), "dist");
|
|
3278
|
+
const outputPath = path10.join(outputDir, `${packageName}.zip`);
|
|
2981
3279
|
await fs.ensureDir(outputDir);
|
|
2982
3280
|
logger.startSpinner("Creating zip archive...");
|
|
2983
3281
|
try {
|
|
@@ -2990,11 +3288,11 @@ async function packageCommand(options) {
|
|
|
2990
3288
|
logger.newLine();
|
|
2991
3289
|
logger.info(`Package: ${packageName}.zip`);
|
|
2992
3290
|
logger.log(`Size: ${sizeMB} MB`);
|
|
2993
|
-
logger.log(`Location: ${
|
|
3291
|
+
logger.log(`Location: ${path10.relative(process.cwd(), outputPath)}`);
|
|
2994
3292
|
logger.newLine();
|
|
2995
3293
|
logger.section("Next steps:");
|
|
2996
3294
|
logger.log(
|
|
2997
|
-
` onexthm deploy --package ${
|
|
3295
|
+
` onexthm deploy --package ${path10.relative(process.cwd(), outputPath)}`
|
|
2998
3296
|
);
|
|
2999
3297
|
} catch (error) {
|
|
3000
3298
|
logger.stopSpinner(false, "Failed to create package");
|
|
@@ -3006,13 +3304,13 @@ async function packageCommand(options) {
|
|
|
3006
3304
|
}
|
|
3007
3305
|
function runCommand2(command, args) {
|
|
3008
3306
|
return new Promise((resolve) => {
|
|
3009
|
-
const proc =
|
|
3307
|
+
const proc = spawn2(command, args, {
|
|
3010
3308
|
cwd: process.cwd(),
|
|
3011
3309
|
stdio: "pipe",
|
|
3012
3310
|
shell: true
|
|
3013
3311
|
});
|
|
3014
3312
|
let hasError = false;
|
|
3015
|
-
proc.stderr
|
|
3313
|
+
proc.stderr?.on("data", (data) => {
|
|
3016
3314
|
const message = data.toString();
|
|
3017
3315
|
if (message.includes("error") || message.includes("Error") || message.includes("ERROR")) {
|
|
3018
3316
|
hasError = true;
|
|
@@ -3052,9 +3350,9 @@ async function deployCommand(options) {
|
|
|
3052
3350
|
ensureOneXProject();
|
|
3053
3351
|
let packagePath;
|
|
3054
3352
|
if (options.package) {
|
|
3055
|
-
packagePath =
|
|
3353
|
+
packagePath = path10.resolve(options.package);
|
|
3056
3354
|
} else if (options.theme) {
|
|
3057
|
-
const distDir =
|
|
3355
|
+
const distDir = path10.join(process.cwd(), "dist");
|
|
3058
3356
|
if (!fs.existsSync(distDir)) {
|
|
3059
3357
|
logger.error("No dist/ directory found. Run 'onexthm package' first.");
|
|
3060
3358
|
process.exit(1);
|
|
@@ -3069,7 +3367,7 @@ async function deployCommand(options) {
|
|
|
3069
3367
|
process.exit(1);
|
|
3070
3368
|
}
|
|
3071
3369
|
packageFiles.sort().reverse();
|
|
3072
|
-
packagePath =
|
|
3370
|
+
packagePath = path10.join(distDir, packageFiles[0]);
|
|
3073
3371
|
} else {
|
|
3074
3372
|
logger.error("Either --package or --theme must be specified.");
|
|
3075
3373
|
logger.info("Examples:");
|
|
@@ -3083,11 +3381,11 @@ async function deployCommand(options) {
|
|
|
3083
3381
|
}
|
|
3084
3382
|
const stats = await fs.stat(packagePath);
|
|
3085
3383
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
3086
|
-
const fileName =
|
|
3384
|
+
const fileName = path10.basename(packagePath);
|
|
3087
3385
|
logger.newLine();
|
|
3088
3386
|
logger.info(`Package: ${fileName}`);
|
|
3089
3387
|
logger.log(`Size: ${sizeMB} MB`);
|
|
3090
|
-
logger.log(`Path: ${
|
|
3388
|
+
logger.log(`Path: ${path10.relative(process.cwd(), packagePath)}`);
|
|
3091
3389
|
logger.newLine();
|
|
3092
3390
|
const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
|
|
3093
3391
|
const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
|
|
@@ -3191,11 +3489,11 @@ function getBucketName(env) {
|
|
|
3191
3489
|
return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
|
|
3192
3490
|
}
|
|
3193
3491
|
async function findCompiledThemeDir(themeId, version2) {
|
|
3194
|
-
const searchPaths = [
|
|
3492
|
+
const searchPaths = [path10.resolve(process.cwd(), "dist")];
|
|
3195
3493
|
for (const dir of searchPaths) {
|
|
3196
3494
|
if (await fs.pathExists(dir)) {
|
|
3197
|
-
const hasManifest = await fs.pathExists(
|
|
3198
|
-
const hasThemeEntry = await fs.pathExists(
|
|
3495
|
+
const hasManifest = await fs.pathExists(path10.join(dir, "manifest.json"));
|
|
3496
|
+
const hasThemeEntry = await fs.pathExists(path10.join(dir, "bundle-entry.js")) || await fs.pathExists(path10.join(dir, "theme.config.js")) || await fs.pathExists(path10.join(dir, "index.js"));
|
|
3199
3497
|
if (hasManifest || hasThemeEntry) {
|
|
3200
3498
|
return dir;
|
|
3201
3499
|
}
|
|
@@ -3204,7 +3502,7 @@ async function findCompiledThemeDir(themeId, version2) {
|
|
|
3204
3502
|
return null;
|
|
3205
3503
|
}
|
|
3206
3504
|
async function readManifest() {
|
|
3207
|
-
const manifestTsPath =
|
|
3505
|
+
const manifestTsPath = path10.resolve(process.cwd(), "manifest.ts");
|
|
3208
3506
|
if (await fs.pathExists(manifestTsPath)) {
|
|
3209
3507
|
try {
|
|
3210
3508
|
const module = await import(manifestTsPath);
|
|
@@ -3213,7 +3511,7 @@ async function readManifest() {
|
|
|
3213
3511
|
logger.warning("Failed to import manifest.ts, trying package.json");
|
|
3214
3512
|
}
|
|
3215
3513
|
}
|
|
3216
|
-
const packageJsonPath =
|
|
3514
|
+
const packageJsonPath = path10.resolve(process.cwd(), "package.json");
|
|
3217
3515
|
if (await fs.pathExists(packageJsonPath)) {
|
|
3218
3516
|
const pkg = await fs.readJson(packageJsonPath);
|
|
3219
3517
|
return {
|
|
@@ -3247,13 +3545,13 @@ async function findSourceDir(themeId, explicitDir) {
|
|
|
3247
3545
|
}
|
|
3248
3546
|
const searchPaths = [
|
|
3249
3547
|
process.cwd(),
|
|
3250
|
-
|
|
3251
|
-
|
|
3548
|
+
path10.resolve(process.cwd(), `../../themes/${themeId}`),
|
|
3549
|
+
path10.resolve(process.cwd(), `../themes/${themeId}`)
|
|
3252
3550
|
];
|
|
3253
3551
|
const markers = ["theme.config.ts", "bundle-entry.ts"];
|
|
3254
3552
|
for (const dir of searchPaths) {
|
|
3255
3553
|
for (const marker of markers) {
|
|
3256
|
-
if (await fs.pathExists(
|
|
3554
|
+
if (await fs.pathExists(path10.join(dir, marker))) {
|
|
3257
3555
|
return dir;
|
|
3258
3556
|
}
|
|
3259
3557
|
}
|
|
@@ -3304,8 +3602,8 @@ async function uploadCommand(options) {
|
|
|
3304
3602
|
}
|
|
3305
3603
|
spinner.succeed(`Found compiled theme at: ${compiledDir}`);
|
|
3306
3604
|
spinner.start("Creating bundle.zip...");
|
|
3307
|
-
const tmpDir =
|
|
3308
|
-
const bundleZipPath =
|
|
3605
|
+
const tmpDir = os.tmpdir();
|
|
3606
|
+
const bundleZipPath = path10.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
|
|
3309
3607
|
await createZipFromDir(compiledDir, bundleZipPath);
|
|
3310
3608
|
const bundleZipBuffer = await fs.readFile(bundleZipPath);
|
|
3311
3609
|
const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
|
|
@@ -3359,7 +3657,7 @@ async function uploadCommand(options) {
|
|
|
3359
3657
|
if (sourceDir) {
|
|
3360
3658
|
spinner.succeed(`Found source at: ${sourceDir}`);
|
|
3361
3659
|
spinner.start("Creating source.zip...");
|
|
3362
|
-
const sourceZipPath =
|
|
3660
|
+
const sourceZipPath = path10.join(
|
|
3363
3661
|
tmpDir,
|
|
3364
3662
|
`${themeId}-${version2}-source.zip`
|
|
3365
3663
|
);
|
|
@@ -3493,8 +3791,8 @@ async function resolveLatestVersion(s3Client, bucket, themeId) {
|
|
|
3493
3791
|
async function createCompatibilityFiles(outputDir, manifest) {
|
|
3494
3792
|
const entryFile = manifest.output?.entry || "bundle-entry.js";
|
|
3495
3793
|
if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
|
|
3496
|
-
const hashedPath =
|
|
3497
|
-
const stablePath =
|
|
3794
|
+
const hashedPath = path10.join(outputDir, entryFile);
|
|
3795
|
+
const stablePath = path10.join(outputDir, "bundle-entry.js");
|
|
3498
3796
|
if (await fs.pathExists(hashedPath)) {
|
|
3499
3797
|
await fs.copy(hashedPath, stablePath);
|
|
3500
3798
|
const mapPath = hashedPath + ".map";
|
|
@@ -3503,13 +3801,13 @@ async function createCompatibilityFiles(outputDir, manifest) {
|
|
|
3503
3801
|
}
|
|
3504
3802
|
}
|
|
3505
3803
|
}
|
|
3506
|
-
const sectionsRegistryPath =
|
|
3804
|
+
const sectionsRegistryPath = path10.join(outputDir, "sections-registry.js");
|
|
3507
3805
|
const content = `// Re-export all sections from bundle-entry
|
|
3508
3806
|
// This file exists to maintain compatibility with the import path
|
|
3509
3807
|
export * from './bundle-entry.js';
|
|
3510
3808
|
`;
|
|
3511
3809
|
await fs.writeFile(sectionsRegistryPath, content, "utf-8");
|
|
3512
|
-
const pkgJsonPath =
|
|
3810
|
+
const pkgJsonPath = path10.join(outputDir, "package.json");
|
|
3513
3811
|
await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
|
|
3514
3812
|
}
|
|
3515
3813
|
function showDownloadFailureHelp(themeId, bucket) {
|
|
@@ -3571,6 +3869,18 @@ async function downloadCommand(options) {
|
|
|
3571
3869
|
spinner.succeed(
|
|
3572
3870
|
`Resolved latest version: ${chalk4.cyan(resolvedVersion)}`
|
|
3573
3871
|
);
|
|
3872
|
+
const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.VERCEL);
|
|
3873
|
+
if (isCI) {
|
|
3874
|
+
console.log(
|
|
3875
|
+
chalk4.yellow(
|
|
3876
|
+
`
|
|
3877
|
+
Warning: Resolved "latest" to ${resolvedVersion} in CI environment.
|
|
3878
|
+
For production builds, pin to a specific version:
|
|
3879
|
+
THEME_VERSION=${resolvedVersion}
|
|
3880
|
+
`
|
|
3881
|
+
)
|
|
3882
|
+
);
|
|
3883
|
+
}
|
|
3574
3884
|
}
|
|
3575
3885
|
spinner.start(
|
|
3576
3886
|
`Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
|
|
@@ -3592,7 +3902,7 @@ async function downloadCommand(options) {
|
|
|
3592
3902
|
zip.extractAllTo(outputDir, true);
|
|
3593
3903
|
const entries = zip.getEntries().filter((e) => !e.isDirectory);
|
|
3594
3904
|
spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
|
|
3595
|
-
const manifestPath =
|
|
3905
|
+
const manifestPath = path10.join(outputDir, "manifest.json");
|
|
3596
3906
|
const manifest = await fs.readJson(manifestPath);
|
|
3597
3907
|
await createCompatibilityFiles(outputDir, manifest);
|
|
3598
3908
|
console.log();
|
|
@@ -3692,10 +4002,9 @@ async function resolveLatestVersion2(s3Client, bucket, themeId) {
|
|
|
3692
4002
|
}
|
|
3693
4003
|
function runInstall(cwd) {
|
|
3694
4004
|
return new Promise((resolve) => {
|
|
3695
|
-
const proc =
|
|
4005
|
+
const proc = spawn2("pnpm", ["install"], {
|
|
3696
4006
|
cwd,
|
|
3697
|
-
stdio: "inherit"
|
|
3698
|
-
shell: true
|
|
4007
|
+
stdio: "inherit"
|
|
3699
4008
|
});
|
|
3700
4009
|
proc.on("close", (code) => resolve(code === 0));
|
|
3701
4010
|
proc.on("error", () => resolve(false));
|
|
@@ -3726,7 +4035,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3726
4035
|
const oldPrefix = `${oldName}-`;
|
|
3727
4036
|
const newPrefix = `${newName}-`;
|
|
3728
4037
|
const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3729
|
-
const pkgPath =
|
|
4038
|
+
const pkgPath = path10.join(themeDir, "package.json");
|
|
3730
4039
|
if (await fs.pathExists(pkgPath)) {
|
|
3731
4040
|
const pkg = await fs.readJson(pkgPath);
|
|
3732
4041
|
pkg.name = `@onex-themes/${newName}`;
|
|
@@ -3742,7 +4051,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3742
4051
|
}
|
|
3743
4052
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3744
4053
|
}
|
|
3745
|
-
const configPath =
|
|
4054
|
+
const configPath = path10.join(themeDir, "theme.config.ts");
|
|
3746
4055
|
if (await fs.pathExists(configPath)) {
|
|
3747
4056
|
let content = await fs.readFile(configPath, "utf-8");
|
|
3748
4057
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
@@ -3752,7 +4061,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3752
4061
|
);
|
|
3753
4062
|
await fs.writeFile(configPath, content);
|
|
3754
4063
|
}
|
|
3755
|
-
const layoutPath =
|
|
4064
|
+
const layoutPath = path10.join(themeDir, "theme.layout.ts");
|
|
3756
4065
|
if (await fs.pathExists(layoutPath)) {
|
|
3757
4066
|
let content = await fs.readFile(layoutPath, "utf-8");
|
|
3758
4067
|
content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
|
|
@@ -3765,7 +4074,7 @@ async function renameTheme(themeDir, oldName, newName) {
|
|
|
3765
4074
|
const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3766
4075
|
const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
|
|
3767
4076
|
for (const file of tsFiles) {
|
|
3768
|
-
const filePath =
|
|
4077
|
+
const filePath = path10.join(themeDir, file);
|
|
3769
4078
|
let content = await fs.readFile(filePath, "utf-8");
|
|
3770
4079
|
const original = content;
|
|
3771
4080
|
content = content.replace(
|
|
@@ -3794,7 +4103,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3794
4103
|
const spinner = ora("Initializing clone...").start();
|
|
3795
4104
|
try {
|
|
3796
4105
|
const bucket = options.bucket || getBucketName3(options.environment);
|
|
3797
|
-
const outputDir = options.output ||
|
|
4106
|
+
const outputDir = options.output || path10.resolve(process.cwd(), newName);
|
|
3798
4107
|
const s3Client = getS3Client3();
|
|
3799
4108
|
if (await fs.pathExists(outputDir)) {
|
|
3800
4109
|
spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
|
|
@@ -3849,7 +4158,7 @@ async function cloneCommand(themeName, options) {
|
|
|
3849
4158
|
spinner.succeed(
|
|
3850
4159
|
`Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
|
|
3851
4160
|
);
|
|
3852
|
-
const envExamplePath =
|
|
4161
|
+
const envExamplePath = path10.join(outputDir, ".env.example");
|
|
3853
4162
|
if (!await fs.pathExists(envExamplePath)) {
|
|
3854
4163
|
await fs.writeFile(
|
|
3855
4164
|
envExamplePath,
|
|
@@ -3862,32 +4171,18 @@ async function cloneCommand(themeName, options) {
|
|
|
3862
4171
|
].join("\n")
|
|
3863
4172
|
);
|
|
3864
4173
|
}
|
|
3865
|
-
const
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
name: "figmaApiKey",
|
|
3872
|
-
message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
|
|
3873
|
-
}
|
|
3874
|
-
]);
|
|
3875
|
-
let mcpContent = await fs.readFile(mcpJsonPath, "utf-8");
|
|
3876
|
-
if (figmaApiKey) {
|
|
3877
|
-
mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
|
|
3878
|
-
} else {
|
|
3879
|
-
try {
|
|
3880
|
-
const mcpJson = JSON.parse(mcpContent);
|
|
3881
|
-
delete mcpJson.mcpServers?.figma;
|
|
3882
|
-
mcpContent = JSON.stringify(mcpJson, null, 2) + "\n";
|
|
3883
|
-
} catch {
|
|
3884
|
-
}
|
|
4174
|
+
const { default: inquirerMod } = await import('inquirer');
|
|
4175
|
+
const { figmaApiKey } = await inquirerMod.prompt([
|
|
4176
|
+
{
|
|
4177
|
+
type: "password",
|
|
4178
|
+
name: "figmaApiKey",
|
|
4179
|
+
message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
|
|
3885
4180
|
}
|
|
3886
|
-
|
|
3887
|
-
|
|
4181
|
+
]);
|
|
4182
|
+
ensureMcpJson(outputDir, figmaApiKey || void 0);
|
|
3888
4183
|
if (options.install !== false) {
|
|
3889
4184
|
const hasPkgJson = await fs.pathExists(
|
|
3890
|
-
|
|
4185
|
+
path10.join(outputDir, "package.json")
|
|
3891
4186
|
);
|
|
3892
4187
|
if (hasPkgJson) {
|
|
3893
4188
|
spinner.start("Installing dependencies...");
|
|
@@ -3914,9 +4209,10 @@ async function cloneCommand(themeName, options) {
|
|
|
3914
4209
|
console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
|
|
3915
4210
|
console.log();
|
|
3916
4211
|
console.log(chalk4.cyan("Next steps:"));
|
|
3917
|
-
console.log(chalk4.gray(` cd ${
|
|
4212
|
+
console.log(chalk4.gray(` cd ${path10.relative(process.cwd(), outputDir)}`));
|
|
4213
|
+
const copyCmd = process.platform === "win32" ? "copy .env.example .env" : "cp .env.example .env";
|
|
3918
4214
|
console.log(
|
|
3919
|
-
chalk4.gray(
|
|
4215
|
+
chalk4.gray(` ${copyCmd} # then add your Company ID`)
|
|
3920
4216
|
);
|
|
3921
4217
|
if (options.install === false) {
|
|
3922
4218
|
console.log(chalk4.gray(" pnpm install"));
|
|
@@ -3949,7 +4245,7 @@ var MIME_TYPES = {
|
|
|
3949
4245
|
};
|
|
3950
4246
|
function createDevServer(options) {
|
|
3951
4247
|
const clients = /* @__PURE__ */ new Set();
|
|
3952
|
-
const themeDataPath =
|
|
4248
|
+
const themeDataPath = path10.join(options.distDir, "theme-data.json");
|
|
3953
4249
|
const server = http.createServer((req, res) => {
|
|
3954
4250
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
3955
4251
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
@@ -3975,8 +4271,8 @@ function createDevServer(options) {
|
|
|
3975
4271
|
if (pathname.startsWith("/_assets/")) {
|
|
3976
4272
|
const parts = pathname.replace(/^\/_assets\//, "").split("/");
|
|
3977
4273
|
const assetSubpath = parts.slice(1).join("/");
|
|
3978
|
-
const assetPath =
|
|
3979
|
-
if (!assetPath.startsWith(
|
|
4274
|
+
const assetPath = path10.join(options.themePath, "assets", assetSubpath);
|
|
4275
|
+
if (!assetPath.startsWith(path10.join(options.themePath, "assets"))) {
|
|
3980
4276
|
res.writeHead(403);
|
|
3981
4277
|
res.end("Forbidden");
|
|
3982
4278
|
return;
|
|
@@ -3987,8 +4283,8 @@ function createDevServer(options) {
|
|
|
3987
4283
|
if (pathname.startsWith("/themes/")) {
|
|
3988
4284
|
const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
|
|
3989
4285
|
if (match) {
|
|
3990
|
-
const assetPath =
|
|
3991
|
-
if (!assetPath.startsWith(
|
|
4286
|
+
const assetPath = path10.join(options.themePath, "assets", match[1]);
|
|
4287
|
+
if (!assetPath.startsWith(path10.join(options.themePath, "assets"))) {
|
|
3992
4288
|
res.writeHead(403);
|
|
3993
4289
|
res.end("Forbidden");
|
|
3994
4290
|
return;
|
|
@@ -4002,26 +4298,26 @@ function createDevServer(options) {
|
|
|
4002
4298
|
const segments = subpath.split("/");
|
|
4003
4299
|
let assetPath;
|
|
4004
4300
|
if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
|
|
4005
|
-
assetPath =
|
|
4301
|
+
assetPath = path10.join(
|
|
4006
4302
|
options.themePath,
|
|
4007
4303
|
"assets",
|
|
4008
4304
|
segments.slice(1).join("/")
|
|
4009
4305
|
);
|
|
4010
4306
|
} else {
|
|
4011
|
-
assetPath =
|
|
4307
|
+
assetPath = path10.join(options.themePath, "assets", subpath);
|
|
4012
4308
|
}
|
|
4013
|
-
if (assetPath.startsWith(
|
|
4309
|
+
if (assetPath.startsWith(path10.join(options.themePath, "assets")) && fs4.existsSync(assetPath)) {
|
|
4014
4310
|
serveFile(res, assetPath);
|
|
4015
4311
|
return;
|
|
4016
4312
|
}
|
|
4017
4313
|
}
|
|
4018
|
-
const filePath =
|
|
4314
|
+
const filePath = path10.join(options.distDir, pathname);
|
|
4019
4315
|
if (!filePath.startsWith(options.distDir)) {
|
|
4020
4316
|
res.writeHead(403);
|
|
4021
4317
|
res.end("Forbidden");
|
|
4022
4318
|
return;
|
|
4023
4319
|
}
|
|
4024
|
-
if (
|
|
4320
|
+
if (fs4.existsSync(filePath) && fs4.statSync(filePath).isFile()) {
|
|
4025
4321
|
serveFile(res, filePath);
|
|
4026
4322
|
} else {
|
|
4027
4323
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
@@ -4053,14 +4349,14 @@ function createDevServer(options) {
|
|
|
4053
4349
|
}
|
|
4054
4350
|
function serveFile(res, filePath) {
|
|
4055
4351
|
try {
|
|
4056
|
-
if (!
|
|
4352
|
+
if (!fs4.existsSync(filePath)) {
|
|
4057
4353
|
res.writeHead(404);
|
|
4058
4354
|
res.end("Not Found");
|
|
4059
4355
|
return;
|
|
4060
4356
|
}
|
|
4061
|
-
const ext =
|
|
4357
|
+
const ext = path10.extname(filePath);
|
|
4062
4358
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
4063
|
-
const content =
|
|
4359
|
+
const content = fs4.readFileSync(filePath);
|
|
4064
4360
|
res.writeHead(200, { "Content-Type": contentType });
|
|
4065
4361
|
res.end(content);
|
|
4066
4362
|
} catch {
|
|
@@ -4073,7 +4369,7 @@ function generatePreviewHTML(themeName, port, themeDataPath) {
|
|
|
4073
4369
|
let fontVarsCSS = "";
|
|
4074
4370
|
if (themeDataPath) {
|
|
4075
4371
|
try {
|
|
4076
|
-
const themeData = JSON.parse(
|
|
4372
|
+
const themeData = JSON.parse(fs4.readFileSync(themeDataPath, "utf-8"));
|
|
4077
4373
|
const typography = (themeData?.themeConfig || themeData?.theme?.config)?.typography?.fontFamily;
|
|
4078
4374
|
if (typography) {
|
|
4079
4375
|
const fontFamilies = /* @__PURE__ */ new Set();
|
|
@@ -4161,14 +4457,14 @@ async function devCommand(options) {
|
|
|
4161
4457
|
if (options.theme) {
|
|
4162
4458
|
themeName = options.theme;
|
|
4163
4459
|
try {
|
|
4164
|
-
const workspaceThemePath =
|
|
4460
|
+
const workspaceThemePath = path10.join(getThemesDir(), themeName);
|
|
4165
4461
|
if (fs.existsSync(workspaceThemePath)) {
|
|
4166
4462
|
themePath = workspaceThemePath;
|
|
4167
4463
|
} else {
|
|
4168
|
-
themePath =
|
|
4464
|
+
themePath = path10.join(process.cwd(), themeName);
|
|
4169
4465
|
}
|
|
4170
4466
|
} catch {
|
|
4171
|
-
themePath =
|
|
4467
|
+
themePath = path10.join(process.cwd(), themeName);
|
|
4172
4468
|
}
|
|
4173
4469
|
if (!fs.existsSync(themePath)) {
|
|
4174
4470
|
logger.error(`Theme "${themeName}" not found.`);
|
|
@@ -4179,10 +4475,10 @@ async function devCommand(options) {
|
|
|
4179
4475
|
"theme.config.ts",
|
|
4180
4476
|
"bundle-entry.ts",
|
|
4181
4477
|
"manifest.ts"
|
|
4182
|
-
].some((f) => fs.existsSync(
|
|
4478
|
+
].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
|
|
4183
4479
|
if (isThemeDir) {
|
|
4184
4480
|
themePath = process.cwd();
|
|
4185
|
-
themeName =
|
|
4481
|
+
themeName = path10.basename(themePath);
|
|
4186
4482
|
} else {
|
|
4187
4483
|
logger.error(
|
|
4188
4484
|
"Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
|
|
@@ -4251,9 +4547,9 @@ async function devCommand(options) {
|
|
|
4251
4547
|
watcher.close();
|
|
4252
4548
|
await context2.dispose();
|
|
4253
4549
|
server.close();
|
|
4254
|
-
const shimPath =
|
|
4550
|
+
const shimPath = path10.join(outputDir, ".process-shim.js");
|
|
4255
4551
|
try {
|
|
4256
|
-
await
|
|
4552
|
+
await fs9.unlink(shimPath);
|
|
4257
4553
|
} catch {
|
|
4258
4554
|
}
|
|
4259
4555
|
process.exit(0);
|
|
@@ -4262,8 +4558,8 @@ async function devCommand(options) {
|
|
|
4262
4558
|
|
|
4263
4559
|
// src/commands/config.ts
|
|
4264
4560
|
init_logger();
|
|
4265
|
-
var CONFIG_DIR =
|
|
4266
|
-
var CONFIG_FILE =
|
|
4561
|
+
var CONFIG_DIR = path10.join(os.homedir(), ".onexthm");
|
|
4562
|
+
var CONFIG_FILE = path10.join(CONFIG_DIR, ".env");
|
|
4267
4563
|
var CONFIG_ENTRIES = [
|
|
4268
4564
|
{
|
|
4269
4565
|
key: "AWS_ACCESS_KEY_ID",
|
|
@@ -4405,122 +4701,6 @@ async function configCommand() {
|
|
|
4405
4701
|
|
|
4406
4702
|
// src/commands/login.ts
|
|
4407
4703
|
init_logger();
|
|
4408
|
-
var AUTH_DIR = path8.join(os3.homedir(), ".onexthm");
|
|
4409
|
-
var AUTH_FILE = path8.join(AUTH_DIR, "auth.json");
|
|
4410
|
-
function getApiUrl() {
|
|
4411
|
-
return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
|
|
4412
|
-
}
|
|
4413
|
-
async function saveAuthTokens(tokens) {
|
|
4414
|
-
await fs.ensureDir(AUTH_DIR);
|
|
4415
|
-
const key = getMachineKey();
|
|
4416
|
-
const data = JSON.stringify(tokens);
|
|
4417
|
-
const encrypted = encrypt(data, key);
|
|
4418
|
-
await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
|
|
4419
|
-
}
|
|
4420
|
-
function loadAuthTokens() {
|
|
4421
|
-
try {
|
|
4422
|
-
if (!fs.existsSync(AUTH_FILE)) return null;
|
|
4423
|
-
const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
|
|
4424
|
-
const key = getMachineKey();
|
|
4425
|
-
const data = decrypt(encrypted, key);
|
|
4426
|
-
return JSON.parse(data);
|
|
4427
|
-
} catch {
|
|
4428
|
-
return null;
|
|
4429
|
-
}
|
|
4430
|
-
}
|
|
4431
|
-
async function clearAuthTokens() {
|
|
4432
|
-
try {
|
|
4433
|
-
await fs.remove(AUTH_FILE);
|
|
4434
|
-
} catch {
|
|
4435
|
-
}
|
|
4436
|
-
}
|
|
4437
|
-
function isTokenExpired(tokens) {
|
|
4438
|
-
return Date.now() / 1e3 > tokens.expiresAt - 60;
|
|
4439
|
-
}
|
|
4440
|
-
async function getValidTokens() {
|
|
4441
|
-
const tokens = loadAuthTokens();
|
|
4442
|
-
if (!tokens) return null;
|
|
4443
|
-
if (!isTokenExpired(tokens)) return tokens;
|
|
4444
|
-
try {
|
|
4445
|
-
const apiUrl = getApiUrl();
|
|
4446
|
-
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
4447
|
-
method: "POST",
|
|
4448
|
-
headers: { "Content-Type": "application/json" },
|
|
4449
|
-
body: JSON.stringify({ refresh_token: tokens.refreshToken })
|
|
4450
|
-
});
|
|
4451
|
-
if (!response.ok) {
|
|
4452
|
-
await clearAuthTokens();
|
|
4453
|
-
return null;
|
|
4454
|
-
}
|
|
4455
|
-
const data = await response.json();
|
|
4456
|
-
const body = data.statusCode ? data.body : data;
|
|
4457
|
-
const refreshed = {
|
|
4458
|
-
...tokens,
|
|
4459
|
-
accessToken: body.AccessToken || tokens.accessToken,
|
|
4460
|
-
idToken: body.IdToken || tokens.idToken,
|
|
4461
|
-
expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
|
|
4462
|
-
};
|
|
4463
|
-
await saveAuthTokens(refreshed);
|
|
4464
|
-
return refreshed;
|
|
4465
|
-
} catch {
|
|
4466
|
-
await clearAuthTokens();
|
|
4467
|
-
return null;
|
|
4468
|
-
}
|
|
4469
|
-
}
|
|
4470
|
-
async function authenticatedFetch(url, init) {
|
|
4471
|
-
const tokens = await getValidTokens();
|
|
4472
|
-
if (!tokens) {
|
|
4473
|
-
throw new Error("Not logged in. Run: onexthm login");
|
|
4474
|
-
}
|
|
4475
|
-
const headers = new Headers(init?.headers);
|
|
4476
|
-
headers.set("Authorization", `Bearer ${tokens.idToken}`);
|
|
4477
|
-
headers.set("Content-Type", "application/json");
|
|
4478
|
-
return fetch(url, { ...init, headers });
|
|
4479
|
-
}
|
|
4480
|
-
function getMachineKey() {
|
|
4481
|
-
let seed;
|
|
4482
|
-
if (process.platform === "darwin") {
|
|
4483
|
-
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4484
|
-
} else if (process.platform === "linux") {
|
|
4485
|
-
try {
|
|
4486
|
-
seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
|
|
4487
|
-
} catch {
|
|
4488
|
-
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4489
|
-
}
|
|
4490
|
-
} else {
|
|
4491
|
-
seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
|
|
4492
|
-
}
|
|
4493
|
-
return crypto2.createHash("sha256").update(seed).digest();
|
|
4494
|
-
}
|
|
4495
|
-
function encrypt(text, key) {
|
|
4496
|
-
const iv = crypto2.randomBytes(16);
|
|
4497
|
-
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
4498
|
-
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
4499
|
-
encrypted += cipher.final("hex");
|
|
4500
|
-
const tag = cipher.getAuthTag();
|
|
4501
|
-
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
|
|
4502
|
-
}
|
|
4503
|
-
function decrypt(text, key) {
|
|
4504
|
-
const [ivHex, tagHex, encrypted] = text.split(":");
|
|
4505
|
-
const iv = Buffer.from(ivHex, "hex");
|
|
4506
|
-
const tag = Buffer.from(tagHex, "hex");
|
|
4507
|
-
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
4508
|
-
decipher.setAuthTag(tag);
|
|
4509
|
-
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
4510
|
-
decrypted += decipher.final("utf-8");
|
|
4511
|
-
return decrypted;
|
|
4512
|
-
}
|
|
4513
|
-
function parseJwtClaims(idToken) {
|
|
4514
|
-
try {
|
|
4515
|
-
const payload = idToken.split(".")[1];
|
|
4516
|
-
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
4517
|
-
return JSON.parse(decoded);
|
|
4518
|
-
} catch {
|
|
4519
|
-
return {};
|
|
4520
|
-
}
|
|
4521
|
-
}
|
|
4522
|
-
|
|
4523
|
-
// src/commands/login.ts
|
|
4524
4704
|
async function loginCommand() {
|
|
4525
4705
|
logger.header("OneX Theme Developer Login");
|
|
4526
4706
|
const existing = loadAuthTokens();
|
|
@@ -4646,13 +4826,13 @@ async function publishCommand(options) {
|
|
|
4646
4826
|
logger.info(`Logged in as: ${tokens.user.email}`);
|
|
4647
4827
|
let themePath;
|
|
4648
4828
|
if (options.theme) {
|
|
4649
|
-
themePath =
|
|
4829
|
+
themePath = path10.resolve(options.theme);
|
|
4650
4830
|
} else {
|
|
4651
4831
|
const isThemeDir = [
|
|
4652
4832
|
"theme.config.ts",
|
|
4653
4833
|
"bundle-entry.ts",
|
|
4654
4834
|
"manifest.ts"
|
|
4655
|
-
].some((f) => fs.existsSync(
|
|
4835
|
+
].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
|
|
4656
4836
|
if (isThemeDir) {
|
|
4657
4837
|
themePath = process.cwd();
|
|
4658
4838
|
} else {
|
|
@@ -4662,14 +4842,31 @@ async function publishCommand(options) {
|
|
|
4662
4842
|
process.exit(1);
|
|
4663
4843
|
}
|
|
4664
4844
|
}
|
|
4665
|
-
const pkgPath =
|
|
4845
|
+
const pkgPath = path10.join(themePath, "package.json");
|
|
4666
4846
|
if (!fs.existsSync(pkgPath)) {
|
|
4667
4847
|
logger.error("No package.json found in theme directory");
|
|
4668
4848
|
process.exit(1);
|
|
4669
4849
|
}
|
|
4670
4850
|
const pkg = fs.readJsonSync(pkgPath);
|
|
4671
|
-
const themeId = pkg.name?.replace("@onex-themes/", "") ||
|
|
4851
|
+
const themeId = pkg.name?.replace("@onex-themes/", "") || path10.basename(themePath);
|
|
4852
|
+
if (options.bump) {
|
|
4853
|
+
const currentVersion = pkg.version || "1.0.0";
|
|
4854
|
+
const newVersion = semver.inc(currentVersion, options.bump);
|
|
4855
|
+
if (!newVersion) {
|
|
4856
|
+
logger.error(`Failed to bump version from ${currentVersion}`);
|
|
4857
|
+
process.exit(1);
|
|
4858
|
+
}
|
|
4859
|
+
pkg.version = newVersion;
|
|
4860
|
+
fs.writeJsonSync(pkgPath, pkg, { spaces: 2 });
|
|
4861
|
+
logger.info(`Bumped version: ${currentVersion} -> ${newVersion}`);
|
|
4862
|
+
}
|
|
4672
4863
|
const version2 = pkg.version || "1.0.0";
|
|
4864
|
+
if (!semver.valid(version2)) {
|
|
4865
|
+
logger.error(
|
|
4866
|
+
`Invalid version "${version2}" in package.json. Must be valid semver (e.g., 1.0.0)`
|
|
4867
|
+
);
|
|
4868
|
+
process.exit(1);
|
|
4869
|
+
}
|
|
4673
4870
|
logger.newLine();
|
|
4674
4871
|
logger.info(`Theme: ${themeId}`);
|
|
4675
4872
|
logger.info(`Version: ${version2}`);
|
|
@@ -4685,7 +4882,11 @@ async function publishCommand(options) {
|
|
|
4685
4882
|
themeId,
|
|
4686
4883
|
name: pkg.displayName || themeId,
|
|
4687
4884
|
description: pkg.description || "",
|
|
4688
|
-
email: tokens.user.email
|
|
4885
|
+
email: tokens.user.email,
|
|
4886
|
+
author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name || tokens.user.name || "",
|
|
4887
|
+
category: pkg.onex?.category || "MINIMAL",
|
|
4888
|
+
tags: pkg.keywords || [],
|
|
4889
|
+
thumbnail_url: pkg.onex?.thumbnail || ""
|
|
4689
4890
|
})
|
|
4690
4891
|
}
|
|
4691
4892
|
);
|
|
@@ -4702,6 +4903,38 @@ async function publishCommand(options) {
|
|
|
4702
4903
|
logger.error(error instanceof Error ? error.message : "Connection failed");
|
|
4703
4904
|
process.exit(1);
|
|
4704
4905
|
}
|
|
4906
|
+
logger.startSpinner("Checking version availability...");
|
|
4907
|
+
try {
|
|
4908
|
+
const checkResponse = await authenticatedFetch(
|
|
4909
|
+
`${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/exists`,
|
|
4910
|
+
{ method: "GET" }
|
|
4911
|
+
);
|
|
4912
|
+
const checkData = await checkResponse.json();
|
|
4913
|
+
const checkBody = checkData.statusCode ? checkData.body : checkData;
|
|
4914
|
+
if (checkBody.exists) {
|
|
4915
|
+
logger.stopSpinner(false, "Version already published");
|
|
4916
|
+
const patchVer = semver.inc(version2, "patch") || "?";
|
|
4917
|
+
const minorVer = semver.inc(version2, "minor") || "?";
|
|
4918
|
+
const majorVer = semver.inc(version2, "major") || "?";
|
|
4919
|
+
logger.error(
|
|
4920
|
+
`
|
|
4921
|
+
Version ${version2} of "${themeId}" is already published and cannot be overwritten.
|
|
4922
|
+
|
|
4923
|
+
To publish a new version:
|
|
4924
|
+
1. Bump version in package.json (e.g., ${version2} -> ${patchVer})
|
|
4925
|
+
2. Run: onexthm publish
|
|
4926
|
+
|
|
4927
|
+
Or use the --bump flag:
|
|
4928
|
+
onexthm publish --bump patch (${version2} -> ${patchVer})
|
|
4929
|
+
onexthm publish --bump minor (${version2} -> ${minorVer})
|
|
4930
|
+
onexthm publish --bump major (${version2} -> ${majorVer})`
|
|
4931
|
+
);
|
|
4932
|
+
process.exit(1);
|
|
4933
|
+
}
|
|
4934
|
+
logger.stopSpinner(true, `Version ${version2} is available`);
|
|
4935
|
+
} catch (error) {
|
|
4936
|
+
logger.stopSpinner(true, "Version check skipped (endpoint not available)");
|
|
4937
|
+
}
|
|
4705
4938
|
logger.startSpinner("Building theme...");
|
|
4706
4939
|
try {
|
|
4707
4940
|
const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
|
|
@@ -4747,13 +4980,13 @@ async function publishCommand(options) {
|
|
|
4747
4980
|
try {
|
|
4748
4981
|
const archiver3 = await import('archiver');
|
|
4749
4982
|
const { createWriteStream } = await import('fs');
|
|
4750
|
-
const distDir =
|
|
4983
|
+
const distDir = path10.join(themePath, "dist");
|
|
4751
4984
|
if (!fs.existsSync(distDir)) {
|
|
4752
4985
|
logger.stopSpinner(false, "No dist/ directory");
|
|
4753
4986
|
logger.error("Build the theme first: onexthm build");
|
|
4754
4987
|
process.exit(1);
|
|
4755
4988
|
}
|
|
4756
|
-
const bundleZipPath =
|
|
4989
|
+
const bundleZipPath = path10.join(themePath, "dist", "bundle.zip");
|
|
4757
4990
|
await createZip(distDir, bundleZipPath, [
|
|
4758
4991
|
"bundle.zip",
|
|
4759
4992
|
"source.zip",
|
|
@@ -4778,7 +5011,7 @@ async function publishCommand(options) {
|
|
|
4778
5011
|
}
|
|
4779
5012
|
logger.startSpinner("Uploading source...");
|
|
4780
5013
|
try {
|
|
4781
|
-
const sourceZipPath =
|
|
5014
|
+
const sourceZipPath = path10.join(themePath, "dist", "source.zip");
|
|
4782
5015
|
await createZip(themePath, sourceZipPath, [
|
|
4783
5016
|
"node_modules",
|
|
4784
5017
|
"dist",
|
|
@@ -4866,14 +5099,14 @@ async function createZip(sourceDir, outputPath, exclude) {
|
|
|
4866
5099
|
try {
|
|
4867
5100
|
const projectRoot = getProjectRoot();
|
|
4868
5101
|
dotenv.config({
|
|
4869
|
-
path:
|
|
5102
|
+
path: path10.join(projectRoot, ".env.local"),
|
|
4870
5103
|
quiet: true
|
|
4871
5104
|
});
|
|
4872
|
-
dotenv.config({ path:
|
|
5105
|
+
dotenv.config({ path: path10.join(projectRoot, ".env"), quiet: true });
|
|
4873
5106
|
} catch {
|
|
4874
5107
|
}
|
|
4875
5108
|
dotenv.config({
|
|
4876
|
-
path:
|
|
5109
|
+
path: path10.join(os.homedir(), ".onexthm", ".env"),
|
|
4877
5110
|
quiet: true
|
|
4878
5111
|
});
|
|
4879
5112
|
var require2 = createRequire(import.meta.url);
|
|
@@ -4933,7 +5166,10 @@ program.command("config").description("Configure OneX CLI credentials (AWS, API
|
|
|
4933
5166
|
program.command("login").description("Login to OneX platform").action(loginCommand);
|
|
4934
5167
|
program.command("logout").description("Logout from OneX platform").action(logoutCommand);
|
|
4935
5168
|
program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
|
|
4936
|
-
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").
|
|
5169
|
+
program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
|
|
5170
|
+
"--bump <type>",
|
|
5171
|
+
"Auto-bump version before publish (patch|minor|major)"
|
|
5172
|
+
).action(publishCommand);
|
|
4937
5173
|
program.configureOutput({
|
|
4938
5174
|
writeErr: (str) => process.stderr.write(chalk4.red(str))
|
|
4939
5175
|
});
|