@punkestu/react-html 0.1.0 → 1.0.0

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.
Files changed (93) hide show
  1. package/README.md +72 -15
  2. package/dist/cli/docs-content.d.ts +8 -0
  3. package/dist/cli/docs-content.d.ts.map +1 -0
  4. package/dist/cli/docs-content.js +328 -0
  5. package/dist/cli/docs-content.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +135 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/tui-docs.d.ts +5 -0
  11. package/dist/cli/tui-docs.d.ts.map +1 -0
  12. package/dist/cli/tui-docs.js +191 -0
  13. package/dist/cli/tui-docs.js.map +1 -0
  14. package/dist/client/hydrate.d.ts +4 -0
  15. package/dist/client/hydrate.d.ts.map +1 -0
  16. package/dist/client/hydrate.js +80 -0
  17. package/dist/client/hydrate.js.map +1 -0
  18. package/dist/core/bundler.d.ts +9 -0
  19. package/dist/core/bundler.d.ts.map +1 -0
  20. package/dist/core/bundler.js +245 -0
  21. package/dist/core/bundler.js.map +1 -0
  22. package/dist/core/formatter.d.ts +6 -0
  23. package/dist/core/formatter.d.ts.map +1 -0
  24. package/dist/core/formatter.js +74 -0
  25. package/dist/core/formatter.js.map +1 -0
  26. package/dist/core/island-client.d.ts +9 -0
  27. package/dist/core/island-client.d.ts.map +1 -0
  28. package/dist/core/island-client.js +8 -0
  29. package/dist/core/island-client.js.map +1 -0
  30. package/dist/core/island-discovery.d.ts +19 -0
  31. package/dist/core/island-discovery.d.ts.map +1 -0
  32. package/dist/core/island-discovery.js +92 -0
  33. package/dist/core/island-discovery.js.map +1 -0
  34. package/dist/core/island-registry.d.ts +15 -0
  35. package/dist/core/island-registry.d.ts.map +1 -0
  36. package/dist/core/island-registry.js +17 -0
  37. package/dist/core/island-registry.js.map +1 -0
  38. package/dist/core/island.d.ts +32 -0
  39. package/dist/core/island.d.ts.map +1 -0
  40. package/dist/core/island.js +47 -0
  41. package/dist/core/island.js.map +1 -0
  42. package/dist/core/loader.d.ts +11 -0
  43. package/dist/core/loader.d.ts.map +1 -0
  44. package/dist/core/loader.js +87 -0
  45. package/dist/core/loader.js.map +1 -0
  46. package/dist/core/renderer.d.ts +20 -0
  47. package/dist/core/renderer.d.ts.map +1 -0
  48. package/dist/core/renderer.js +53 -0
  49. package/dist/core/renderer.js.map +1 -0
  50. package/dist/index.d.ts +27 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +110 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/server/dev-server.d.ts +8 -0
  55. package/dist/server/dev-server.d.ts.map +1 -0
  56. package/dist/server/dev-server.js +112 -0
  57. package/dist/server/dev-server.js.map +1 -0
  58. package/dist/server/engine.d.ts +50 -0
  59. package/dist/server/engine.d.ts.map +1 -0
  60. package/dist/server/engine.js +225 -0
  61. package/dist/server/engine.js.map +1 -0
  62. package/{src/server/index.ts → dist/server/index.d.ts} +1 -1
  63. package/dist/server/index.d.ts.map +1 -0
  64. package/dist/server/index.js +3 -0
  65. package/dist/server/index.js.map +1 -0
  66. package/package.json +5 -1
  67. package/.react-html/cache/page-fefe905ae5d2.mjs +0 -9
  68. package/DOCS.md +0 -317
  69. package/example/public/styles.css +0 -184
  70. package/example/src/components/Card.tsx +0 -17
  71. package/example/src/components/Counter.island.tsx +0 -35
  72. package/example/src/components/Footer.tsx +0 -13
  73. package/example/src/components/Header.tsx +0 -20
  74. package/example/src/components/Layout.tsx +0 -39
  75. package/example/src/components/Tabs.island.tsx +0 -59
  76. package/example/src/pages/index.tsx +0 -49
  77. package/example/src/pages/pure.tsx +0 -56
  78. package/src/cli/docs-content.ts +0 -268
  79. package/src/cli/index.ts +0 -113
  80. package/src/cli/tui-docs.ts +0 -214
  81. package/src/client/hydrate.ts +0 -84
  82. package/src/core/bundler.ts +0 -122
  83. package/src/core/formatter.ts +0 -92
  84. package/src/core/island-client.ts +0 -23
  85. package/src/core/island-registry.ts +0 -35
  86. package/src/core/island.tsx +0 -110
  87. package/src/core/loader.ts +0 -89
  88. package/src/core/renderer.ts +0 -80
  89. package/src/index.ts +0 -138
  90. package/src/server/dev-server.ts +0 -125
  91. package/src/server/engine.ts +0 -270
  92. package/tests/engine.test.mjs +0 -288
  93. package/tsconfig.json +0 -21
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/core/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import React, { ComponentType, ReactNode } from 'react';
2
+ export declare function Island<P extends Record<string, unknown>>({ component: Component, props, children }: {
3
+ component: ComponentType<P>;
4
+ props?: P;
5
+ children?: ReactNode;
6
+ [key: string]: unknown;
7
+ }): React.ReactElement;
8
+ export declare function island<P extends Record<string, unknown>>(Component: ComponentType<P>, _sourcePath?: string, _customName?: string): ComponentType<P>;
9
+ //# sourceMappingURL=island-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-client.d.ts","sourceRoot":"","sources":["../../src/core/island-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAExD,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxD,SAAS,EAAE,SAAS,EACpB,KAAe,EACf,QAAQ,EACT,EAAE;IACD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,GAAG,KAAK,CAAC,YAAY,CAErB;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,aAAa,CAAC,CAAC,CAAC,CAElB"}
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export function Island({ component: Component, props = {}, children }) {
3
+ return React.createElement(Component, props, children);
4
+ }
5
+ export function island(Component, _sourcePath, _customName) {
6
+ return Component;
7
+ }
8
+ //# sourceMappingURL=island-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-client.js","sourceRoot":"","sources":["../../src/core/island-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAExD,MAAM,UAAU,MAAM,CAAoC,EACxD,SAAS,EAAE,SAAS,EACpB,KAAK,GAAG,EAAO,EACf,QAAQ,EAMT;IACC,OAAO,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,SAA2B,EAC3B,WAAoB,EACpB,WAAoB;IAEpB,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Checks whether a given file path should be treated as a reactive island.
3
+ * Matches:
4
+ * - *.island.tsx, *.island.jsx, *.island.ts, *.island.js
5
+ * - Any component located in an `islands/` or `src/islands/` folder
6
+ */
7
+ export declare function isIslandFile(filePath: string): boolean;
8
+ /**
9
+ * Derives a clean PascalCase island identifier name from a file path.
10
+ * e.g. "Counter.island.tsx" -> "Counter"
11
+ * e.g. "dropdown-menu.island.jsx" -> "DropdownMenu"
12
+ */
13
+ export declare function getIslandName(filePath: string): string;
14
+ /**
15
+ * Transforms an island component source code to automatically wrap its default export
16
+ * with `island(...)` for server-side rendering, if not already wrapped.
17
+ */
18
+ export declare function transformIslandSource(code: string, filePath: string): string;
19
+ //# sourceMappingURL=island-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-discovery.d.ts","sourceRoot":"","sources":["../../src/core/island-discovery.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6E5E"}
@@ -0,0 +1,92 @@
1
+ import path from 'node:path';
2
+ import { pathToFileURL, fileURLToPath } from 'node:url';
3
+ /**
4
+ * Checks whether a given file path should be treated as a reactive island.
5
+ * Matches:
6
+ * - *.island.tsx, *.island.jsx, *.island.ts, *.island.js
7
+ * - Any component located in an `islands/` or `src/islands/` folder
8
+ */
9
+ export function isIslandFile(filePath) {
10
+ if (filePath.includes('node_modules') || filePath.includes('.test.')) {
11
+ return false;
12
+ }
13
+ const isIslandExt = /\.island\.(tsx|ts|jsx|js)$/i.test(filePath);
14
+ const isInIslandsDir = /(?:^|[\\/])islands[\\/].*?\.(tsx|ts|jsx|js)$/i.test(filePath);
15
+ return isIslandExt || isInIslandsDir;
16
+ }
17
+ /**
18
+ * Derives a clean PascalCase island identifier name from a file path.
19
+ * e.g. "Counter.island.tsx" -> "Counter"
20
+ * e.g. "dropdown-menu.island.jsx" -> "DropdownMenu"
21
+ */
22
+ export function getIslandName(filePath) {
23
+ const base = path.basename(filePath)
24
+ .replace(/\.island\.(tsx|ts|jsx|js)$/i, '')
25
+ .replace(/\.(tsx|ts|jsx|js)$/i, '');
26
+ // Convert kebab-case or snake_case to PascalCase
27
+ const pascal = base.replace(/(?:^|[-_])([a-z0-9])/gi, (_, char) => char.toUpperCase());
28
+ // Ensure it is a valid JavaScript identifier
29
+ return pascal.replace(/[^a-zA-Z0-9_$]/g, '') || 'AnonymousIsland';
30
+ }
31
+ /**
32
+ * Transforms an island component source code to automatically wrap its default export
33
+ * with `island(...)` for server-side rendering, if not already wrapped.
34
+ */
35
+ export function transformIslandSource(code, filePath) {
36
+ // If the file already manually uses island() or createIsland(), preserve it as is
37
+ if (/\bisland\s*\(/i.test(code) || /\bcreateIsland\s*\(/i.test(code)) {
38
+ return code;
39
+ }
40
+ // Must have an export default to auto-wrap
41
+ if (!code.includes('export default')) {
42
+ return code;
43
+ }
44
+ const islandName = getIslandName(filePath);
45
+ const fileUrl = pathToFileURL(path.resolve(filePath)).href;
46
+ let transformed = code;
47
+ let targetIdentifier = '__react_html_island_target__';
48
+ // Case 1: export default function [Name](...) or export default function(...)
49
+ const fnMatch = transformed.match(/export\s+default\s+function(?:\s+([a-zA-Z0-9_$]+))?\s*\(/);
50
+ if (fnMatch) {
51
+ const existingName = fnMatch[1];
52
+ if (existingName) {
53
+ targetIdentifier = existingName;
54
+ transformed = transformed.replace(/export\s+default\s+function\s+([a-zA-Z0-9_$]+)/, 'function $1');
55
+ }
56
+ else {
57
+ transformed = transformed.replace(/export\s+default\s+function\s*\(/, `function ${targetIdentifier}(`);
58
+ }
59
+ }
60
+ // Case 2: export default class [Name]
61
+ else if (/export\s+default\s+class\s+([a-zA-Z0-9_$]+)/.test(transformed)) {
62
+ const classMatch = transformed.match(/export\s+default\s+class\s+([a-zA-Z0-9_$]+)/);
63
+ targetIdentifier = classMatch[1];
64
+ transformed = transformed.replace(/export\s+default\s+class\s+([a-zA-Z0-9_$]+)/, 'class $1');
65
+ }
66
+ // Case 3: export default Identifier;
67
+ else if (/export\s+default\s+([a-zA-Z0-9_$]+)\s*;?$/.test(transformed.trim())) {
68
+ const idMatch = transformed.trim().match(/export\s+default\s+([a-zA-Z0-9_$]+)\s*;?$/);
69
+ if (idMatch) {
70
+ targetIdentifier = idMatch[1];
71
+ transformed = transformed.replace(new RegExp(`export\\s+default\\s+${targetIdentifier}\\s*;?`), '');
72
+ }
73
+ }
74
+ // Case 4: export default <Expression> (e.g. arrow function or memo call)
75
+ else {
76
+ transformed = transformed.replace(/export\s+default\s+/, `const ${targetIdentifier} = `);
77
+ }
78
+ const islandHelperPath = fileURLToPath(new URL('./island.js', import.meta.url));
79
+ // Append island wrapping code
80
+ const wrapperCode = `
81
+ import { island as __react_html_auto_island__ } from ${JSON.stringify(islandHelperPath)};
82
+ const __react_html_wrapped_island__ = __react_html_auto_island__(
83
+ ${targetIdentifier},
84
+ ${JSON.stringify(fileUrl)},
85
+ ${JSON.stringify(islandName)}
86
+ );
87
+ export default __react_html_wrapped_island__;
88
+ export { __react_html_wrapped_island__ as ${islandName} };
89
+ `;
90
+ return `${transformed}\n${wrapperCode}`;
91
+ }
92
+ //# sourceMappingURL=island-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-discovery.js","sourceRoot":"","sources":["../../src/core/island-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface IslandInstance {
2
+ id: string;
3
+ name: string;
4
+ entryPath: string;
5
+ props: Record<string, unknown>;
6
+ clientDirective?: 'load' | 'idle' | 'visible';
7
+ }
8
+ export interface RenderContext {
9
+ pagePath: string;
10
+ islands: IslandInstance[];
11
+ }
12
+ export declare function runWithRenderContext<T>(context: RenderContext, fn: () => T): T;
13
+ export declare function getCurrentRenderContext(): RenderContext | undefined;
14
+ export declare function registerIslandInContext(island: IslandInstance): void;
15
+ //# sourceMappingURL=island-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-registry.d.ts","sourceRoot":"","sources":["../../src/core/island-registry.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAQD,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE9E;AAED,wBAAgB,uBAAuB,IAAI,aAAa,GAAG,SAAS,CAEnE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAKpE"}
@@ -0,0 +1,17 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ const GLOBAL_STORAGE_KEY = Symbol.for('__react_html_render_context__');
3
+ const storage = globalThis[GLOBAL_STORAGE_KEY] ||
4
+ (globalThis[GLOBAL_STORAGE_KEY] = new AsyncLocalStorage());
5
+ export function runWithRenderContext(context, fn) {
6
+ return storage.run(context, fn);
7
+ }
8
+ export function getCurrentRenderContext() {
9
+ return storage.getStore();
10
+ }
11
+ export function registerIslandInContext(island) {
12
+ const ctx = storage.getStore();
13
+ if (ctx) {
14
+ ctx.islands.push(island);
15
+ }
16
+ }
17
+ //# sourceMappingURL=island-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-registry.js","sourceRoot":"","sources":["../../src/core/island-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAerD,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;AAEvE,MAAM,OAAO,GACV,UAAkB,CAAC,kBAAkB,CAAC;IACvC,CAAE,UAAkB,CAAC,kBAAkB,CAAC,GAAG,IAAI,iBAAiB,EAAiB,CAAC,CAAC;AAErF,MAAM,UAAU,oBAAoB,CAAI,OAAsB,EAAE,EAAW;IACzE,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAsB;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC/B,IAAI,GAAG,EAAE,CAAC;QACR,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,32 @@
1
+ import React, { ComponentType, ReactNode } from 'react';
2
+ export interface IslandProps<P = Record<string, unknown>> {
3
+ /** Explicit island name (optional) */
4
+ name?: string;
5
+ /** Path to the island component file (relative or absolute) */
6
+ src?: string;
7
+ /** The React component to render */
8
+ component: ComponentType<P>;
9
+ /** Props passed to the component */
10
+ props?: P;
11
+ /** Client hydration directive (default: 'load') */
12
+ client?: 'load' | 'idle' | 'visible';
13
+ /** Optional wrapper tag, default: 'div' */
14
+ as?: string;
15
+ className?: string;
16
+ style?: React.CSSProperties;
17
+ children?: ReactNode;
18
+ }
19
+ export declare function Island<P extends Record<string, unknown>>({ name, src, component: Component, props, client, as: Tag, className, style, children }: IslandProps<P>): React.ReactElement;
20
+ /**
21
+ * Helper to wrap any React component into a ready-to-use Island component
22
+ * Example:
23
+ * export const Counter = island(CounterComponent, import.meta.url, 'Counter');
24
+ * // In page: <Counter initialCount={5} />
25
+ */
26
+ export declare function island<P extends Record<string, unknown>>(Component: ComponentType<P>, sourcePath: string, customName?: string): ComponentType<P & {
27
+ client?: 'load' | 'idle' | 'visible';
28
+ as?: string;
29
+ className?: string;
30
+ style?: React.CSSProperties;
31
+ }>;
32
+ //# sourceMappingURL=island.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island.d.ts","sourceRoot":"","sources":["../../src/core/island.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMxD,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,oCAAoC;IACpC,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,2CAA2C;IAC3C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxD,IAAI,EACJ,GAAG,EACH,SAAS,EAAE,SAAS,EACpB,KAAe,EACf,MAAe,EACf,EAAE,EAAE,GAAW,EACf,SAAS,EACT,KAAK,EACL,QAAQ,EACT,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAqCrC;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,aAAa,CAAC,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAAE,CAAC,CA0B3H"}
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { renderToString } from 'react-dom/server';
4
+ import { registerIslandInContext } from './island-registry.js';
5
+ let islandCounter = 0;
6
+ export function Island({ name, src, component: Component, props = {}, client = 'load', as: Tag = 'div', className, style, children }) {
7
+ const componentName = name ||
8
+ Component.__islandName ||
9
+ Component.displayName ||
10
+ Component.name ||
11
+ 'AnonymousIsland';
12
+ const islandId = `island-${++islandCounter}`;
13
+ // If src is specified or attached to component
14
+ const entryPath = src || Component.__islandPath || '';
15
+ registerIslandInContext({
16
+ id: islandId,
17
+ name: componentName,
18
+ entryPath,
19
+ props,
20
+ clientDirective: client
21
+ });
22
+ const Wrapper = Tag;
23
+ // Use renderToString for the island's inner tree to produce hydration-compatible SSR markup
24
+ const innerHtml = renderToString(React.createElement(Component, props, children));
25
+ return (_jsx(Wrapper, { "data-island": componentName, "data-island-id": islandId, "data-props": JSON.stringify(props), "data-client": client, className: className, style: style, dangerouslySetInnerHTML: { __html: `<!--__ISLAND_START__-->${innerHtml}<!--__ISLAND_END__-->` } }));
26
+ }
27
+ /**
28
+ * Helper to wrap any React component into a ready-to-use Island component
29
+ * Example:
30
+ * export const Counter = island(CounterComponent, import.meta.url, 'Counter');
31
+ * // In page: <Counter initialCount={5} />
32
+ */
33
+ export function island(Component, sourcePath, customName) {
34
+ const resolvedName = customName || Component.displayName || Component.name || 'AnonymousIsland';
35
+ Component.__islandPath = sourcePath;
36
+ Component.__islandName = resolvedName;
37
+ const IslandWrapper = (props) => {
38
+ const { client, as, className, style, ...componentProps } = props;
39
+ return (_jsx(Island, { name: resolvedName, src: sourcePath, component: Component, props: componentProps, client: client, as: as, className: className, style: style }));
40
+ };
41
+ IslandWrapper.displayName = `Island(${resolvedName})`;
42
+ IslandWrapper.__islandPath = sourcePath;
43
+ IslandWrapper.__islandName = resolvedName;
44
+ IslandWrapper.__rawComponent = Component;
45
+ return IslandWrapper;
46
+ }
47
+ //# sourceMappingURL=island.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island.js","sourceRoot":"","sources":["../../src/core/island.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,IAAI,aAAa,GAAG,CAAC,CAAC;AAoBtB,MAAM,UAAU,MAAM,CAAoC,EACxD,IAAI,EACJ,GAAG,EACH,SAAS,EAAE,SAAS,EACpB,KAAK,GAAG,EAAO,EACf,MAAM,GAAG,MAAM,EACf,EAAE,EAAE,GAAG,GAAG,KAAK,EACf,SAAS,EACT,KAAK,EACL,QAAQ,EACO;IACf,MAAM,aAAa,GACjB,IAAI;QACH,SAAiB,CAAC,YAAY;QAC/B,SAAS,CAAC,WAAW;QACrB,SAAS,CAAC,IAAI;QACd,iBAAiB,CAAC;IAEpB,MAAM,QAAQ,GAAG,UAAU,EAAE,aAAa,EAAE,CAAC;IAE7C,+CAA+C;IAC/C,MAAM,SAAS,GAAG,GAAG,IAAK,SAAiB,CAAC,YAAY,IAAI,EAAE,CAAC;IAE/D,uBAAuB,CAAC;QACtB,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,aAAa;QACnB,SAAS;QACT,KAAK;QACL,eAAe,EAAE,MAAM;KACxB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,GAAU,CAAC;IAE3B,4FAA4F;IAC5F,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IAElF,OAAO,CACL,KAAC,OAAO,mBACO,aAAa,oBACV,QAAQ,gBACZ,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,iBACpB,MAAM,EACnB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EACZ,uBAAuB,EAAE,EAAE,MAAM,EAAE,0BAA0B,SAAS,uBAAuB,EAAE,GAC/F,CACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CACpB,SAA2B,EAC3B,UAAkB,EAClB,UAAmB;IAEnB,MAAM,YAAY,GAAG,UAAU,IAAI,SAAS,CAAC,WAAW,IAAI,SAAS,CAAC,IAAI,IAAI,iBAAiB,CAAC;IAC/F,SAAiB,CAAC,YAAY,GAAG,UAAU,CAAC;IAC5C,SAAiB,CAAC,YAAY,GAAG,YAAY,CAAC;IAE/C,MAAM,aAAa,GAAG,CAAC,KAAiH,EAAE,EAAE;QAC1I,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,cAAc,EAAE,GAAG,KAAK,CAAC;QAClE,OAAO,CACL,KAAC,MAAM,IACL,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,cAAmB,EAC1B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,EAAE,EACN,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,GACZ,CACH,CAAC;IACJ,CAAC,CAAC;IAEF,aAAa,CAAC,WAAW,GAAG,UAAU,YAAY,GAAG,CAAC;IACrD,aAAqB,CAAC,YAAY,GAAG,UAAU,CAAC;IAChD,aAAqB,CAAC,YAAY,GAAG,YAAY,CAAC;IAClD,aAAqB,CAAC,cAAc,GAAG,SAAS,CAAC;IAClD,OAAO,aAAa,CAAC;AACvB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface LoadedPage {
2
+ Component: React.ComponentType<any>;
3
+ metadata?: {
4
+ title?: string;
5
+ description?: string;
6
+ [key: string]: unknown;
7
+ };
8
+ }
9
+ export declare function loadPageModule(pageFilePath: string): Promise<LoadedPage>;
10
+ export declare function clearCache(): Promise<void>;
11
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AA2CD,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0C9E;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhD"}
@@ -0,0 +1,87 @@
1
+ import * as esbuild from 'esbuild';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { isIslandFile, transformIslandSource } from './island-discovery.js';
7
+ const CACHE_DIR = path.resolve(process.cwd(), '.react-html/cache');
8
+ // Plugin to auto-wrap island components and preserve import.meta.url
9
+ const islandAndMetaPlugin = {
10
+ name: 'island-auto-discovery-and-meta',
11
+ setup(build) {
12
+ build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
13
+ if (args.path.includes('node_modules'))
14
+ return null;
15
+ let text = await fs.readFile(args.path, 'utf8');
16
+ let modified = false;
17
+ // 1. Auto-wrap island files if they match island conventions
18
+ if (isIslandFile(args.path)) {
19
+ const transformed = transformIslandSource(text, args.path);
20
+ if (transformed !== text) {
21
+ text = transformed;
22
+ modified = true;
23
+ }
24
+ }
25
+ // 2. Preserve real file URL for any import.meta.url
26
+ if (text.includes('import.meta.url')) {
27
+ const fileUrl = pathToFileURL(args.path).href;
28
+ text = text.replaceAll('import.meta.url', JSON.stringify(fileUrl));
29
+ modified = true;
30
+ }
31
+ if (modified) {
32
+ const ext = path.extname(args.path).slice(1);
33
+ return {
34
+ contents: text,
35
+ loader: (ext === 'js' || ext === 'jsx' || ext === 'ts' || ext === 'tsx') ? ext : 'tsx'
36
+ };
37
+ }
38
+ return null;
39
+ });
40
+ }
41
+ };
42
+ export async function loadPageModule(pageFilePath) {
43
+ await fs.mkdir(CACHE_DIR, { recursive: true });
44
+ const resolvedPath = path.resolve(pageFilePath);
45
+ const hash = crypto.createHash('sha256').update(resolvedPath).digest('hex').slice(0, 12);
46
+ const cacheFilePath = path.join(CACHE_DIR, `page-${hash}.mjs`);
47
+ // Build the page bundle for Node.js execution
48
+ await esbuild.build({
49
+ entryPoints: [resolvedPath],
50
+ outfile: cacheFilePath,
51
+ bundle: true,
52
+ format: 'esm',
53
+ platform: 'node',
54
+ target: 'node22',
55
+ packages: 'external',
56
+ sourcemap: 'inline',
57
+ plugins: [islandAndMetaPlugin],
58
+ loader: {
59
+ '.tsx': 'tsx',
60
+ '.ts': 'ts',
61
+ '.jsx': 'jsx',
62
+ '.js': 'js',
63
+ '.css': 'empty'
64
+ }
65
+ });
66
+ // Dynamic import with timestamp cache buster
67
+ const fileUrl = `${pathToFileURL(cacheFilePath).href}?t=${Date.now()}`;
68
+ const importedModule = await import(fileUrl);
69
+ const Component = importedModule.default;
70
+ if (!Component) {
71
+ throw new Error(`Page "${pageFilePath}" does not have a default export. Please provide "export default function Page()".`);
72
+ }
73
+ const { default: _, ...metadata } = importedModule;
74
+ return {
75
+ Component,
76
+ metadata
77
+ };
78
+ }
79
+ export async function clearCache() {
80
+ try {
81
+ await fs.rm(CACHE_DIR, { recursive: true, force: true });
82
+ }
83
+ catch {
84
+ // Ignore cache cleanup errors
85
+ }
86
+ }
87
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { IslandInstance } from './island-registry.js';
3
+ export interface RenderResult {
4
+ html: string;
5
+ islands: IslandInstance[];
6
+ }
7
+ export interface RenderOptions {
8
+ pagePath: string;
9
+ Component: React.ComponentType<any>;
10
+ props?: Record<string, unknown>;
11
+ metadata?: {
12
+ title?: string;
13
+ description?: string;
14
+ lang?: string;
15
+ [key: string]: unknown;
16
+ };
17
+ basePath?: string;
18
+ }
19
+ export declare function renderPageToHtml(options: RenderOptions): RenderResult;
20
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/core/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAuDrE"}
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { runWithRenderContext } from './island-registry.js';
4
+ import { formatHtml } from './formatter.js';
5
+ export function renderPageToHtml(options) {
6
+ const { pagePath, Component, props = {}, metadata = {}, basePath = '' } = options;
7
+ const islands = [];
8
+ const rawMarkup = runWithRenderContext({ pagePath, islands }, () => renderToStaticMarkup(React.createElement(Component, props)));
9
+ let finalHtml = rawMarkup;
10
+ const hasHtmlTag = /<html[^>]*>/i.test(finalHtml);
11
+ const hasDoctype = /<!DOCTYPE html>/i.test(finalHtml);
12
+ // If component returned only body contents / fragments, wrap in standard HTML shell
13
+ if (!hasHtmlTag) {
14
+ const lang = metadata.lang || 'en';
15
+ const titleTag = metadata.title ? `<title>${metadata.title}</title>` : '';
16
+ const descTag = metadata.description ? `<meta name="description" content="${metadata.description}">` : '';
17
+ finalHtml = `<!DOCTYPE html>
18
+ <html lang="${lang}">
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ ${titleTag}
23
+ ${descTag}
24
+ </head>
25
+ <body>
26
+ ${finalHtml}
27
+ </body>
28
+ </html>`;
29
+ }
30
+ else if (!hasDoctype) {
31
+ finalHtml = `<!DOCTYPE html>\n${finalHtml}`;
32
+ }
33
+ // If there are islands, inject script tags before </body> or at the end of the file
34
+ if (islands.length > 0) {
35
+ // Collect unique island names
36
+ const uniqueIslands = Array.from(new Set(islands.map(i => i.name)));
37
+ const assetPrefix = basePath ? (basePath.endsWith('/') ? basePath : `${basePath}/`) : '/';
38
+ const scriptTags = uniqueIslands
39
+ .map(name => `<script type="module" src="${assetPrefix}assets/islands/${name}.js"></script>`)
40
+ .join('\n');
41
+ if (/<\/body>/i.test(finalHtml)) {
42
+ finalHtml = finalHtml.replace(/<\/body>/i, `${scriptTags}\n</body>`);
43
+ }
44
+ else {
45
+ finalHtml = `${finalHtml}\n${scriptTags}`;
46
+ }
47
+ }
48
+ return {
49
+ html: formatHtml(finalHtml),
50
+ islands
51
+ };
52
+ }
53
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/core/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAkB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAoB5C,MAAM,UAAU,gBAAgB,CAAC,OAAsB;IACrD,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAClF,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,MAAM,SAAS,GAAG,oBAAoB,CACpC,EAAE,QAAQ,EAAE,OAAO,EAAE,EACrB,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAClE,CAAC;IAEF,IAAI,SAAS,GAAG,SAAS,CAAC;IAC1B,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEtD,oFAAoF;IACpF,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC;QACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,QAAQ,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,qCAAqC,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAE1G,SAAS,GAAG;cACF,IAAI;;;;IAId,QAAQ;IACR,OAAO;;;IAGP,SAAS;;QAEL,CAAC;IACP,CAAC;SAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACvB,SAAS,GAAG,oBAAoB,SAAS,EAAE,CAAC;IAC9C,CAAC;IAED,oFAAoF;IACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,8BAA8B;QAC9B,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC1F,MAAM,UAAU,GAAG,aAAa;aAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,8BAA8B,WAAW,kBAAkB,IAAI,gBAAgB,CAAC;aAC5F,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,UAAU,WAAW,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,GAAG,SAAS,KAAK,UAAU,EAAE,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC;QAC3B,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ export { Island, island } from './core/island.js';
2
+ export type { IslandProps } from './core/island.js';
3
+ export { hydrateIsland } from './client/hydrate.js';
4
+ export { renderPageToHtml } from './core/renderer.js';
5
+ export { isIslandFile, getIslandName, transformIslandSource } from './core/island-discovery.js';
6
+ export { bundleIslands } from './core/bundler.js';
7
+ export type { ClientRuntime, BundlerOptions } from './core/bundler.js';
8
+ export { createEngine, ReactHtmlEngine } from './server/engine.js';
9
+ export type { EngineOptions } from './server/engine.js';
10
+ export interface BuildConfig {
11
+ root?: string;
12
+ pagesDir?: string;
13
+ outDir?: string;
14
+ publicDir?: string;
15
+ minify?: boolean;
16
+ clientRuntime?: 'react' | 'preact';
17
+ }
18
+ export interface BuildResult {
19
+ pages: {
20
+ input: string;
21
+ output: string;
22
+ }[];
23
+ islands: string[];
24
+ outDir: string;
25
+ }
26
+ export declare function build(config?: BuildConfig): Promise<BuildResult>;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAqBD,wBAAsB,KAAK,CAAC,MAAM,GAAE,WAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CA6F1E"}
package/dist/index.js ADDED
@@ -0,0 +1,110 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { loadPageModule, clearCache } from './core/loader.js';
4
+ import { renderPageToHtml } from './core/renderer.js';
5
+ import { bundleIslands } from './core/bundler.js';
6
+ export { Island, island } from './core/island.js';
7
+ export { hydrateIsland } from './client/hydrate.js';
8
+ export { renderPageToHtml } from './core/renderer.js';
9
+ export { isIslandFile, getIslandName, transformIslandSource } from './core/island-discovery.js';
10
+ export { bundleIslands } from './core/bundler.js';
11
+ export { createEngine, ReactHtmlEngine } from './server/engine.js';
12
+ async function findFilesRecursively(dir, extensions) {
13
+ const entries = await fs.readdir(dir, { withFileTypes: true });
14
+ const files = [];
15
+ for (const entry of entries) {
16
+ const fullPath = path.join(dir, entry.name);
17
+ if (entry.isDirectory()) {
18
+ files.push(...(await findFilesRecursively(fullPath, extensions)));
19
+ }
20
+ else if (entry.isFile()) {
21
+ const ext = path.extname(entry.name);
22
+ if (extensions.includes(ext) && !entry.name.startsWith('_') && !entry.name.includes('.test.')) {
23
+ files.push(fullPath);
24
+ }
25
+ }
26
+ }
27
+ return files;
28
+ }
29
+ export async function build(config = {}) {
30
+ const root = path.resolve(config.root || process.cwd());
31
+ const outDir = path.resolve(root, config.outDir || 'dist');
32
+ const minify = config.minify ?? true;
33
+ // Locate pages directory
34
+ let pagesDir = config.pagesDir ? path.resolve(root, config.pagesDir) : '';
35
+ if (!pagesDir) {
36
+ const candidateDirs = ['src/pages', 'pages'];
37
+ for (const cand of candidateDirs) {
38
+ const p = path.join(root, cand);
39
+ try {
40
+ const stat = await fs.stat(p);
41
+ if (stat.isDirectory()) {
42
+ pagesDir = p;
43
+ break;
44
+ }
45
+ }
46
+ catch {
47
+ // Continue checking
48
+ }
49
+ }
50
+ }
51
+ if (!pagesDir) {
52
+ throw new Error(`Pages directory not found. Please create 'src/pages' or specify 'pagesDir'.`);
53
+ }
54
+ // Ensure output directory exists and is clean
55
+ await fs.mkdir(outDir, { recursive: true });
56
+ // Copy public static directory if present
57
+ const publicDir = path.resolve(root, config.publicDir || 'public');
58
+ try {
59
+ const publicStat = await fs.stat(publicDir);
60
+ if (publicStat.isDirectory()) {
61
+ await fs.cp(publicDir, outDir, { recursive: true });
62
+ }
63
+ }
64
+ catch {
65
+ // Public directory optional
66
+ }
67
+ const pageFiles = await findFilesRecursively(pagesDir, ['.tsx', '.jsx', '.ts', '.js']);
68
+ if (pageFiles.length === 0) {
69
+ console.warn(`[react-html] No pages found in ${pagesDir}`);
70
+ }
71
+ const allDiscoveredIslands = [];
72
+ const processedPages = [];
73
+ for (const pageFile of pageFiles) {
74
+ const relativePage = path.relative(pagesDir, pageFile);
75
+ const parsed = path.parse(relativePage);
76
+ // Compute destination path: index.tsx -> index.html, about.tsx -> about.html
77
+ const outHtmlFile = path.join(outDir, parsed.dir, `${parsed.name}.html`);
78
+ await fs.mkdir(path.dirname(outHtmlFile), { recursive: true });
79
+ // Load page module
80
+ const { Component, metadata } = await loadPageModule(pageFile);
81
+ // Render to pure HTML and extract islands
82
+ const { html, islands } = renderPageToHtml({
83
+ pagePath: pageFile,
84
+ Component,
85
+ metadata
86
+ });
87
+ await fs.writeFile(outHtmlFile, html, 'utf8');
88
+ processedPages.push({ input: pageFile, output: outHtmlFile });
89
+ if (islands.length > 0) {
90
+ allDiscoveredIslands.push(...islands);
91
+ }
92
+ }
93
+ // Bundle reactive islands
94
+ let bundledIslands = [];
95
+ if (allDiscoveredIslands.length > 0) {
96
+ bundledIslands = await bundleIslands(allDiscoveredIslands, {
97
+ outDir,
98
+ minify,
99
+ clientRuntime: config.clientRuntime
100
+ });
101
+ }
102
+ // Cleanup compile cache
103
+ await clearCache();
104
+ return {
105
+ pages: processedPages,
106
+ islands: bundledIslands,
107
+ outDir
108
+ };
109
+ }
110
+ //# sourceMappingURL=index.js.map