@onexapis/cli 1.1.30 → 1.1.32
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 +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/preview/preview-app.tsx +23 -20
- package/package.json +2 -2
- package/templates/default/CLAUDE.md +44 -16
|
@@ -20,27 +20,16 @@ import * as coreProducts from "@onexapis/core/products";
|
|
|
20
20
|
import * as coreOrders from "@onexapis/core/orders";
|
|
21
21
|
import * as coreBlog from "@onexapis/core/blog";
|
|
22
22
|
import * as coreFinance from "@onexapis/core/finance";
|
|
23
|
+
import { initializeOnex } from "@onexapis/core";
|
|
24
|
+
import * as coreHooksAll from "@onexapis/core/hooks";
|
|
23
25
|
import * as coreInternal from "@onexapis/core/internal";
|
|
24
26
|
import * as coreTypes from "@onexapis/core/types";
|
|
25
27
|
import { CartProvider, FlyToCartProvider } from "@onexapis/core/contexts";
|
|
26
28
|
import { LocaleProvider } from "@onexapis/core/contexts";
|
|
27
29
|
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
const coreHooks = {
|
|
32
|
-
...coreCommerceHooks,
|
|
33
|
-
};
|
|
34
|
-
// Add context/state hooks individually (avoid spreading full modules)
|
|
35
|
-
for (const mod of [coreContexts, coreAuth, coreOrders, coreCart] as any[]) {
|
|
36
|
-
if (mod && typeof mod === "object") {
|
|
37
|
-
for (const [k, v] of Object.entries(mod)) {
|
|
38
|
-
if (typeof v === "function" && k.startsWith("use") && !(k in coreHooks)) {
|
|
39
|
-
(coreHooks as any)[k] = v;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
30
|
+
// @onexapis/core/hooks is the single source of truth — no manual composition.
|
|
31
|
+
// coreHooksAll re-exports all hooks (commerce, cart, auth, checkout, payment, etc.)
|
|
32
|
+
const coreHooks = coreHooksAll;
|
|
44
33
|
|
|
45
34
|
// Set React globals
|
|
46
35
|
(globalThis as any).__ONEX_REACT__ = React;
|
|
@@ -100,6 +89,12 @@ try {
|
|
|
100
89
|
if (apiUrl && companyId && CommerceClient && setCommerceClient) {
|
|
101
90
|
// Real API connection — preview will show actual products/blogs
|
|
102
91
|
setCommerceClient(new CommerceClient({ apiUrl, companyId }));
|
|
92
|
+
// Initialize all services (orders, finance, auth, etc.) so checkout/payment hooks work
|
|
93
|
+
try {
|
|
94
|
+
initializeOnex({ apiUrl, companyId });
|
|
95
|
+
} catch {
|
|
96
|
+
// May fail if already initialized — safe to ignore
|
|
97
|
+
}
|
|
103
98
|
} else if (setCommerceClient) {
|
|
104
99
|
// No API config — use stub that returns empty data
|
|
105
100
|
const emptyPagination = {
|
|
@@ -342,9 +337,12 @@ function discoverPageConfigs(
|
|
|
342
337
|
}
|
|
343
338
|
|
|
344
339
|
// Sort: "home" page first
|
|
340
|
+
// Sort: home page first (by handle, not type — multiple pages can have type "home")
|
|
345
341
|
pages.sort((a, b) => {
|
|
346
|
-
|
|
347
|
-
|
|
342
|
+
const aIsHome = a.config.handle === "home" || a.key === "homePageConfig";
|
|
343
|
+
const bIsHome = b.config.handle === "home" || b.key === "homePageConfig";
|
|
344
|
+
if (aIsHome && !bIsHome) return -1;
|
|
345
|
+
if (bIsHome && !aIsHome) return 1;
|
|
348
346
|
return a.label.localeCompare(b.label);
|
|
349
347
|
});
|
|
350
348
|
|
|
@@ -384,7 +382,10 @@ const PREVIEW_SUPPORTED_LOCALES = ["vi", "en"];
|
|
|
384
382
|
* "/vi/san-pham/slug" → { locale: "vi", rest: "/san-pham/slug" }
|
|
385
383
|
* "/products/slug" → { locale: "en", rest: "/products/slug" }
|
|
386
384
|
*/
|
|
387
|
-
function extractLocaleFromUrl(pathname: string): {
|
|
385
|
+
function extractLocaleFromUrl(pathname: string): {
|
|
386
|
+
locale: string;
|
|
387
|
+
rest: string;
|
|
388
|
+
} {
|
|
388
389
|
const parts = pathname.replace(/^\/+/, "").split("/");
|
|
389
390
|
if (parts[0] && PREVIEW_SUPPORTED_LOCALES.includes(parts[0])) {
|
|
390
391
|
return { locale: parts[0], rest: "/" + parts.slice(1).join("/") };
|
|
@@ -427,7 +428,9 @@ function getInitialPageFromURL(
|
|
|
427
428
|
if (!effectivePath) continue;
|
|
428
429
|
|
|
429
430
|
// Static locale path match (e.g., /gioi-thieu matches localizedPaths.vi: "/gioi-thieu")
|
|
430
|
-
const normalizedEffective = effectivePath
|
|
431
|
+
const normalizedEffective = effectivePath
|
|
432
|
+
.replace(/^\/+|\/+$/g, "")
|
|
433
|
+
.toLowerCase();
|
|
431
434
|
if (normalizedEffective === pathname) return i;
|
|
432
435
|
|
|
433
436
|
// Dynamic locale path match (e.g., /san-pham/my-slug matches localizedPaths.vi: "/san-pham/[slug]")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onexapis/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.32",
|
|
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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@aws-sdk/client-s3": "^3.470.0",
|
|
53
|
-
"@onexapis/core": "^1.0.
|
|
53
|
+
"@onexapis/core": "^1.0.5",
|
|
54
54
|
"@tanstack/react-query": "^5.90.16",
|
|
55
55
|
"adm-zip": "^0.5.16",
|
|
56
56
|
"archiver": "^7.0.1",
|
|
@@ -605,12 +605,15 @@ Dynamic pages use URL parameters like `/products/[slug]` to render detail views.
|
|
|
605
605
|
// pages/product-detail.ts
|
|
606
606
|
import type { PageConfig } from "@onexapis/core/types";
|
|
607
607
|
|
|
608
|
-
export const productDetailPageConfig: Omit<
|
|
608
|
+
export const productDetailPageConfig: Omit<
|
|
609
|
+
PageConfig,
|
|
610
|
+
"id" | "createdAt" | "updatedAt"
|
|
611
|
+
> = {
|
|
609
612
|
title: "Product Detail",
|
|
610
613
|
handle: "product-detail",
|
|
611
|
-
path: "/products/[slug]",
|
|
612
|
-
isDynamic: true,
|
|
613
|
-
dynamicSegments: ["slug"],
|
|
614
|
+
path: "/products/[slug]", // Dynamic segment in brackets
|
|
615
|
+
isDynamic: true, // Flag as dynamic
|
|
616
|
+
dynamicSegments: ["slug"], // Parameter names
|
|
614
617
|
type: "product",
|
|
615
618
|
renderMode: "sections",
|
|
616
619
|
themeId: "my-theme",
|
|
@@ -633,6 +636,7 @@ export default productDetailPageConfig;
|
|
|
633
636
|
```
|
|
634
637
|
|
|
635
638
|
Export in `bundle-entry.ts`:
|
|
639
|
+
|
|
636
640
|
```typescript
|
|
637
641
|
export { default as productDetailPageConfig } from "./pages/product-detail";
|
|
638
642
|
```
|
|
@@ -642,9 +646,14 @@ export { default as productDetailPageConfig } from "./pages/product-detail";
|
|
|
642
646
|
Sections receive `data.routeParams` with the extracted URL parameters:
|
|
643
647
|
|
|
644
648
|
```tsx
|
|
645
|
-
export function ProductDetail({
|
|
649
|
+
export function ProductDetail({
|
|
650
|
+
section,
|
|
651
|
+
schema,
|
|
652
|
+
isEditing,
|
|
653
|
+
data,
|
|
654
|
+
}: SectionComponentProps) {
|
|
646
655
|
const routeParams = (data?.routeParams || {}) as Record<string, string>;
|
|
647
|
-
const slug = routeParams.slug || "";
|
|
656
|
+
const slug = routeParams.slug || ""; // "blue-shirt" from /products/blue-shirt
|
|
648
657
|
|
|
649
658
|
const { data: product, isLoading } = useProductBySlug(slug, !!slug);
|
|
650
659
|
// ...
|
|
@@ -658,12 +667,13 @@ Use `localizedPaths` to define different URL slugs per locale:
|
|
|
658
667
|
```typescript
|
|
659
668
|
export const productDetailPageConfig = {
|
|
660
669
|
handle: "product-detail",
|
|
661
|
-
path: "/products/[slug]",
|
|
670
|
+
path: "/products/[slug]", // Default/fallback
|
|
662
671
|
isDynamic: true,
|
|
663
672
|
dynamicSegments: ["slug"],
|
|
664
|
-
localizedPaths: {
|
|
665
|
-
|
|
666
|
-
|
|
673
|
+
localizedPaths: {
|
|
674
|
+
// Locale-specific paths (typed by Locale enum)
|
|
675
|
+
vi: "/san-pham/[slug]", // /vi/san-pham/blue-shirt
|
|
676
|
+
en: "/products/[slug]", // /en/products/blue-shirt
|
|
667
677
|
},
|
|
668
678
|
type: "product",
|
|
669
679
|
// ...
|
|
@@ -673,8 +683,9 @@ export const productDetailPageConfig = {
|
|
|
673
683
|
The `Locale` type (`"vi" | "en"`) is from `@onexapis/core/types` — gives autocomplete.
|
|
674
684
|
|
|
675
685
|
Sections also receive `data.locale` to know the current locale:
|
|
686
|
+
|
|
676
687
|
```tsx
|
|
677
|
-
const locale = data?.locale as string;
|
|
688
|
+
const locale = data?.locale as string; // "vi" or "en"
|
|
678
689
|
```
|
|
679
690
|
|
|
680
691
|
### Key conventions
|
|
@@ -1010,7 +1021,15 @@ The cart system uses `useCart()` for state and a flexible fly-to-cart animation
|
|
|
1010
1021
|
```tsx
|
|
1011
1022
|
import { useCart } from "@onexapis/core/hooks";
|
|
1012
1023
|
|
|
1013
|
-
const {
|
|
1024
|
+
const {
|
|
1025
|
+
items,
|
|
1026
|
+
addItem,
|
|
1027
|
+
removeItem,
|
|
1028
|
+
updateQuantity,
|
|
1029
|
+
clearCart,
|
|
1030
|
+
itemCount,
|
|
1031
|
+
subtotal,
|
|
1032
|
+
} = useCart();
|
|
1014
1033
|
|
|
1015
1034
|
// Add to cart
|
|
1016
1035
|
addItem({
|
|
@@ -1033,11 +1052,11 @@ When a user clicks "Add to Cart", the product thumbnail flies from the button to
|
|
|
1033
1052
|
<div data-fly-to-cart-target className="my-cart-icon">
|
|
1034
1053
|
<ShoppingCartIcon />
|
|
1035
1054
|
<span>{itemCount}</span>
|
|
1036
|
-
</div
|
|
1055
|
+
</div>;
|
|
1037
1056
|
|
|
1038
1057
|
// Option B: Use CartIcon convenience component (auto-adds data-fly-to-cart-target)
|
|
1039
1058
|
import { CartIcon } from "@onexapis/core/components";
|
|
1040
|
-
<CartIcon count={itemCount} onClick={() => openCartDrawer()}
|
|
1059
|
+
<CartIcon count={itemCount} onClick={() => openCartDrawer()} />;
|
|
1041
1060
|
```
|
|
1042
1061
|
|
|
1043
1062
|
**Step 2: ProductCard triggers animation automatically** — just pass `onAddToCart`:
|
|
@@ -1045,7 +1064,14 @@ import { CartIcon } from "@onexapis/core/components";
|
|
|
1045
1064
|
```tsx
|
|
1046
1065
|
<ProductCard
|
|
1047
1066
|
product={product}
|
|
1048
|
-
onAddToCart={(p) =>
|
|
1067
|
+
onAddToCart={(p) =>
|
|
1068
|
+
addItem({
|
|
1069
|
+
productId: p.id,
|
|
1070
|
+
name: p.title,
|
|
1071
|
+
image: p.image,
|
|
1072
|
+
price: p.salePrice,
|
|
1073
|
+
})
|
|
1074
|
+
}
|
|
1049
1075
|
/>
|
|
1050
1076
|
```
|
|
1051
1077
|
|
|
@@ -1087,7 +1113,9 @@ import {
|
|
|
1087
1113
|
} from "@onexapis/core/server";
|
|
1088
1114
|
|
|
1089
1115
|
// Fetch products (server-side with ISR caching)
|
|
1090
|
-
const { data: products, pagination } = await fetchProducts(companyId, {
|
|
1116
|
+
const { data: products, pagination } = await fetchProducts(companyId, {
|
|
1117
|
+
limit: 12,
|
|
1118
|
+
});
|
|
1091
1119
|
|
|
1092
1120
|
// Fetch website settings
|
|
1093
1121
|
const settings = await fetchSettings(companyId);
|