@nextsparkjs/core 0.1.0-beta.135 → 0.1.0-beta.136

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.
@@ -1,18 +1,12 @@
1
1
  /**
2
- * PageRenderer Component — Async Server Component with RSC Streaming
2
+ * PageRenderer Component
3
3
  *
4
- * Renders pages from the Page Builder using async dynamic imports
5
- * per block, wrapped in Suspense boundaries for streaming SSR.
4
+ * Renders pages from the Page Builder by iterating over blocks
5
+ * and directly loading block components from the SSR registry.
6
6
  *
7
- * How it works:
8
- * 1. Each block is loaded via `loadBlockSSR(slug)` an async import
9
- * 2. React Server Components resolve the import on the server
10
- * 3. Suspense boundaries let React stream HTML progressively
11
- * 4. Client receives complete HTML (zero CLS, SEO-safe)
12
- * 5. Only JS chunks for blocks actually on the page are sent to client
13
- *
14
- * This gives per-block code splitting WITHOUT the CLS issues of next/dynamic,
15
- * because the server fully resolves each block before flushing the HTML.
7
+ * Uses direct imports (BLOCK_COMPONENTS_SSR) for synchronous hydration.
8
+ * This ensures zero CLS HTML is fully visible and stable without
9
+ * waiting for async chunk resolution on the client.
16
10
  *
17
11
  * @module core/components/public/pageBuilder
18
12
  */
@@ -1 +1 @@
1
- {"version":3,"file":"PageRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/public/pageBuilder/PageRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAqC1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,aAAa,EAAE,CAAA;QACvB,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CAiCvD"}
1
+ {"version":3,"file":"PageRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/public/pageBuilder/PageRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAiC1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,aAAa,EAAE,CAAA;QACvB,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA+BvD"}
@@ -1,6 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Suspense } from "react";
3
- import { loadBlockSSR, normalizeBlockProps } from "../../../lib/blocks/loader.js";
2
+ import { getBlockComponentSSR, normalizeBlockProps } from "../../../lib/blocks/loader.js";
4
3
  function BlockError({ blockSlug }) {
5
4
  return /* @__PURE__ */ jsx("div", { className: "w-full py-12 px-4 bg-destructive/10 border border-destructive/20 rounded", children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto text-center", children: [
6
5
  /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
@@ -10,8 +9,8 @@ function BlockError({ blockSlug }) {
10
9
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "This block may not be available or there was an error rendering it." })
11
10
  ] }) });
12
11
  }
13
- async function BlockRenderer({ block }) {
14
- const BlockComponent = await loadBlockSSR(block.blockSlug);
12
+ function BlockRenderer({ block }) {
13
+ const BlockComponent = getBlockComponentSSR(block.blockSlug);
15
14
  if (!BlockComponent) {
16
15
  console.warn(`Block component not found for slug: ${block.blockSlug}`);
17
16
  return /* @__PURE__ */ jsx(BlockError, { blockSlug: block.blockSlug });
@@ -33,7 +32,7 @@ function PageRenderer({ page }) {
33
32
  className: "@container w-full",
34
33
  "data-block-id": block.id,
35
34
  "data-block-slug": block.blockSlug,
36
- children: /* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(BlockRenderer, { block }) })
35
+ children: /* @__PURE__ */ jsx(BlockRenderer, { block })
37
36
  },
38
37
  block.id
39
38
  )) });
@@ -23,26 +23,14 @@ export declare function getBlockComponents(): Record<string, BlockComponent>;
23
23
  */
24
24
  export declare function getBlockComponent(slug: string): BlockComponent | undefined;
25
25
  /**
26
- * @deprecated Use loadBlockSSR for async Server Component rendering
26
+ * Get all SSR block components (direct imports, no React.lazy)
27
+ * Use for public page rendering where no-JS SSR is required
27
28
  */
28
29
  export declare function getBlockComponentsSSR(): Record<string, BlockComponent>;
29
30
  /**
30
- * @deprecated Use loadBlockSSR for async Server Component rendering
31
+ * Get a specific SSR block component by slug (direct import, no React.lazy)
31
32
  */
32
33
  export declare function getBlockComponentSSR(slug: string): BlockComponent | undefined;
33
- /**
34
- * Async block loader for RSC streaming.
35
- *
36
- * Dynamically imports a block component by slug. Used by PageRenderer
37
- * (async Server Component) with Suspense for:
38
- * - Per-block code splitting (only used blocks ship JS to client)
39
- * - Streaming SSR (server flushes HTML progressively)
40
- * - Zero CLS (HTML complete before client JS runs)
41
- *
42
- * @param slug - Block slug (e.g., 'hero', 'accordion')
43
- * @returns Resolved block component, or undefined if not found
44
- */
45
- export declare function loadBlockSSR(slug: string): Promise<BlockComponent | undefined>;
46
34
  /**
47
35
  * Check if a block exists in the registry
48
36
  *
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAIrC,KAAK,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;AAExC;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAKpF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgD3F"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAIrC,KAAK,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;AAExC;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgD3F"}
@@ -1,4 +1,4 @@
1
- import { BLOCK_COMPONENTS, BLOCK_COMPONENTS_SSR, BLOCK_IMPORTS_SSR } from "@nextsparkjs/registries/block-registry";
1
+ import { BLOCK_COMPONENTS, BLOCK_COMPONENTS_SSR } from "@nextsparkjs/registries/block-registry";
2
2
  function getBlockComponents() {
3
3
  return BLOCK_COMPONENTS;
4
4
  }
@@ -11,14 +11,8 @@ function getBlockComponentsSSR() {
11
11
  function getBlockComponentSSR(slug) {
12
12
  return BLOCK_COMPONENTS_SSR[slug];
13
13
  }
14
- async function loadBlockSSR(slug) {
15
- const importFn = BLOCK_IMPORTS_SSR[slug];
16
- if (!importFn) return void 0;
17
- const mod = await importFn();
18
- return mod.default;
19
- }
20
14
  function hasBlock(slug) {
21
- return slug in BLOCK_COMPONENTS || slug in BLOCK_IMPORTS_SSR;
15
+ return slug in BLOCK_COMPONENTS;
22
16
  }
23
17
  function normalizeBlockProps(props) {
24
18
  if (!props || typeof props !== "object" || Array.isArray(props)) {
@@ -64,6 +58,5 @@ export {
64
58
  getBlockComponents,
65
59
  getBlockComponentsSSR,
66
60
  hasBlock,
67
- loadBlockSSR,
68
61
  normalizeBlockProps
69
62
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-04-07T17:07:35.712Z",
2
+ "generated": "2026-04-07T17:31:21.161Z",
3
3
  "totalClasses": 1078,
4
4
  "classes": [
5
5
  "!text-2xl",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextsparkjs/core",
3
- "version": "0.1.0-beta.135",
3
+ "version": "0.1.0-beta.136",
4
4
  "description": "NextSpark - The complete SaaS framework for Next.js",
5
5
  "license": "MIT",
6
6
  "author": "NextSpark <hello@nextspark.dev>",
@@ -462,7 +462,7 @@
462
462
  "tailwind-merge": "^3.3.1",
463
463
  "uuid": "^13.0.0",
464
464
  "zod": "^4.1.5",
465
- "@nextsparkjs/testing": "0.1.0-beta.135"
465
+ "@nextsparkjs/testing": "0.1.0-beta.136"
466
466
  },
467
467
  "scripts": {
468
468
  "postinstall": "node scripts/postinstall.mjs || true",
@@ -47,11 +47,6 @@ export const BLOCK_CATEGORIES: string[] = []
47
47
 
48
48
  export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<React.ComponentType<unknown>>> = {}
49
49
 
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- export type BlockImportFn = () => Promise<{ default: React.ComponentType<any> }>
52
- export const BLOCK_IMPORTS_SSR: Record<string, BlockImportFn> = {}
53
-
54
- /** @deprecated Use BLOCK_IMPORTS_SSR */
55
50
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
51
  export const BLOCK_COMPONENTS_SSR: Record<string, React.ComponentType<any>> = {}
57
52
 
@@ -65,22 +60,23 @@ export const BLOCK_METADATA = {
65
60
  `
66
61
  }
67
62
 
68
- // Import field definitions and examples (if they exist)
63
+ // Import field definitions, examples, and components
69
64
  const imports = []
65
+ const ssrImports = []
70
66
 
71
67
  blocks.forEach(block => {
72
68
  const slugVar = block.slug.replace(/-/g, '_')
73
69
 
74
- // Always import field definitions
75
70
  imports.push(`import { fieldDefinitions as ${slugVar}_fields } from '${block.paths.fields}'`)
71
+ ssrImports.push(`import { default as ${slugVar}_component } from '${block.paths.component}'`)
76
72
 
77
- // Conditionally import examples if they exist
78
73
  if (block.hasExamples) {
79
74
  imports.push(`import { examples as ${slugVar}_examples } from '${block.paths.examples}'`)
80
75
  }
81
76
  })
82
77
 
83
78
  const fieldImports = imports.join('\n')
79
+ const ssrComponentImports = ssrImports.join('\n')
84
80
 
85
81
  const registryEntries = blocks.map(block => {
86
82
  const slugVar = block.slug.replace(/-/g, '_')
@@ -132,6 +128,9 @@ import type { BlockConfig, BlockCategory } from '${convertCorePath('@/core/types
132
128
 
133
129
  ${fieldImports}
134
130
 
131
+ // Direct component imports for SSR — zero CLS, synchronous hydration
132
+ ${ssrComponentImports}
133
+
135
134
  export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
136
135
  ${registryEntries}
137
136
  }
@@ -149,32 +148,18 @@ ${blocks.map(block => {
149
148
  }
150
149
 
151
150
  /**
152
- * Server-side block import functions for RSC streaming.
153
- *
154
- * Each entry is an async import function NOT a resolved component.
155
- * PageRenderer (async Server Component) calls these per-block with Suspense,
156
- * enabling:
157
- * - Per-block code splitting (only used blocks ship JS to client)
158
- * - Streaming SSR (server flushes HTML progressively as blocks resolve)
159
- * - Zero CLS (HTML is complete before client JS runs)
160
- *
161
- * For admin/client rendering, use BLOCK_COMPONENTS (React.lazy) instead.
151
+ * Direct-imported block components for SSR rendering.
152
+ * Static imports ensure synchronous hydration — zero CLS.
153
+ * HTML is fully visible without client JS (SEO-safe, no-JS compatible).
162
154
  */
163
155
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
- export type BlockImportFn = () => Promise<{ default: React.ComponentType<any> }>
165
- export const BLOCK_IMPORTS_SSR: Record<string, BlockImportFn> = {
156
+ export const BLOCK_COMPONENTS_SSR: Record<string, React.ComponentType<any>> = {
166
157
  ${blocks.map(block => {
167
- return ` '${block.slug}': () => import('${block.paths.component}')`
158
+ const slugVar = block.slug.replace(/-/g, '_')
159
+ return ` '${block.slug}': ${slugVar}_component`
168
160
  }).join(',\n')}
169
161
  }
170
162
 
171
- /**
172
- * @deprecated Use BLOCK_IMPORTS_SSR with async PageRenderer instead.
173
- * Kept for backwards compatibility with themes that override PageRenderer.
174
- */
175
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
- export const BLOCK_COMPONENTS_SSR: Record<string, React.ComponentType<any>> = {}
177
-
178
163
  /**
179
164
  * Block registry metadata
180
165
  */