@onexapis/cli 1.1.2 → 1.1.3
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 +157 -125
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +156 -125
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +153 -121
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -121
- package/dist/index.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +112 -21
- package/package.json +3 -1
- package/templates/default/.env.example +4 -0
- package/templates/default/package.json.ejs +2 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { useState, useEffect, useRef, useCallback } from "react";
|
|
4
4
|
import { createRoot } from "react-dom/client";
|
|
5
5
|
import * as jsxRuntime from "react/jsx-runtime";
|
|
6
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
6
7
|
|
|
7
8
|
// Core subpath imports — MUST match what setup-theme-globals.ts sets
|
|
8
9
|
// Only import browser-safe subpaths (NOT the root @onexapis/core which pulls server code)
|
|
@@ -11,6 +12,8 @@ import * as coreUtils from "@onexapis/core/utils";
|
|
|
11
12
|
import * as coreContexts from "@onexapis/core/contexts";
|
|
12
13
|
import * as coreComponents from "@onexapis/core/components";
|
|
13
14
|
import * as coreRegistry from "@onexapis/core/registry";
|
|
15
|
+
import * as coreCommerce from "@onexapis/core/commerce";
|
|
16
|
+
import * as coreCommerceHooks from "@onexapis/core/commerce/hooks";
|
|
14
17
|
|
|
15
18
|
// Set React globals
|
|
16
19
|
(globalThis as any).__ONEX_REACT__ = React;
|
|
@@ -26,8 +29,40 @@ import * as coreRegistry from "@onexapis/core/registry";
|
|
|
26
29
|
contexts: coreContexts,
|
|
27
30
|
components: coreComponents,
|
|
28
31
|
registry: coreRegistry,
|
|
32
|
+
commerce: coreCommerce,
|
|
33
|
+
"commerce/hooks": coreCommerceHooks,
|
|
29
34
|
};
|
|
30
35
|
|
|
36
|
+
// Initialize CommerceClient for preview
|
|
37
|
+
// If .env has NEXT_PUBLIC_API_URL + NEXT_PUBLIC_COMPANY_ID, use real API data.
|
|
38
|
+
// Otherwise, use a stub that returns empty data.
|
|
39
|
+
try {
|
|
40
|
+
const { CommerceClient, setCommerceClient } = coreCommerce as any;
|
|
41
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
42
|
+
const companyId = process.env.NEXT_PUBLIC_COMPANY_ID;
|
|
43
|
+
|
|
44
|
+
if (apiUrl && companyId && CommerceClient && setCommerceClient) {
|
|
45
|
+
// Real API connection — preview will show actual products/blogs
|
|
46
|
+
setCommerceClient(new CommerceClient({ apiUrl, companyId }));
|
|
47
|
+
} else if (setCommerceClient) {
|
|
48
|
+
// No API config — use stub that returns empty data
|
|
49
|
+
const emptyPagination = { total: 0, page: 1, limit: 10, totalPages: 0, hasNext: false, hasPrev: false };
|
|
50
|
+
const emptyList = { data: [], pagination: emptyPagination };
|
|
51
|
+
setCommerceClient({
|
|
52
|
+
getProducts: () => Promise.resolve(emptyList),
|
|
53
|
+
getProductBySlug: () => Promise.resolve(null),
|
|
54
|
+
getProductById: () => Promise.resolve(null),
|
|
55
|
+
getProductCategories: () => Promise.resolve([]),
|
|
56
|
+
getBlogs: () => Promise.resolve(emptyList),
|
|
57
|
+
getBlogBySlug: () => Promise.resolve(null),
|
|
58
|
+
getBlogById: () => Promise.resolve(null),
|
|
59
|
+
getBlogCategories: () => Promise.resolve([]),
|
|
60
|
+
getSettings: () => Promise.resolve({}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// CommerceClient not available — safe to ignore
|
|
65
|
+
}
|
|
31
66
|
|
|
32
67
|
// ===== 2. THEME LOADING =====
|
|
33
68
|
|
|
@@ -39,11 +74,12 @@ interface ThemeExports {
|
|
|
39
74
|
|
|
40
75
|
async function loadThemeBundle(timestamp?: number): Promise<ThemeExports> {
|
|
41
76
|
const cacheBust = timestamp ? `?t=${timestamp}` : `?t=${Date.now()}`;
|
|
42
|
-
const module = await import(
|
|
77
|
+
const module = await import(
|
|
78
|
+
/* @vite-ignore */ `/bundle-entry.js${cacheBust}`
|
|
79
|
+
);
|
|
43
80
|
return module;
|
|
44
81
|
}
|
|
45
82
|
|
|
46
|
-
|
|
47
83
|
// ===== 3. SECTION RESOLUTION (adapted from storefront section-renderer.tsx) =====
|
|
48
84
|
|
|
49
85
|
function getSectionComponent(
|
|
@@ -62,12 +98,18 @@ function getSectionComponent(
|
|
|
62
98
|
const componentKey = toPascalCase(baseName) + toPascalCase(template);
|
|
63
99
|
const schemaKey = toCamelCase(baseName) + "Schema";
|
|
64
100
|
|
|
65
|
-
const Component = themeExports[componentKey] as
|
|
101
|
+
const Component = themeExports[componentKey] as
|
|
102
|
+
| React.ComponentType<any>
|
|
103
|
+
| undefined;
|
|
66
104
|
const schema = themeExports[schemaKey] as any | undefined;
|
|
67
105
|
|
|
68
106
|
if (!Component) return null;
|
|
69
107
|
|
|
70
|
-
return {
|
|
108
|
+
return {
|
|
109
|
+
Component,
|
|
110
|
+
schema,
|
|
111
|
+
template: { id: template, name: capitalize(template) },
|
|
112
|
+
};
|
|
71
113
|
}
|
|
72
114
|
|
|
73
115
|
function capitalize(str: string): string {
|
|
@@ -79,10 +121,12 @@ function toCamelCase(str: string): string {
|
|
|
79
121
|
}
|
|
80
122
|
|
|
81
123
|
function toPascalCase(str: string): string {
|
|
82
|
-
return str
|
|
124
|
+
return str
|
|
125
|
+
.split("-")
|
|
126
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
127
|
+
.join("");
|
|
83
128
|
}
|
|
84
129
|
|
|
85
|
-
|
|
86
130
|
// ===== 4. SECTION DATA ENRICHMENT =====
|
|
87
131
|
|
|
88
132
|
/**
|
|
@@ -111,7 +155,6 @@ function enrichSectionWithDefaults(section: any, schema: any): any {
|
|
|
111
155
|
};
|
|
112
156
|
}
|
|
113
157
|
|
|
114
|
-
|
|
115
158
|
// ===== 5. PAGE CONFIG DISCOVERY =====
|
|
116
159
|
|
|
117
160
|
function discoverPageConfigs(
|
|
@@ -144,15 +187,18 @@ function discoverPageConfigs(
|
|
|
144
187
|
return pages;
|
|
145
188
|
}
|
|
146
189
|
|
|
147
|
-
|
|
148
190
|
// ===== 6. PREVIEW APP COMPONENT =====
|
|
149
191
|
|
|
150
192
|
/**
|
|
151
193
|
* Match URL pathname to a page index.
|
|
152
194
|
* "/" or "/home" → home page, "/showcase" → showcase page, etc.
|
|
153
195
|
*/
|
|
154
|
-
function getInitialPageFromURL(
|
|
155
|
-
|
|
196
|
+
function getInitialPageFromURL(
|
|
197
|
+
pages: Array<{ key: string; label: string; config: any }>
|
|
198
|
+
): number {
|
|
199
|
+
const pathname = window.location.pathname
|
|
200
|
+
.replace(/^\/+|\/+$/g, "")
|
|
201
|
+
.toLowerCase();
|
|
156
202
|
if (!pathname || pathname === "home") return 0; // home is always first after sorting
|
|
157
203
|
|
|
158
204
|
const idx = pages.findIndex((p) => {
|
|
@@ -258,7 +304,9 @@ function PreviewApp() {
|
|
|
258
304
|
|
|
259
305
|
if (!themeExports) {
|
|
260
306
|
return (
|
|
261
|
-
<div
|
|
307
|
+
<div
|
|
308
|
+
style={{ padding: 40, textAlign: "center", fontFamily: "system-ui" }}
|
|
309
|
+
>
|
|
262
310
|
<p>Loading theme...</p>
|
|
263
311
|
</div>
|
|
264
312
|
);
|
|
@@ -270,19 +318,42 @@ function PreviewApp() {
|
|
|
270
318
|
const themePrefix = themeId ? `${themeId}-` : "";
|
|
271
319
|
|
|
272
320
|
// Resolve initial page from URL path (once pages are discovered)
|
|
273
|
-
const resolvedPage =
|
|
321
|
+
const resolvedPage =
|
|
322
|
+
selectedPage === -1 ? getInitialPageFromURL(pages) : selectedPage;
|
|
274
323
|
|
|
275
324
|
// Page not found (-2)
|
|
276
325
|
if (resolvedPage === -2) {
|
|
277
326
|
const pathname = window.location.pathname;
|
|
278
327
|
return (
|
|
279
|
-
<div
|
|
328
|
+
<div
|
|
329
|
+
style={{
|
|
330
|
+
padding: "80px 40px",
|
|
331
|
+
textAlign: "center",
|
|
332
|
+
fontFamily: "system-ui",
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
280
335
|
<h1 style={{ fontSize: "4rem", margin: 0, color: "#E11D48" }}>404</h1>
|
|
281
336
|
<p style={{ fontSize: "1.25rem", color: "#333", marginTop: 12 }}>
|
|
282
|
-
Page not found:
|
|
337
|
+
Page not found:{" "}
|
|
338
|
+
<code
|
|
339
|
+
style={{
|
|
340
|
+
background: "#f1f5f9",
|
|
341
|
+
padding: "2px 8px",
|
|
342
|
+
borderRadius: 4,
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
{pathname}
|
|
346
|
+
</code>
|
|
283
347
|
</p>
|
|
284
348
|
<p style={{ color: "#888", marginTop: 8 }}>Available pages:</p>
|
|
285
|
-
<div
|
|
349
|
+
<div
|
|
350
|
+
style={{
|
|
351
|
+
display: "flex",
|
|
352
|
+
gap: 8,
|
|
353
|
+
justifyContent: "center",
|
|
354
|
+
marginTop: 12,
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
286
357
|
{pages.map((page) => {
|
|
287
358
|
const pageSlug = page.key.replace("PageConfig", "").toLowerCase();
|
|
288
359
|
const pagePath = pageSlug === "home" ? "/" : `/${pageSlug}`;
|
|
@@ -291,8 +362,13 @@ function PreviewApp() {
|
|
|
291
362
|
key={page.key}
|
|
292
363
|
href={pagePath}
|
|
293
364
|
style={{
|
|
294
|
-
padding: "6px 16px",
|
|
295
|
-
|
|
365
|
+
padding: "6px 16px",
|
|
366
|
+
borderRadius: 6,
|
|
367
|
+
background: "#E11D48",
|
|
368
|
+
color: "white",
|
|
369
|
+
textDecoration: "none",
|
|
370
|
+
fontSize: 14,
|
|
371
|
+
fontWeight: 500,
|
|
296
372
|
}}
|
|
297
373
|
>
|
|
298
374
|
{page.label}
|
|
@@ -308,10 +384,13 @@ function PreviewApp() {
|
|
|
308
384
|
|
|
309
385
|
if (!currentPage) {
|
|
310
386
|
return (
|
|
311
|
-
<div
|
|
387
|
+
<div
|
|
388
|
+
style={{ padding: 40, textAlign: "center", fontFamily: "system-ui" }}
|
|
389
|
+
>
|
|
312
390
|
<p>No page configs found in theme exports.</p>
|
|
313
391
|
<p style={{ color: "#888", marginTop: 8 }}>
|
|
314
|
-
Ensure your bundle-entry.ts exports page configs (e.g.,
|
|
392
|
+
Ensure your bundle-entry.ts exports page configs (e.g.,
|
|
393
|
+
homePageConfig).
|
|
315
394
|
</p>
|
|
316
395
|
</div>
|
|
317
396
|
);
|
|
@@ -428,8 +507,20 @@ function updateToolbar(status: string, label: string) {
|
|
|
428
507
|
}
|
|
429
508
|
}
|
|
430
509
|
|
|
431
|
-
|
|
432
510
|
// ===== 7. MOUNT =====
|
|
433
511
|
|
|
512
|
+
const queryClient = new QueryClient({
|
|
513
|
+
defaultOptions: {
|
|
514
|
+
queries: {
|
|
515
|
+
retry: false,
|
|
516
|
+
refetchOnWindowFocus: false,
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
|
|
434
521
|
const root = createRoot(document.getElementById("onex-preview-root")!);
|
|
435
|
-
root.render(
|
|
522
|
+
root.render(
|
|
523
|
+
<QueryClientProvider client={queryClient}>
|
|
524
|
+
<PreviewApp />
|
|
525
|
+
</QueryClientProvider>
|
|
526
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onexapis/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "CLI tool for OneX theme development - scaffolds themes using @onexapis/core",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"registry": "https://registry.npmjs.org/"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@onexapis/core": "workspace:*",
|
|
52
|
+
"@tanstack/react-query": "^5.90.16",
|
|
51
53
|
"@aws-sdk/client-s3": "^3.470.0",
|
|
52
54
|
"adm-zip": "^0.5.16",
|
|
53
55
|
"archiver": "^7.0.1",
|