@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.
@@ -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(/* @vite-ignore */ `/bundle-entry.js${cacheBust}`);
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 React.ComponentType<any> | undefined;
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 { Component, schema, template: { id: template, name: capitalize(template) } };
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.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
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(pages: Array<{ key: string; label: string; config: any }>): number {
155
- const pathname = window.location.pathname.replace(/^\/+|\/+$/g, "").toLowerCase();
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 style={{ padding: 40, textAlign: "center", fontFamily: "system-ui" }}>
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 = selectedPage === -1 ? getInitialPageFromURL(pages) : selectedPage;
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 style={{ padding: "80px 40px", textAlign: "center", fontFamily: "system-ui" }}>
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: <code style={{ background: "#f1f5f9", padding: "2px 8px", borderRadius: 4 }}>{pathname}</code>
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 style={{ display: "flex", gap: 8, justifyContent: "center", marginTop: 12 }}>
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", borderRadius: 6, background: "#E11D48",
295
- color: "white", textDecoration: "none", fontSize: 14, fontWeight: 500,
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 style={{ padding: 40, textAlign: "center", fontFamily: "system-ui" }}>
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., homePageConfig).
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(<PreviewApp />);
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.2",
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",
@@ -0,0 +1,4 @@
1
+ # API Configuration (optional — enables real data in preview)
2
+ # Get your Company ID from the OneX dashboard
3
+ NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com
4
+ NEXT_PUBLIC_COMPANY_ID=
@@ -14,7 +14,8 @@
14
14
  "author": "<%= author %>",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@onexapis/core": "^1.0.0"
17
+ "@onexapis/core": "^1.0.2",
18
+ "@tanstack/react-query": "^5.90.16"
18
19
  },
19
20
  "devDependencies": {
20
21
  "@types/react": "^19",