@absolutejs/absolute 0.19.0-beta.96 → 0.19.0-beta.97
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/.absolutejs/tsconfig.tsbuildinfo +1 -1
- package/dist/build.js +110 -54
- package/dist/build.js.map +5 -5
- package/dist/dev/client/handlers/react.ts +19 -35
- package/dist/index.js +113 -57
- package/dist/index.js.map +5 -5
- package/dist/src/dev/transformCache.d.ts +2 -0
- package/package.json +2 -2
- package/types/globals.d.ts +0 -1
- package/dist/freshBuildWorker.js +0 -559
- package/dist/src/build/freshReadPlugin.d.ts +0 -2
|
@@ -3,4 +3,6 @@ export declare const setTransformed: (filePath: string, content: string, mtime:
|
|
|
3
3
|
export declare const getInvalidationVersion: (filePath: string) => number;
|
|
4
4
|
export declare const invalidate: (filePath: string) => void;
|
|
5
5
|
export declare const invalidateAll: () => void;
|
|
6
|
+
export declare const markAsDataFile: (filePath: string) => void;
|
|
7
|
+
export declare const isDataFile: (filePath: string) => boolean;
|
|
6
8
|
export declare const findNearestComponent: (filePath: string) => string | undefined;
|
package/package.json
CHANGED
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"url": "https://github.com/absolutejs/absolutejs.git"
|
|
151
151
|
},
|
|
152
152
|
"scripts": {
|
|
153
|
-
"build": "rm -rf dist && bun build src/index.ts src/build.ts src/angular/index.ts src/react/index.ts src/react/hooks/index.ts src/svelte/index.ts src/vue/index.ts --outdir dist --sourcemap --target=bun --external react --external react-dom --external vue --external @vue/compiler-sfc --external vue/server-renderer --external svelte --external svelte/compiler --external svelte/server --external elysia --external @elysiajs/static --external @angular/compiler-cli --external @angular/core --external @angular/common --external @angular/platform-browser --external @angular/platform-server --external @angular/ssr --external zone.js --external debug --external @absolutejs/native-linux-x64 --external @absolutejs/native-linux-arm64 --external @absolutejs/native-darwin-x64 --external @absolutejs/native-darwin-arm64 && bun build src/
|
|
153
|
+
"build": "rm -rf dist && bun build src/index.ts src/build.ts src/angular/index.ts src/react/index.ts src/react/hooks/index.ts src/svelte/index.ts src/vue/index.ts --outdir dist --sourcemap --target=bun --external react --external react-dom --external vue --external @vue/compiler-sfc --external vue/server-renderer --external svelte --external svelte/compiler --external svelte/server --external elysia --external @elysiajs/static --external @angular/compiler-cli --external @angular/core --external @angular/common --external @angular/platform-browser --external @angular/platform-server --external @angular/ssr --external zone.js --external debug --external @absolutejs/native-linux-x64 --external @absolutejs/native-linux-arm64 --external @absolutejs/native-darwin-x64 --external @absolutejs/native-darwin-arm64 && bun build src/cli/index.ts --outfile dist/cli/index.js --target=bun && tsc --emitDeclarationOnly --project tsconfig.build.json && mkdir -p dist/dev && cp -r src/dev/client dist/dev/client",
|
|
154
154
|
"build:native": "./native/build.sh",
|
|
155
155
|
"db:push": "drizzle-kit push",
|
|
156
156
|
"db:studio": "drizzle-kit studio",
|
|
@@ -164,5 +164,5 @@
|
|
|
164
164
|
"typecheck": "bun run vue-tsc --noEmit"
|
|
165
165
|
},
|
|
166
166
|
"types": "./dist/src/index.d.ts",
|
|
167
|
-
"version": "0.19.0-beta.
|
|
167
|
+
"version": "0.19.0-beta.97"
|
|
168
168
|
}
|
package/types/globals.d.ts
CHANGED
|
@@ -41,7 +41,6 @@ declare global {
|
|
|
41
41
|
__ERROR_BOUNDARY__?: { reset: () => void };
|
|
42
42
|
__INITIAL_PROPS__?: Record<string, unknown>;
|
|
43
43
|
__REACT_COMPONENT_KEY__?: string;
|
|
44
|
-
__REACT_PAGE_MODULE__?: string;
|
|
45
44
|
__REACT_ROOT__?: { render: (element: unknown) => void };
|
|
46
45
|
__SVELTE_COMPONENT__?: Record<string, unknown>;
|
|
47
46
|
__SVELTE_UNMOUNT__?: () => void;
|
package/dist/freshBuildWorker.js
DELETED
|
@@ -1,559 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
-
var __require = import.meta.require;
|
|
18
|
-
|
|
19
|
-
// src/build/buildReactVendor.ts
|
|
20
|
-
var exports_buildReactVendor = {};
|
|
21
|
-
__export(exports_buildReactVendor, {
|
|
22
|
-
computeVendorPaths: () => computeVendorPaths,
|
|
23
|
-
buildReactVendor: () => buildReactVendor
|
|
24
|
-
});
|
|
25
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
26
|
-
import { join as join2 } from "path";
|
|
27
|
-
import { rm as rm2 } from "fs/promises";
|
|
28
|
-
var {build: bunBuild } = globalThis.Bun;
|
|
29
|
-
var reactSpecifiers, isResolvable = (specifier) => {
|
|
30
|
-
try {
|
|
31
|
-
__require.resolve(specifier);
|
|
32
|
-
return true;
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}, resolveVendorSpecifiers = () => {
|
|
37
|
-
if (isResolvable("react-refresh/runtime")) {
|
|
38
|
-
return [...reactSpecifiers, "react-refresh/runtime"];
|
|
39
|
-
}
|
|
40
|
-
return reactSpecifiers;
|
|
41
|
-
}, toSafeFileName = (specifier) => specifier.replace(/\//g, "_"), computeVendorPaths = () => {
|
|
42
|
-
const paths = {};
|
|
43
|
-
for (const specifier of resolveVendorSpecifiers()) {
|
|
44
|
-
paths[specifier] = `/react/vendor/${toSafeFileName(specifier)}.js`;
|
|
45
|
-
}
|
|
46
|
-
return paths;
|
|
47
|
-
}, generateEntrySource = async (specifier) => {
|
|
48
|
-
const mod = await import(specifier);
|
|
49
|
-
const exportNames = Object.keys(mod).filter((key) => key !== "default" && key !== "__esModule");
|
|
50
|
-
const lines = [];
|
|
51
|
-
if (exportNames.length > 0) {
|
|
52
|
-
lines.push(`export { ${exportNames.join(", ")} } from '${specifier}';`);
|
|
53
|
-
}
|
|
54
|
-
if ("default" in mod) {
|
|
55
|
-
lines.push(`export { default } from '${specifier}';`);
|
|
56
|
-
}
|
|
57
|
-
return `${lines.join(`
|
|
58
|
-
`)}
|
|
59
|
-
`;
|
|
60
|
-
}, buildReactVendor = async (buildDir) => {
|
|
61
|
-
const vendorDir = join2(buildDir, "react", "vendor");
|
|
62
|
-
mkdirSync2(vendorDir, { recursive: true });
|
|
63
|
-
const tmpDir = join2(buildDir, "_vendor_tmp");
|
|
64
|
-
mkdirSync2(tmpDir, { recursive: true });
|
|
65
|
-
const specifiers = resolveVendorSpecifiers();
|
|
66
|
-
const entrypoints = await Promise.all(specifiers.map(async (specifier) => {
|
|
67
|
-
const safeName = toSafeFileName(specifier);
|
|
68
|
-
const entryPath = join2(tmpDir, `${safeName}.ts`);
|
|
69
|
-
const source = await generateEntrySource(specifier);
|
|
70
|
-
await Bun.write(entryPath, source);
|
|
71
|
-
return entryPath;
|
|
72
|
-
}));
|
|
73
|
-
const result = await bunBuild({
|
|
74
|
-
entrypoints,
|
|
75
|
-
format: "esm",
|
|
76
|
-
minify: false,
|
|
77
|
-
naming: "[name].[ext]",
|
|
78
|
-
outdir: vendorDir,
|
|
79
|
-
splitting: true,
|
|
80
|
-
target: "browser",
|
|
81
|
-
throw: false
|
|
82
|
-
});
|
|
83
|
-
await rm2(tmpDir, { force: true, recursive: true });
|
|
84
|
-
if (!result.success) {
|
|
85
|
-
console.warn("\u26A0\uFE0F React vendor build had errors:", result.logs);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
var init_buildReactVendor = __esm(() => {
|
|
89
|
-
reactSpecifiers = [
|
|
90
|
-
"react",
|
|
91
|
-
"react-dom",
|
|
92
|
-
"react-dom/client",
|
|
93
|
-
"react/jsx-runtime",
|
|
94
|
-
"react/jsx-dev-runtime"
|
|
95
|
-
];
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// src/build/freshBuildWorker.ts
|
|
99
|
-
var {build: bunBuild2, Glob: Glob2 } = globalThis.Bun;
|
|
100
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync3, rmSync } from "fs";
|
|
101
|
-
import { join as join3, resolve as resolve2 } from "path";
|
|
102
|
-
|
|
103
|
-
// src/build/generateReactIndexes.ts
|
|
104
|
-
import { existsSync, mkdirSync } from "fs";
|
|
105
|
-
import { readdir, rm, writeFile } from "fs/promises";
|
|
106
|
-
import { basename, join, relative, resolve } from "path";
|
|
107
|
-
var {Glob } = globalThis.Bun;
|
|
108
|
-
var indexContentCache = new Map;
|
|
109
|
-
var resolveDevClientDir = () => {
|
|
110
|
-
const fromSource = resolve(import.meta.dir, "../dev/client");
|
|
111
|
-
if (existsSync(fromSource))
|
|
112
|
-
return fromSource;
|
|
113
|
-
return resolve(import.meta.dir, "./dev/client");
|
|
114
|
-
};
|
|
115
|
-
var devClientDir = resolveDevClientDir();
|
|
116
|
-
var errorOverlayPath = join(devClientDir, "errorOverlay.ts").replace(/\\/g, "/");
|
|
117
|
-
var hmrClientPath = join(devClientDir, "hmrClient.ts").replace(/\\/g, "/");
|
|
118
|
-
var refreshSetupPath = join(devClientDir, "reactRefreshSetup.ts").replace(/\\/g, "/");
|
|
119
|
-
var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory, isDev = false) => {
|
|
120
|
-
if (!existsSync(reactIndexesDirectory)) {
|
|
121
|
-
mkdirSync(reactIndexesDirectory, { recursive: true });
|
|
122
|
-
}
|
|
123
|
-
const pagesGlob = new Glob("*.*");
|
|
124
|
-
const files = [];
|
|
125
|
-
for await (const file of pagesGlob.scan({ cwd: reactPagesDirectory })) {
|
|
126
|
-
files.push(file);
|
|
127
|
-
}
|
|
128
|
-
const currentPageNames = new Set(files.map((file) => basename(file).split(".")[0]));
|
|
129
|
-
const emptyStringArray = [];
|
|
130
|
-
const existingIndexes = await readdir(reactIndexesDirectory).catch(() => emptyStringArray);
|
|
131
|
-
const staleIndexes = existingIndexes.filter((indexFile) => {
|
|
132
|
-
const indexName = indexFile.replace(/\.tsx$/, "");
|
|
133
|
-
return indexName !== "_refresh" && !currentPageNames.has(indexName);
|
|
134
|
-
});
|
|
135
|
-
if (staleIndexes.length > 0) {
|
|
136
|
-
await Promise.all(staleIndexes.map((indexFile) => {
|
|
137
|
-
indexContentCache.delete(join(reactIndexesDirectory, indexFile));
|
|
138
|
-
return rm(join(reactIndexesDirectory, indexFile), {
|
|
139
|
-
force: true
|
|
140
|
-
});
|
|
141
|
-
}));
|
|
142
|
-
}
|
|
143
|
-
const promises = files.map(async (file) => {
|
|
144
|
-
const fileName = basename(file);
|
|
145
|
-
const [componentName] = fileName.split(".");
|
|
146
|
-
const hmrPreamble = isDev ? [
|
|
147
|
-
`window.__HMR_FRAMEWORK__ = "react";`,
|
|
148
|
-
`window.__REACT_COMPONENT_KEY__ = "${componentName}Index";`,
|
|
149
|
-
`window.__REACT_PAGE_MODULE__ = "/@src/${relative(process.cwd(), resolve(reactPagesDirectory, componentName + ".tsx")).replace(/\\\\/g, "/")}";`,
|
|
150
|
-
`import '${refreshSetupPath}';`,
|
|
151
|
-
`import '${hmrClientPath}';`,
|
|
152
|
-
`import { showErrorOverlay, hideErrorOverlay } from '${errorOverlayPath}';
|
|
153
|
-
`
|
|
154
|
-
] : [];
|
|
155
|
-
const reactImports = isDev ? [
|
|
156
|
-
`import { hydrateRoot, createRoot } from 'react-dom/client';`,
|
|
157
|
-
`import { createElement, Component } from 'react';`
|
|
158
|
-
] : [
|
|
159
|
-
`import { hydrateRoot, createRoot } from 'react-dom/client';`,
|
|
160
|
-
`import { createElement } from 'react';`
|
|
161
|
-
];
|
|
162
|
-
const errorBoundaryDef = isDev ? [
|
|
163
|
-
`
|
|
164
|
-
// Dev-only Error Boundary to catch React render errors`,
|
|
165
|
-
`class ErrorBoundary extends Component {`,
|
|
166
|
-
` constructor(props) {`,
|
|
167
|
-
` super(props);`,
|
|
168
|
-
` this.state = { hasError: false };`,
|
|
169
|
-
` window.__ERROR_BOUNDARY__ = this;`,
|
|
170
|
-
` }`,
|
|
171
|
-
` static getDerivedStateFromError() {`,
|
|
172
|
-
` return { hasError: true };`,
|
|
173
|
-
` }`,
|
|
174
|
-
` componentDidCatch(error) {`,
|
|
175
|
-
` showErrorOverlay({`,
|
|
176
|
-
` framework: 'react',`,
|
|
177
|
-
` kind: 'runtime',`,
|
|
178
|
-
` message: error && error.stack ? error.stack : String(error)`,
|
|
179
|
-
` });`,
|
|
180
|
-
` }`,
|
|
181
|
-
` componentDidUpdate(prevProps, prevState) {`,
|
|
182
|
-
` if (prevState.hasError && !this.state.hasError) {`,
|
|
183
|
-
` hideErrorOverlay();`,
|
|
184
|
-
` }`,
|
|
185
|
-
` }`,
|
|
186
|
-
` reset() {`,
|
|
187
|
-
` this.setState({ hasError: false });`,
|
|
188
|
-
` }`,
|
|
189
|
-
` render() {`,
|
|
190
|
-
` if (this.state.hasError) return null;`,
|
|
191
|
-
``,
|
|
192
|
-
` return this.props.children;`,
|
|
193
|
-
` }`,
|
|
194
|
-
`}
|
|
195
|
-
`
|
|
196
|
-
] : [];
|
|
197
|
-
const content = [
|
|
198
|
-
...hmrPreamble,
|
|
199
|
-
...reactImports,
|
|
200
|
-
`import type { ComponentType } from 'react'`,
|
|
201
|
-
`import { ${componentName} } from '../pages/${componentName}';
|
|
202
|
-
`,
|
|
203
|
-
`type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
|
|
204
|
-
`,
|
|
205
|
-
`declare global {`,
|
|
206
|
-
` interface Window {`,
|
|
207
|
-
` __INITIAL_PROPS__?: PropsOf<typeof ${componentName}>`,
|
|
208
|
-
` __REACT_ROOT__?: ReturnType<typeof hydrateRoot | typeof createRoot>`,
|
|
209
|
-
` __HMR_CLIENT_ONLY_MODE__?: boolean`,
|
|
210
|
-
` }`,
|
|
211
|
-
`}
|
|
212
|
-
`,
|
|
213
|
-
...errorBoundaryDef,
|
|
214
|
-
`// Hydration with error handling and fallback`,
|
|
215
|
-
`const isDev = ${isDev};`,
|
|
216
|
-
`const componentPath = '../pages/${componentName}';
|
|
217
|
-
`,
|
|
218
|
-
`function isHydrationError(error) {`,
|
|
219
|
-
` if (!error) return false;`,
|
|
220
|
-
` const errorMessage = error instanceof Error ? error.message : String(error);`,
|
|
221
|
-
` const errorString = String(error);`,
|
|
222
|
-
` const fullMessage = errorMessage + ' ' + errorString;`,
|
|
223
|
-
` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
|
|
224
|
-
` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
|
|
225
|
-
` `,
|
|
226
|
-
` // Ignore whitespace-only mismatches in <head> - these are harmless formatting differences`,
|
|
227
|
-
` // The error often shows: + <link...> vs - {"\\n "} which is just formatting`,
|
|
228
|
-
` if (isHydration) {`,
|
|
229
|
-
` // Check if this is a head/link/stylesheet related mismatch`,
|
|
230
|
-
` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
|
|
231
|
-
` `,
|
|
232
|
-
` // Check if the mismatch involves only whitespace/newlines`,
|
|
233
|
-
` // Pattern: looks for {"\\n"} or {"\\n "} or similar whitespace-only content`,
|
|
234
|
-
` // Also check for patterns like: - {"\\n "} or + <link...>`,
|
|
235
|
-
` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
|
|
236
|
-
` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
|
|
237
|
-
` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
|
|
238
|
-
` `,
|
|
239
|
-
` // If it's head-related and involves whitespace/newlines, ignore it`,
|
|
240
|
-
` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
|
|
241
|
-
` return false; // Don't treat whitespace-only head mismatches as errors`,
|
|
242
|
-
` }`,
|
|
243
|
-
` }`,
|
|
244
|
-
` return isHydration;`,
|
|
245
|
-
`}
|
|
246
|
-
`,
|
|
247
|
-
`function logHydrationError(error, componentName) {`,
|
|
248
|
-
` if (!isDev) return;`,
|
|
249
|
-
` if (window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN) {`,
|
|
250
|
-
` try {`,
|
|
251
|
-
` window.__HMR_WS__.send(JSON.stringify({`,
|
|
252
|
-
` type: 'hydration-error',`,
|
|
253
|
-
` data: {`,
|
|
254
|
-
` componentName: '${componentName}',`,
|
|
255
|
-
` componentPath: componentPath,`,
|
|
256
|
-
` error: error instanceof Error ? error.message : String(error),`,
|
|
257
|
-
` timestamp: Date.now()`,
|
|
258
|
-
` }`,
|
|
259
|
-
` }));`,
|
|
260
|
-
` } catch (err) {}`,
|
|
261
|
-
` }`,
|
|
262
|
-
`}
|
|
263
|
-
`,
|
|
264
|
-
`// Track if we've already switched to client-only mode`,
|
|
265
|
-
`let hasSwitchedToClientOnly = false;`,
|
|
266
|
-
`let hydrationErrorDetected = false;
|
|
267
|
-
`,
|
|
268
|
-
`function handleHydrationFallback(error) {`,
|
|
269
|
-
` if (hasSwitchedToClientOnly) return; // Already handled`,
|
|
270
|
-
` hasSwitchedToClientOnly = true;`,
|
|
271
|
-
` hydrationErrorDetected = true;
|
|
272
|
-
`,
|
|
273
|
-
` logHydrationError(error, '${componentName}');
|
|
274
|
-
`,
|
|
275
|
-
` // Fallback: client-only render (no hydration)`,
|
|
276
|
-
` try {`,
|
|
277
|
-
` // Unmount existing root if it exists`,
|
|
278
|
-
` if (window.__REACT_ROOT__ && typeof window.__REACT_ROOT__.unmount === 'function') {`,
|
|
279
|
-
` try {`,
|
|
280
|
-
` window.__REACT_ROOT__.unmount();`,
|
|
281
|
-
` } catch (e) {`,
|
|
282
|
-
` // Ignore unmount errors`,
|
|
283
|
-
` }`,
|
|
284
|
-
` }
|
|
285
|
-
`,
|
|
286
|
-
` // Render into the same root container when falling back to client-only`,
|
|
287
|
-
` const root = createRoot(container);`,
|
|
288
|
-
` root.render(${isDev ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
|
|
289
|
-
` window.__REACT_ROOT__ = root;`,
|
|
290
|
-
` window.__HMR_CLIENT_ONLY_MODE__ = true;`,
|
|
291
|
-
` } catch (fallbackError) {`,
|
|
292
|
-
` window.location.reload();`,
|
|
293
|
-
` }`,
|
|
294
|
-
`}
|
|
295
|
-
`,
|
|
296
|
-
`// HMR State Preservation: Check for preserved state and merge with initial props`,
|
|
297
|
-
`// This allows state to be preserved across HMR updates without modifying component files`,
|
|
298
|
-
`let preservedState = (typeof window !== 'undefined' && window.__HMR_PRESERVED_STATE__) ? window.__HMR_PRESERVED_STATE__ : {};
|
|
299
|
-
`,
|
|
300
|
-
`// Also check sessionStorage for state that survived a page reload (for React HMR)`,
|
|
301
|
-
`if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {`,
|
|
302
|
-
` const hmrStateJson = sessionStorage.getItem('__REACT_HMR_STATE__');`,
|
|
303
|
-
` if (hmrStateJson) {`,
|
|
304
|
-
` try {`,
|
|
305
|
-
` const hmrState = JSON.parse(hmrStateJson);`,
|
|
306
|
-
` preservedState = { ...preservedState, ...hmrState };`,
|
|
307
|
-
` sessionStorage.removeItem('__REACT_HMR_STATE__');`,
|
|
308
|
-
` } catch (e) {}`,
|
|
309
|
-
` }`,
|
|
310
|
-
`}
|
|
311
|
-
`,
|
|
312
|
-
`const mergedProps = { ...(window.__INITIAL_PROPS__ || {}), ...preservedState };`,
|
|
313
|
-
`// Clear preserved state after using it (so it doesn't persist across multiple updates)`,
|
|
314
|
-
`if (typeof window !== 'undefined') {`,
|
|
315
|
-
` window.__HMR_PRESERVED_STATE__ = undefined;`,
|
|
316
|
-
`}
|
|
317
|
-
`,
|
|
318
|
-
`// Attempt hydration with error handling`,
|
|
319
|
-
`// Use document (not document.body) when the page renders <html><head><body>`,
|
|
320
|
-
`// to avoid "In HTML, <html> cannot be a child of <body>" hydration error`,
|
|
321
|
-
`const container = typeof document !== 'undefined' ? document : null;`,
|
|
322
|
-
`if (!container) {`,
|
|
323
|
-
` throw new Error('React root container not found: document is null');`,
|
|
324
|
-
`}
|
|
325
|
-
`,
|
|
326
|
-
`// Guard: only hydrate on first load. During HMR re-imports, skip hydration`,
|
|
327
|
-
`// so React Fast Refresh can swap components in-place and preserve state.`,
|
|
328
|
-
`if (!window.__REACT_ROOT__) {`,
|
|
329
|
-
` let root;`,
|
|
330
|
-
` try {`,
|
|
331
|
-
` // Use onRecoverableError to catch hydration errors (React 19)`,
|
|
332
|
-
` root = hydrateRoot(`,
|
|
333
|
-
` container,`,
|
|
334
|
-
` ${isDev ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`},`,
|
|
335
|
-
` {`,
|
|
336
|
-
` onRecoverableError: (error) => {`,
|
|
337
|
-
` // Check if this is a hydration error (isHydrationError filters out whitespace-only head mismatches)`,
|
|
338
|
-
` if (isDev && isHydrationError(error)) {`,
|
|
339
|
-
` // Real hydration error - handle it`,
|
|
340
|
-
` handleHydrationFallback(error);`,
|
|
341
|
-
` } else {`,
|
|
342
|
-
` // Not a hydration error, or it's a whitespace-only mismatch that was filtered out`,
|
|
343
|
-
` // Check if it's a whitespace-only head mismatch using the same logic as isHydrationError`,
|
|
344
|
-
` const errorMessage = error instanceof Error ? error.message : String(error);`,
|
|
345
|
-
` const errorString = String(error);`,
|
|
346
|
-
` const fullMessage = errorMessage + ' ' + errorString;`,
|
|
347
|
-
` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
|
|
348
|
-
` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
|
|
349
|
-
` if (isHydration) {`,
|
|
350
|
-
` // Check if this is a head/link/stylesheet related mismatch`,
|
|
351
|
-
` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
|
|
352
|
-
` // Check if the mismatch involves only whitespace/newlines`,
|
|
353
|
-
` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
|
|
354
|
-
` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
|
|
355
|
-
` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
|
|
356
|
-
` // If it's head-related and involves whitespace/newlines, silently ignore it`,
|
|
357
|
-
` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
|
|
358
|
-
` // Already logged by isHydrationError, just return silently`,
|
|
359
|
-
` return;`,
|
|
360
|
-
` }`,
|
|
361
|
-
` }`,
|
|
362
|
-
` // Log other recoverable errors`,
|
|
363
|
-
` console.error('React recoverable error:', error);`,
|
|
364
|
-
` }`,
|
|
365
|
-
` }`,
|
|
366
|
-
` }`,
|
|
367
|
-
` );`,
|
|
368
|
-
` window.__REACT_ROOT__ = root;`,
|
|
369
|
-
` } catch (error) {`,
|
|
370
|
-
` // Catch synchronous errors (shouldn't happen with hydrateRoot, but safety net)`,
|
|
371
|
-
` if (isDev && isHydrationError(error)) {`,
|
|
372
|
-
` handleHydrationFallback(error);`,
|
|
373
|
-
` } else {`,
|
|
374
|
-
` throw error;`,
|
|
375
|
-
` }`,
|
|
376
|
-
` }
|
|
377
|
-
`,
|
|
378
|
-
` // Also listen for hydration errors via console.error (React logs them there)`,
|
|
379
|
-
` if (isDev) {`,
|
|
380
|
-
` const originalError = console.error;`,
|
|
381
|
-
` console.error = function(...args) {`,
|
|
382
|
-
` const errorMessage = args.map(arg => {`,
|
|
383
|
-
` if (arg instanceof Error) return arg.message;`,
|
|
384
|
-
` return String(arg);`,
|
|
385
|
-
` }).join(' ');`,
|
|
386
|
-
` `,
|
|
387
|
-
` // Check if this is a hydration error`,
|
|
388
|
-
` if (isHydrationError({ message: errorMessage }) && !hydrationErrorDetected) {`,
|
|
389
|
-
` hydrationErrorDetected = true;`,
|
|
390
|
-
` // Create a synthetic error for fallback`,
|
|
391
|
-
` const syntheticError = new Error(errorMessage);`,
|
|
392
|
-
` // Use setTimeout to ensure this happens after React's error handling`,
|
|
393
|
-
` setTimeout(() => {`,
|
|
394
|
-
` handleHydrationFallback(syntheticError);`,
|
|
395
|
-
` }, 0);`,
|
|
396
|
-
` }`,
|
|
397
|
-
` `,
|
|
398
|
-
` // Call original console.error`,
|
|
399
|
-
` originalError.apply(console, args);`,
|
|
400
|
-
` };`,
|
|
401
|
-
` }`,
|
|
402
|
-
`}`
|
|
403
|
-
].join(`
|
|
404
|
-
`);
|
|
405
|
-
const indexPath = join(reactIndexesDirectory, `${componentName}.tsx`);
|
|
406
|
-
const hasher = new Bun.CryptoHasher("md5");
|
|
407
|
-
hasher.update(content);
|
|
408
|
-
const contentHash = hasher.digest("hex");
|
|
409
|
-
if (indexContentCache.get(indexPath) === contentHash && existsSync(indexPath)) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
indexContentCache.set(indexPath, contentHash);
|
|
413
|
-
await writeFile(indexPath, content);
|
|
414
|
-
});
|
|
415
|
-
await Promise.all(promises);
|
|
416
|
-
if (!isDev) {
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
const refreshPath = join(reactIndexesDirectory, "_refresh.tsx");
|
|
420
|
-
if (!existsSync(refreshPath)) {
|
|
421
|
-
await writeFile(refreshPath, `import '${refreshSetupPath}';
|
|
422
|
-
import 'react';
|
|
423
|
-
import 'react-dom/client';
|
|
424
|
-
`);
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
// src/build/generateManifest.ts
|
|
429
|
-
import { extname } from "path";
|
|
430
|
-
|
|
431
|
-
// src/constants.ts
|
|
432
|
-
var HOURS_IN_DAY = 24;
|
|
433
|
-
var MILLISECONDS_IN_A_SECOND = 1000;
|
|
434
|
-
var MINUTES_IN_AN_HOUR = 60;
|
|
435
|
-
var SECONDS_IN_A_MINUTE = 60;
|
|
436
|
-
var MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
437
|
-
var MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
|
|
438
|
-
var TWO_THIRDS = 2 / 3;
|
|
439
|
-
var UNFOUND_INDEX = -1;
|
|
440
|
-
|
|
441
|
-
// src/utils/normalizePath.ts
|
|
442
|
-
var normalizePath = (path) => path.replace(/\\/g, "/");
|
|
443
|
-
|
|
444
|
-
// src/utils/stringModifiers.ts
|
|
445
|
-
var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-");
|
|
446
|
-
var toPascal = (str) => {
|
|
447
|
-
if (!str.includes("-") && !str.includes("_")) {
|
|
448
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
449
|
-
}
|
|
450
|
-
return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// src/build/generateManifest.ts
|
|
454
|
-
var getManifestKey = (folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular) => {
|
|
455
|
-
if (folder === "indexes")
|
|
456
|
-
return `${pascalName}Index`;
|
|
457
|
-
if (isClientComponent)
|
|
458
|
-
return `${pascalName}Client`;
|
|
459
|
-
if (folder !== "pages")
|
|
460
|
-
return pascalName;
|
|
461
|
-
if (isReact)
|
|
462
|
-
return `${pascalName}Page`;
|
|
463
|
-
if (isVue || isSvelte || isAngular)
|
|
464
|
-
return pascalName;
|
|
465
|
-
return `${pascalName}Page`;
|
|
466
|
-
};
|
|
467
|
-
var generateManifest = (outputs, buildPath) => outputs.reduce((manifest, artifact) => {
|
|
468
|
-
const normalizedArtifactPath = normalizePath(artifact.path);
|
|
469
|
-
const normalizedBuildPath = normalizePath(buildPath);
|
|
470
|
-
let relative2 = normalizedArtifactPath.startsWith(normalizedBuildPath) ? normalizedArtifactPath.slice(normalizedBuildPath.length) : normalizedArtifactPath;
|
|
471
|
-
relative2 = relative2.replace(/^\/+/, "");
|
|
472
|
-
const segments = relative2.split("/");
|
|
473
|
-
const fileWithHash = segments.pop();
|
|
474
|
-
if (!fileWithHash)
|
|
475
|
-
return manifest;
|
|
476
|
-
const [baseName] = fileWithHash.split(`.${artifact.hash}.`);
|
|
477
|
-
if (!baseName)
|
|
478
|
-
return manifest;
|
|
479
|
-
const pascalName = toPascal(baseName);
|
|
480
|
-
const ext = extname(fileWithHash);
|
|
481
|
-
if (ext === ".css") {
|
|
482
|
-
manifest[`${pascalName}CSS`] = `/${relative2}`;
|
|
483
|
-
return manifest;
|
|
484
|
-
}
|
|
485
|
-
const idx = segments.findIndex((seg) => seg === "indexes" || seg === "pages" || seg === "client");
|
|
486
|
-
const folder = idx > UNFOUND_INDEX ? segments[idx] : segments[0];
|
|
487
|
-
const isReact = segments.some((seg) => seg === "react");
|
|
488
|
-
const isVue = segments.some((seg) => seg === "vue");
|
|
489
|
-
const isSvelte = segments.some((seg) => seg === "svelte");
|
|
490
|
-
const isAngular = segments.some((seg) => seg === "angular");
|
|
491
|
-
const isClientComponent = segments.includes("client");
|
|
492
|
-
const manifestKey = getManifestKey(folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular);
|
|
493
|
-
manifest[manifestKey] = `/${relative2}`;
|
|
494
|
-
return manifest;
|
|
495
|
-
}, {});
|
|
496
|
-
|
|
497
|
-
// src/build/freshBuildWorker.ts
|
|
498
|
-
var configArg = process.argv.find((a) => a.startsWith("{"));
|
|
499
|
-
var config = JSON.parse(configArg);
|
|
500
|
-
var reactDir = config.reactDirectory ? resolve2(config.reactDirectory) : undefined;
|
|
501
|
-
var buildDir = resolve2(config.buildDirectory || "build");
|
|
502
|
-
var hmr = config.options?.injectHMR === true;
|
|
503
|
-
var baseManifest = config.options?.baseManifest ?? {};
|
|
504
|
-
if (!reactDir) {
|
|
505
|
-
console.log("__MANIFEST__" + JSON.stringify(baseManifest));
|
|
506
|
-
process.exit(0);
|
|
507
|
-
}
|
|
508
|
-
var pagesDir = join3(reactDir, "pages");
|
|
509
|
-
var indexesDir = join3(reactDir, "indexes");
|
|
510
|
-
mkdirSync3(indexesDir, { recursive: true });
|
|
511
|
-
await generateReactIndexFiles(pagesDir, indexesDir, hmr);
|
|
512
|
-
var entries = [];
|
|
513
|
-
var glob = new Glob2("*.tsx");
|
|
514
|
-
for (const f of glob.scanSync({ cwd: indexesDir, absolute: true })) {
|
|
515
|
-
entries.push(f);
|
|
516
|
-
}
|
|
517
|
-
if (hmr) {
|
|
518
|
-
const refresh = join3(indexesDir, "_refresh.tsx");
|
|
519
|
-
if (existsSync2(refresh) && !entries.includes(refresh)) {
|
|
520
|
-
entries.push(refresh);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (entries.length === 0) {
|
|
524
|
-
console.log("__MANIFEST__" + JSON.stringify(baseManifest));
|
|
525
|
-
process.exit(0);
|
|
526
|
-
}
|
|
527
|
-
var externals = [];
|
|
528
|
-
try {
|
|
529
|
-
const { computeVendorPaths: computeVendorPaths2 } = await Promise.resolve().then(() => (init_buildReactVendor(), exports_buildReactVendor));
|
|
530
|
-
const vendorPaths = computeVendorPaths2();
|
|
531
|
-
if (vendorPaths)
|
|
532
|
-
externals = Object.keys(vendorPaths);
|
|
533
|
-
} catch {}
|
|
534
|
-
rmSync(join3(buildDir, "react", "indexes"), {
|
|
535
|
-
force: true,
|
|
536
|
-
recursive: true
|
|
537
|
-
});
|
|
538
|
-
var result = await bunBuild2({
|
|
539
|
-
entrypoints: entries,
|
|
540
|
-
...externals.length > 0 ? { external: externals } : {},
|
|
541
|
-
format: "esm",
|
|
542
|
-
...hmr ? { jsx: { development: true }, reactFastRefresh: true } : {},
|
|
543
|
-
naming: "[dir]/[name].[hash].[ext]",
|
|
544
|
-
outdir: buildDir,
|
|
545
|
-
root: reactDir,
|
|
546
|
-
splitting: true,
|
|
547
|
-
target: "browser",
|
|
548
|
-
throw: false
|
|
549
|
-
});
|
|
550
|
-
if (!result.success) {
|
|
551
|
-
for (const log of result.logs) {
|
|
552
|
-
console.error(log);
|
|
553
|
-
}
|
|
554
|
-
process.exit(1);
|
|
555
|
-
}
|
|
556
|
-
var manifest = await generateManifest(result.outputs, buildDir);
|
|
557
|
-
rmSync(indexesDir, { force: true, recursive: true });
|
|
558
|
-
var merged = { ...baseManifest, ...manifest };
|
|
559
|
-
console.log("__MANIFEST__" + JSON.stringify(merged));
|