@onexapis/cli 1.1.20 → 1.1.21

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.
@@ -124,34 +124,53 @@ async function loadThemeBundle(timestamp?: number): Promise<ThemeExports> {
124
124
  return module;
125
125
  }
126
126
 
127
- // ===== 3. SECTION RESOLUTION (adapted from storefront section-renderer.tsx) =====
127
+ // ===== 3. SECTION RESOLUTION Dynamic registry from schema exports =====
128
+
129
+ /**
130
+ * Build a type→component map by scanning all *Schema exports.
131
+ * No hardcoded prefixes — uses schema.type as the lookup key.
132
+ */
133
+ function buildSectionRegistry(
134
+ themeExports: ThemeExports
135
+ ): Map<string, { Component: React.ComponentType<any>; schema: any }> {
136
+ const registry = new Map<
137
+ string,
138
+ { Component: React.ComponentType<any>; schema: any }
139
+ >();
140
+
141
+ for (const [exportName, exportValue] of Object.entries(themeExports)) {
142
+ if (!exportName.endsWith("Schema") || !exportValue) continue;
143
+ const schema = exportValue as any;
144
+ const sectionType = schema?.type;
145
+ if (!sectionType) continue;
146
+
147
+ // "heroSchema" → "Hero" → "HeroDefault"
148
+ const baseName = exportName.replace(/Schema$/, "");
149
+ const componentName =
150
+ baseName.charAt(0).toUpperCase() + baseName.slice(1) + "Default";
151
+ const Component = themeExports[componentName] as
152
+ | React.ComponentType<any>
153
+ | undefined;
154
+
155
+ if (Component) {
156
+ registry.set(sectionType, { Component, schema });
157
+ }
158
+ }
159
+
160
+ return registry;
161
+ }
128
162
 
129
163
  function getSectionComponent(
130
- themeExports: ThemeExports,
131
- themePrefix: string,
164
+ registry: Map<string, { Component: React.ComponentType<any>; schema: any }>,
132
165
  sectionType: string,
133
166
  template: string = "default"
134
167
  ) {
135
- // Strip theme prefix: "cool-store-hero" -> "hero"
136
- let baseName = sectionType;
137
- if (themePrefix && baseName.startsWith(themePrefix)) {
138
- baseName = baseName.substring(themePrefix.length);
139
- }
140
-
141
- // Derive export names: "hero" + "default" -> "HeroDefault" + "heroSchema"
142
- const componentKey = toPascalCase(baseName) + toPascalCase(template);
143
- const schemaKey = toCamelCase(baseName) + "Schema";
144
-
145
- const Component = themeExports[componentKey] as
146
- | React.ComponentType<any>
147
- | undefined;
148
- const schema = themeExports[schemaKey] as any | undefined;
149
-
150
- if (!Component) return null;
168
+ const reg = registry.get(sectionType);
169
+ if (!reg) return null;
151
170
 
152
171
  return {
153
- Component,
154
- schema,
172
+ Component: reg.Component,
173
+ schema: reg.schema,
155
174
  template: { id: template, name: capitalize(template) },
156
175
  };
157
176
  }
@@ -160,17 +179,6 @@ function capitalize(str: string): string {
160
179
  return str.charAt(0).toUpperCase() + str.slice(1);
161
180
  }
162
181
 
163
- function toCamelCase(str: string): string {
164
- return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
165
- }
166
-
167
- function toPascalCase(str: string): string {
168
- return str
169
- .split("-")
170
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
171
- .join("");
172
- }
173
-
174
182
  // ===== 4. THEME CSS VARIABLE INJECTION =====
175
183
 
176
184
  const PALETTE_TO_CSS_VAR: Record<string, string> = {
@@ -246,16 +254,14 @@ function buildThemeStyleCSS(themeConfig: any): string | null {
246
254
 
247
255
  function resolveSections(
248
256
  sectionConfigs: any[],
249
- themeExports: ThemeExports,
250
- themePrefix: string
257
+ registry: Map<string, { Component: React.ComponentType<any>; schema: any }>
251
258
  ) {
252
259
  return (sectionConfigs || [])
253
260
  .filter((s: any) => s.enabled !== false)
254
261
  .sort((a: any, b: any) => a.order - b.order)
255
262
  .map((section: any) => {
256
263
  const reg = getSectionComponent(
257
- themeExports,
258
- themePrefix,
264
+ registry,
259
265
  section.type,
260
266
  section.template || "default"
261
267
  );
@@ -446,10 +452,9 @@ function PreviewApp() {
446
452
  );
447
453
  }
448
454
 
449
- // Discover pages and get themeId
455
+ // Discover pages and build dynamic section registry
450
456
  const pages = discoverPageConfigs(themeExports);
451
- const themeId = themeExports.themeConfig?.id || "";
452
- const themePrefix = themeId ? `${themeId}-` : "";
457
+ const sectionRegistry = buildSectionRegistry(themeExports);
453
458
 
454
459
  // Resolve initial page from URL path (once pages are discovered)
455
460
  const resolvedPage =
@@ -543,20 +548,17 @@ function PreviewApp() {
543
548
  const layoutConfig = themeExports.layoutConfig;
544
549
  const headerSections = resolveSections(
545
550
  layoutConfig?.headerSections || [],
546
- themeExports,
547
- themePrefix
551
+ sectionRegistry
548
552
  );
549
553
  const footerSections = resolveSections(
550
554
  layoutConfig?.footerSections || [],
551
- themeExports,
552
- themePrefix
555
+ sectionRegistry
553
556
  );
554
557
 
555
558
  // Resolve page sections
556
559
  const pageSections = resolveSections(
557
560
  currentPage.config.sections || [],
558
- themeExports,
559
- themePrefix
561
+ sectionRegistry
560
562
  );
561
563
 
562
564
  // Build theme CSS (Gap 2)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onexapis/cli",
3
- "version": "1.1.20",
3
+ "version": "1.1.21",
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",
@@ -68,7 +68,9 @@
68
68
  "node-fetch": "^3.3.2",
69
69
  "open": "^10.1.0",
70
70
  "ora": "^8.0.1",
71
- "ws": "^8.18.0"
71
+ "ws": "^8.18.0",
72
+ "tailwindcss": "^3.4.0",
73
+ "postcss": "^8.4.0"
72
74
  },
73
75
  "devDependencies": {
74
76
  "@types/adm-zip": "^0.5.7",