@absolutejs/absolute 0.13.11 → 0.15.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.
- package/.claude/settings.local.json +7 -1
- package/CLAUDE.md +65 -56
- package/LICENSE +80 -24
- package/README.md +163 -163
- package/THIRD_PARTY_NOTICES.md +61 -0
- package/dist/cli/index.js +24 -8
- package/dist/index.js +3013 -480
- package/dist/index.js.map +56 -26
- package/dist/{build → src/build}/compileSvelte.d.ts +2 -1
- package/dist/src/build/compileVue.d.ts +33 -0
- package/dist/src/build/generateReactIndexes.d.ts +1 -0
- package/dist/src/build/htmlScriptHMRPlugin.d.ts +13 -0
- package/dist/src/build/wrapHTMLScript.d.ts +24 -0
- package/dist/src/core/build.d.ts +2 -0
- package/dist/src/core/devBuild.d.ts +6 -0
- package/dist/{core → src/core}/index.d.ts +2 -1
- package/dist/src/core/lookup.d.ts +3 -0
- package/dist/{core → src/core}/pageHandlers.d.ts +4 -4
- package/dist/src/dev/assetStore.d.ts +12 -0
- package/dist/src/dev/buildHMRClient.d.ts +1 -0
- package/dist/src/dev/clientManager.d.ts +26 -0
- package/dist/src/dev/configResolver.d.ts +13 -0
- package/dist/src/dev/dependencyGraph.d.ts +13 -0
- package/dist/src/dev/fileHashTracker.d.ts +2 -0
- package/dist/src/dev/fileWatcher.d.ts +3 -0
- package/dist/src/dev/moduleMapper.d.ts +21 -0
- package/dist/src/dev/moduleVersionTracker.d.ts +7 -0
- package/dist/src/dev/pathUtils.d.ts +5 -0
- package/dist/src/dev/reactComponentClassifier.d.ts +2 -0
- package/dist/src/dev/rebuildTrigger.d.ts +10 -0
- package/dist/src/dev/simpleHTMLHMR.d.ts +4 -0
- package/dist/src/dev/simpleHTMXHMR.d.ts +4 -0
- package/dist/src/dev/simpleSvelteHMR.d.ts +1 -0
- package/dist/src/dev/simpleVueHMR.d.ts +1 -0
- package/dist/src/dev/webSocket.d.ts +9 -0
- package/dist/{index.d.ts → src/index.d.ts} +1 -0
- package/dist/src/plugins/hmr.d.ts +62 -0
- package/dist/{plugins → src/plugins}/index.d.ts +2 -1
- package/dist/{plugins → src/plugins}/networking.d.ts +3 -0
- package/dist/{svelte → src/svelte}/renderToReadableStream.d.ts +3 -1
- package/dist/src/utils/getRegisterClientScript.d.ts +10 -0
- package/dist/{utils → src/utils}/index.d.ts +2 -0
- package/dist/src/utils/logger.d.ts +45 -0
- package/dist/src/utils/networking.d.ts +2 -0
- package/dist/src/utils/normalizePath.d.ts +9 -0
- package/dist/src/utils/registerClientScript.d.ts +51 -0
- package/dist/{types.d.ts → types/build.d.ts} +7 -0
- package/dist/types/client.d.ts +104 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/messages.d.ts +138 -0
- package/dist/types/websocket.d.ts +6 -0
- package/eslint.config.mjs +238 -234
- package/package.json +17 -12
- package/tsconfig.build.json +20 -20
- package/types/build.ts +46 -0
- package/types/client.ts +109 -0
- package/types/index.ts +4 -0
- package/types/messages.ts +205 -0
- package/types/websocket.ts +12 -0
- package/types/window-globals.ts +53 -0
- package/dist/build/compileVue.d.ts +0 -5
- package/dist/build/generateReactIndexes.d.ts +0 -1
- package/dist/core/build.d.ts +0 -2
- package/dist/core/lookup.d.ts +0 -1
- package/dist/utils/networking.d.ts +0 -1
- /package/dist/{build → src/build}/generateManifest.d.ts +0 -0
- /package/dist/{build → src/build}/outputLogs.d.ts +0 -0
- /package/dist/{build → src/build}/scanEntryPoints.d.ts +0 -0
- /package/dist/{build → src/build}/updateAssetPaths.d.ts +0 -0
- /package/dist/{cli → src/cli}/index.d.ts +0 -0
- /package/dist/{constants.d.ts → src/constants.d.ts} +0 -0
- /package/dist/{plugins → src/plugins}/pageRouter.d.ts +0 -0
- /package/dist/{svelte → src/svelte}/renderToPipeableStream.d.ts +0 -0
- /package/dist/{svelte → src/svelte}/renderToString.d.ts +0 -0
- /package/dist/{utils → src/utils}/cleanup.d.ts +0 -0
- /package/dist/{utils → src/utils}/commonAncestor.d.ts +0 -0
- /package/dist/{utils → src/utils}/escapeScriptContent.d.ts +0 -0
- /package/dist/{utils → src/utils}/generateHeadElement.d.ts +0 -0
- /package/dist/{utils → src/utils}/getDurationString.d.ts +0 -0
- /package/dist/{utils → src/utils}/getEnv.d.ts +0 -0
- /package/dist/{utils → src/utils}/stringModifiers.d.ts +0 -0
- /package/dist/{utils → src/utils}/validateSafePath.d.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,143 +1,47 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
13
|
+
var __require = import.meta.require;
|
|
14
|
+
|
|
2
15
|
// src/constants.ts
|
|
3
|
-
var UNFOUND_INDEX = -1;
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
|
|
10
|
-
var TIME_PRECISION = 2;
|
|
11
|
-
var TWO_THIRDS = 2 / 3;
|
|
12
|
-
var DEFAULT_PORT = 3000;
|
|
13
|
-
var DEFAULT_CHUNK_SIZE = 16384;
|
|
14
|
-
var BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet";
|
|
15
|
-
// src/core/build.ts
|
|
16
|
-
import { copyFileSync, cpSync, mkdirSync } from "fs";
|
|
17
|
-
import { rm as rm3 } from "fs/promises";
|
|
18
|
-
import { basename as basename4, join as join5 } from "path";
|
|
19
|
-
import { cwd, env as env2, exit } from "process";
|
|
20
|
-
var {$, build: bunBuild, Glob: Glob3 } = globalThis.Bun;
|
|
16
|
+
var UNFOUND_INDEX = -1, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_SECOND = 1000, MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR = 60, HOURS_IN_DAY = 24, MILLISECONDS_IN_A_DAY, TIME_PRECISION = 2, TWO_THIRDS, DEFAULT_PORT = 3000, DEFAULT_CHUNK_SIZE = 16384, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet";
|
|
17
|
+
var init_constants = __esm(() => {
|
|
18
|
+
MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
19
|
+
MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
|
|
20
|
+
TWO_THIRDS = 2 / 3;
|
|
21
|
+
});
|
|
21
22
|
|
|
22
|
-
// src/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
sep
|
|
32
|
-
} from "path";
|
|
33
|
-
import { env } from "process";
|
|
34
|
-
var {write, file, Transpiler } = globalThis.Bun;
|
|
35
|
-
import { compile, compileModule, preprocess } from "svelte/compiler";
|
|
36
|
-
var transpiler = new Transpiler({ loader: "ts", target: "browser" });
|
|
37
|
-
var exists = async (path) => {
|
|
38
|
-
try {
|
|
39
|
-
await stat(path);
|
|
40
|
-
return true;
|
|
41
|
-
} catch {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
var resolveSvelte = async (spec, from) => {
|
|
46
|
-
const basePath = resolve(dirname(from), spec);
|
|
47
|
-
const explicit = /\.(svelte|svelte\.(?:ts|js))$/.test(basePath);
|
|
48
|
-
if (!explicit) {
|
|
49
|
-
const extensions = [".svelte", ".svelte.ts", ".svelte.js"];
|
|
50
|
-
const paths = extensions.map((ext) => `${basePath}${ext}`);
|
|
51
|
-
const checks = await Promise.all(paths.map(exists));
|
|
52
|
-
const match = paths.find((_, index) => checks[index]);
|
|
53
|
-
return match ?? null;
|
|
23
|
+
// src/utils/stringModifiers.ts
|
|
24
|
+
var exports_stringModifiers = {};
|
|
25
|
+
__export(exports_stringModifiers, {
|
|
26
|
+
toPascal: () => toPascal,
|
|
27
|
+
toKebab: () => toKebab
|
|
28
|
+
});
|
|
29
|
+
var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toPascal = (str) => {
|
|
30
|
+
if (!str.includes("-") && !str.includes("_")) {
|
|
31
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
54
32
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!basePath.endsWith(".svelte"))
|
|
58
|
-
return null;
|
|
59
|
-
const tsPath = `${basePath}.ts`;
|
|
60
|
-
if (await exists(tsPath))
|
|
61
|
-
return tsPath;
|
|
62
|
-
const jsPath = `${basePath}.js`;
|
|
63
|
-
if (await exists(jsPath))
|
|
64
|
-
return jsPath;
|
|
65
|
-
return null;
|
|
66
|
-
};
|
|
67
|
-
var compileSvelte = async (entryPoints, svelteRoot, cache = new Map) => {
|
|
68
|
-
const compiledRoot = join(svelteRoot, "compiled");
|
|
69
|
-
const clientDir = join(compiledRoot, "client");
|
|
70
|
-
const indexDir = join(compiledRoot, "indexes");
|
|
71
|
-
const pagesDir = join(compiledRoot, "pages");
|
|
72
|
-
await Promise.all([clientDir, indexDir, pagesDir].map((dir) => mkdir(dir, { recursive: true })));
|
|
73
|
-
const dev = env.NODE_ENV === "development";
|
|
74
|
-
const build = async (src) => {
|
|
75
|
-
const memoized = cache.get(src);
|
|
76
|
-
if (memoized)
|
|
77
|
-
return memoized;
|
|
78
|
-
const raw = await file(src).text();
|
|
79
|
-
const isModule = src.endsWith(".svelte.ts") || src.endsWith(".svelte.js");
|
|
80
|
-
const preprocessed = isModule ? raw : (await preprocess(raw, {})).code;
|
|
81
|
-
const transpiled = src.endsWith(".ts") || src.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed) : preprocessed;
|
|
82
|
-
const relDir = dirname(relative(svelteRoot, src)).replace(/\\/g, "/");
|
|
83
|
-
const baseName = basename(src).replace(/\.svelte(\.(ts|js))?$/, "");
|
|
84
|
-
const importPaths = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((path) => path !== undefined);
|
|
85
|
-
const resolvedImports = await Promise.all(importPaths.map((importPath) => resolveSvelte(importPath, src)));
|
|
86
|
-
const childSources = resolvedImports.filter((path) => path !== null);
|
|
87
|
-
await Promise.all(childSources.map((child) => build(child)));
|
|
88
|
-
const generate = (mode) => (isModule ? compileModule(transpiled, { dev, filename: src }).js.code : compile(transpiled, {
|
|
89
|
-
css: "injected",
|
|
90
|
-
dev,
|
|
91
|
-
filename: src,
|
|
92
|
-
generate: mode
|
|
93
|
-
}).js.code).replace(/\.svelte(?:\.(?:ts|js))?(['"])/g, ".js$1");
|
|
94
|
-
const ssrPath = join(pagesDir, relDir, `${baseName}.js`);
|
|
95
|
-
const clientPath = join(clientDir, relDir, `${baseName}.js`);
|
|
96
|
-
await Promise.all([
|
|
97
|
-
mkdir(dirname(ssrPath), { recursive: true }),
|
|
98
|
-
mkdir(dirname(clientPath), { recursive: true })
|
|
99
|
-
]);
|
|
100
|
-
if (isModule) {
|
|
101
|
-
const bundle = generate("client");
|
|
102
|
-
await Promise.all([
|
|
103
|
-
write(ssrPath, bundle),
|
|
104
|
-
write(clientPath, bundle)
|
|
105
|
-
]);
|
|
106
|
-
} else {
|
|
107
|
-
const serverBundle = generate("server");
|
|
108
|
-
const clientBundle = generate("client");
|
|
109
|
-
await Promise.all([
|
|
110
|
-
write(ssrPath, serverBundle),
|
|
111
|
-
write(clientPath, clientBundle)
|
|
112
|
-
]);
|
|
113
|
-
}
|
|
114
|
-
const built = { client: clientPath, ssr: ssrPath };
|
|
115
|
-
cache.set(src, built);
|
|
116
|
-
return built;
|
|
117
|
-
};
|
|
118
|
-
const roots = await Promise.all(entryPoints.map(build));
|
|
119
|
-
await Promise.all(roots.map(async ({ client }) => {
|
|
120
|
-
const relClientDir = dirname(relative(clientDir, client));
|
|
121
|
-
const name = basename(client, extname(client));
|
|
122
|
-
const indexPath = join(indexDir, relClientDir, `${name}.js`);
|
|
123
|
-
const importRaw = relative(dirname(indexPath), client).split(sep).join("/");
|
|
124
|
-
const importPath = importRaw.startsWith(".") || importRaw.startsWith("/") ? importRaw : `./${importRaw}`;
|
|
125
|
-
const bootstrap = `import C from "${importPath}";
|
|
126
|
-
import { hydrate } from "svelte";
|
|
127
|
-
hydrate(C,{target:document.body,props:window.__INITIAL_PROPS__??{}});`;
|
|
128
|
-
await mkdir(dirname(indexPath), { recursive: true });
|
|
129
|
-
return write(indexPath, bootstrap);
|
|
130
|
-
}));
|
|
131
|
-
return {
|
|
132
|
-
svelteClientPaths: roots.map(({ client }) => {
|
|
133
|
-
const rel = dirname(relative(clientDir, client));
|
|
134
|
-
return join(indexDir, rel, basename(client));
|
|
135
|
-
}),
|
|
136
|
-
svelteServerPaths: roots.map(({ ssr }) => ssr)
|
|
137
|
-
};
|
|
138
|
-
};
|
|
33
|
+
return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
|
|
34
|
+
}, toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
139
35
|
|
|
140
36
|
// src/build/compileVue.ts
|
|
37
|
+
var exports_compileVue = {};
|
|
38
|
+
__export(exports_compileVue, {
|
|
39
|
+
vueHmrMetadata: () => vueHmrMetadata,
|
|
40
|
+
generateVueHmrId: () => generateVueHmrId,
|
|
41
|
+
detectVueChangeType: () => detectVueChangeType,
|
|
42
|
+
compileVue: () => compileVue,
|
|
43
|
+
clearVueHmrCaches: () => clearVueHmrCaches
|
|
44
|
+
});
|
|
141
45
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
142
46
|
import { basename as basename2, dirname as dirname2, join as join2, relative as relative2, resolve as resolve2 } from "path";
|
|
143
47
|
import {
|
|
@@ -147,29 +51,51 @@ import {
|
|
|
147
51
|
compileStyle
|
|
148
52
|
} from "@vue/compiler-sfc";
|
|
149
53
|
var {file: file2, write: write2, Transpiler: Transpiler2 } = globalThis.Bun;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
54
|
+
var hmrClientPath2, transpiler2, scriptCache, scriptSetupCache, templateCache, styleCache, vueHmrMetadata, detectVueChangeType = (filePath, descriptor) => {
|
|
55
|
+
const prevScript = scriptCache.get(filePath);
|
|
56
|
+
const prevScriptSetup = scriptSetupCache.get(filePath);
|
|
57
|
+
const prevTemplate = templateCache.get(filePath);
|
|
58
|
+
const prevStyle = styleCache.get(filePath);
|
|
59
|
+
const currentScript = descriptor.script?.content ?? "";
|
|
60
|
+
const currentScriptSetup = descriptor.scriptSetup?.content ?? "";
|
|
61
|
+
const currentTemplate = descriptor.template?.content ?? "";
|
|
62
|
+
const currentStyle = descriptor.styles.map((s) => `${s.scoped ? "scoped:" : ""}${s.content}`).join("|||");
|
|
63
|
+
scriptCache.set(filePath, currentScript);
|
|
64
|
+
scriptSetupCache.set(filePath, currentScriptSetup);
|
|
65
|
+
templateCache.set(filePath, currentTemplate);
|
|
66
|
+
styleCache.set(filePath, currentStyle);
|
|
67
|
+
if (prevScript === undefined && prevScriptSetup === undefined) {
|
|
68
|
+
return "full";
|
|
156
69
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
70
|
+
const scriptChanged = prevScript !== currentScript;
|
|
71
|
+
const scriptSetupChanged = prevScriptSetup !== currentScriptSetup;
|
|
72
|
+
const templateChanged = prevTemplate !== currentTemplate;
|
|
73
|
+
const styleChanged = prevStyle !== currentStyle;
|
|
74
|
+
if (scriptChanged || scriptSetupChanged) {
|
|
75
|
+
return "script";
|
|
76
|
+
}
|
|
77
|
+
if (styleChanged && !templateChanged) {
|
|
78
|
+
return "style-only";
|
|
79
|
+
}
|
|
80
|
+
if (templateChanged) {
|
|
81
|
+
return "template-only";
|
|
82
|
+
}
|
|
83
|
+
return "full";
|
|
84
|
+
}, generateVueHmrId = (sourceFilePath, vueRootDir) => {
|
|
85
|
+
return relative2(vueRootDir, sourceFilePath).replace(/\\/g, "/").replace(/\.vue$/, "");
|
|
86
|
+
}, clearVueHmrCaches = () => {
|
|
87
|
+
scriptCache.clear();
|
|
88
|
+
scriptSetupCache.clear();
|
|
89
|
+
templateCache.clear();
|
|
90
|
+
styleCache.clear();
|
|
91
|
+
vueHmrMetadata.clear();
|
|
92
|
+
}, extractImports = (sourceCode) => Array.from(sourceCode.matchAll(/import\s+[\s\S]+?['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((importPath) => importPath !== undefined), toJs = (filePath) => {
|
|
165
93
|
if (filePath.endsWith(".vue"))
|
|
166
94
|
return filePath.replace(/\.vue$/, ".js");
|
|
167
95
|
if (filePath.endsWith(".ts"))
|
|
168
96
|
return filePath.replace(/\.ts$/, ".js");
|
|
169
97
|
return `${filePath}.js`;
|
|
170
|
-
}
|
|
171
|
-
var stripExports = (code) => code.replace(/export\s+default/, "const script =").replace(/^export\s+/gm, "");
|
|
172
|
-
var mergeVueImports = (code) => {
|
|
98
|
+
}, stripExports = (code) => code.replace(/export\s+default/, "const script =").replace(/^export\s+/gm, ""), mergeVueImports = (code) => {
|
|
173
99
|
const lines = code.split(`
|
|
174
100
|
`);
|
|
175
101
|
const specifierSet = new Set;
|
|
@@ -186,8 +112,7 @@ var mergeVueImports = (code) => {
|
|
|
186
112
|
].join(`
|
|
187
113
|
`) : nonVueLines.join(`
|
|
188
114
|
`);
|
|
189
|
-
}
|
|
190
|
-
var compileVueFile = async (sourceFilePath, outputDirs, cacheMap, isEntryPoint, vueRootDir) => {
|
|
115
|
+
}, compileVueFile = async (sourceFilePath, outputDirs, cacheMap, isEntryPoint, vueRootDir) => {
|
|
191
116
|
const cachedResult = cacheMap.get(sourceFilePath);
|
|
192
117
|
if (cachedResult)
|
|
193
118
|
return cachedResult;
|
|
@@ -197,6 +122,9 @@ var compileVueFile = async (sourceFilePath, outputDirs, cacheMap, isEntryPoint,
|
|
|
197
122
|
const componentId = toKebab(fileBaseName);
|
|
198
123
|
const sourceContent = await file2(sourceFilePath).text();
|
|
199
124
|
const { descriptor } = parse(sourceContent, { filename: sourceFilePath });
|
|
125
|
+
const hmrId = generateVueHmrId(sourceFilePath, vueRootDir);
|
|
126
|
+
const changeType = detectVueChangeType(sourceFilePath, descriptor);
|
|
127
|
+
vueHmrMetadata.set(sourceFilePath, { hmrId, changeType });
|
|
200
128
|
const scriptSource = descriptor.scriptSetup?.content ?? descriptor.script?.content ?? "";
|
|
201
129
|
const importPaths = extractImports(scriptSource);
|
|
202
130
|
const childComponentPaths = importPaths.filter((path) => path.startsWith(".") && path.endsWith(".vue"));
|
|
@@ -239,15 +167,31 @@ var compileVueFile = async (sourceFilePath, outputDirs, cacheMap, isEntryPoint,
|
|
|
239
167
|
`));
|
|
240
168
|
cssOutputPaths = [cssOutputFile];
|
|
241
169
|
}
|
|
242
|
-
const assembleModule = (renderCode, renderFnName) =>
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
170
|
+
const assembleModule = (renderCode, renderFnName, includeHmr) => {
|
|
171
|
+
const hasScoped = descriptor.styles.some((styleBlock) => styleBlock.scoped);
|
|
172
|
+
const scopeIdCode = hasScoped ? `script.__scopeId = "data-v-${componentId}";` : "";
|
|
173
|
+
const hmrCode = includeHmr ? `
|
|
174
|
+
// Vue Native HMR Registration
|
|
175
|
+
script.__hmrId = ${JSON.stringify(hmrId)};
|
|
176
|
+
if (typeof __VUE_HMR_RUNTIME__ !== 'undefined') {
|
|
177
|
+
__VUE_HMR_RUNTIME__.createRecord(script.__hmrId, script);
|
|
178
|
+
if (typeof window !== 'undefined') {
|
|
179
|
+
window.__VUE_HMR_COMPONENTS__ = window.__VUE_HMR_COMPONENTS__ || {};
|
|
180
|
+
window.__VUE_HMR_COMPONENTS__[script.__hmrId] = script;
|
|
181
|
+
}
|
|
182
|
+
}` : "";
|
|
183
|
+
return mergeVueImports([
|
|
184
|
+
transpiledScript,
|
|
185
|
+
renderCode,
|
|
186
|
+
`script.${renderFnName} = ${renderFnName};`,
|
|
187
|
+
scopeIdCode,
|
|
188
|
+
hmrCode,
|
|
189
|
+
"export default script;"
|
|
190
|
+
].join(`
|
|
248
191
|
`));
|
|
249
|
-
|
|
250
|
-
const
|
|
192
|
+
};
|
|
193
|
+
const clientCode = assembleModule(generateRenderFunction(false), "render", true);
|
|
194
|
+
const serverCode = assembleModule(generateRenderFunction(true), "ssrRender", false);
|
|
251
195
|
const clientOutputPath = join2(outputDirs.client, `${relativeWithoutExtension}.js`);
|
|
252
196
|
const serverOutputPath = join2(outputDirs.server, `${relativeWithoutExtension}.js`);
|
|
253
197
|
await mkdir2(dirname2(clientOutputPath), { recursive: true });
|
|
@@ -262,12 +206,12 @@ var compileVueFile = async (sourceFilePath, outputDirs, cacheMap, isEntryPoint,
|
|
|
262
206
|
tsHelperPaths: [
|
|
263
207
|
...helperModulePaths.map((helper) => resolve2(dirname2(sourceFilePath), helper.endsWith(".ts") ? helper : `${helper}.ts`)),
|
|
264
208
|
...childBuildResults.flatMap((child) => child.tsHelperPaths)
|
|
265
|
-
]
|
|
209
|
+
],
|
|
210
|
+
hmrId
|
|
266
211
|
};
|
|
267
212
|
cacheMap.set(sourceFilePath, result);
|
|
268
213
|
return result;
|
|
269
|
-
}
|
|
270
|
-
var compileVue = async (entryPoints, vueRootDir) => {
|
|
214
|
+
}, compileVue = async (entryPoints, vueRootDir, isDev = false) => {
|
|
271
215
|
const compiledOutputRoot = join2(vueRootDir, "compiled");
|
|
272
216
|
const clientOutputDir = join2(compiledOutputRoot, "client");
|
|
273
217
|
const indexOutputDir = join2(compiledOutputRoot, "indexes");
|
|
@@ -292,14 +236,92 @@ var compileVue = async (entryPoints, vueRootDir) => {
|
|
|
292
236
|
const indexOutputFile = join2(indexOutputDir, `${entryBaseName}.js`);
|
|
293
237
|
const clientOutputFile = join2(clientOutputDir, relative2(vueRootDir, entryPath).replace(/\\/g, "/").replace(/\.vue$/, ".js"));
|
|
294
238
|
await mkdir2(dirname2(indexOutputFile), { recursive: true });
|
|
239
|
+
const vueHmrImports = isDev ? [
|
|
240
|
+
`window.__HMR_FRAMEWORK__ = "vue";`,
|
|
241
|
+
`import "${hmrClientPath2}";`
|
|
242
|
+
] : [];
|
|
295
243
|
await write2(indexOutputFile, [
|
|
244
|
+
...vueHmrImports,
|
|
296
245
|
`import Comp from "${relative2(dirname2(indexOutputFile), clientOutputFile).replace(/\\/g, "/")}";`,
|
|
297
246
|
'import { createSSRApp } from "vue";',
|
|
298
|
-
"
|
|
299
|
-
|
|
247
|
+
"",
|
|
248
|
+
"// HMR State Preservation: Check for preserved state from HMR",
|
|
249
|
+
'let preservedState = (typeof window !== "undefined" && window.__HMR_PRESERVED_STATE__) ? window.__HMR_PRESERVED_STATE__ : {};',
|
|
250
|
+
"",
|
|
251
|
+
"// Fallback: check sessionStorage if window state is empty",
|
|
252
|
+
'if (typeof window !== "undefined" && Object.keys(preservedState).length === 0) {',
|
|
253
|
+
" try {",
|
|
254
|
+
' const stored = sessionStorage.getItem("__VUE_HMR_STATE__");',
|
|
255
|
+
" if (stored) {",
|
|
256
|
+
" preservedState = JSON.parse(stored);",
|
|
257
|
+
' sessionStorage.removeItem("__VUE_HMR_STATE__");',
|
|
258
|
+
" }",
|
|
259
|
+
" } catch (e) {}",
|
|
260
|
+
"}",
|
|
261
|
+
"",
|
|
262
|
+
"const mergedProps = { ...(window.__INITIAL_PROPS__ ?? {}), ...preservedState };",
|
|
263
|
+
"",
|
|
264
|
+
"// Use createSSRApp for proper hydration of server-rendered content",
|
|
265
|
+
"const app = createSSRApp(Comp, mergedProps);",
|
|
266
|
+
'app.mount("#root");',
|
|
267
|
+
"",
|
|
268
|
+
"// Store app instance for HMR - used for manual component updates",
|
|
269
|
+
'if (typeof window !== "undefined") {',
|
|
270
|
+
" window.__VUE_APP__ = app;",
|
|
271
|
+
"}",
|
|
272
|
+
"",
|
|
273
|
+
"// Post-mount: Apply preserved state to reactive refs in component tree",
|
|
274
|
+
"// This restores state that lives in refs (like count) rather than props",
|
|
275
|
+
'if (typeof window !== "undefined" && Object.keys(preservedState).length > 0) {',
|
|
276
|
+
" requestAnimationFrame(function() {",
|
|
277
|
+
" if (window.__VUE_APP__ && window.__VUE_APP__._instance) {",
|
|
278
|
+
" applyPreservedState(window.__VUE_APP__._instance, preservedState);",
|
|
279
|
+
" }",
|
|
280
|
+
" });",
|
|
281
|
+
"}",
|
|
282
|
+
"",
|
|
283
|
+
"function applyPreservedState(instance, state) {",
|
|
284
|
+
" // Apply to root component setupState",
|
|
285
|
+
" if (instance.setupState) {",
|
|
286
|
+
" Object.keys(state).forEach(function(key) {",
|
|
287
|
+
" const ref = instance.setupState[key];",
|
|
288
|
+
' if (ref && typeof ref === "object" && "value" in ref) {',
|
|
289
|
+
" ref.value = state[key];",
|
|
290
|
+
" }",
|
|
291
|
+
" });",
|
|
292
|
+
" }",
|
|
293
|
+
" // Also apply to child components",
|
|
294
|
+
" if (instance.subTree) {",
|
|
295
|
+
" walkAndApply(instance.subTree, state);",
|
|
296
|
+
" }",
|
|
297
|
+
"}",
|
|
298
|
+
"",
|
|
299
|
+
"function walkAndApply(vnode, state) {",
|
|
300
|
+
" if (!vnode) return;",
|
|
301
|
+
" if (vnode.component && vnode.component.setupState) {",
|
|
302
|
+
" Object.keys(state).forEach(function(key) {",
|
|
303
|
+
" const ref = vnode.component.setupState[key];",
|
|
304
|
+
' if (ref && typeof ref === "object" && "value" in ref) {',
|
|
305
|
+
" ref.value = state[key];",
|
|
306
|
+
" }",
|
|
307
|
+
" });",
|
|
308
|
+
" }",
|
|
309
|
+
" if (vnode.children && Array.isArray(vnode.children)) {",
|
|
310
|
+
" vnode.children.forEach(function(child) { walkAndApply(child, state); });",
|
|
311
|
+
" }",
|
|
312
|
+
" if (vnode.component && vnode.component.subTree) {",
|
|
313
|
+
" walkAndApply(vnode.component.subTree, state);",
|
|
314
|
+
" }",
|
|
315
|
+
"}",
|
|
316
|
+
"",
|
|
317
|
+
"// Clear preserved state after applying",
|
|
318
|
+
'if (typeof window !== "undefined") {',
|
|
319
|
+
" window.__HMR_PRESERVED_STATE__ = undefined;",
|
|
320
|
+
"}"
|
|
300
321
|
].join(`
|
|
301
322
|
`));
|
|
302
323
|
return {
|
|
324
|
+
clientPath: clientOutputFile,
|
|
303
325
|
cssPaths: result.cssPaths,
|
|
304
326
|
indexPath: indexOutputFile,
|
|
305
327
|
serverPath: result.serverPath
|
|
@@ -317,97 +339,901 @@ var compileVue = async (entryPoints, vueRootDir) => {
|
|
|
317
339
|
await write2(outServerPath, transpiledCode);
|
|
318
340
|
}));
|
|
319
341
|
return {
|
|
342
|
+
vueClientPaths: compiledPages.map((result) => result.clientPath),
|
|
320
343
|
vueCssPaths: compiledPages.flatMap((result) => result.cssPaths),
|
|
321
344
|
vueIndexPaths: compiledPages.map((result) => result.indexPath),
|
|
322
|
-
vueServerPaths: compiledPages.map((result) => result.serverPath)
|
|
345
|
+
vueServerPaths: compiledPages.map((result) => result.serverPath),
|
|
346
|
+
hmrMetadata: new Map(vueHmrMetadata)
|
|
323
347
|
};
|
|
324
348
|
};
|
|
349
|
+
var init_compileVue = __esm(() => {
|
|
350
|
+
hmrClientPath2 = resolve2(import.meta.dir, "../dev/client/hmrClient.ts").replace(/\\/g, "/");
|
|
351
|
+
transpiler2 = new Transpiler2({ loader: "ts", target: "browser" });
|
|
352
|
+
scriptCache = new Map;
|
|
353
|
+
scriptSetupCache = new Map;
|
|
354
|
+
templateCache = new Map;
|
|
355
|
+
styleCache = new Map;
|
|
356
|
+
vueHmrMetadata = new Map;
|
|
357
|
+
});
|
|
325
358
|
|
|
326
|
-
// src/
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return manifest;
|
|
354
|
-
}, {});
|
|
355
|
-
|
|
356
|
-
// src/build/generateReactIndexes.ts
|
|
357
|
-
import { mkdir as mkdir3, rm, writeFile } from "fs/promises";
|
|
358
|
-
import { basename as basename3, join as join3 } from "path";
|
|
359
|
-
var {Glob } = globalThis.Bun;
|
|
360
|
-
var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory) => {
|
|
361
|
-
await rm(reactIndexesDirectory, { force: true, recursive: true });
|
|
362
|
-
await mkdir3(reactIndexesDirectory);
|
|
363
|
-
const pagesGlob = new Glob("*.*");
|
|
364
|
-
const files = [];
|
|
365
|
-
for await (const file3 of pagesGlob.scan({ cwd: reactPagesDirectory })) {
|
|
366
|
-
files.push(file3);
|
|
359
|
+
// src/dev/simpleHTMLHMR.ts
|
|
360
|
+
var exports_simpleHTMLHMR = {};
|
|
361
|
+
__export(exports_simpleHTMLHMR, {
|
|
362
|
+
handleHTMLUpdate: () => handleHTMLUpdate
|
|
363
|
+
});
|
|
364
|
+
import { readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
|
|
365
|
+
import { resolve as resolve13 } from "path";
|
|
366
|
+
var handleHTMLUpdate = async (htmlFilePath) => {
|
|
367
|
+
try {
|
|
368
|
+
const resolvedPath = resolve13(htmlFilePath);
|
|
369
|
+
if (!existsSync3(resolvedPath)) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const htmlContent = readFileSync4(resolvedPath, "utf-8");
|
|
373
|
+
const headMatch = htmlContent.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
|
|
374
|
+
const bodyMatch = htmlContent.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
375
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
376
|
+
const bodyContent = bodyMatch[1].trim();
|
|
377
|
+
const headContent = headMatch && headMatch[1] ? headMatch[1].trim() : null;
|
|
378
|
+
return {
|
|
379
|
+
body: bodyContent,
|
|
380
|
+
head: headContent
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return htmlContent;
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
367
386
|
}
|
|
368
|
-
const promises = files.map(async (file3) => {
|
|
369
|
-
const fileName = basename3(file3);
|
|
370
|
-
const [componentName] = fileName.split(".");
|
|
371
|
-
const content = [
|
|
372
|
-
`import { hydrateRoot } from 'react-dom/client';`,
|
|
373
|
-
`import type { ComponentType } from 'react'`,
|
|
374
|
-
`import { ${componentName} } from '../pages/${componentName}';
|
|
375
|
-
`,
|
|
376
|
-
`type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
|
|
377
|
-
`,
|
|
378
|
-
`declare global {`,
|
|
379
|
-
` interface Window {`,
|
|
380
|
-
` __INITIAL_PROPS__: PropsOf<typeof ${componentName}>`,
|
|
381
|
-
` }`,
|
|
382
|
-
`}
|
|
383
|
-
`,
|
|
384
|
-
`hydrateRoot(document, <${componentName} {...window.__INITIAL_PROPS__} />);`
|
|
385
|
-
].join(`
|
|
386
|
-
`);
|
|
387
|
-
return writeFile(join3(reactIndexesDirectory, `${componentName}.tsx`), content);
|
|
388
|
-
});
|
|
389
|
-
await Promise.all(promises);
|
|
390
387
|
};
|
|
388
|
+
var init_simpleHTMLHMR = () => {};
|
|
391
389
|
|
|
392
|
-
// src/
|
|
393
|
-
var
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
390
|
+
// src/utils/escapeScriptContent.ts
|
|
391
|
+
var ESCAPE_LOOKUP, ESCAPE_REGEX, escapeScriptContent = (content) => content.replace(ESCAPE_REGEX, (char) => {
|
|
392
|
+
const escaped = ESCAPE_LOOKUP[char];
|
|
393
|
+
return escaped !== undefined ? escaped : char;
|
|
394
|
+
});
|
|
395
|
+
var init_escapeScriptContent = __esm(() => {
|
|
396
|
+
ESCAPE_LOOKUP = {
|
|
397
|
+
"\u2028": "\\u2028",
|
|
398
|
+
"\u2029": "\\u2029",
|
|
399
|
+
"&": "\\u0026",
|
|
400
|
+
"<": "\\u003C",
|
|
401
|
+
">": "\\u003E"
|
|
402
|
+
};
|
|
403
|
+
ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// src/svelte/renderToReadableStream.ts
|
|
407
|
+
import { render } from "svelte/server";
|
|
408
|
+
var renderToReadableStream = async (component, props, {
|
|
409
|
+
bootstrapScriptContent,
|
|
410
|
+
bootstrapScripts = [],
|
|
411
|
+
bootstrapModules = [],
|
|
412
|
+
nonce,
|
|
413
|
+
onError = console.error,
|
|
414
|
+
progressiveChunkSize = DEFAULT_CHUNK_SIZE,
|
|
415
|
+
signal,
|
|
416
|
+
headContent,
|
|
417
|
+
bodyContent
|
|
418
|
+
} = {}) => {
|
|
419
|
+
try {
|
|
420
|
+
const { head, body } = typeof props === "undefined" ? render(component) : render(component, { props });
|
|
421
|
+
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
422
|
+
const scripts = (bootstrapScriptContent ? `<script${nonceAttr}>${escapeScriptContent(bootstrapScriptContent)}</script>` : "") + bootstrapScripts.map((src) => `<script${nonceAttr} src="${src}"></script>`).join("") + bootstrapModules.map((src) => `<script${nonceAttr} type="module" src="${src}"></script>`).join("");
|
|
423
|
+
const encoder = new TextEncoder;
|
|
424
|
+
const full = encoder.encode(`<!DOCTYPE html><html lang="en"><head>${head}${headContent ?? ""}</head><body>${body}${scripts}${bodyContent ?? ""}</body></html>`);
|
|
425
|
+
let offset = 0;
|
|
426
|
+
return new ReadableStream({
|
|
427
|
+
type: "bytes",
|
|
428
|
+
cancel(reason) {
|
|
429
|
+
onError?.(reason);
|
|
430
|
+
},
|
|
431
|
+
pull(controller) {
|
|
432
|
+
if (signal?.aborted) {
|
|
433
|
+
controller.close();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (offset >= full.length) {
|
|
437
|
+
controller.close();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const end = Math.min(offset + progressiveChunkSize, full.length);
|
|
441
|
+
controller.enqueue(full.subarray(offset, end));
|
|
442
|
+
offset = end;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
} catch (error) {
|
|
446
|
+
onError?.(error);
|
|
447
|
+
throw error;
|
|
403
448
|
}
|
|
404
449
|
};
|
|
450
|
+
var init_renderToReadableStream = __esm(() => {
|
|
451
|
+
init_constants();
|
|
452
|
+
init_escapeScriptContent();
|
|
453
|
+
});
|
|
405
454
|
|
|
406
|
-
// src/
|
|
407
|
-
var
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
455
|
+
// src/core/pageHandlers.ts
|
|
456
|
+
var exports_pageHandlers = {};
|
|
457
|
+
__export(exports_pageHandlers, {
|
|
458
|
+
handleVuePageRequest: () => handleVuePageRequest,
|
|
459
|
+
handleSveltePageRequest: () => handleSveltePageRequest,
|
|
460
|
+
handleReactPageRequest: () => handleReactPageRequest,
|
|
461
|
+
handlePageRequest: () => handlePageRequest,
|
|
462
|
+
handleHTMXPageRequest: () => handleHTMXPageRequest,
|
|
463
|
+
handleHTMLPageRequest: () => handleHTMLPageRequest
|
|
464
|
+
});
|
|
465
|
+
var {file: file3 } = globalThis.Bun;
|
|
466
|
+
import { createElement } from "react";
|
|
467
|
+
import { renderToReadableStream as renderReactToReadableStream } from "react-dom/server";
|
|
468
|
+
import { createSSRApp, h } from "vue";
|
|
469
|
+
import { renderToWebStream as renderVueToWebStream } from "vue/server-renderer";
|
|
470
|
+
var handleReactPageRequest = async (PageComponent, index, ...props) => {
|
|
471
|
+
const [maybeProps] = props;
|
|
472
|
+
const element = maybeProps !== undefined ? createElement(PageComponent, maybeProps) : createElement(PageComponent);
|
|
473
|
+
const stream = await renderReactToReadableStream(element, {
|
|
474
|
+
bootstrapModules: [index],
|
|
475
|
+
bootstrapScriptContent: maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)}` : undefined
|
|
476
|
+
});
|
|
477
|
+
return new Response(stream, {
|
|
478
|
+
headers: { "Content-Type": "text/html" }
|
|
479
|
+
});
|
|
480
|
+
}, handleSveltePageRequest = async (_PageComponent, pagePath, indexPath, props) => {
|
|
481
|
+
const { default: ImportedPageComponent } = await import(pagePath);
|
|
482
|
+
const stream = await renderToReadableStream(ImportedPageComponent, props, {
|
|
483
|
+
bootstrapModules: indexPath ? [indexPath] : [],
|
|
484
|
+
bootstrapScriptContent: `window.__INITIAL_PROPS__=${JSON.stringify(props)}`
|
|
485
|
+
});
|
|
486
|
+
return new Response(stream, {
|
|
487
|
+
headers: { "Content-Type": "text/html" }
|
|
488
|
+
});
|
|
489
|
+
}, handleVuePageRequest = async (_PageComponent, pagePath, indexPath, headTag = "<head></head>", ...props) => {
|
|
490
|
+
const [maybeProps] = props;
|
|
491
|
+
const { default: ImportedPageComponent } = await import(pagePath);
|
|
492
|
+
const app = createSSRApp({
|
|
493
|
+
render: () => h(ImportedPageComponent, maybeProps ?? null)
|
|
494
|
+
});
|
|
495
|
+
const bodyStream = renderVueToWebStream(app);
|
|
496
|
+
const head = `<!DOCTYPE html><html>${headTag}<body><div id="root">`;
|
|
497
|
+
const tail = `</div><script>window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})}</script><script type="module" src="${indexPath}"></script></body></html>`;
|
|
498
|
+
const stream = new ReadableStream({
|
|
499
|
+
start(controller) {
|
|
500
|
+
controller.enqueue(head);
|
|
501
|
+
const reader = bodyStream.getReader();
|
|
502
|
+
const pumpLoop = () => {
|
|
503
|
+
reader.read().then(({ done, value }) => done ? (controller.enqueue(tail), controller.close()) : (controller.enqueue(value), pumpLoop())).catch((err) => controller.error(err));
|
|
504
|
+
};
|
|
505
|
+
pumpLoop();
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
return new Response(stream, {
|
|
509
|
+
headers: { "Content-Type": "text/html" }
|
|
510
|
+
});
|
|
511
|
+
}, handleHTMLPageRequest = (pagePath) => file3(pagePath), handleHTMXPageRequest = (pagePath) => file3(pagePath), handlePageRequest = (PageComponent, ...props) => {
|
|
512
|
+
console.log("handlePageRequest coming soon.", PageComponent, props);
|
|
513
|
+
};
|
|
514
|
+
var init_pageHandlers = __esm(() => {
|
|
515
|
+
init_renderToReadableStream();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// src/utils/generateHeadElement.ts
|
|
519
|
+
var exports_generateHeadElement = {};
|
|
520
|
+
__export(exports_generateHeadElement, {
|
|
521
|
+
generateHeadElement: () => generateHeadElement
|
|
522
|
+
});
|
|
523
|
+
var generateHeadElement = ({
|
|
524
|
+
cssPath,
|
|
525
|
+
title = "AbsoluteJS",
|
|
526
|
+
description = "A page created using AbsoluteJS",
|
|
527
|
+
font,
|
|
528
|
+
icon = "/assets/ico/favicon.ico"
|
|
529
|
+
} = {}) => `<head>
|
|
530
|
+
<meta charset="UTF-8">
|
|
531
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
532
|
+
<title>${title}</title>
|
|
533
|
+
<meta name="description" content="${description}">
|
|
534
|
+
<link rel="icon" href="${icon}" type="image/x-icon">
|
|
535
|
+
${font ? `<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
536
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
537
|
+
<link href="https://fonts.googleapis.com/css2?family=${font}:wght@100..900&display=swap" rel="stylesheet">` : ""}
|
|
538
|
+
${cssPath ? `<link rel="stylesheet" href="${cssPath}" type="text/css">` : ""}
|
|
539
|
+
</head>`;
|
|
540
|
+
|
|
541
|
+
// src/dev/simpleVueHMR.ts
|
|
542
|
+
var exports_simpleVueHMR = {};
|
|
543
|
+
__export(exports_simpleVueHMR, {
|
|
544
|
+
handleVueUpdate: () => handleVueUpdate
|
|
545
|
+
});
|
|
546
|
+
import { basename as basename6, join as join7, resolve as resolve14 } from "path";
|
|
547
|
+
var handleVueUpdate = async (vueFilePath, manifest, buildDir) => {
|
|
548
|
+
try {
|
|
549
|
+
const resolvedPath = resolve14(vueFilePath);
|
|
550
|
+
const fileName = basename6(resolvedPath);
|
|
551
|
+
const baseName = fileName.replace(/\.vue$/, "");
|
|
552
|
+
const pascalName = toPascal(baseName);
|
|
553
|
+
const componentKey = pascalName;
|
|
554
|
+
const indexKey = `${pascalName}Index`;
|
|
555
|
+
const cssKey = `${pascalName}CSS`;
|
|
556
|
+
const serverPath = manifest[componentKey];
|
|
557
|
+
if (!serverPath) {
|
|
558
|
+
console.warn("[Vue HMR] Server path not found in manifest for:", componentKey);
|
|
559
|
+
console.warn("[Vue HMR] Available manifest keys:", Object.keys(manifest).join(", "));
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
const absoluteServerPath = resolve14(serverPath) === serverPath || serverPath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(serverPath) ? serverPath : join7(buildDir || process.cwd(), serverPath.replace(/^\//, ""));
|
|
563
|
+
const cacheBuster = `?t=${Date.now()}`;
|
|
564
|
+
const serverModule = await import(`${absoluteServerPath}${cacheBuster}`);
|
|
565
|
+
if (!serverModule || !serverModule.default) {
|
|
566
|
+
console.warn("[Vue HMR] Module has no default export:", absoluteServerPath);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const indexPath = manifest[indexKey];
|
|
570
|
+
if (!indexPath) {
|
|
571
|
+
console.warn("[Vue HMR] Index path not found in manifest for:", indexKey);
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
const { handleVuePageRequest: handleVuePageRequest2 } = await Promise.resolve().then(() => (init_pageHandlers(), exports_pageHandlers));
|
|
575
|
+
const { generateHeadElement: generateHeadElement2 } = await Promise.resolve().then(() => exports_generateHeadElement);
|
|
576
|
+
const response = await handleVuePageRequest2(serverModule.default, serverPath, indexPath, generateHeadElement2({
|
|
577
|
+
cssPath: manifest[cssKey] || "",
|
|
578
|
+
title: "AbsoluteJS + Vue"
|
|
579
|
+
}), { initialCount: 0 });
|
|
580
|
+
const html = await response.text();
|
|
581
|
+
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
582
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
583
|
+
const bodyContent = bodyMatch[1].trim();
|
|
584
|
+
return bodyContent;
|
|
585
|
+
}
|
|
586
|
+
return html;
|
|
587
|
+
} catch (err) {
|
|
588
|
+
console.error("[Vue HMR] Error in handleVueUpdate:", err);
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
var init_simpleVueHMR = () => {};
|
|
593
|
+
|
|
594
|
+
// src/dev/simpleSvelteHMR.ts
|
|
595
|
+
var exports_simpleSvelteHMR = {};
|
|
596
|
+
__export(exports_simpleSvelteHMR, {
|
|
597
|
+
handleSvelteUpdate: () => handleSvelteUpdate
|
|
598
|
+
});
|
|
599
|
+
import { basename as basename7, join as join8, resolve as resolve15 } from "path";
|
|
600
|
+
var handleSvelteUpdate = async (svelteFilePath, manifest, buildDir) => {
|
|
601
|
+
try {
|
|
602
|
+
const resolvedPath = resolve15(svelteFilePath);
|
|
603
|
+
const fileName = basename7(resolvedPath);
|
|
604
|
+
const baseName = fileName.replace(/\.svelte$/, "");
|
|
605
|
+
const pascalName = toPascal(baseName);
|
|
606
|
+
const componentKey = pascalName;
|
|
607
|
+
const indexKey = `${pascalName}Index`;
|
|
608
|
+
const cssKey = `${pascalName}CSS`;
|
|
609
|
+
const serverPath = manifest[componentKey];
|
|
610
|
+
if (!serverPath) {
|
|
611
|
+
console.warn("[Svelte HMR] Server path not found in manifest for:", componentKey);
|
|
612
|
+
console.warn("[Svelte HMR] Available manifest keys:", Object.keys(manifest).join(", "));
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
const absoluteServerPath = resolve15(serverPath) === serverPath || serverPath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(serverPath) ? serverPath : join8(buildDir || process.cwd(), serverPath.replace(/^\//, ""));
|
|
616
|
+
const cacheBuster = `?t=${Date.now()}`;
|
|
617
|
+
const serverModule = await import(`${absoluteServerPath}${cacheBuster}`);
|
|
618
|
+
if (!serverModule || !serverModule.default) {
|
|
619
|
+
console.warn("[Svelte HMR] Module has no default export:", absoluteServerPath);
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const indexPath = manifest[indexKey];
|
|
623
|
+
if (!indexPath) {
|
|
624
|
+
console.warn("[Svelte HMR] Index path not found in manifest for:", indexKey);
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
const { handleSveltePageRequest: handleSveltePageRequest2 } = await Promise.resolve().then(() => (init_pageHandlers(), exports_pageHandlers));
|
|
628
|
+
const response = await handleSveltePageRequest2(serverModule.default, serverPath, indexPath, {
|
|
629
|
+
cssPath: manifest[cssKey] || "",
|
|
630
|
+
initialCount: 0
|
|
631
|
+
});
|
|
632
|
+
const html = await response.text();
|
|
633
|
+
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
634
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
635
|
+
const bodyContent = bodyMatch[1].trim();
|
|
636
|
+
return bodyContent;
|
|
637
|
+
}
|
|
638
|
+
return html;
|
|
639
|
+
} catch (err) {
|
|
640
|
+
console.error("[Svelte HMR] Error in handleSvelteUpdate:", err);
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
var init_simpleSvelteHMR = () => {};
|
|
645
|
+
|
|
646
|
+
// src/dev/simpleHTMXHMR.ts
|
|
647
|
+
var exports_simpleHTMXHMR = {};
|
|
648
|
+
__export(exports_simpleHTMXHMR, {
|
|
649
|
+
handleHTMXUpdate: () => handleHTMXUpdate
|
|
650
|
+
});
|
|
651
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
|
|
652
|
+
import { resolve as resolve16 } from "path";
|
|
653
|
+
var handleHTMXUpdate = async (htmxFilePath) => {
|
|
654
|
+
try {
|
|
655
|
+
const resolvedPath = resolve16(htmxFilePath);
|
|
656
|
+
if (!existsSync4(resolvedPath)) {
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
const htmlContent = readFileSync5(resolvedPath, "utf-8");
|
|
660
|
+
const headMatch = htmlContent.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
|
|
661
|
+
const bodyMatch = htmlContent.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
662
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
663
|
+
const bodyContent = bodyMatch[1].trim();
|
|
664
|
+
const headContent = headMatch && headMatch[1] ? headMatch[1].trim() : null;
|
|
665
|
+
return {
|
|
666
|
+
body: bodyContent,
|
|
667
|
+
head: headContent
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return htmlContent;
|
|
671
|
+
} catch {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
var init_simpleHTMXHMR = () => {};
|
|
676
|
+
// types/client.ts
|
|
677
|
+
var hmrState = {
|
|
678
|
+
isConnected: false,
|
|
679
|
+
isFirstHMRUpdate: true,
|
|
680
|
+
isHMRUpdating: false,
|
|
681
|
+
pingInterval: null,
|
|
682
|
+
reconnectTimeout: null
|
|
683
|
+
};
|
|
684
|
+
// types/messages.ts
|
|
685
|
+
var isValidHMRClientMessage = (data) => {
|
|
686
|
+
if (!data || typeof data !== "object") {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
const message = data;
|
|
690
|
+
if (!("type" in message) || typeof message.type !== "string") {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
switch (message.type) {
|
|
694
|
+
case "ping":
|
|
695
|
+
return true;
|
|
696
|
+
case "ready":
|
|
697
|
+
return true;
|
|
698
|
+
case "request-rebuild":
|
|
699
|
+
return true;
|
|
700
|
+
case "hydration-error":
|
|
701
|
+
return true;
|
|
702
|
+
default:
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
// types/websocket.ts
|
|
707
|
+
var WS_READY_STATE_OPEN = 1;
|
|
708
|
+
// src/index.ts
|
|
709
|
+
init_constants();
|
|
710
|
+
|
|
711
|
+
// src/core/build.ts
|
|
712
|
+
import {
|
|
713
|
+
copyFileSync,
|
|
714
|
+
cpSync,
|
|
715
|
+
mkdirSync,
|
|
716
|
+
readFileSync,
|
|
717
|
+
writeFileSync
|
|
718
|
+
} from "fs";
|
|
719
|
+
import { rm as rm3 } from "fs/promises";
|
|
720
|
+
import { basename as basename4, join as join5, resolve as resolve6 } from "path";
|
|
721
|
+
import { cwd, env as env2, exit } from "process";
|
|
722
|
+
var {$, build: bunBuild2, Glob: Glob3 } = globalThis.Bun;
|
|
723
|
+
|
|
724
|
+
// src/build/compileSvelte.ts
|
|
725
|
+
import { mkdir, stat } from "fs/promises";
|
|
726
|
+
import {
|
|
727
|
+
dirname,
|
|
728
|
+
join,
|
|
729
|
+
basename,
|
|
730
|
+
extname,
|
|
731
|
+
resolve,
|
|
732
|
+
relative,
|
|
733
|
+
sep
|
|
734
|
+
} from "path";
|
|
735
|
+
import { env } from "process";
|
|
736
|
+
var {write, file, Transpiler } = globalThis.Bun;
|
|
737
|
+
import { compile, compileModule, preprocess } from "svelte/compiler";
|
|
738
|
+
var hmrClientPath = resolve(import.meta.dir, "../dev/client/hmrClient.ts").replace(/\\/g, "/");
|
|
739
|
+
var transpiler = new Transpiler({ loader: "ts", target: "browser" });
|
|
740
|
+
var exists = async (path) => {
|
|
741
|
+
try {
|
|
742
|
+
await stat(path);
|
|
743
|
+
return true;
|
|
744
|
+
} catch {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
var resolveSvelte = async (spec, from) => {
|
|
749
|
+
const basePath = resolve(dirname(from), spec);
|
|
750
|
+
const explicit = /\.(svelte|svelte\.(?:ts|js))$/.test(basePath);
|
|
751
|
+
if (!explicit) {
|
|
752
|
+
const extensions = [".svelte", ".svelte.ts", ".svelte.js"];
|
|
753
|
+
const paths = extensions.map((ext) => `${basePath}${ext}`);
|
|
754
|
+
const checks = await Promise.all(paths.map(exists));
|
|
755
|
+
const match = paths.find((_, index) => checks[index]);
|
|
756
|
+
return match ?? null;
|
|
757
|
+
}
|
|
758
|
+
if (await exists(basePath))
|
|
759
|
+
return basePath;
|
|
760
|
+
if (!basePath.endsWith(".svelte"))
|
|
761
|
+
return null;
|
|
762
|
+
const tsPath = `${basePath}.ts`;
|
|
763
|
+
if (await exists(tsPath))
|
|
764
|
+
return tsPath;
|
|
765
|
+
const jsPath = `${basePath}.js`;
|
|
766
|
+
if (await exists(jsPath))
|
|
767
|
+
return jsPath;
|
|
768
|
+
return null;
|
|
769
|
+
};
|
|
770
|
+
var compileSvelte = async (entryPoints, svelteRoot, cache = new Map, isDev = false) => {
|
|
771
|
+
const compiledRoot = join(svelteRoot, "compiled");
|
|
772
|
+
const clientDir = join(compiledRoot, "client");
|
|
773
|
+
const indexDir = join(compiledRoot, "indexes");
|
|
774
|
+
const pagesDir = join(compiledRoot, "pages");
|
|
775
|
+
await Promise.all([clientDir, indexDir, pagesDir].map((dir) => mkdir(dir, { recursive: true })));
|
|
776
|
+
const dev = env.NODE_ENV !== "production";
|
|
777
|
+
const build2 = async (src) => {
|
|
778
|
+
const memoized = cache.get(src);
|
|
779
|
+
if (memoized)
|
|
780
|
+
return memoized;
|
|
781
|
+
const raw = await file(src).text();
|
|
782
|
+
const isModule = src.endsWith(".svelte.ts") || src.endsWith(".svelte.js");
|
|
783
|
+
const preprocessed = isModule ? raw : (await preprocess(raw, {})).code;
|
|
784
|
+
const transpiled = src.endsWith(".ts") || src.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed) : preprocessed;
|
|
785
|
+
const relDir = dirname(relative(svelteRoot, src)).replace(/\\/g, "/");
|
|
786
|
+
const baseName = basename(src).replace(/\.svelte(\.(ts|js))?$/, "");
|
|
787
|
+
const importPaths = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((path) => path !== undefined);
|
|
788
|
+
const resolvedImports = await Promise.all(importPaths.map((importPath) => resolveSvelte(importPath, src)));
|
|
789
|
+
const childSources = resolvedImports.filter((path) => path !== null);
|
|
790
|
+
await Promise.all(childSources.map((child) => build2(child)));
|
|
791
|
+
const generate = (mode) => (isModule ? compileModule(transpiled, { dev, filename: src }).js.code : compile(transpiled, {
|
|
792
|
+
css: "injected",
|
|
793
|
+
dev,
|
|
794
|
+
filename: src,
|
|
795
|
+
generate: mode
|
|
796
|
+
}).js.code).replace(/\.svelte(?:\.(?:ts|js))?(['"])/g, ".js$1");
|
|
797
|
+
const ssrPath = join(pagesDir, relDir, `${baseName}.js`);
|
|
798
|
+
const clientPath = join(clientDir, relDir, `${baseName}.js`);
|
|
799
|
+
await Promise.all([
|
|
800
|
+
mkdir(dirname(ssrPath), { recursive: true }),
|
|
801
|
+
mkdir(dirname(clientPath), { recursive: true })
|
|
802
|
+
]);
|
|
803
|
+
if (isModule) {
|
|
804
|
+
const bundle = generate("client");
|
|
805
|
+
await Promise.all([
|
|
806
|
+
write(ssrPath, bundle),
|
|
807
|
+
write(clientPath, bundle)
|
|
808
|
+
]);
|
|
809
|
+
} else {
|
|
810
|
+
const serverBundle = generate("server");
|
|
811
|
+
const clientBundle = generate("client");
|
|
812
|
+
await Promise.all([
|
|
813
|
+
write(ssrPath, serverBundle),
|
|
814
|
+
write(clientPath, clientBundle)
|
|
815
|
+
]);
|
|
816
|
+
}
|
|
817
|
+
const built = { client: clientPath, ssr: ssrPath };
|
|
818
|
+
cache.set(src, built);
|
|
819
|
+
return built;
|
|
820
|
+
};
|
|
821
|
+
const roots = await Promise.all(entryPoints.map(build2));
|
|
822
|
+
await Promise.all(roots.map(async ({ client: client2 }) => {
|
|
823
|
+
const relClientDir = dirname(relative(clientDir, client2));
|
|
824
|
+
const name = basename(client2, extname(client2));
|
|
825
|
+
const indexPath = join(indexDir, relClientDir, `${name}.js`);
|
|
826
|
+
const importRaw = relative(dirname(indexPath), client2).split(sep).join("/");
|
|
827
|
+
const importPath = importRaw.startsWith(".") || importRaw.startsWith("/") ? importRaw : `./${importRaw}`;
|
|
828
|
+
const hmrImports = isDev ? `window.__HMR_FRAMEWORK__ = "svelte";
|
|
829
|
+
import "${hmrClientPath}";
|
|
830
|
+
` : "";
|
|
831
|
+
const bootstrap = `${hmrImports}import Component from "${importPath}";
|
|
832
|
+
import { hydrate, mount, unmount } from "svelte";
|
|
833
|
+
|
|
834
|
+
var initialProps = (typeof window !== "undefined" && window.__INITIAL_PROPS__) ? window.__INITIAL_PROPS__ : {};
|
|
835
|
+
var isHMR = typeof window !== "undefined" && window.__SVELTE_COMPONENT__ !== undefined;
|
|
836
|
+
var component;
|
|
837
|
+
|
|
838
|
+
if (isHMR) {
|
|
839
|
+
var headLinks = document.querySelectorAll('link[rel="stylesheet"]');
|
|
840
|
+
var preservedLinks = [];
|
|
841
|
+
headLinks.forEach(function(link) {
|
|
842
|
+
var clone = link.cloneNode(true);
|
|
843
|
+
clone.setAttribute("data-hmr-preserve", "true");
|
|
844
|
+
document.head.appendChild(clone);
|
|
845
|
+
preservedLinks.push(clone);
|
|
846
|
+
});
|
|
847
|
+
if (typeof window.__SVELTE_UNMOUNT__ === "function") {
|
|
848
|
+
try { window.__SVELTE_UNMOUNT__(); } catch (err) { console.warn("[HMR] unmount error:", err); }
|
|
849
|
+
}
|
|
850
|
+
var preservedState = window.__HMR_PRESERVED_STATE__;
|
|
851
|
+
if (!preservedState) {
|
|
852
|
+
try {
|
|
853
|
+
var stored = sessionStorage.getItem("__SVELTE_HMR_STATE__");
|
|
854
|
+
if (stored) preservedState = JSON.parse(stored);
|
|
855
|
+
} catch (err) { /* ignore */ }
|
|
856
|
+
}
|
|
857
|
+
var mergedProps = preservedState ? Object.assign({}, initialProps, preservedState) : initialProps;
|
|
858
|
+
component = mount(Component, { target: document.body, props: mergedProps });
|
|
859
|
+
requestAnimationFrame(function() {
|
|
860
|
+
preservedLinks.forEach(function(link) { link.remove(); });
|
|
861
|
+
});
|
|
862
|
+
window.__HMR_PRESERVED_STATE__ = undefined;
|
|
863
|
+
} else {
|
|
864
|
+
component = hydrate(Component, { target: document.body, props: initialProps });
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
if (typeof window !== "undefined") {
|
|
868
|
+
window.__SVELTE_COMPONENT__ = component;
|
|
869
|
+
window.__SVELTE_UNMOUNT__ = function() { unmount(component); };
|
|
870
|
+
}`;
|
|
871
|
+
await mkdir(dirname(indexPath), { recursive: true });
|
|
872
|
+
return write(indexPath, bootstrap);
|
|
873
|
+
}));
|
|
874
|
+
return {
|
|
875
|
+
svelteIndexPaths: roots.map(({ client: client2 }) => {
|
|
876
|
+
const rel = dirname(relative(clientDir, client2));
|
|
877
|
+
return join(indexDir, rel, basename(client2));
|
|
878
|
+
}),
|
|
879
|
+
svelteClientPaths: roots.map(({ client: client2 }) => client2),
|
|
880
|
+
svelteServerPaths: roots.map(({ ssr }) => ssr)
|
|
881
|
+
};
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
// src/core/build.ts
|
|
885
|
+
init_compileVue();
|
|
886
|
+
|
|
887
|
+
// src/build/generateManifest.ts
|
|
888
|
+
init_constants();
|
|
889
|
+
import { extname as extname2 } from "path";
|
|
890
|
+
|
|
891
|
+
// src/utils/normalizePath.ts
|
|
892
|
+
var normalizePath = (path) => path.replace(/\\/g, "/");
|
|
893
|
+
|
|
894
|
+
// src/build/generateManifest.ts
|
|
895
|
+
var generateManifest = (outputs, buildPath) => outputs.reduce((manifest, artifact) => {
|
|
896
|
+
const normalizedArtifactPath = normalizePath(artifact.path);
|
|
897
|
+
const normalizedBuildPath = normalizePath(buildPath);
|
|
898
|
+
let relative3 = normalizedArtifactPath.startsWith(normalizedBuildPath) ? normalizedArtifactPath.slice(normalizedBuildPath.length) : normalizedArtifactPath;
|
|
899
|
+
relative3 = relative3.replace(/^\/+/, "");
|
|
900
|
+
const segments = relative3.split("/");
|
|
901
|
+
const fileWithHash = segments.pop();
|
|
902
|
+
if (!fileWithHash)
|
|
903
|
+
return manifest;
|
|
904
|
+
const [baseName] = fileWithHash.split(`.${artifact.hash}.`);
|
|
905
|
+
if (!baseName)
|
|
906
|
+
return manifest;
|
|
907
|
+
const pascalName = toPascal(baseName);
|
|
908
|
+
const ext = extname2(fileWithHash);
|
|
909
|
+
if (ext === ".css") {
|
|
910
|
+
manifest[`${pascalName}CSS`] = `/${relative3}`;
|
|
911
|
+
return manifest;
|
|
912
|
+
}
|
|
913
|
+
const idx = segments.findIndex((seg) => seg === "indexes" || seg === "pages" || seg === "client");
|
|
914
|
+
const folder = idx > UNFOUND_INDEX ? segments[idx] : segments[0];
|
|
915
|
+
const isReact = segments.some((seg) => seg === "react");
|
|
916
|
+
const isVue = segments.some((seg) => seg === "vue");
|
|
917
|
+
const isSvelte = segments.some((seg) => seg === "svelte");
|
|
918
|
+
const isClientComponent = segments.includes("client");
|
|
919
|
+
if (folder === "indexes") {
|
|
920
|
+
manifest[`${pascalName}Index`] = `/${relative3}`;
|
|
921
|
+
} else if (isClientComponent) {
|
|
922
|
+
manifest[`${pascalName}Client`] = `/${relative3}`;
|
|
923
|
+
} else if (folder === "pages") {
|
|
924
|
+
if (isReact) {
|
|
925
|
+
manifest[`${pascalName}Page`] = `/${relative3}`;
|
|
926
|
+
} else if (isVue || isSvelte) {
|
|
927
|
+
manifest[pascalName] = `/${relative3}`;
|
|
928
|
+
} else {
|
|
929
|
+
manifest[`${pascalName}Page`] = `/${relative3}`;
|
|
930
|
+
}
|
|
931
|
+
} else {
|
|
932
|
+
manifest[pascalName] = `/${relative3}`;
|
|
933
|
+
}
|
|
934
|
+
return manifest;
|
|
935
|
+
}, {});
|
|
936
|
+
|
|
937
|
+
// src/build/generateReactIndexes.ts
|
|
938
|
+
import { mkdir as mkdir3, rm, writeFile } from "fs/promises";
|
|
939
|
+
import { basename as basename3, join as join3, resolve as resolve3 } from "path";
|
|
940
|
+
var {Glob } = globalThis.Bun;
|
|
941
|
+
var hmrClientPath3 = resolve3(import.meta.dir, "../dev/client/hmrClient.ts").replace(/\\/g, "/");
|
|
942
|
+
var refreshSetupPath = resolve3(import.meta.dir, "../dev/client/reactRefreshSetup.ts").replace(/\\/g, "/");
|
|
943
|
+
var generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory, isDev = false) => {
|
|
944
|
+
await rm(reactIndexesDirectory, { force: true, recursive: true });
|
|
945
|
+
await mkdir3(reactIndexesDirectory);
|
|
946
|
+
const pagesGlob = new Glob("*.*");
|
|
947
|
+
const files = [];
|
|
948
|
+
for await (const file3 of pagesGlob.scan({ cwd: reactPagesDirectory })) {
|
|
949
|
+
files.push(file3);
|
|
950
|
+
}
|
|
951
|
+
const promises = files.map(async (file3) => {
|
|
952
|
+
const fileName = basename3(file3);
|
|
953
|
+
const [componentName] = fileName.split(".");
|
|
954
|
+
const hmrPreamble = isDev ? [
|
|
955
|
+
`window.__HMR_FRAMEWORK__ = "react";`,
|
|
956
|
+
`window.__REACT_COMPONENT_KEY__ = "${componentName}Index";`,
|
|
957
|
+
`import '${refreshSetupPath}';`,
|
|
958
|
+
`import '${hmrClientPath3}';
|
|
959
|
+
`
|
|
960
|
+
] : [];
|
|
961
|
+
const content = [
|
|
962
|
+
...hmrPreamble,
|
|
963
|
+
`import { hydrateRoot, createRoot } from 'react-dom/client';`,
|
|
964
|
+
`import { createElement } from 'react';`,
|
|
965
|
+
`import type { ComponentType } from 'react'`,
|
|
966
|
+
`import { ${componentName} } from '../pages/${componentName}';
|
|
967
|
+
`,
|
|
968
|
+
`type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
|
|
969
|
+
`,
|
|
970
|
+
`declare global {`,
|
|
971
|
+
` interface Window {`,
|
|
972
|
+
` __INITIAL_PROPS__?: PropsOf<typeof ${componentName}>`,
|
|
973
|
+
` __REACT_ROOT__?: ReturnType<typeof hydrateRoot | typeof createRoot>`,
|
|
974
|
+
` __HMR_CLIENT_ONLY_MODE__?: boolean`,
|
|
975
|
+
` }`,
|
|
976
|
+
`}
|
|
977
|
+
`,
|
|
978
|
+
`// Hydration with error handling and fallback`,
|
|
979
|
+
`const isDev = ${isDev};`,
|
|
980
|
+
`const componentPath = '../pages/${componentName}';
|
|
981
|
+
`,
|
|
982
|
+
`function isHydrationError(error) {`,
|
|
983
|
+
` if (!error) return false;`,
|
|
984
|
+
` const errorMessage = error instanceof Error ? error.message : String(error);`,
|
|
985
|
+
` const errorString = String(error);`,
|
|
986
|
+
` const fullMessage = errorMessage + ' ' + errorString;`,
|
|
987
|
+
` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
|
|
988
|
+
` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
|
|
989
|
+
` `,
|
|
990
|
+
` // Ignore whitespace-only mismatches in <head> - these are harmless formatting differences`,
|
|
991
|
+
` // The error often shows: + <link...> vs - {"\\n "} which is just formatting`,
|
|
992
|
+
` if (isHydration) {`,
|
|
993
|
+
` // Check if this is a head/link/stylesheet related mismatch`,
|
|
994
|
+
` 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"');`,
|
|
995
|
+
` `,
|
|
996
|
+
` // Check if the mismatch involves only whitespace/newlines`,
|
|
997
|
+
` // Pattern: looks for {"\\n"} or {"\\n "} or similar whitespace-only content`,
|
|
998
|
+
` // Also check for patterns like: - {"\\n "} or + <link...>`,
|
|
999
|
+
` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
|
|
1000
|
+
` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
|
|
1001
|
+
` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
|
|
1002
|
+
` `,
|
|
1003
|
+
` // If it's head-related and involves whitespace/newlines, ignore it`,
|
|
1004
|
+
` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
|
|
1005
|
+
` return false; // Don't treat whitespace-only head mismatches as errors`,
|
|
1006
|
+
` }`,
|
|
1007
|
+
` }`,
|
|
1008
|
+
` return isHydration;`,
|
|
1009
|
+
`}
|
|
1010
|
+
`,
|
|
1011
|
+
`function logHydrationError(error, componentName) {`,
|
|
1012
|
+
` if (!isDev) return;`,
|
|
1013
|
+
` if (window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN) {`,
|
|
1014
|
+
` try {`,
|
|
1015
|
+
` window.__HMR_WS__.send(JSON.stringify({`,
|
|
1016
|
+
` type: 'hydration-error',`,
|
|
1017
|
+
` data: {`,
|
|
1018
|
+
` componentName: '${componentName}',`,
|
|
1019
|
+
` componentPath: componentPath,`,
|
|
1020
|
+
` error: error instanceof Error ? error.message : String(error),`,
|
|
1021
|
+
` timestamp: Date.now()`,
|
|
1022
|
+
` }`,
|
|
1023
|
+
` }));`,
|
|
1024
|
+
` } catch (err) {}`,
|
|
1025
|
+
` }`,
|
|
1026
|
+
`}
|
|
1027
|
+
`,
|
|
1028
|
+
`// Track if we've already switched to client-only mode`,
|
|
1029
|
+
`let hasSwitchedToClientOnly = false;`,
|
|
1030
|
+
`let hydrationErrorDetected = false;
|
|
1031
|
+
`,
|
|
1032
|
+
`function handleHydrationFallback(error) {`,
|
|
1033
|
+
` if (hasSwitchedToClientOnly) return; // Already handled`,
|
|
1034
|
+
` hasSwitchedToClientOnly = true;`,
|
|
1035
|
+
` hydrationErrorDetected = true;
|
|
1036
|
+
`,
|
|
1037
|
+
` logHydrationError(error, '${componentName}');
|
|
1038
|
+
`,
|
|
1039
|
+
` // Fallback: client-only render (no hydration)`,
|
|
1040
|
+
` try {`,
|
|
1041
|
+
` // Unmount existing root if it exists`,
|
|
1042
|
+
` if (window.__REACT_ROOT__ && typeof window.__REACT_ROOT__.unmount === 'function') {`,
|
|
1043
|
+
` try {`,
|
|
1044
|
+
` window.__REACT_ROOT__.unmount();`,
|
|
1045
|
+
` } catch (e) {`,
|
|
1046
|
+
` // Ignore unmount errors`,
|
|
1047
|
+
` }`,
|
|
1048
|
+
` }
|
|
1049
|
+
`,
|
|
1050
|
+
` // Render into the same root container when falling back to client-only`,
|
|
1051
|
+
` const root = createRoot(container);`,
|
|
1052
|
+
` root.render(createElement(${componentName}, mergedProps));`,
|
|
1053
|
+
` window.__REACT_ROOT__ = root;`,
|
|
1054
|
+
` window.__HMR_CLIENT_ONLY_MODE__ = true;`,
|
|
1055
|
+
` } catch (fallbackError) {`,
|
|
1056
|
+
` window.location.reload();`,
|
|
1057
|
+
` }`,
|
|
1058
|
+
`}
|
|
1059
|
+
`,
|
|
1060
|
+
`// HMR State Preservation: Check for preserved state and merge with initial props`,
|
|
1061
|
+
`// This allows state to be preserved across HMR updates without modifying component files`,
|
|
1062
|
+
`let preservedState = (typeof window !== 'undefined' && window.__HMR_PRESERVED_STATE__) ? window.__HMR_PRESERVED_STATE__ : {};
|
|
1063
|
+
`,
|
|
1064
|
+
`// Also check sessionStorage for state that survived a page reload (for React HMR)`,
|
|
1065
|
+
`if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {`,
|
|
1066
|
+
` const hmrStateJson = sessionStorage.getItem('__REACT_HMR_STATE__');`,
|
|
1067
|
+
` if (hmrStateJson) {`,
|
|
1068
|
+
` try {`,
|
|
1069
|
+
` const hmrState = JSON.parse(hmrStateJson);`,
|
|
1070
|
+
` preservedState = { ...preservedState, ...hmrState };`,
|
|
1071
|
+
` sessionStorage.removeItem('__REACT_HMR_STATE__');`,
|
|
1072
|
+
` } catch (e) {}`,
|
|
1073
|
+
` }`,
|
|
1074
|
+
`}
|
|
1075
|
+
`,
|
|
1076
|
+
`const mergedProps = { ...(window.__INITIAL_PROPS__ || {}), ...preservedState };`,
|
|
1077
|
+
`// Clear preserved state after using it (so it doesn't persist across multiple updates)`,
|
|
1078
|
+
`if (typeof window !== 'undefined') {`,
|
|
1079
|
+
` window.__HMR_PRESERVED_STATE__ = undefined;`,
|
|
1080
|
+
`}
|
|
1081
|
+
`,
|
|
1082
|
+
`// Attempt hydration with error handling`,
|
|
1083
|
+
`// Use document (not document.body) when the page renders <html><head><body>`,
|
|
1084
|
+
`// to avoid "In HTML, <html> cannot be a child of <body>" hydration error`,
|
|
1085
|
+
`const container = typeof document !== 'undefined' ? document : null;`,
|
|
1086
|
+
`if (!container) {`,
|
|
1087
|
+
` throw new Error('React root container not found: document is null');`,
|
|
1088
|
+
`}
|
|
1089
|
+
`,
|
|
1090
|
+
`// Guard: only hydrate on first load. During HMR re-imports, skip hydration`,
|
|
1091
|
+
`// so React Fast Refresh can swap components in-place and preserve state.`,
|
|
1092
|
+
`if (!window.__REACT_ROOT__) {`,
|
|
1093
|
+
` let root;`,
|
|
1094
|
+
` try {`,
|
|
1095
|
+
` // Use onRecoverableError to catch hydration errors (React 19)`,
|
|
1096
|
+
` root = hydrateRoot(`,
|
|
1097
|
+
` container,`,
|
|
1098
|
+
` createElement(${componentName}, mergedProps),`,
|
|
1099
|
+
` {`,
|
|
1100
|
+
` onRecoverableError: (error) => {`,
|
|
1101
|
+
` // Check if this is a hydration error (isHydrationError filters out whitespace-only head mismatches)`,
|
|
1102
|
+
` if (isDev && isHydrationError(error)) {`,
|
|
1103
|
+
` // Real hydration error - handle it`,
|
|
1104
|
+
` handleHydrationFallback(error);`,
|
|
1105
|
+
` } else {`,
|
|
1106
|
+
` // Not a hydration error, or it's a whitespace-only mismatch that was filtered out`,
|
|
1107
|
+
` // Check if it's a whitespace-only head mismatch using the same logic as isHydrationError`,
|
|
1108
|
+
` const errorMessage = error instanceof Error ? error.message : String(error);`,
|
|
1109
|
+
` const errorString = String(error);`,
|
|
1110
|
+
` const fullMessage = errorMessage + ' ' + errorString;`,
|
|
1111
|
+
` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
|
|
1112
|
+
` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
|
|
1113
|
+
` if (isHydration) {`,
|
|
1114
|
+
` // Check if this is a head/link/stylesheet related mismatch`,
|
|
1115
|
+
` 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"');`,
|
|
1116
|
+
` // Check if the mismatch involves only whitespace/newlines`,
|
|
1117
|
+
` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
|
|
1118
|
+
` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
|
|
1119
|
+
` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
|
|
1120
|
+
` // If it's head-related and involves whitespace/newlines, silently ignore it`,
|
|
1121
|
+
` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
|
|
1122
|
+
` // Already logged by isHydrationError, just return silently`,
|
|
1123
|
+
` return;`,
|
|
1124
|
+
` }`,
|
|
1125
|
+
` }`,
|
|
1126
|
+
` // Log other recoverable errors`,
|
|
1127
|
+
` console.error('React recoverable error:', error);`,
|
|
1128
|
+
` }`,
|
|
1129
|
+
` }`,
|
|
1130
|
+
` }`,
|
|
1131
|
+
` );`,
|
|
1132
|
+
` window.__REACT_ROOT__ = root;`,
|
|
1133
|
+
` } catch (error) {`,
|
|
1134
|
+
` // Catch synchronous errors (shouldn't happen with hydrateRoot, but safety net)`,
|
|
1135
|
+
` if (isDev && isHydrationError(error)) {`,
|
|
1136
|
+
` handleHydrationFallback(error);`,
|
|
1137
|
+
` } else {`,
|
|
1138
|
+
` throw error;`,
|
|
1139
|
+
` }`,
|
|
1140
|
+
` }
|
|
1141
|
+
`,
|
|
1142
|
+
` // Also listen for hydration errors via console.error (React logs them there)`,
|
|
1143
|
+
` if (isDev) {`,
|
|
1144
|
+
` const originalError = console.error;`,
|
|
1145
|
+
` console.error = function(...args) {`,
|
|
1146
|
+
` const errorMessage = args.map(arg => {`,
|
|
1147
|
+
` if (arg instanceof Error) return arg.message;`,
|
|
1148
|
+
` return String(arg);`,
|
|
1149
|
+
` }).join(' ');`,
|
|
1150
|
+
` `,
|
|
1151
|
+
` // Check if this is a hydration error`,
|
|
1152
|
+
` if (isHydrationError({ message: errorMessage }) && !hydrationErrorDetected) {`,
|
|
1153
|
+
` hydrationErrorDetected = true;`,
|
|
1154
|
+
` // Create a synthetic error for fallback`,
|
|
1155
|
+
` const syntheticError = new Error(errorMessage);`,
|
|
1156
|
+
` // Use setTimeout to ensure this happens after React's error handling`,
|
|
1157
|
+
` setTimeout(() => {`,
|
|
1158
|
+
` handleHydrationFallback(syntheticError);`,
|
|
1159
|
+
` }, 0);`,
|
|
1160
|
+
` }`,
|
|
1161
|
+
` `,
|
|
1162
|
+
` // Call original console.error`,
|
|
1163
|
+
` originalError.apply(console, args);`,
|
|
1164
|
+
` };`,
|
|
1165
|
+
` }`,
|
|
1166
|
+
`}`
|
|
1167
|
+
].join(`
|
|
1168
|
+
`);
|
|
1169
|
+
return writeFile(join3(reactIndexesDirectory, `${componentName}.tsx`), content);
|
|
1170
|
+
});
|
|
1171
|
+
await Promise.all(promises);
|
|
1172
|
+
if (isDev) {
|
|
1173
|
+
await writeFile(join3(reactIndexesDirectory, "_refresh.tsx"), `import 'react';
|
|
1174
|
+
import 'react-dom/client';
|
|
1175
|
+
`);
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
// src/build/wrapHTMLScript.ts
|
|
1180
|
+
var wrapHTMLScriptWithHMR = (code, scriptId) => {
|
|
1181
|
+
const escapedId = JSON.stringify(scriptId);
|
|
1182
|
+
return `${code}
|
|
1183
|
+
|
|
1184
|
+
// HMR acceptance - allows this script to be hot-reloaded
|
|
1185
|
+
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
1186
|
+
import.meta.hot.accept();
|
|
1187
|
+
console.log('[HMR] Script ready:', ${escapedId});
|
|
1188
|
+
}
|
|
1189
|
+
`;
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
// src/build/htmlScriptHMRPlugin.ts
|
|
1193
|
+
var createHTMLScriptHMRPlugin = (htmlDir, htmxDir) => {
|
|
1194
|
+
return {
|
|
1195
|
+
name: "html-script-hmr",
|
|
1196
|
+
setup(build2) {
|
|
1197
|
+
build2.onLoad({ filter: /\.(ts|js|tsx|jsx)$/ }, async (args) => {
|
|
1198
|
+
const normalizedPath = args.path.replace(/\\/g, "/");
|
|
1199
|
+
const isHtmlScript = htmlDir && normalizedPath.includes(htmlDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
|
|
1200
|
+
const isHtmxScript = htmxDir && normalizedPath.includes(htmxDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
|
|
1201
|
+
if (!isHtmlScript && !isHtmxScript) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
const text = await Bun.file(args.path).text();
|
|
1205
|
+
const wrapped = wrapHTMLScriptWithHMR(text, normalizedPath);
|
|
1206
|
+
const ext = args.path.split(".").pop() || "ts";
|
|
1207
|
+
const loader = ext;
|
|
1208
|
+
return {
|
|
1209
|
+
contents: wrapped,
|
|
1210
|
+
loader
|
|
1211
|
+
};
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
// src/build/outputLogs.ts
|
|
1218
|
+
init_constants();
|
|
1219
|
+
var outputLogs = (logs) => {
|
|
1220
|
+
for (const log of logs) {
|
|
1221
|
+
if (log.message.includes(BUN_BUILD_WARNING_SUPPRESSION))
|
|
1222
|
+
continue;
|
|
1223
|
+
if (log.level === "error")
|
|
1224
|
+
console.error(log);
|
|
1225
|
+
else if (log.level === "warning")
|
|
1226
|
+
console.warn(log);
|
|
1227
|
+
else
|
|
1228
|
+
console.info(log);
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// src/build/scanEntryPoints.ts
|
|
1233
|
+
var {Glob: Glob2 } = globalThis.Bun;
|
|
1234
|
+
var scanEntryPoints = async (dir, pattern) => {
|
|
1235
|
+
const entryPaths = [];
|
|
1236
|
+
const glob = new Glob2(pattern);
|
|
411
1237
|
for await (const file3 of glob.scan({ absolute: true, cwd: dir })) {
|
|
412
1238
|
entryPaths.push(file3);
|
|
413
1239
|
}
|
|
@@ -433,6 +1259,13 @@ var updateAssetPaths = async (manifest, directory) => {
|
|
|
433
1259
|
}
|
|
434
1260
|
const newPath = manifest[key];
|
|
435
1261
|
if (newPath) {
|
|
1262
|
+
if (ext === ".js" || ext === ".ts") {
|
|
1263
|
+
const hasTypeModule = /type\s*=\s*["']module["']/i.test(match);
|
|
1264
|
+
if (!hasTypeModule) {
|
|
1265
|
+
const newSuffix = suffix.replace(/>$/, ' type="module">');
|
|
1266
|
+
return `${prefix}${newPath}${newSuffix}`;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
436
1269
|
return `${prefix}${newPath}${suffix}`;
|
|
437
1270
|
}
|
|
438
1271
|
console.error(`error: no manifest entry for ${ext.slice(1)} "${name}" referenced in ${filePath}`);
|
|
@@ -443,6 +1276,24 @@ var updateAssetPaths = async (manifest, directory) => {
|
|
|
443
1276
|
await Promise.all(tasks);
|
|
444
1277
|
};
|
|
445
1278
|
|
|
1279
|
+
// src/dev/buildHMRClient.ts
|
|
1280
|
+
var {build: bunBuild } = globalThis.Bun;
|
|
1281
|
+
import { resolve as resolve4 } from "path";
|
|
1282
|
+
var buildHMRClient = async () => {
|
|
1283
|
+
const entryPoint = resolve4(import.meta.dir, "client/hmrClient.ts");
|
|
1284
|
+
const result = await bunBuild({
|
|
1285
|
+
entrypoints: [entryPoint],
|
|
1286
|
+
format: "iife",
|
|
1287
|
+
minify: false,
|
|
1288
|
+
target: "browser"
|
|
1289
|
+
});
|
|
1290
|
+
if (!result.success) {
|
|
1291
|
+
console.error("Failed to build HMR client:", result.logs);
|
|
1292
|
+
return "// HMR client build failed";
|
|
1293
|
+
}
|
|
1294
|
+
return await result.outputs[0].text();
|
|
1295
|
+
};
|
|
1296
|
+
|
|
446
1297
|
// src/utils/cleanup.ts
|
|
447
1298
|
import { rm as rm2 } from "fs/promises";
|
|
448
1299
|
import { join as join4 } from "path";
|
|
@@ -462,19 +1313,19 @@ var cleanup = async ({
|
|
|
462
1313
|
};
|
|
463
1314
|
|
|
464
1315
|
// src/utils/commonAncestor.ts
|
|
465
|
-
import { sep as sep2 } from "path";
|
|
466
1316
|
var commonAncestor = (paths, fallback) => {
|
|
467
1317
|
if (paths.length === 0)
|
|
468
1318
|
return fallback;
|
|
469
|
-
const segmentsList = paths.map((p) => p.split(
|
|
1319
|
+
const segmentsList = paths.map((p) => normalizePath(p).split("/"));
|
|
470
1320
|
const [first] = segmentsList;
|
|
471
1321
|
if (!first)
|
|
472
1322
|
return fallback;
|
|
473
1323
|
const commonSegments = first.filter((segment, index) => segmentsList.every((pathSegs) => pathSegs[index] === segment));
|
|
474
|
-
return commonSegments.length ? commonSegments.join(
|
|
1324
|
+
return commonSegments.length ? commonSegments.join("/") : fallback;
|
|
475
1325
|
};
|
|
476
1326
|
|
|
477
1327
|
// src/utils/getDurationString.ts
|
|
1328
|
+
init_constants();
|
|
478
1329
|
var getDurationString = (duration) => {
|
|
479
1330
|
let durationString;
|
|
480
1331
|
if (duration < MILLISECONDS_IN_A_SECOND) {
|
|
@@ -487,27 +1338,127 @@ var getDurationString = (duration) => {
|
|
|
487
1338
|
return durationString;
|
|
488
1339
|
};
|
|
489
1340
|
|
|
1341
|
+
// src/utils/logger.ts
|
|
1342
|
+
var colors = {
|
|
1343
|
+
reset: "\x1B[0m",
|
|
1344
|
+
bold: "\x1B[1m",
|
|
1345
|
+
dim: "\x1B[2m",
|
|
1346
|
+
cyan: "\x1B[36m",
|
|
1347
|
+
green: "\x1B[32m",
|
|
1348
|
+
yellow: "\x1B[33m",
|
|
1349
|
+
red: "\x1B[31m",
|
|
1350
|
+
blue: "\x1B[34m",
|
|
1351
|
+
magenta: "\x1B[35m",
|
|
1352
|
+
white: "\x1B[37m"
|
|
1353
|
+
};
|
|
1354
|
+
var frameworkColors = {
|
|
1355
|
+
react: colors.blue,
|
|
1356
|
+
vue: colors.green,
|
|
1357
|
+
svelte: colors.yellow,
|
|
1358
|
+
angular: colors.magenta,
|
|
1359
|
+
html: colors.white,
|
|
1360
|
+
htmx: colors.white,
|
|
1361
|
+
css: colors.cyan,
|
|
1362
|
+
assets: colors.dim
|
|
1363
|
+
};
|
|
1364
|
+
var formatTimestamp = () => {
|
|
1365
|
+
const now = new Date;
|
|
1366
|
+
let hours = now.getHours();
|
|
1367
|
+
const minutes = now.getMinutes().toString().padStart(2, "0");
|
|
1368
|
+
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
1369
|
+
const ampm = hours >= 12 ? "PM" : "AM";
|
|
1370
|
+
hours = hours % 12 || 12;
|
|
1371
|
+
return `${hours}:${minutes}:${seconds} ${ampm}`;
|
|
1372
|
+
};
|
|
1373
|
+
var formatPath = (filePath) => {
|
|
1374
|
+
const cwd = process.cwd();
|
|
1375
|
+
let relative3 = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
1376
|
+
relative3 = relative3.replace(/\\/g, "/");
|
|
1377
|
+
if (!relative3.startsWith("/")) {
|
|
1378
|
+
relative3 = "/" + relative3;
|
|
1379
|
+
}
|
|
1380
|
+
return relative3;
|
|
1381
|
+
};
|
|
1382
|
+
var getFrameworkColor = (framework) => {
|
|
1383
|
+
return frameworkColors[framework] || colors.white;
|
|
1384
|
+
};
|
|
1385
|
+
var log = (action, options) => {
|
|
1386
|
+
const timestamp = `${colors.dim}${formatTimestamp()}${colors.reset}`;
|
|
1387
|
+
const tag = `${colors.cyan}[hmr]${colors.reset}`;
|
|
1388
|
+
let message = action;
|
|
1389
|
+
if (options?.path) {
|
|
1390
|
+
const pathColor = options.framework ? getFrameworkColor(options.framework) : colors.white;
|
|
1391
|
+
message += ` ${pathColor}${formatPath(options.path)}${colors.reset}`;
|
|
1392
|
+
}
|
|
1393
|
+
if (options?.duration !== undefined) {
|
|
1394
|
+
message += ` ${colors.dim}(${options.duration}ms)${colors.reset}`;
|
|
1395
|
+
}
|
|
1396
|
+
console.log(`${timestamp} ${tag} ${message}`);
|
|
1397
|
+
};
|
|
1398
|
+
var logError = (message, error) => {
|
|
1399
|
+
const timestamp = `${colors.dim}${formatTimestamp()}${colors.reset}`;
|
|
1400
|
+
const tag = `${colors.red}[hmr]${colors.reset}`;
|
|
1401
|
+
const errorMsg = error instanceof Error ? error.message : error;
|
|
1402
|
+
const fullMessage = `${colors.red}error${colors.reset} ${message}${errorMsg ? `: ${errorMsg}` : ""}`;
|
|
1403
|
+
console.error(`${timestamp} ${tag} ${fullMessage}`);
|
|
1404
|
+
};
|
|
1405
|
+
var logWarn = (message) => {
|
|
1406
|
+
const timestamp = `${colors.dim}${formatTimestamp()}${colors.reset}`;
|
|
1407
|
+
const tag = `${colors.yellow}[hmr]${colors.reset}`;
|
|
1408
|
+
console.warn(`${timestamp} ${tag} ${colors.yellow}warning${colors.reset} ${message}`);
|
|
1409
|
+
};
|
|
1410
|
+
var logger = {
|
|
1411
|
+
hmrUpdate(path, framework) {
|
|
1412
|
+
log("hmr update", { path, framework });
|
|
1413
|
+
},
|
|
1414
|
+
pageReload(path, framework) {
|
|
1415
|
+
log("page reload", { path, framework });
|
|
1416
|
+
},
|
|
1417
|
+
cssUpdate(path, framework) {
|
|
1418
|
+
log("css update", { path, framework: framework ?? "css" });
|
|
1419
|
+
},
|
|
1420
|
+
scriptUpdate(path, framework) {
|
|
1421
|
+
log("script update", { path, framework });
|
|
1422
|
+
},
|
|
1423
|
+
rebuilt(duration) {
|
|
1424
|
+
const timestamp = `${colors.dim}${formatTimestamp()}${colors.reset}`;
|
|
1425
|
+
const tag = `${colors.cyan}[hmr]${colors.reset}`;
|
|
1426
|
+
const message = `${colors.green}rebuilt${colors.reset} ${colors.dim}(${duration}ms)${colors.reset}`;
|
|
1427
|
+
console.log(`${timestamp} ${tag} ${message}`);
|
|
1428
|
+
},
|
|
1429
|
+
error(message, error) {
|
|
1430
|
+
logError(message, error);
|
|
1431
|
+
},
|
|
1432
|
+
warn(message) {
|
|
1433
|
+
logWarn(message);
|
|
1434
|
+
},
|
|
1435
|
+
info(message) {
|
|
1436
|
+
log(message);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
490
1439
|
// src/utils/validateSafePath.ts
|
|
491
|
-
import { resolve as
|
|
1440
|
+
import { resolve as resolve5, relative as relative3 } from "path";
|
|
492
1441
|
var validateSafePath = (targetPath, baseDirectory) => {
|
|
493
|
-
const absoluteBase =
|
|
494
|
-
const absoluteTarget =
|
|
495
|
-
|
|
1442
|
+
const absoluteBase = resolve5(baseDirectory);
|
|
1443
|
+
const absoluteTarget = resolve5(baseDirectory, targetPath);
|
|
1444
|
+
const relativePath = normalizePath(relative3(absoluteBase, absoluteTarget));
|
|
1445
|
+
if (relativePath.startsWith("../") || relativePath === "..") {
|
|
496
1446
|
throw new Error(`Unsafe path: ${targetPath}`);
|
|
497
1447
|
}
|
|
498
1448
|
return absoluteTarget;
|
|
499
1449
|
};
|
|
500
1450
|
|
|
501
1451
|
// src/core/build.ts
|
|
502
|
-
var isDev = env2.NODE_ENV
|
|
1452
|
+
var isDev = env2.NODE_ENV !== "production";
|
|
503
1453
|
var vueFeatureFlags = {
|
|
504
1454
|
__VUE_OPTIONS_API__: "true",
|
|
505
1455
|
__VUE_PROD_DEVTOOLS__: isDev ? "true" : "false",
|
|
506
1456
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: isDev ? "true" : "false"
|
|
507
1457
|
};
|
|
508
|
-
var
|
|
1458
|
+
var build2 = async ({
|
|
509
1459
|
buildDirectory = "build",
|
|
510
|
-
assetsDirectory
|
|
1460
|
+
assetsDirectory,
|
|
1461
|
+
publicDirectory,
|
|
511
1462
|
reactDirectory,
|
|
512
1463
|
htmlDirectory,
|
|
513
1464
|
htmxDirectory,
|
|
@@ -515,10 +1466,17 @@ var build = async ({
|
|
|
515
1466
|
svelteDirectory,
|
|
516
1467
|
vueDirectory,
|
|
517
1468
|
tailwind,
|
|
518
|
-
options
|
|
1469
|
+
options,
|
|
1470
|
+
incrementalFiles
|
|
519
1471
|
}) => {
|
|
520
1472
|
const buildStart = performance.now();
|
|
521
1473
|
const projectRoot = cwd();
|
|
1474
|
+
const isIncremental = incrementalFiles && incrementalFiles.length > 0;
|
|
1475
|
+
const normalizedIncrementalFiles = incrementalFiles?.map(normalizePath);
|
|
1476
|
+
if (isIncremental) {
|
|
1477
|
+
console.log(`\u26A1 Incremental build: ${incrementalFiles.length} file(s) to rebuild`);
|
|
1478
|
+
}
|
|
1479
|
+
const throwOnError = options?.throwOnError === true;
|
|
522
1480
|
const buildPath = validateSafePath(buildDirectory, projectRoot);
|
|
523
1481
|
const assetsPath = assetsDirectory && validateSafePath(assetsDirectory, projectRoot);
|
|
524
1482
|
const reactDir = reactDirectory && validateSafePath(reactDirectory, projectRoot);
|
|
@@ -544,6 +1502,14 @@ var build = async ({
|
|
|
544
1502
|
angularDir
|
|
545
1503
|
].filter(Boolean);
|
|
546
1504
|
const isSingle = frontends.length === 1;
|
|
1505
|
+
const clientRoots = [
|
|
1506
|
+
reactDir,
|
|
1507
|
+
svelteDir,
|
|
1508
|
+
htmlDir,
|
|
1509
|
+
vueDir,
|
|
1510
|
+
angularDir
|
|
1511
|
+
].filter((dir) => Boolean(dir));
|
|
1512
|
+
const clientRoot = isSingle ? clientRoots[0] ?? projectRoot : commonAncestor(clientRoots, projectRoot);
|
|
547
1513
|
let serverOutDir;
|
|
548
1514
|
if (svelteDir)
|
|
549
1515
|
serverOutDir = join5(buildPath, basename4(svelteDir), "pages");
|
|
@@ -554,34 +1520,83 @@ var build = async ({
|
|
|
554
1520
|
serverRoot = sveltePagesPath;
|
|
555
1521
|
else if (vuePagesPath)
|
|
556
1522
|
serverRoot = vuePagesPath;
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
1523
|
+
const publicPath = publicDirectory && validateSafePath(publicDirectory, projectRoot);
|
|
1524
|
+
if (!isIncremental) {
|
|
1525
|
+
await rm3(buildPath, { force: true, recursive: true });
|
|
1526
|
+
}
|
|
1527
|
+
mkdirSync(buildPath, { recursive: true });
|
|
1528
|
+
if (publicPath)
|
|
1529
|
+
cpSync(publicPath, buildPath, { force: true, recursive: true });
|
|
1530
|
+
const filterToIncrementalEntries = (entryPoints, mapToSource) => {
|
|
1531
|
+
if (!isIncremental || !incrementalFiles)
|
|
1532
|
+
return entryPoints;
|
|
1533
|
+
const normalizedIncremental = new Set(incrementalFiles.map((f) => resolve6(f)));
|
|
1534
|
+
const matchingEntries = [];
|
|
1535
|
+
for (const entry of entryPoints) {
|
|
1536
|
+
const sourceFile = mapToSource(entry);
|
|
1537
|
+
if (sourceFile && normalizedIncremental.has(resolve6(sourceFile))) {
|
|
1538
|
+
matchingEntries.push(entry);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return matchingEntries;
|
|
1542
|
+
};
|
|
1543
|
+
if (reactIndexesPath && reactPagesPath) {
|
|
1544
|
+
await generateReactIndexFiles(reactPagesPath, reactIndexesPath, isDev);
|
|
1545
|
+
}
|
|
1546
|
+
if (assetsPath && (!isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/assets/")))) {
|
|
562
1547
|
cpSync(assetsPath, join5(buildPath, "assets"), {
|
|
563
1548
|
force: true,
|
|
564
1549
|
recursive: true
|
|
565
1550
|
});
|
|
566
|
-
|
|
1551
|
+
}
|
|
1552
|
+
if (tailwind && (!isIncremental || normalizedIncrementalFiles?.some((f) => f.endsWith(".css")))) {
|
|
567
1553
|
await $`bunx @tailwindcss/cli -i ${tailwind.input} -o ${join5(buildPath, tailwind.output)}`;
|
|
568
|
-
|
|
569
|
-
const
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
const
|
|
1554
|
+
}
|
|
1555
|
+
const allReactEntries = reactIndexesPath ? await scanEntryPoints(reactIndexesPath, "*.tsx") : [];
|
|
1556
|
+
const allHtmlEntries = htmlScriptsPath ? await scanEntryPoints(htmlScriptsPath, "*.{js,ts}") : [];
|
|
1557
|
+
const allSvelteEntries = sveltePagesPath ? await scanEntryPoints(sveltePagesPath, "*.svelte") : [];
|
|
1558
|
+
const allVueEntries = vuePagesPath ? await scanEntryPoints(vuePagesPath, "*.vue") : [];
|
|
1559
|
+
const allAngularEntries = angularPagesPath ? await scanEntryPoints(angularPagesPath, "*.ts") : [];
|
|
1560
|
+
const allHtmlCssEntries = htmlDir ? await scanEntryPoints(join5(htmlDir, "styles"), "*.css") : [];
|
|
1561
|
+
const allHtmxCssEntries = htmxDir ? await scanEntryPoints(join5(htmxDir, "styles"), "*.css") : [];
|
|
1562
|
+
const allReactCssEntries = reactDir ? await scanEntryPoints(join5(reactDir, "styles"), "*.css") : [];
|
|
1563
|
+
const allSvelteCssEntries = svelteDir ? await scanEntryPoints(join5(svelteDir, "styles"), "*.css") : [];
|
|
1564
|
+
const shouldIncludeHtmlAssets = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/html/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1565
|
+
const shouldIncludeHtmxAssets = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/htmx/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1566
|
+
const reactEntries = isIncremental && reactIndexesPath && reactPagesPath ? filterToIncrementalEntries(allReactEntries, (entry) => {
|
|
1567
|
+
if (entry.startsWith(resolve6(reactIndexesPath))) {
|
|
1568
|
+
const pageName = basename4(entry, ".tsx");
|
|
1569
|
+
return join5(reactPagesPath, `${pageName}.tsx`);
|
|
1570
|
+
}
|
|
1571
|
+
return null;
|
|
1572
|
+
}) : allReactEntries;
|
|
1573
|
+
const htmlEntries = isIncremental && htmlScriptsPath && !shouldIncludeHtmlAssets ? filterToIncrementalEntries(allHtmlEntries, (entry) => entry) : allHtmlEntries;
|
|
1574
|
+
const svelteEntries = isIncremental ? filterToIncrementalEntries(allSvelteEntries, (entry) => entry) : allSvelteEntries;
|
|
1575
|
+
const vueEntries = isIncremental ? filterToIncrementalEntries(allVueEntries, (entry) => entry) : allVueEntries;
|
|
1576
|
+
const angularEntries = isIncremental ? filterToIncrementalEntries(allAngularEntries, (entry) => entry) : allAngularEntries;
|
|
1577
|
+
const htmlCssEntries = isIncremental && !shouldIncludeHtmlAssets ? filterToIncrementalEntries(allHtmlCssEntries, (entry) => entry) : allHtmlCssEntries;
|
|
1578
|
+
const htmxCssEntries = isIncremental && !shouldIncludeHtmxAssets ? filterToIncrementalEntries(allHtmxCssEntries, (entry) => entry) : allHtmxCssEntries;
|
|
1579
|
+
const reactCssEntries = isIncremental ? filterToIncrementalEntries(allReactCssEntries, (entry) => entry) : allReactCssEntries;
|
|
1580
|
+
const svelteCssEntries = isIncremental ? filterToIncrementalEntries(allSvelteCssEntries, (entry) => entry) : allSvelteCssEntries;
|
|
1581
|
+
const { svelteServerPaths, svelteIndexPaths, svelteClientPaths } = svelteDir ? await compileSvelte(svelteEntries, svelteDir, new Map, isDev) : {
|
|
1582
|
+
svelteClientPaths: [],
|
|
1583
|
+
svelteIndexPaths: [],
|
|
1584
|
+
svelteServerPaths: []
|
|
1585
|
+
};
|
|
1586
|
+
const { vueServerPaths, vueIndexPaths, vueClientPaths, vueCssPaths } = vueDir ? await compileVue(vueEntries, vueDir, isDev) : {
|
|
1587
|
+
vueClientPaths: [],
|
|
1588
|
+
vueCssPaths: [],
|
|
1589
|
+
vueIndexPaths: [],
|
|
1590
|
+
vueServerPaths: []
|
|
1591
|
+
};
|
|
579
1592
|
const serverEntryPoints = [...svelteServerPaths, ...vueServerPaths];
|
|
580
|
-
const
|
|
581
|
-
|
|
1593
|
+
const reactClientEntryPoints = [...reactEntries];
|
|
1594
|
+
const nonReactClientEntryPoints = [
|
|
1595
|
+
...svelteIndexPaths,
|
|
582
1596
|
...svelteClientPaths,
|
|
583
1597
|
...htmlEntries,
|
|
584
|
-
...vueIndexPaths
|
|
1598
|
+
...vueIndexPaths,
|
|
1599
|
+
...vueClientPaths
|
|
585
1600
|
];
|
|
586
1601
|
const cssEntryPoints = [
|
|
587
1602
|
...vueCssPaths,
|
|
@@ -590,244 +1605,1697 @@ var build = async ({
|
|
|
590
1605
|
...htmlCssEntries,
|
|
591
1606
|
...htmxCssEntries
|
|
592
1607
|
];
|
|
593
|
-
if (serverEntryPoints.length === 0 &&
|
|
594
|
-
|
|
1608
|
+
if (serverEntryPoints.length === 0 && reactClientEntryPoints.length === 0 && nonReactClientEntryPoints.length === 0 && htmxDir === undefined && htmlDir === undefined) {
|
|
1609
|
+
logger.warn("No entry points found, manifest will be empty");
|
|
595
1610
|
return {};
|
|
596
1611
|
}
|
|
597
1612
|
let serverLogs = [];
|
|
598
1613
|
let serverOutputs = [];
|
|
599
1614
|
if (serverEntryPoints.length > 0) {
|
|
600
|
-
const
|
|
1615
|
+
const result = await bunBuild2({
|
|
601
1616
|
entrypoints: serverEntryPoints,
|
|
602
1617
|
format: "esm",
|
|
603
1618
|
naming: `[dir]/[name].[hash].[ext]`,
|
|
604
1619
|
outdir: serverOutDir,
|
|
605
1620
|
root: serverRoot,
|
|
606
|
-
target: "bun"
|
|
607
|
-
|
|
608
|
-
console.error("Server build failed:", err);
|
|
609
|
-
exit(1);
|
|
1621
|
+
target: "bun",
|
|
1622
|
+
throw: false
|
|
610
1623
|
});
|
|
611
|
-
serverLogs = logs;
|
|
612
|
-
serverOutputs = outputs;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1624
|
+
serverLogs = result.logs;
|
|
1625
|
+
serverOutputs = result.outputs;
|
|
1626
|
+
if (!result.success && result.logs.length > 0) {
|
|
1627
|
+
const errLog = result.logs.find((l) => l.level === "error") ?? result.logs[0];
|
|
1628
|
+
const err = new Error(typeof errLog.message === "string" ? errLog.message : String(errLog.message));
|
|
1629
|
+
err.logs = result.logs;
|
|
1630
|
+
logger.error("Server build failed", err);
|
|
1631
|
+
if (throwOnError)
|
|
1632
|
+
throw err;
|
|
1633
|
+
exit(1);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
let reactClientLogs = [];
|
|
1637
|
+
let reactClientOutputs = [];
|
|
1638
|
+
if (isDev && reactIndexesPath && reactClientEntryPoints.length > 0) {
|
|
1639
|
+
const refreshEntry = join5(reactIndexesPath, "_refresh.tsx");
|
|
1640
|
+
if (!reactClientEntryPoints.includes(refreshEntry)) {
|
|
1641
|
+
reactClientEntryPoints.push(refreshEntry);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
if (reactClientEntryPoints.length > 0) {
|
|
1645
|
+
const reactBuildConfig = {
|
|
1646
|
+
entrypoints: reactClientEntryPoints,
|
|
628
1647
|
format: "esm",
|
|
629
|
-
minify:
|
|
1648
|
+
minify: !isDev,
|
|
630
1649
|
naming: `[dir]/[name].[hash].[ext]`,
|
|
631
1650
|
outdir: buildPath,
|
|
632
1651
|
root: clientRoot,
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1652
|
+
splitting: true,
|
|
1653
|
+
target: "browser",
|
|
1654
|
+
throw: false
|
|
1655
|
+
};
|
|
1656
|
+
if (isDev) {
|
|
1657
|
+
reactBuildConfig.reactFastRefresh = true;
|
|
1658
|
+
}
|
|
1659
|
+
const reactClientResult = await bunBuild2(reactBuildConfig);
|
|
1660
|
+
reactClientLogs = reactClientResult.logs;
|
|
1661
|
+
reactClientOutputs = reactClientResult.outputs;
|
|
1662
|
+
if (!reactClientResult.success && reactClientResult.logs.length > 0) {
|
|
1663
|
+
const errLog = reactClientResult.logs.find((l) => l.level === "error") ?? reactClientResult.logs[0];
|
|
1664
|
+
const err = new Error(typeof errLog.message === "string" ? errLog.message : String(errLog.message));
|
|
1665
|
+
err.logs = reactClientResult.logs;
|
|
1666
|
+
logger.error("React client build failed", err);
|
|
1667
|
+
if (throwOnError)
|
|
1668
|
+
throw err;
|
|
636
1669
|
exit(1);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
let nonReactClientLogs = [];
|
|
1673
|
+
let nonReactClientOutputs = [];
|
|
1674
|
+
if (nonReactClientEntryPoints.length > 0) {
|
|
1675
|
+
const htmlScriptPlugin = isDev ? createHTMLScriptHMRPlugin(htmlDir, htmxDir) : undefined;
|
|
1676
|
+
const nonReactClientResult = await bunBuild2({
|
|
1677
|
+
define: vueDirectory ? vueFeatureFlags : undefined,
|
|
1678
|
+
entrypoints: nonReactClientEntryPoints,
|
|
1679
|
+
format: "esm",
|
|
1680
|
+
minify: !isDev,
|
|
1681
|
+
naming: `[dir]/[name].[hash].[ext]`,
|
|
1682
|
+
outdir: buildPath,
|
|
1683
|
+
plugins: htmlScriptPlugin ? [htmlScriptPlugin] : undefined,
|
|
1684
|
+
root: clientRoot,
|
|
1685
|
+
target: "browser",
|
|
1686
|
+
splitting: !isDev,
|
|
1687
|
+
throw: false
|
|
637
1688
|
});
|
|
638
|
-
|
|
639
|
-
|
|
1689
|
+
nonReactClientLogs = nonReactClientResult.logs;
|
|
1690
|
+
nonReactClientOutputs = nonReactClientResult.outputs;
|
|
1691
|
+
if (!nonReactClientResult.success && nonReactClientResult.logs.length > 0) {
|
|
1692
|
+
const errLog = nonReactClientResult.logs.find((l) => l.level === "error") ?? nonReactClientResult.logs[0];
|
|
1693
|
+
const err = new Error(typeof errLog.message === "string" ? errLog.message : String(errLog.message));
|
|
1694
|
+
err.logs = nonReactClientResult.logs;
|
|
1695
|
+
logger.error("Non-React client build failed", err);
|
|
1696
|
+
if (throwOnError)
|
|
1697
|
+
throw err;
|
|
1698
|
+
exit(1);
|
|
1699
|
+
}
|
|
640
1700
|
}
|
|
641
1701
|
let cssLogs = [];
|
|
642
1702
|
let cssOutputs = [];
|
|
643
1703
|
if (cssEntryPoints.length > 0) {
|
|
644
|
-
const
|
|
1704
|
+
const cssResult = await bunBuild2({
|
|
645
1705
|
entrypoints: cssEntryPoints,
|
|
646
1706
|
naming: `[name].[hash].[ext]`,
|
|
647
|
-
outdir: join5(buildPath, basename4(assetsPath), "css"),
|
|
648
|
-
target: "browser"
|
|
649
|
-
|
|
650
|
-
|
|
1707
|
+
outdir: join5(buildPath, assetsPath ? basename4(assetsPath) : "assets", "css"),
|
|
1708
|
+
target: "browser",
|
|
1709
|
+
throw: false
|
|
1710
|
+
});
|
|
1711
|
+
cssLogs = cssResult.logs;
|
|
1712
|
+
cssOutputs = cssResult.outputs;
|
|
1713
|
+
if (!cssResult.success && cssResult.logs.length > 0) {
|
|
1714
|
+
const errLog = cssResult.logs.find((l) => l.level === "error") ?? cssResult.logs[0];
|
|
1715
|
+
const err = new Error(typeof errLog.message === "string" ? errLog.message : String(errLog.message));
|
|
1716
|
+
err.logs = cssResult.logs;
|
|
1717
|
+
logger.error("CSS build failed", err);
|
|
1718
|
+
if (throwOnError)
|
|
1719
|
+
throw err;
|
|
651
1720
|
exit(1);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const allLogs = [
|
|
1724
|
+
...serverLogs,
|
|
1725
|
+
...reactClientLogs,
|
|
1726
|
+
...nonReactClientLogs,
|
|
1727
|
+
...cssLogs
|
|
1728
|
+
];
|
|
1729
|
+
outputLogs(allLogs);
|
|
1730
|
+
const manifest = generateManifest([
|
|
1731
|
+
...serverOutputs,
|
|
1732
|
+
...reactClientOutputs,
|
|
1733
|
+
...nonReactClientOutputs,
|
|
1734
|
+
...cssOutputs
|
|
1735
|
+
], buildPath);
|
|
1736
|
+
for (const artifact of serverOutputs) {
|
|
1737
|
+
const fileWithHash = basename4(artifact.path);
|
|
1738
|
+
const [baseName] = fileWithHash.split(`.${artifact.hash}.`);
|
|
1739
|
+
if (!baseName)
|
|
1740
|
+
continue;
|
|
1741
|
+
manifest[toPascal(baseName)] = artifact.path;
|
|
1742
|
+
}
|
|
1743
|
+
const htmlOrHtmlCssChanged = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/html/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1744
|
+
const htmxOrHtmxCssChanged = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/htmx/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1745
|
+
const shouldCopyHtml = htmlOrHtmlCssChanged;
|
|
1746
|
+
const shouldCopyHtmx = htmxOrHtmxCssChanged;
|
|
1747
|
+
const shouldUpdateHtmlAssetPaths = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/html/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1748
|
+
const shouldUpdateHtmxAssetPaths = !isIncremental || normalizedIncrementalFiles?.some((f) => f.includes("/htmx/") && (f.endsWith(".html") || f.endsWith(".css")));
|
|
1749
|
+
const hmrClientBundle = isDev && (htmlDir || htmxDir) ? await buildHMRClient() : null;
|
|
1750
|
+
const injectHMRIntoHTMLFile = (filePath, framework) => {
|
|
1751
|
+
if (!hmrClientBundle)
|
|
1752
|
+
return;
|
|
1753
|
+
let html = readFileSync(filePath, "utf-8");
|
|
1754
|
+
if (html.includes("data-hmr-client"))
|
|
1755
|
+
return;
|
|
1756
|
+
const tag = `<script>window.__HMR_FRAMEWORK__="${framework}";</script>` + `<script data-hmr-client>${hmrClientBundle}</script>`;
|
|
1757
|
+
const bodyClose = /<\/body\s*>/i.exec(html);
|
|
1758
|
+
html = bodyClose ? html.slice(0, bodyClose.index) + tag + html.slice(bodyClose.index) : html + tag;
|
|
1759
|
+
writeFileSync(filePath, html);
|
|
1760
|
+
};
|
|
1761
|
+
if (htmlDir && htmlPagesPath) {
|
|
1762
|
+
const outputHtmlPages = isSingle ? join5(buildPath, "pages") : join5(buildPath, basename4(htmlDir), "pages");
|
|
1763
|
+
if (shouldCopyHtml) {
|
|
1764
|
+
mkdirSync(outputHtmlPages, { recursive: true });
|
|
1765
|
+
cpSync(htmlPagesPath, outputHtmlPages, {
|
|
1766
|
+
force: true,
|
|
1767
|
+
recursive: true
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
if (shouldUpdateHtmlAssetPaths) {
|
|
1771
|
+
await updateAssetPaths(manifest, outputHtmlPages);
|
|
1772
|
+
}
|
|
1773
|
+
const htmlPageFiles = await scanEntryPoints(outputHtmlPages, "*.html");
|
|
1774
|
+
for (const htmlFile of htmlPageFiles) {
|
|
1775
|
+
if (isDev)
|
|
1776
|
+
injectHMRIntoHTMLFile(htmlFile, "html");
|
|
1777
|
+
const fileName = basename4(htmlFile, ".html");
|
|
1778
|
+
manifest[fileName] = htmlFile;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (htmxDir && htmxPagesPath) {
|
|
1782
|
+
const outputHtmxPages = isSingle ? join5(buildPath, "pages") : join5(buildPath, basename4(htmxDir), "pages");
|
|
1783
|
+
if (shouldCopyHtmx) {
|
|
1784
|
+
mkdirSync(outputHtmxPages, { recursive: true });
|
|
1785
|
+
cpSync(htmxPagesPath, outputHtmxPages, {
|
|
1786
|
+
force: true,
|
|
1787
|
+
recursive: true
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
if (shouldCopyHtmx) {
|
|
1791
|
+
const htmxDestDir = isSingle ? buildPath : join5(buildPath, basename4(htmxDir));
|
|
1792
|
+
mkdirSync(htmxDestDir, { recursive: true });
|
|
1793
|
+
const glob = new Glob3("htmx*.min.js");
|
|
1794
|
+
for (const relPath of glob.scanSync({ cwd: htmxDir })) {
|
|
1795
|
+
const src = join5(htmxDir, relPath);
|
|
1796
|
+
const dest = join5(htmxDestDir, "htmx.min.js");
|
|
1797
|
+
copyFileSync(src, dest);
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
if (shouldUpdateHtmxAssetPaths) {
|
|
1802
|
+
await updateAssetPaths(manifest, outputHtmxPages);
|
|
1803
|
+
}
|
|
1804
|
+
const htmxPageFiles = await scanEntryPoints(outputHtmxPages, "*.html");
|
|
1805
|
+
for (const htmxFile of htmxPageFiles) {
|
|
1806
|
+
if (isDev)
|
|
1807
|
+
injectHMRIntoHTMLFile(htmxFile, "htmx");
|
|
1808
|
+
const fileName = basename4(htmxFile, ".html");
|
|
1809
|
+
manifest[fileName] = htmxFile;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
if (!options?.preserveIntermediateFiles)
|
|
1813
|
+
await cleanup({
|
|
1814
|
+
reactIndexesPath,
|
|
1815
|
+
svelteDir,
|
|
1816
|
+
vueDir
|
|
1817
|
+
});
|
|
1818
|
+
console.log(`Build completed in ${getDurationString(performance.now() - buildStart)}`);
|
|
1819
|
+
return manifest;
|
|
1820
|
+
};
|
|
1821
|
+
// src/core/devBuild.ts
|
|
1822
|
+
import { statSync } from "fs";
|
|
1823
|
+
import { resolve as resolve18 } from "path";
|
|
1824
|
+
|
|
1825
|
+
// src/dev/dependencyGraph.ts
|
|
1826
|
+
import { readFileSync as readFileSync2, readdirSync, existsSync } from "fs";
|
|
1827
|
+
import { resolve as resolve7 } from "path";
|
|
1828
|
+
var createDependencyGraph = () => ({
|
|
1829
|
+
dependencies: new Map,
|
|
1830
|
+
dependents: new Map
|
|
1831
|
+
});
|
|
1832
|
+
var extractDependencies = (filePath) => {
|
|
1833
|
+
try {
|
|
1834
|
+
if (!existsSync(filePath)) {
|
|
1835
|
+
return [];
|
|
1836
|
+
}
|
|
1837
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1838
|
+
const dependencies = [];
|
|
1839
|
+
const lowerPath = filePath.toLowerCase();
|
|
1840
|
+
const isHtml = lowerPath.endsWith(".html") || lowerPath.endsWith(".htm");
|
|
1841
|
+
if (isHtml) {
|
|
1842
|
+
const linkRegex = /<link\s+[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi;
|
|
1843
|
+
let matchLink;
|
|
1844
|
+
while ((matchLink = linkRegex.exec(content)) !== null) {
|
|
1845
|
+
const href = matchLink[1];
|
|
1846
|
+
if (!href)
|
|
1847
|
+
continue;
|
|
1848
|
+
const resolvedHref = resolveImportPath(href, filePath);
|
|
1849
|
+
if (resolvedHref) {
|
|
1850
|
+
dependencies.push(resolvedHref);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
return dependencies;
|
|
1854
|
+
}
|
|
1855
|
+
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
1856
|
+
const importWithoutFromRegex = /import\s+['"]([^'"]+)['"]/g;
|
|
1857
|
+
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1858
|
+
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1859
|
+
let match;
|
|
1860
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
1861
|
+
if (match[1]) {
|
|
1862
|
+
const resolved = resolveImportPath(match[1], filePath);
|
|
1863
|
+
if (resolved)
|
|
1864
|
+
dependencies.push(resolved);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
while ((match = importWithoutFromRegex.exec(content)) !== null) {
|
|
1868
|
+
if (match[1]) {
|
|
1869
|
+
const resolved = resolveImportPath(match[1], filePath);
|
|
1870
|
+
if (resolved)
|
|
1871
|
+
dependencies.push(resolved);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
1875
|
+
if (match[1]) {
|
|
1876
|
+
const resolved = resolveImportPath(match[1], filePath);
|
|
1877
|
+
if (resolved)
|
|
1878
|
+
dependencies.push(resolved);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
1882
|
+
if (match[1]) {
|
|
1883
|
+
const resolved = resolveImportPath(match[1], filePath);
|
|
1884
|
+
if (resolved)
|
|
1885
|
+
dependencies.push(resolved);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return dependencies;
|
|
1889
|
+
} catch {
|
|
1890
|
+
return [];
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
var resolveImportPath = (importPath, fromFile) => {
|
|
1894
|
+
if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
|
|
1895
|
+
return null;
|
|
1896
|
+
}
|
|
1897
|
+
const fromDir = resolve7(fromFile, "..");
|
|
1898
|
+
const resolved = resolve7(fromDir, importPath);
|
|
1899
|
+
const normalized = resolve7(resolved);
|
|
1900
|
+
const extensions = [
|
|
1901
|
+
".ts",
|
|
1902
|
+
".tsx",
|
|
1903
|
+
".js",
|
|
1904
|
+
".jsx",
|
|
1905
|
+
".vue",
|
|
1906
|
+
".svelte",
|
|
1907
|
+
".css",
|
|
1908
|
+
".html"
|
|
1909
|
+
];
|
|
1910
|
+
for (const ext of extensions) {
|
|
1911
|
+
const withExt = normalized + ext;
|
|
1912
|
+
try {
|
|
1913
|
+
readFileSync2(withExt);
|
|
1914
|
+
return normalized + ext;
|
|
1915
|
+
} catch {}
|
|
1916
|
+
}
|
|
1917
|
+
try {
|
|
1918
|
+
readFileSync2(normalized);
|
|
1919
|
+
return normalized;
|
|
1920
|
+
} catch {
|
|
1921
|
+
return null;
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
var addFileToGraph = (graph, filePath) => {
|
|
1925
|
+
const normalizedPath = resolve7(filePath);
|
|
1926
|
+
if (!existsSync(normalizedPath)) {
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
const dependencies = extractDependencies(normalizedPath);
|
|
1930
|
+
const existingDeps = graph.dependencies.get(normalizedPath);
|
|
1931
|
+
if (existingDeps) {
|
|
1932
|
+
for (const dep of existingDeps) {
|
|
1933
|
+
const dependents = graph.dependents.get(dep);
|
|
1934
|
+
if (dependents) {
|
|
1935
|
+
dependents.delete(normalizedPath);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
const newDeps = new Set(dependencies);
|
|
1940
|
+
graph.dependencies.set(normalizedPath, newDeps);
|
|
1941
|
+
for (const dep of dependencies) {
|
|
1942
|
+
if (!graph.dependents.has(dep)) {
|
|
1943
|
+
graph.dependents.set(dep, new Set);
|
|
1944
|
+
}
|
|
1945
|
+
graph.dependents.get(dep).add(normalizedPath);
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
var getAffectedFiles = (graph, changedFile) => {
|
|
1949
|
+
const normalizedPath = resolve7(changedFile);
|
|
1950
|
+
const affected = new Set;
|
|
1951
|
+
const toProcess = [normalizedPath];
|
|
1952
|
+
while (toProcess.length > 0) {
|
|
1953
|
+
const current = toProcess.pop();
|
|
1954
|
+
if (affected.has(current)) {
|
|
1955
|
+
continue;
|
|
1956
|
+
}
|
|
1957
|
+
affected.add(current);
|
|
1958
|
+
const dependents = graph.dependents.get(current);
|
|
1959
|
+
if (dependents) {
|
|
1960
|
+
for (const dependent of dependents) {
|
|
1961
|
+
toProcess.push(dependent);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
return Array.from(affected);
|
|
1966
|
+
};
|
|
1967
|
+
var removeFileFromGraph = (graph, filePath) => {
|
|
1968
|
+
const normalizedPath = resolve7(filePath);
|
|
1969
|
+
const deps = graph.dependencies.get(normalizedPath);
|
|
1970
|
+
if (deps) {
|
|
1971
|
+
for (const dep of deps) {
|
|
1972
|
+
const dependents2 = graph.dependents.get(dep);
|
|
1973
|
+
if (dependents2) {
|
|
1974
|
+
dependents2.delete(normalizedPath);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
graph.dependencies.delete(normalizedPath);
|
|
1978
|
+
}
|
|
1979
|
+
const dependents = graph.dependents.get(normalizedPath);
|
|
1980
|
+
if (dependents) {
|
|
1981
|
+
for (const dependent of dependents) {
|
|
1982
|
+
const depList = graph.dependencies.get(dependent);
|
|
1983
|
+
if (depList) {
|
|
1984
|
+
depList.delete(normalizedPath);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
graph.dependents.delete(normalizedPath);
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
var buildInitialDependencyGraph = (graph, directories) => {
|
|
1991
|
+
const processedFiles = new Set;
|
|
1992
|
+
const scanDirectory = (dir) => {
|
|
1993
|
+
const normalizedDir = resolve7(dir);
|
|
1994
|
+
try {
|
|
1995
|
+
const entries = readdirSync(normalizedDir, { withFileTypes: true });
|
|
1996
|
+
for (const entry of entries) {
|
|
1997
|
+
const fullPath = resolve7(normalizedDir, entry.name);
|
|
1998
|
+
if (fullPath.includes("/node_modules/") || fullPath.includes("/.git/") || fullPath.includes("/build/") || fullPath.includes("/compiled/") || fullPath.includes("/indexes/") || entry.name.startsWith(".")) {
|
|
1999
|
+
continue;
|
|
2000
|
+
}
|
|
2001
|
+
if (entry.isDirectory()) {
|
|
2002
|
+
scanDirectory(fullPath);
|
|
2003
|
+
} else if (entry.isFile()) {
|
|
2004
|
+
const ext = entry.name.split(".").pop()?.toLowerCase();
|
|
2005
|
+
if ([
|
|
2006
|
+
"ts",
|
|
2007
|
+
"tsx",
|
|
2008
|
+
"js",
|
|
2009
|
+
"jsx",
|
|
2010
|
+
"vue",
|
|
2011
|
+
"svelte",
|
|
2012
|
+
"html",
|
|
2013
|
+
"htm"
|
|
2014
|
+
].includes(ext || "")) {
|
|
2015
|
+
if (!processedFiles.has(fullPath)) {
|
|
2016
|
+
addFileToGraph(graph, fullPath);
|
|
2017
|
+
processedFiles.add(fullPath);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
} catch {}
|
|
2023
|
+
};
|
|
2024
|
+
for (const dir of directories) {
|
|
2025
|
+
const resolvedDir = resolve7(dir);
|
|
2026
|
+
if (existsSync(resolvedDir)) {
|
|
2027
|
+
scanDirectory(resolvedDir);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
|
|
2032
|
+
// src/dev/moduleVersionTracker.ts
|
|
2033
|
+
var globalVersionCounter = 0;
|
|
2034
|
+
var getNextVersion = () => ++globalVersionCounter;
|
|
2035
|
+
var createModuleVersionTracker = () => new Map;
|
|
2036
|
+
var incrementModuleVersion = (versions, modulePath) => {
|
|
2037
|
+
const newVersion = getNextVersion();
|
|
2038
|
+
versions.set(modulePath, newVersion);
|
|
2039
|
+
return newVersion;
|
|
2040
|
+
};
|
|
2041
|
+
var incrementModuleVersions = (versions, modulePaths) => {
|
|
2042
|
+
const updated = new Map;
|
|
2043
|
+
for (const path of modulePaths) {
|
|
2044
|
+
const version = incrementModuleVersion(versions, path);
|
|
2045
|
+
updated.set(path, version);
|
|
2046
|
+
}
|
|
2047
|
+
return updated;
|
|
2048
|
+
};
|
|
2049
|
+
var serializeModuleVersions = (versions) => {
|
|
2050
|
+
const serialized = {};
|
|
2051
|
+
for (const [path, version] of versions.entries()) {
|
|
2052
|
+
serialized[path] = version;
|
|
2053
|
+
}
|
|
2054
|
+
return serialized;
|
|
2055
|
+
};
|
|
2056
|
+
|
|
2057
|
+
// src/dev/configResolver.ts
|
|
2058
|
+
import { resolve as resolve8 } from "path";
|
|
2059
|
+
var resolveBuildPaths = (config) => {
|
|
2060
|
+
const cwd2 = process.cwd();
|
|
2061
|
+
const normalize = (path) => path.replace(/\\/g, "/");
|
|
2062
|
+
const withDefault = (value, fallback) => normalize(resolve8(cwd2, value ?? fallback));
|
|
2063
|
+
const optional = (value) => value ? normalize(resolve8(cwd2, value)) : undefined;
|
|
2064
|
+
return {
|
|
2065
|
+
buildDir: withDefault(config.buildDirectory, "build"),
|
|
2066
|
+
assetsDir: optional(config.assetsDirectory),
|
|
2067
|
+
reactDir: optional(config.reactDirectory),
|
|
2068
|
+
svelteDir: optional(config.svelteDirectory),
|
|
2069
|
+
vueDir: optional(config.vueDirectory),
|
|
2070
|
+
angularDir: optional(config.angularDirectory),
|
|
2071
|
+
htmlDir: optional(config.htmlDirectory),
|
|
2072
|
+
htmxDir: optional(config.htmxDirectory)
|
|
2073
|
+
};
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
// src/dev/clientManager.ts
|
|
2077
|
+
var createHMRState = (config) => ({
|
|
2078
|
+
connectedClients: new Set,
|
|
2079
|
+
debounceTimeout: null,
|
|
2080
|
+
dependencyGraph: createDependencyGraph(),
|
|
2081
|
+
fileChangeQueue: new Map,
|
|
2082
|
+
fileHashes: new Map,
|
|
2083
|
+
isRebuilding: false,
|
|
2084
|
+
moduleVersions: createModuleVersionTracker(),
|
|
2085
|
+
rebuildQueue: new Set,
|
|
2086
|
+
rebuildTimeout: null,
|
|
2087
|
+
sourceFileVersions: new Map,
|
|
2088
|
+
watchers: [],
|
|
2089
|
+
config,
|
|
2090
|
+
resolvedPaths: resolveBuildPaths(config),
|
|
2091
|
+
vueChangeTypes: new Map,
|
|
2092
|
+
assetStore: new Map
|
|
2093
|
+
});
|
|
2094
|
+
var incrementSourceFileVersion = (state, filePath) => {
|
|
2095
|
+
const currentVersion = state.sourceFileVersions.get(filePath) || 0;
|
|
2096
|
+
const newVersion = currentVersion + 1;
|
|
2097
|
+
state.sourceFileVersions.set(filePath, newVersion);
|
|
2098
|
+
return newVersion;
|
|
2099
|
+
};
|
|
2100
|
+
var incrementSourceFileVersions = (state, filePaths) => {
|
|
2101
|
+
for (const filePath of filePaths) {
|
|
2102
|
+
incrementSourceFileVersion(state, filePath);
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
|
|
2106
|
+
// src/dev/fileWatcher.ts
|
|
2107
|
+
import { watch } from "fs";
|
|
2108
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2109
|
+
import { join as join6, resolve as resolve9 } from "path";
|
|
2110
|
+
|
|
2111
|
+
// src/dev/pathUtils.ts
|
|
2112
|
+
var getWatchPaths = (config, resolved) => {
|
|
2113
|
+
const paths = [];
|
|
2114
|
+
const push = (base, sub) => {
|
|
2115
|
+
if (!base)
|
|
2116
|
+
return;
|
|
2117
|
+
const normalizedBase = normalizePath(base);
|
|
2118
|
+
paths.push(sub ? `${normalizedBase}/${sub}` : normalizedBase);
|
|
2119
|
+
};
|
|
2120
|
+
const cfg = resolved ?? {
|
|
2121
|
+
reactDir: config.reactDirectory,
|
|
2122
|
+
svelteDir: config.svelteDirectory,
|
|
2123
|
+
vueDir: config.vueDirectory,
|
|
2124
|
+
angularDir: config.angularDirectory,
|
|
2125
|
+
htmlDir: config.htmlDirectory,
|
|
2126
|
+
htmxDir: config.htmxDirectory,
|
|
2127
|
+
assetsDir: config.assetsDirectory
|
|
2128
|
+
};
|
|
2129
|
+
push(cfg.reactDir, "components");
|
|
2130
|
+
push(cfg.reactDir, "pages");
|
|
2131
|
+
push(cfg.reactDir, "styles");
|
|
2132
|
+
push(cfg.svelteDir, "components");
|
|
2133
|
+
push(cfg.svelteDir, "pages");
|
|
2134
|
+
push(cfg.svelteDir, "composables");
|
|
2135
|
+
push(cfg.svelteDir, "styles");
|
|
2136
|
+
push(cfg.vueDir, "components");
|
|
2137
|
+
push(cfg.vueDir, "pages");
|
|
2138
|
+
push(cfg.vueDir, "composables");
|
|
2139
|
+
push(cfg.vueDir, "styles");
|
|
2140
|
+
push(cfg.angularDir, "components");
|
|
2141
|
+
push(cfg.angularDir, "pages");
|
|
2142
|
+
push(cfg.angularDir, "styles");
|
|
2143
|
+
push(cfg.htmlDir, "pages");
|
|
2144
|
+
push(cfg.htmlDir, "scripts");
|
|
2145
|
+
push(cfg.htmlDir, "styles");
|
|
2146
|
+
push(cfg.htmxDir, "pages");
|
|
2147
|
+
push(cfg.htmxDir, "styles");
|
|
2148
|
+
push(cfg.assetsDir);
|
|
2149
|
+
return paths;
|
|
2150
|
+
};
|
|
2151
|
+
var shouldIgnorePath = (path) => {
|
|
2152
|
+
const normalizedPath = path.replace(/\\/g, "/");
|
|
2153
|
+
return normalizedPath.includes("/build/") || normalizedPath.includes("/compiled/") || normalizedPath.includes("/indexes/") || normalizedPath.includes("/node_modules/") || normalizedPath.includes("/.git/") || normalizedPath.endsWith(".log") || normalizedPath.endsWith(".tmp") || normalizedPath.startsWith(".") || normalizedPath === "compiled" || normalizedPath.endsWith("/compiled") || normalizedPath.endsWith("/compiled/");
|
|
2154
|
+
};
|
|
2155
|
+
var detectFramework = (filePath, resolved) => {
|
|
2156
|
+
if (shouldIgnorePath(filePath)) {
|
|
2157
|
+
return "ignored";
|
|
2158
|
+
}
|
|
2159
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
2160
|
+
const startsWithDir = (dir) => dir ? normalized.startsWith(dir.replace(/\\/g, "/")) : false;
|
|
2161
|
+
if (resolved) {
|
|
2162
|
+
if (startsWithDir(resolved.htmxDir))
|
|
2163
|
+
return "htmx";
|
|
2164
|
+
if (startsWithDir(resolved.reactDir))
|
|
2165
|
+
return "react";
|
|
2166
|
+
if (startsWithDir(resolved.svelteDir))
|
|
2167
|
+
return "svelte";
|
|
2168
|
+
if (startsWithDir(resolved.vueDir))
|
|
2169
|
+
return "vue";
|
|
2170
|
+
if (startsWithDir(resolved.angularDir))
|
|
2171
|
+
return "angular";
|
|
2172
|
+
if (startsWithDir(resolved.htmlDir))
|
|
2173
|
+
return "html";
|
|
2174
|
+
if (startsWithDir(resolved.assetsDir))
|
|
2175
|
+
return "assets";
|
|
2176
|
+
} else {
|
|
2177
|
+
if (normalized.includes("/htmx/"))
|
|
2178
|
+
return "htmx";
|
|
2179
|
+
if (normalized.includes("/react/"))
|
|
2180
|
+
return "react";
|
|
2181
|
+
if (normalized.includes("/svelte/"))
|
|
2182
|
+
return "svelte";
|
|
2183
|
+
if (normalized.includes("/vue/"))
|
|
2184
|
+
return "vue";
|
|
2185
|
+
if (normalized.includes("/angular/"))
|
|
2186
|
+
return "angular";
|
|
2187
|
+
if (normalized.includes("/html/"))
|
|
2188
|
+
return "html";
|
|
2189
|
+
}
|
|
2190
|
+
if (normalized.endsWith(".tsx") || normalized.endsWith(".jsx"))
|
|
2191
|
+
return "react";
|
|
2192
|
+
if (normalized.endsWith(".svelte"))
|
|
2193
|
+
return "svelte";
|
|
2194
|
+
if (normalized.endsWith(".vue"))
|
|
2195
|
+
return "vue";
|
|
2196
|
+
if (normalized.endsWith(".html"))
|
|
2197
|
+
return "html";
|
|
2198
|
+
if (normalized.endsWith(".ts") && normalized.includes("angular"))
|
|
2199
|
+
return "angular";
|
|
2200
|
+
if (normalized.includes("/assets/"))
|
|
2201
|
+
return "assets";
|
|
2202
|
+
if (normalized.endsWith(".css")) {
|
|
2203
|
+
if (normalized.includes("/vue/") || normalized.includes("/vue-"))
|
|
2204
|
+
return "vue";
|
|
2205
|
+
if (normalized.includes("/svelte/") || normalized.includes("/svelte-"))
|
|
2206
|
+
return "svelte";
|
|
2207
|
+
if (normalized.includes("/react/") || normalized.includes("/react-"))
|
|
2208
|
+
return "react";
|
|
2209
|
+
if (normalized.includes("/angular/") || normalized.includes("/angular-"))
|
|
2210
|
+
return "angular";
|
|
2211
|
+
if (normalized.includes("/html/") || normalized.includes("/html-"))
|
|
2212
|
+
return "html";
|
|
2213
|
+
if (normalized.includes("/htmx/") || normalized.includes("/htmx-"))
|
|
2214
|
+
return "htmx";
|
|
2215
|
+
return "assets";
|
|
2216
|
+
}
|
|
2217
|
+
return "unknown";
|
|
2218
|
+
};
|
|
2219
|
+
|
|
2220
|
+
// src/dev/fileWatcher.ts
|
|
2221
|
+
var startFileWatching = (state, config, onFileChange) => {
|
|
2222
|
+
const watchPaths = getWatchPaths(config, state.resolvedPaths);
|
|
2223
|
+
for (const path of watchPaths) {
|
|
2224
|
+
const absolutePath = resolve9(path).replace(/\\/g, "/");
|
|
2225
|
+
if (!existsSync2(absolutePath)) {
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
const watcher = watch(absolutePath, { recursive: true }, (event, filename) => {
|
|
2229
|
+
if (!filename)
|
|
2230
|
+
return;
|
|
2231
|
+
if (filename === "compiled" || filename === "build" || filename === "indexes" || filename.includes("/compiled") || filename.includes("/build") || filename.includes("/indexes") || filename.endsWith("/")) {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const fullPath = join6(absolutePath, filename).replace(/\\/g, "/");
|
|
2235
|
+
if (shouldIgnorePath(fullPath)) {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
if (event === "rename" && !existsSync2(fullPath)) {
|
|
2239
|
+
try {
|
|
2240
|
+
removeFileFromGraph(state.dependencyGraph, fullPath);
|
|
2241
|
+
} catch {}
|
|
2242
|
+
onFileChange(fullPath);
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
if (existsSync2(fullPath)) {
|
|
2246
|
+
onFileChange(fullPath);
|
|
2247
|
+
try {
|
|
2248
|
+
addFileToGraph(state.dependencyGraph, fullPath);
|
|
2249
|
+
} catch {}
|
|
2250
|
+
}
|
|
652
2251
|
});
|
|
653
|
-
|
|
654
|
-
|
|
2252
|
+
state.watchers.push(watcher);
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
|
|
2256
|
+
// src/dev/assetStore.ts
|
|
2257
|
+
import { resolve as resolve10 } from "path";
|
|
2258
|
+
import { readdir, unlink } from "fs/promises";
|
|
2259
|
+
var mimeTypes = {
|
|
2260
|
+
".css": "text/css",
|
|
2261
|
+
".html": "text/html",
|
|
2262
|
+
".js": "application/javascript",
|
|
2263
|
+
".mjs": "application/javascript",
|
|
2264
|
+
".json": "application/json",
|
|
2265
|
+
".svg": "image/svg+xml",
|
|
2266
|
+
".woff": "font/woff",
|
|
2267
|
+
".woff2": "font/woff2"
|
|
2268
|
+
};
|
|
2269
|
+
var getMimeType = (filePath) => {
|
|
2270
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
2271
|
+
return mimeTypes[ext] ?? "application/octet-stream";
|
|
2272
|
+
};
|
|
2273
|
+
var HASHED_FILE_RE = /\.[a-z0-9]{8}\.(js|css|mjs)$/;
|
|
2274
|
+
var stripHash = (webPath) => webPath.replace(/\.[a-z0-9]{8}(\.(js|css|mjs))$/, "$1");
|
|
2275
|
+
var populateAssetStore = async (store, manifest, buildDir) => {
|
|
2276
|
+
const loadPromises = [];
|
|
2277
|
+
const newIdentities = new Map;
|
|
2278
|
+
for (const webPath of Object.values(manifest)) {
|
|
2279
|
+
if (!webPath.startsWith("/"))
|
|
2280
|
+
continue;
|
|
2281
|
+
newIdentities.set(stripHash(webPath), webPath);
|
|
2282
|
+
}
|
|
2283
|
+
for (const existingPath of store.keys()) {
|
|
2284
|
+
const identity = stripHash(existingPath);
|
|
2285
|
+
const replacement = newIdentities.get(identity);
|
|
2286
|
+
if (replacement && replacement !== existingPath) {
|
|
2287
|
+
store.delete(existingPath);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
for (const webPath of newIdentities.values()) {
|
|
2291
|
+
loadPromises.push(Bun.file(resolve10(buildDir, webPath.slice(1))).bytes().then((bytes) => {
|
|
2292
|
+
store.set(webPath, bytes);
|
|
2293
|
+
}).catch(() => {}));
|
|
2294
|
+
}
|
|
2295
|
+
try {
|
|
2296
|
+
const scanDir = async (dir, prefix) => {
|
|
2297
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2298
|
+
const subTasks = [];
|
|
2299
|
+
for (const entry of entries) {
|
|
2300
|
+
if (entry.isDirectory()) {
|
|
2301
|
+
subTasks.push(scanDir(resolve10(dir, entry.name), `${prefix}${entry.name}/`));
|
|
2302
|
+
} else if (entry.name.startsWith("chunk-")) {
|
|
2303
|
+
const webPath = `/${prefix}${entry.name}`;
|
|
2304
|
+
if (!store.has(webPath)) {
|
|
2305
|
+
subTasks.push(Bun.file(resolve10(dir, entry.name)).bytes().then((bytes) => {
|
|
2306
|
+
store.set(webPath, bytes);
|
|
2307
|
+
}).catch(() => {}));
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
await Promise.all(subTasks);
|
|
2312
|
+
};
|
|
2313
|
+
await scanDir(buildDir, "");
|
|
2314
|
+
} catch {}
|
|
2315
|
+
await Promise.all(loadPromises);
|
|
2316
|
+
};
|
|
2317
|
+
var cleanStaleAssets = async (store, manifest, buildDir) => {
|
|
2318
|
+
const liveByIdentity = new Map;
|
|
2319
|
+
for (const webPath of store.keys()) {
|
|
2320
|
+
const diskPath = resolve10(buildDir, webPath.slice(1));
|
|
2321
|
+
liveByIdentity.set(stripHash(diskPath), diskPath);
|
|
2322
|
+
}
|
|
2323
|
+
const absBuildDir = resolve10(buildDir);
|
|
2324
|
+
for (const val of Object.values(manifest)) {
|
|
2325
|
+
if (!HASHED_FILE_RE.test(val))
|
|
2326
|
+
continue;
|
|
2327
|
+
if (val.startsWith(absBuildDir)) {
|
|
2328
|
+
liveByIdentity.set(stripHash(val), val);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
try {
|
|
2332
|
+
const walkAndClean = async (dir) => {
|
|
2333
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2334
|
+
const tasks = [];
|
|
2335
|
+
for (const entry of entries) {
|
|
2336
|
+
const fullPath = resolve10(dir, entry.name);
|
|
2337
|
+
if (entry.isDirectory()) {
|
|
2338
|
+
tasks.push(walkAndClean(fullPath));
|
|
2339
|
+
} else if (HASHED_FILE_RE.test(entry.name)) {
|
|
2340
|
+
const identity = stripHash(fullPath);
|
|
2341
|
+
const livePath = liveByIdentity.get(identity);
|
|
2342
|
+
if (livePath && livePath !== fullPath) {
|
|
2343
|
+
tasks.push(unlink(fullPath).catch(() => {}));
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
await Promise.all(tasks);
|
|
2348
|
+
};
|
|
2349
|
+
await walkAndClean(buildDir);
|
|
2350
|
+
} catch {}
|
|
2351
|
+
};
|
|
2352
|
+
var lookupAsset = (store, path) => store.get(path);
|
|
2353
|
+
|
|
2354
|
+
// src/dev/rebuildTrigger.ts
|
|
2355
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2356
|
+
import { basename as basename8, resolve as resolve17 } from "path";
|
|
2357
|
+
|
|
2358
|
+
// src/dev/fileHashTracker.ts
|
|
2359
|
+
import { createHash } from "crypto";
|
|
2360
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2361
|
+
var computeFileHash = (filePath) => {
|
|
2362
|
+
try {
|
|
2363
|
+
const fileContent = readFileSync3(filePath);
|
|
2364
|
+
const hash = createHash("sha256");
|
|
2365
|
+
hash.update(fileContent);
|
|
2366
|
+
return hash.digest("hex");
|
|
2367
|
+
} catch {
|
|
2368
|
+
return Date.now().toString();
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
var hasFileChanged = (filePath, currentHash, previousHashes) => {
|
|
2372
|
+
const normalizedPath = normalizePath(filePath);
|
|
2373
|
+
const previousHash = previousHashes.get(normalizedPath);
|
|
2374
|
+
if (!previousHash) {
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2377
|
+
return previousHash !== currentHash;
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// src/dev/moduleMapper.ts
|
|
2381
|
+
import { basename as basename5, resolve as resolve12 } from "path";
|
|
2382
|
+
|
|
2383
|
+
// src/dev/reactComponentClassifier.ts
|
|
2384
|
+
import { resolve as resolve11 } from "path";
|
|
2385
|
+
var classifyComponent = (filePath) => {
|
|
2386
|
+
const normalizedPath = resolve11(filePath);
|
|
2387
|
+
if (normalizedPath.includes("/react/pages/")) {
|
|
2388
|
+
return "server";
|
|
2389
|
+
}
|
|
2390
|
+
if (normalizedPath.includes("/react/components/") || normalizedPath.includes("/react/composables/")) {
|
|
2391
|
+
return "client";
|
|
2392
|
+
}
|
|
2393
|
+
return "client";
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// src/dev/moduleMapper.ts
|
|
2397
|
+
var mapSourceFileToManifestKeys = (sourceFile, framework, resolvedPaths) => {
|
|
2398
|
+
const normalizedFile = resolve12(sourceFile);
|
|
2399
|
+
const fileName = basename5(normalizedFile);
|
|
2400
|
+
const baseName = fileName.replace(/\.(tsx?|jsx?|vue|svelte|css|html)$/, "");
|
|
2401
|
+
const pascalName = toPascal(baseName);
|
|
2402
|
+
const keys = [];
|
|
2403
|
+
const inSubdir = (dir, sub) => {
|
|
2404
|
+
if (!dir)
|
|
2405
|
+
return false;
|
|
2406
|
+
const prefix = `${dir.replace(/\\/g, "/")}/${sub}/`;
|
|
2407
|
+
return normalizedFile.startsWith(prefix);
|
|
2408
|
+
};
|
|
2409
|
+
switch (framework) {
|
|
2410
|
+
case "react":
|
|
2411
|
+
if (inSubdir(resolvedPaths?.reactDir, "pages") || normalizedFile.includes("/react/pages/")) {
|
|
2412
|
+
keys.push(`${pascalName}Index`);
|
|
2413
|
+
keys.push(`${pascalName}CSS`);
|
|
2414
|
+
}
|
|
2415
|
+
break;
|
|
2416
|
+
case "svelte":
|
|
2417
|
+
if (inSubdir(resolvedPaths?.svelteDir, "pages") || normalizedFile.includes("/svelte/pages/")) {
|
|
2418
|
+
keys.push(pascalName);
|
|
2419
|
+
keys.push(`${pascalName}Index`);
|
|
2420
|
+
keys.push(`${pascalName}CSS`);
|
|
2421
|
+
}
|
|
2422
|
+
break;
|
|
2423
|
+
case "vue":
|
|
2424
|
+
if (inSubdir(resolvedPaths?.vueDir, "pages") || normalizedFile.includes("/vue/pages/")) {
|
|
2425
|
+
keys.push(pascalName);
|
|
2426
|
+
keys.push(`${pascalName}Index`);
|
|
2427
|
+
keys.push(`${pascalName}CSS`);
|
|
2428
|
+
}
|
|
2429
|
+
break;
|
|
2430
|
+
case "angular":
|
|
2431
|
+
if (inSubdir(resolvedPaths?.angularDir, "pages") || normalizedFile.includes("/angular/pages/")) {
|
|
2432
|
+
keys.push(pascalName);
|
|
2433
|
+
keys.push(`${pascalName}Index`);
|
|
2434
|
+
}
|
|
2435
|
+
break;
|
|
2436
|
+
case "html":
|
|
2437
|
+
case "htmx":
|
|
2438
|
+
break;
|
|
2439
|
+
case "assets":
|
|
2440
|
+
if (normalizedFile.endsWith(".css")) {
|
|
2441
|
+
keys.push(`${pascalName}CSS`);
|
|
2442
|
+
}
|
|
2443
|
+
break;
|
|
655
2444
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
2445
|
+
return keys;
|
|
2446
|
+
};
|
|
2447
|
+
var createModuleUpdates = (changedFiles, framework, manifest, resolvedPaths) => {
|
|
2448
|
+
const updates = [];
|
|
2449
|
+
const processedFiles = new Set;
|
|
2450
|
+
for (const sourceFile of changedFiles) {
|
|
2451
|
+
const normalizedFile = resolve12(sourceFile);
|
|
2452
|
+
const normalizedPath = normalizedFile.replace(/\\/g, "/");
|
|
2453
|
+
if (processedFiles.has(normalizedFile))
|
|
2454
|
+
continue;
|
|
2455
|
+
processedFiles.add(normalizedFile);
|
|
2456
|
+
const moduleKeys = mapSourceFileToManifestKeys(normalizedFile, framework, resolvedPaths);
|
|
2457
|
+
const isReactPage = resolvedPaths?.reactDir ? normalizedPath.startsWith(`${resolvedPaths.reactDir.replace(/\\/g, "/")}/pages/`) : normalizedPath.includes("/react/pages/");
|
|
2458
|
+
if (framework === "react" && !isReactPage) {
|
|
2459
|
+
continue;
|
|
2460
|
+
}
|
|
2461
|
+
const modulePaths = {};
|
|
2462
|
+
for (const key of moduleKeys) {
|
|
2463
|
+
if (manifest[key]) {
|
|
2464
|
+
modulePaths[key] = manifest[key];
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
if (Object.keys(modulePaths).length > 0) {
|
|
2468
|
+
const componentType = framework === "react" ? classifyComponent(normalizedFile) : undefined;
|
|
2469
|
+
updates.push({
|
|
2470
|
+
componentType,
|
|
2471
|
+
framework,
|
|
2472
|
+
moduleKeys: Object.keys(modulePaths),
|
|
2473
|
+
modulePaths,
|
|
2474
|
+
sourceFile: normalizedFile
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
667
2477
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const htmxDestDir = isSingle ? buildPath : join5(buildPath, basename4(htmxDir));
|
|
676
|
-
mkdirSync(htmxDestDir, { recursive: true });
|
|
677
|
-
const glob = new Glob3("htmx*.min.js");
|
|
678
|
-
for (const relativePath of glob.scanSync({ cwd: htmxDir })) {
|
|
679
|
-
const src = join5(htmxDir, relativePath);
|
|
680
|
-
const dest = join5(htmxDestDir, "htmx.min.js");
|
|
681
|
-
copyFileSync(src, dest);
|
|
682
|
-
break;
|
|
2478
|
+
return updates;
|
|
2479
|
+
};
|
|
2480
|
+
var groupModuleUpdatesByFramework = (updates) => {
|
|
2481
|
+
const grouped = new Map;
|
|
2482
|
+
for (const update of updates) {
|
|
2483
|
+
if (!grouped.has(update.framework)) {
|
|
2484
|
+
grouped.set(update.framework, []);
|
|
683
2485
|
}
|
|
684
|
-
|
|
2486
|
+
grouped.get(update.framework).push(update);
|
|
685
2487
|
}
|
|
686
|
-
|
|
687
|
-
await cleanup({
|
|
688
|
-
reactIndexesPath,
|
|
689
|
-
svelteDir,
|
|
690
|
-
vueDir
|
|
691
|
-
});
|
|
692
|
-
console.log(`Build completed in ${getDurationString(performance.now() - buildStart)}`);
|
|
693
|
-
return manifest;
|
|
2488
|
+
return grouped;
|
|
694
2489
|
};
|
|
695
|
-
// src/core/pageHandlers.ts
|
|
696
|
-
var {file: file3 } = globalThis.Bun;
|
|
697
|
-
import { createElement } from "react";
|
|
698
|
-
import { renderToReadableStream as renderReactToReadableStream } from "react-dom/server";
|
|
699
|
-
import { createSSRApp, h } from "vue";
|
|
700
|
-
import { renderToWebStream as renderVueToWebStream } from "vue/server-renderer";
|
|
701
|
-
|
|
702
|
-
// src/svelte/renderToReadableStream.ts
|
|
703
|
-
import { render } from "svelte/server";
|
|
704
2490
|
|
|
705
|
-
// src/
|
|
706
|
-
var
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
2491
|
+
// src/dev/webSocket.ts
|
|
2492
|
+
var handleClientConnect = (state, client2, manifest) => {
|
|
2493
|
+
state.connectedClients.add(client2);
|
|
2494
|
+
const serverVersions = serializeModuleVersions(state.moduleVersions);
|
|
2495
|
+
client2.send(JSON.stringify({
|
|
2496
|
+
data: {
|
|
2497
|
+
manifest,
|
|
2498
|
+
serverVersions
|
|
2499
|
+
},
|
|
2500
|
+
timestamp: Date.now(),
|
|
2501
|
+
type: "manifest"
|
|
2502
|
+
}));
|
|
2503
|
+
client2.send(JSON.stringify({
|
|
2504
|
+
message: "HMR client connected successfully",
|
|
2505
|
+
timestamp: Date.now(),
|
|
2506
|
+
type: "connected"
|
|
2507
|
+
}));
|
|
2508
|
+
};
|
|
2509
|
+
var handleClientDisconnect = (state, client2) => {
|
|
2510
|
+
state.connectedClients.delete(client2);
|
|
2511
|
+
};
|
|
2512
|
+
var handleHMRMessage = (state, client2, message) => {
|
|
2513
|
+
try {
|
|
2514
|
+
let parsedData;
|
|
2515
|
+
if (typeof message === "string") {
|
|
2516
|
+
parsedData = JSON.parse(message);
|
|
2517
|
+
} else if (message instanceof Buffer) {
|
|
2518
|
+
parsedData = JSON.parse(message.toString());
|
|
2519
|
+
} else if (message instanceof ArrayBuffer) {
|
|
2520
|
+
parsedData = JSON.parse(new TextDecoder().decode(new Uint8Array(message)));
|
|
2521
|
+
} else if (ArrayBuffer.isView(message)) {
|
|
2522
|
+
parsedData = JSON.parse(new TextDecoder().decode(message));
|
|
2523
|
+
} else if (typeof message === "object" && message !== null) {
|
|
2524
|
+
parsedData = message;
|
|
2525
|
+
} else {
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
if (!isValidHMRClientMessage(parsedData)) {
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const data = parsedData;
|
|
2532
|
+
switch (data.type) {
|
|
2533
|
+
case "ping":
|
|
2534
|
+
client2.send(JSON.stringify({
|
|
2535
|
+
timestamp: Date.now(),
|
|
2536
|
+
type: "pong"
|
|
2537
|
+
}));
|
|
2538
|
+
break;
|
|
2539
|
+
case "request-rebuild":
|
|
2540
|
+
break;
|
|
2541
|
+
case "ready":
|
|
2542
|
+
break;
|
|
2543
|
+
case "hydration-error":
|
|
2544
|
+
break;
|
|
2545
|
+
}
|
|
2546
|
+
} catch {}
|
|
2547
|
+
};
|
|
2548
|
+
var broadcastToClients = (state, message) => {
|
|
2549
|
+
const messageStr = JSON.stringify({
|
|
2550
|
+
...message,
|
|
2551
|
+
timestamp: Date.now()
|
|
2552
|
+
});
|
|
2553
|
+
let sentCount = 0;
|
|
2554
|
+
const clientsToRemove = [];
|
|
2555
|
+
for (const client2 of state.connectedClients) {
|
|
2556
|
+
if (client2.readyState === WS_READY_STATE_OPEN) {
|
|
2557
|
+
try {
|
|
2558
|
+
client2.send(messageStr);
|
|
2559
|
+
sentCount++;
|
|
2560
|
+
} catch {
|
|
2561
|
+
clientsToRemove.push(client2);
|
|
2562
|
+
}
|
|
2563
|
+
} else {
|
|
2564
|
+
clientsToRemove.push(client2);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
for (const client2 of clientsToRemove) {
|
|
2568
|
+
state.connectedClients.delete(client2);
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
718
2571
|
|
|
719
|
-
// src/
|
|
720
|
-
var
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
2572
|
+
// src/dev/rebuildTrigger.ts
|
|
2573
|
+
var parseErrorLocationFromMessage = (msg) => {
|
|
2574
|
+
const pathLineCol = msg.match(/^([^\s:]+):(\d+)(?::(\d+))?/);
|
|
2575
|
+
if (pathLineCol) {
|
|
2576
|
+
const [, file4, lineStr, colStr] = pathLineCol;
|
|
2577
|
+
return {
|
|
2578
|
+
file: file4,
|
|
2579
|
+
line: lineStr ? parseInt(lineStr, 10) : undefined,
|
|
2580
|
+
column: colStr ? parseInt(colStr, 10) : undefined
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
const atMatch = msg.match(/(?:at|in)\s+([^(:\s]+)(?:\s*\([^)]*line\s*(\d+)[^)]*col(?:umn)?\s*(\d+)[^)]*\)|:(\d+):(\d+)?)/i);
|
|
2584
|
+
if (atMatch) {
|
|
2585
|
+
const [, file4, line1, col1, line2, col2] = atMatch;
|
|
2586
|
+
return {
|
|
2587
|
+
file: file4?.trim(),
|
|
2588
|
+
line: line1 ? parseInt(line1, 10) : line2 ? parseInt(line2, 10) : undefined,
|
|
2589
|
+
column: col1 ? parseInt(col1, 10) : col2 ? parseInt(col2, 10) : undefined
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
const parenMatch = msg.match(/([^\s(]+)\s*\([^)]*line\s*(\d+)[^)]*col(?:umn)?\s*(\d+)/i);
|
|
2593
|
+
if (parenMatch) {
|
|
2594
|
+
const [, file4, lineStr, colStr] = parenMatch;
|
|
2595
|
+
return {
|
|
2596
|
+
file: file4 ?? undefined,
|
|
2597
|
+
line: lineStr ? parseInt(lineStr, 10) : undefined,
|
|
2598
|
+
column: colStr ? parseInt(colStr, 10) : undefined
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
return {};
|
|
2602
|
+
};
|
|
2603
|
+
var extractBuildErrorDetails = (error, affectedFrameworks, resolvedPaths) => {
|
|
2604
|
+
let logs = error?.logs;
|
|
2605
|
+
if (!logs && error instanceof AggregateError && error.errors?.length) {
|
|
2606
|
+
logs = error.errors;
|
|
2607
|
+
}
|
|
2608
|
+
if (logs && Array.isArray(logs) && logs.length > 0) {
|
|
2609
|
+
const errLog = logs.find((l) => l.level === "error") ?? logs[0];
|
|
2610
|
+
const pos = errLog?.position;
|
|
2611
|
+
const file4 = pos && "file" in pos ? pos.file : undefined;
|
|
2612
|
+
const line = pos && "line" in pos ? pos.line : undefined;
|
|
2613
|
+
const column = pos && "column" in pos ? pos.column : undefined;
|
|
2614
|
+
const lineText = pos && "lineText" in pos ? pos.lineText : undefined;
|
|
2615
|
+
const framework = file4 && resolvedPaths ? detectFramework(file4, resolvedPaths) : affectedFrameworks[0] ?? "unknown";
|
|
2616
|
+
return {
|
|
2617
|
+
file: file4,
|
|
2618
|
+
line,
|
|
2619
|
+
column,
|
|
2620
|
+
lineText,
|
|
2621
|
+
framework: framework !== "ignored" ? framework : affectedFrameworks[0]
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2625
|
+
const parsed = parseErrorLocationFromMessage(msg);
|
|
2626
|
+
let fw = affectedFrameworks[0];
|
|
2627
|
+
if (parsed.file && resolvedPaths) {
|
|
2628
|
+
const detected = detectFramework(parsed.file, resolvedPaths);
|
|
2629
|
+
fw = detected !== "ignored" ? detected : affectedFrameworks[0];
|
|
2630
|
+
}
|
|
2631
|
+
return { ...parsed, framework: fw };
|
|
2632
|
+
};
|
|
2633
|
+
var queueFileChange = (state, filePath, config, onRebuildComplete) => {
|
|
2634
|
+
const framework = detectFramework(filePath, state.resolvedPaths);
|
|
2635
|
+
if (framework === "ignored") {
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
const currentHash = computeFileHash(filePath);
|
|
2639
|
+
if (!hasFileChanged(filePath, currentHash, state.fileHashes)) {
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
if (!state.fileChangeQueue.has(framework)) {
|
|
2643
|
+
state.fileChangeQueue.set(framework, []);
|
|
2644
|
+
}
|
|
2645
|
+
const queue = state.fileChangeQueue.get(framework);
|
|
2646
|
+
if (!queue.includes(filePath)) {
|
|
2647
|
+
queue.push(filePath);
|
|
2648
|
+
}
|
|
2649
|
+
if (state.isRebuilding) {
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
if (state.rebuildTimeout) {
|
|
2653
|
+
clearTimeout(state.rebuildTimeout);
|
|
2654
|
+
}
|
|
2655
|
+
const DEBOUNCE_MS = config.options?.hmr?.debounceMs ?? 500;
|
|
2656
|
+
state.rebuildTimeout = setTimeout(() => {
|
|
2657
|
+
const filesToProcess = new Map;
|
|
2658
|
+
const uniqueFilesByFramework = new Map;
|
|
2659
|
+
for (const [fwKey, filePaths] of state.fileChangeQueue) {
|
|
2660
|
+
uniqueFilesByFramework.set(fwKey, new Set(filePaths));
|
|
2661
|
+
}
|
|
2662
|
+
for (const [fwKey, filePathSet] of uniqueFilesByFramework) {
|
|
2663
|
+
const validFiles = [];
|
|
2664
|
+
const processedFiles = new Set;
|
|
2665
|
+
for (const filePathInSet of filePathSet) {
|
|
2666
|
+
if (!existsSync5(filePathInSet)) {
|
|
2667
|
+
state.fileHashes.delete(filePathInSet);
|
|
2668
|
+
try {
|
|
2669
|
+
const affectedFiles = getAffectedFiles(state.dependencyGraph, filePathInSet);
|
|
2670
|
+
const deletedPathResolved = resolve17(filePathInSet);
|
|
2671
|
+
for (const affectedFile of affectedFiles) {
|
|
2672
|
+
if (affectedFile !== deletedPathResolved && !processedFiles.has(affectedFile) && existsSync5(affectedFile)) {
|
|
2673
|
+
validFiles.push(affectedFile);
|
|
2674
|
+
processedFiles.add(affectedFile);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
} catch {}
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
const fileHash = computeFileHash(filePathInSet);
|
|
2681
|
+
const storedHash = state.fileHashes.get(filePathInSet);
|
|
2682
|
+
if (!storedHash || storedHash !== fileHash) {
|
|
2683
|
+
const normalizedFilePath = resolve17(filePathInSet);
|
|
2684
|
+
if (!processedFiles.has(normalizedFilePath)) {
|
|
2685
|
+
validFiles.push(normalizedFilePath);
|
|
2686
|
+
processedFiles.add(normalizedFilePath);
|
|
2687
|
+
}
|
|
2688
|
+
state.fileHashes.set(normalizedFilePath, fileHash);
|
|
2689
|
+
incrementSourceFileVersions(state, [normalizedFilePath]);
|
|
2690
|
+
try {
|
|
2691
|
+
const dependents = state.dependencyGraph.dependents.get(normalizedFilePath);
|
|
2692
|
+
if (dependents && dependents.size > 0) {
|
|
2693
|
+
const dependentFiles = Array.from(dependents).filter((f) => existsSync5(f));
|
|
2694
|
+
if (dependentFiles.length > 0) {
|
|
2695
|
+
incrementSourceFileVersions(state, dependentFiles);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
} catch {}
|
|
2699
|
+
try {
|
|
2700
|
+
const affectedFiles = getAffectedFiles(state.dependencyGraph, normalizedFilePath);
|
|
2701
|
+
for (const affectedFile of affectedFiles) {
|
|
2702
|
+
if (!processedFiles.has(affectedFile) && affectedFile !== normalizedFilePath && existsSync5(affectedFile)) {
|
|
2703
|
+
validFiles.push(affectedFile);
|
|
2704
|
+
processedFiles.add(affectedFile);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
} catch {
|
|
2708
|
+
if (!processedFiles.has(normalizedFilePath)) {
|
|
2709
|
+
validFiles.push(normalizedFilePath);
|
|
2710
|
+
processedFiles.add(normalizedFilePath);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
if (validFiles.length > 0) {
|
|
2716
|
+
const firstFile = validFiles[0];
|
|
2717
|
+
if (firstFile) {
|
|
2718
|
+
const detectedFramework = detectFramework(firstFile, state.resolvedPaths);
|
|
2719
|
+
filesToProcess.set(detectedFramework, validFiles);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
state.fileChangeQueue.clear();
|
|
2724
|
+
if (filesToProcess.size === 0) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
const affectedFrameworks = Array.from(filesToProcess.keys());
|
|
2728
|
+
for (const frameworkKey of affectedFrameworks) {
|
|
2729
|
+
state.rebuildQueue.add(frameworkKey);
|
|
2730
|
+
}
|
|
2731
|
+
const filesToRebuild = [];
|
|
2732
|
+
for (const [, filePaths] of filesToProcess) {
|
|
2733
|
+
filesToRebuild.push(...filePaths);
|
|
2734
|
+
}
|
|
2735
|
+
triggerRebuild(state, config, onRebuildComplete, filesToRebuild);
|
|
2736
|
+
}, DEBOUNCE_MS);
|
|
2737
|
+
};
|
|
2738
|
+
var triggerRebuild = async (state, config, onRebuildComplete, filesToRebuild) => {
|
|
2739
|
+
if (state.isRebuilding) {
|
|
2740
|
+
return null;
|
|
2741
|
+
}
|
|
2742
|
+
state.isRebuilding = true;
|
|
2743
|
+
const affectedFrameworks = Array.from(state.rebuildQueue);
|
|
2744
|
+
state.rebuildQueue.clear();
|
|
2745
|
+
const startTime = Date.now();
|
|
2746
|
+
broadcastToClients(state, {
|
|
2747
|
+
data: { affectedFrameworks },
|
|
2748
|
+
message: "Rebuild started...",
|
|
2749
|
+
type: "rebuild-start"
|
|
2750
|
+
});
|
|
729
2751
|
try {
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
2752
|
+
const manifest = await build2({
|
|
2753
|
+
...config,
|
|
2754
|
+
incrementalFiles: filesToRebuild && filesToRebuild.length > 0 ? filesToRebuild : undefined,
|
|
2755
|
+
options: {
|
|
2756
|
+
...config.options,
|
|
2757
|
+
preserveIntermediateFiles: true,
|
|
2758
|
+
throwOnError: true
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
if (!manifest) {
|
|
2762
|
+
throw new Error("Build failed - no manifest generated");
|
|
2763
|
+
}
|
|
2764
|
+
const duration = Date.now() - startTime;
|
|
2765
|
+
logger.rebuilt(duration);
|
|
2766
|
+
broadcastToClients(state, {
|
|
2767
|
+
data: {
|
|
2768
|
+
affectedFrameworks,
|
|
2769
|
+
manifest
|
|
740
2770
|
},
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
2771
|
+
message: "Rebuild completed successfully",
|
|
2772
|
+
type: "rebuild-complete"
|
|
2773
|
+
});
|
|
2774
|
+
if (filesToRebuild && filesToRebuild.length > 0) {
|
|
2775
|
+
const allModuleUpdates = [];
|
|
2776
|
+
for (const framework of affectedFrameworks) {
|
|
2777
|
+
const frameworkFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === framework);
|
|
2778
|
+
if (frameworkFiles.length > 0) {
|
|
2779
|
+
const moduleUpdates = createModuleUpdates(frameworkFiles, framework, manifest, state.resolvedPaths);
|
|
2780
|
+
if (moduleUpdates.length > 0) {
|
|
2781
|
+
allModuleUpdates.push(...moduleUpdates);
|
|
2782
|
+
}
|
|
745
2783
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
2784
|
+
}
|
|
2785
|
+
if (affectedFrameworks.includes("react") && filesToRebuild && state.resolvedPaths.reactDir) {
|
|
2786
|
+
const reactFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "react");
|
|
2787
|
+
if (reactFiles.length > 0) {
|
|
2788
|
+
const reactPageFiles = reactFiles.filter((file4) => {
|
|
2789
|
+
const normalized = file4.replace(/\\/g, "/");
|
|
2790
|
+
return normalized.includes("/pages/");
|
|
2791
|
+
});
|
|
2792
|
+
const sourceFiles = reactPageFiles.length > 0 ? reactPageFiles : reactFiles;
|
|
2793
|
+
const primarySource = sourceFiles[0];
|
|
2794
|
+
try {
|
|
2795
|
+
const hasComponentChanges = reactFiles.some((file4) => file4.endsWith(".tsx") || file4.endsWith(".ts") || file4.endsWith(".jsx"));
|
|
2796
|
+
const hasCSSChanges = reactFiles.some((file4) => file4.endsWith(".css"));
|
|
2797
|
+
if (hasCSSChanges && !hasComponentChanges) {
|
|
2798
|
+
logger.cssUpdate(primarySource ?? reactFiles[0] ?? "", "react");
|
|
2799
|
+
} else {
|
|
2800
|
+
logger.hmrUpdate(primarySource ?? reactFiles[0] ?? "", "react");
|
|
2801
|
+
}
|
|
2802
|
+
broadcastToClients(state, {
|
|
2803
|
+
data: {
|
|
2804
|
+
framework: "react",
|
|
2805
|
+
manifest,
|
|
2806
|
+
sourceFiles,
|
|
2807
|
+
primarySource,
|
|
2808
|
+
hasComponentChanges,
|
|
2809
|
+
hasCSSChanges
|
|
2810
|
+
},
|
|
2811
|
+
type: "react-update"
|
|
2812
|
+
});
|
|
2813
|
+
} catch {}
|
|
749
2814
|
}
|
|
750
|
-
const end = Math.min(offset + progressiveChunkSize, full.length);
|
|
751
|
-
controller.enqueue(full.subarray(offset, end));
|
|
752
|
-
offset = end;
|
|
753
2815
|
}
|
|
754
|
-
|
|
2816
|
+
if (affectedFrameworks.includes("html") && filesToRebuild && state.resolvedPaths.htmlDir) {
|
|
2817
|
+
const htmlFrameworkFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "html");
|
|
2818
|
+
if (htmlFrameworkFiles.length > 0) {
|
|
2819
|
+
const scriptFiles = htmlFrameworkFiles.filter((f) => (f.endsWith(".ts") || f.endsWith(".js") || f.endsWith(".tsx") || f.endsWith(".jsx")) && f.replace(/\\/g, "/").includes("/scripts/"));
|
|
2820
|
+
const htmlPageFiles = htmlFrameworkFiles.filter((f) => f.endsWith(".html"));
|
|
2821
|
+
if (scriptFiles.length > 0 && htmlPageFiles.length === 0) {
|
|
2822
|
+
for (const scriptFile of scriptFiles) {
|
|
2823
|
+
const { basename: basename9 } = await import("path");
|
|
2824
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
2825
|
+
const scriptBaseName = basename9(scriptFile).replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
2826
|
+
const pascalName = toPascal2(scriptBaseName);
|
|
2827
|
+
const manifestKey = pascalName;
|
|
2828
|
+
const scriptPath = manifest[manifestKey] || null;
|
|
2829
|
+
if (scriptPath) {
|
|
2830
|
+
logger.scriptUpdate(scriptFile, "html");
|
|
2831
|
+
broadcastToClients(state, {
|
|
2832
|
+
data: {
|
|
2833
|
+
framework: "html",
|
|
2834
|
+
scriptPath,
|
|
2835
|
+
sourceFile: scriptFile,
|
|
2836
|
+
manifest
|
|
2837
|
+
},
|
|
2838
|
+
type: "script-update"
|
|
2839
|
+
});
|
|
2840
|
+
} else {
|
|
2841
|
+
logger.warn(`Script not found in manifest: ${manifestKey}`);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (affectedFrameworks.includes("html") && filesToRebuild && state.resolvedPaths.htmlDir) {
|
|
2848
|
+
const htmlFrameworkFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "html");
|
|
2849
|
+
if (htmlFrameworkFiles.length > 0) {
|
|
2850
|
+
const htmlPageFiles = htmlFrameworkFiles.filter((f) => f.endsWith(".html"));
|
|
2851
|
+
const pagesToUpdate = htmlPageFiles;
|
|
2852
|
+
const isSingle = !config.reactDirectory && !config.svelteDirectory && !config.vueDirectory && !config.htmxDirectory;
|
|
2853
|
+
const outputHtmlPages = isSingle ? resolve17(state.resolvedPaths.buildDir, "pages") : resolve17(state.resolvedPaths.buildDir, basename8(config.htmlDirectory ?? "html"), "pages");
|
|
2854
|
+
for (const pageFile of pagesToUpdate) {
|
|
2855
|
+
const htmlPageName = basename8(pageFile);
|
|
2856
|
+
const builtHtmlPagePath = resolve17(outputHtmlPages, htmlPageName);
|
|
2857
|
+
try {
|
|
2858
|
+
const { handleHTMLUpdate: handleHTMLUpdate2 } = await Promise.resolve().then(() => (init_simpleHTMLHMR(), exports_simpleHTMLHMR));
|
|
2859
|
+
const newHTML = await handleHTMLUpdate2(builtHtmlPagePath);
|
|
2860
|
+
if (newHTML) {
|
|
2861
|
+
logger.hmrUpdate(pageFile, "html");
|
|
2862
|
+
broadcastToClients(state, {
|
|
2863
|
+
data: {
|
|
2864
|
+
framework: "html",
|
|
2865
|
+
html: newHTML,
|
|
2866
|
+
sourceFile: builtHtmlPagePath
|
|
2867
|
+
},
|
|
2868
|
+
type: "html-update"
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
} catch {}
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
if (affectedFrameworks.includes("vue") && filesToRebuild && config.vueDirectory) {
|
|
2876
|
+
const vueFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "vue");
|
|
2877
|
+
if (vueFiles.length > 0) {
|
|
2878
|
+
const vueComponentFiles = vueFiles.filter((f) => f.endsWith(".vue"));
|
|
2879
|
+
const vueCssFiles = vueFiles.filter((f) => f.endsWith(".css"));
|
|
2880
|
+
const isCssOnlyChange = vueComponentFiles.length === 0 && vueCssFiles.length > 0;
|
|
2881
|
+
const vuePageFiles = vueFiles.filter((f) => f.replace(/\\/g, "/").includes("/pages/"));
|
|
2882
|
+
const pagesToUpdate = vuePageFiles.length > 0 ? vuePageFiles : vueComponentFiles;
|
|
2883
|
+
if (isCssOnlyChange && vueCssFiles.length > 0) {
|
|
2884
|
+
const { basename: basename9 } = await import("path");
|
|
2885
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
2886
|
+
const cssFile = vueCssFiles[0];
|
|
2887
|
+
if (cssFile) {
|
|
2888
|
+
const cssBaseName = basename9(cssFile, ".css");
|
|
2889
|
+
const cssPascalName = toPascal2(cssBaseName);
|
|
2890
|
+
const cssKey = `${cssPascalName}CSS`;
|
|
2891
|
+
const cssUrl = manifest[cssKey] || null;
|
|
2892
|
+
logger.cssUpdate(cssFile, "vue");
|
|
2893
|
+
broadcastToClients(state, {
|
|
2894
|
+
data: {
|
|
2895
|
+
framework: "vue",
|
|
2896
|
+
updateType: "css-only",
|
|
2897
|
+
cssUrl,
|
|
2898
|
+
cssBaseName,
|
|
2899
|
+
manifest,
|
|
2900
|
+
sourceFile: cssFile
|
|
2901
|
+
},
|
|
2902
|
+
type: "vue-update"
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
for (const vuePagePath of pagesToUpdate) {
|
|
2907
|
+
try {
|
|
2908
|
+
const { basename: basename9, relative: relative5 } = await import("path");
|
|
2909
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
2910
|
+
const fileName = basename9(vuePagePath);
|
|
2911
|
+
const baseName = fileName.replace(/\.vue$/, "");
|
|
2912
|
+
const pascalName = toPascal2(baseName);
|
|
2913
|
+
const vueRoot = config.vueDirectory;
|
|
2914
|
+
const hmrId = vueRoot ? relative5(vueRoot, vuePagePath).replace(/\\/g, "/").replace(/\.vue$/, "") : baseName;
|
|
2915
|
+
const cssKey = `${pascalName}CSS`;
|
|
2916
|
+
const cssUrl = manifest[cssKey] || null;
|
|
2917
|
+
const { vueHmrMetadata: vueHmrMetadata2 } = await Promise.resolve().then(() => (init_compileVue(), exports_compileVue));
|
|
2918
|
+
const hmrMeta = vueHmrMetadata2.get(resolve17(vuePagePath));
|
|
2919
|
+
const changeType = hmrMeta?.changeType ?? "full";
|
|
2920
|
+
if (changeType === "style-only") {
|
|
2921
|
+
logger.cssUpdate(vuePagePath, "vue");
|
|
2922
|
+
broadcastToClients(state, {
|
|
2923
|
+
data: {
|
|
2924
|
+
framework: "vue",
|
|
2925
|
+
updateType: "css-only",
|
|
2926
|
+
changeType: "style-only",
|
|
2927
|
+
cssUrl,
|
|
2928
|
+
cssBaseName: baseName,
|
|
2929
|
+
hmrId,
|
|
2930
|
+
manifest,
|
|
2931
|
+
sourceFile: vuePagePath
|
|
2932
|
+
},
|
|
2933
|
+
type: "vue-update"
|
|
2934
|
+
});
|
|
2935
|
+
continue;
|
|
2936
|
+
}
|
|
2937
|
+
const { handleVueUpdate: handleVueUpdate2 } = await Promise.resolve().then(() => (init_simpleVueHMR(), exports_simpleVueHMR));
|
|
2938
|
+
const newHTML = await handleVueUpdate2(vuePagePath, manifest, state.resolvedPaths.buildDir);
|
|
2939
|
+
const componentPath = manifest[`${pascalName}Client`] || null;
|
|
2940
|
+
logger.hmrUpdate(vuePagePath, "vue");
|
|
2941
|
+
broadcastToClients(state, {
|
|
2942
|
+
data: {
|
|
2943
|
+
framework: "vue",
|
|
2944
|
+
html: newHTML,
|
|
2945
|
+
hmrId,
|
|
2946
|
+
changeType,
|
|
2947
|
+
componentPath,
|
|
2948
|
+
cssUrl,
|
|
2949
|
+
updateType: "full",
|
|
2950
|
+
manifest,
|
|
2951
|
+
sourceFile: vuePagePath
|
|
2952
|
+
},
|
|
2953
|
+
type: "vue-update"
|
|
2954
|
+
});
|
|
2955
|
+
} catch {}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
if (affectedFrameworks.includes("svelte") && filesToRebuild && config.svelteDirectory) {
|
|
2960
|
+
const svelteFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "svelte");
|
|
2961
|
+
if (svelteFiles.length > 0) {
|
|
2962
|
+
const svelteComponentFiles = svelteFiles.filter((f) => f.endsWith(".svelte"));
|
|
2963
|
+
const svelteCssFiles = svelteFiles.filter((f) => f.endsWith(".css"));
|
|
2964
|
+
const isCssOnlyChange = svelteComponentFiles.length === 0 && svelteCssFiles.length > 0;
|
|
2965
|
+
const sveltePageFiles = svelteFiles.filter((f) => f.replace(/\\/g, "/").includes("/pages/"));
|
|
2966
|
+
const pagesToUpdate = sveltePageFiles.length > 0 ? sveltePageFiles : svelteComponentFiles;
|
|
2967
|
+
if (isCssOnlyChange && svelteCssFiles.length > 0) {
|
|
2968
|
+
const { basename: basename9 } = await import("path");
|
|
2969
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
2970
|
+
const cssFile = svelteCssFiles[0];
|
|
2971
|
+
if (cssFile) {
|
|
2972
|
+
const cssBaseName = basename9(cssFile, ".css");
|
|
2973
|
+
const cssPascalName = toPascal2(cssBaseName);
|
|
2974
|
+
const cssKey = `${cssPascalName}CSS`;
|
|
2975
|
+
const cssUrl = manifest[cssKey] || null;
|
|
2976
|
+
logger.cssUpdate(cssFile, "svelte");
|
|
2977
|
+
broadcastToClients(state, {
|
|
2978
|
+
data: {
|
|
2979
|
+
framework: "svelte",
|
|
2980
|
+
updateType: "css-only",
|
|
2981
|
+
cssUrl,
|
|
2982
|
+
cssBaseName,
|
|
2983
|
+
manifest,
|
|
2984
|
+
sourceFile: cssFile
|
|
2985
|
+
},
|
|
2986
|
+
type: "svelte-update"
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
for (const sveltePagePath of pagesToUpdate) {
|
|
2991
|
+
try {
|
|
2992
|
+
const { handleSvelteUpdate: handleSvelteUpdate2 } = await Promise.resolve().then(() => (init_simpleSvelteHMR(), exports_simpleSvelteHMR));
|
|
2993
|
+
const newHTML = await handleSvelteUpdate2(sveltePagePath, manifest, state.resolvedPaths.buildDir);
|
|
2994
|
+
const { basename: basename9 } = await import("path");
|
|
2995
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
2996
|
+
const fileName = basename9(sveltePagePath);
|
|
2997
|
+
const baseName = fileName.replace(/\.svelte$/, "");
|
|
2998
|
+
const pascalName = toPascal2(baseName);
|
|
2999
|
+
const cssKey = `${pascalName}CSS`;
|
|
3000
|
+
const cssUrl = manifest[cssKey] || null;
|
|
3001
|
+
logger.hmrUpdate(sveltePagePath, "svelte");
|
|
3002
|
+
broadcastToClients(state, {
|
|
3003
|
+
data: {
|
|
3004
|
+
framework: "svelte",
|
|
3005
|
+
html: newHTML,
|
|
3006
|
+
cssUrl,
|
|
3007
|
+
cssBaseName: baseName,
|
|
3008
|
+
updateType: "full",
|
|
3009
|
+
manifest,
|
|
3010
|
+
sourceFile: sveltePagePath
|
|
3011
|
+
},
|
|
3012
|
+
type: "svelte-update"
|
|
3013
|
+
});
|
|
3014
|
+
} catch {}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
if (affectedFrameworks.includes("htmx") && filesToRebuild && state.resolvedPaths.htmxDir) {
|
|
3019
|
+
const htmxFrameworkFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "htmx");
|
|
3020
|
+
if (htmxFrameworkFiles.length > 0) {
|
|
3021
|
+
const htmxScriptFiles = htmxFrameworkFiles.filter((f) => (f.endsWith(".ts") || f.endsWith(".js") || f.endsWith(".tsx") || f.endsWith(".jsx")) && f.replace(/\\/g, "/").includes("/scripts/"));
|
|
3022
|
+
const htmxHtmlFiles = htmxFrameworkFiles.filter((f) => f.endsWith(".html"));
|
|
3023
|
+
if (htmxScriptFiles.length > 0 && htmxHtmlFiles.length === 0) {
|
|
3024
|
+
for (const scriptFile of htmxScriptFiles) {
|
|
3025
|
+
const { basename: basename9 } = await import("path");
|
|
3026
|
+
const { toPascal: toPascal2 } = await Promise.resolve().then(() => exports_stringModifiers);
|
|
3027
|
+
const scriptBaseName = basename9(scriptFile).replace(/\.(ts|js|tsx|jsx)$/, "");
|
|
3028
|
+
const pascalName = toPascal2(scriptBaseName);
|
|
3029
|
+
const manifestKey = pascalName;
|
|
3030
|
+
const scriptPath = manifest[manifestKey] || null;
|
|
3031
|
+
if (scriptPath) {
|
|
3032
|
+
logger.scriptUpdate(scriptFile, "htmx");
|
|
3033
|
+
broadcastToClients(state, {
|
|
3034
|
+
data: {
|
|
3035
|
+
framework: "htmx",
|
|
3036
|
+
scriptPath,
|
|
3037
|
+
sourceFile: scriptFile,
|
|
3038
|
+
manifest
|
|
3039
|
+
},
|
|
3040
|
+
type: "script-update"
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
if (affectedFrameworks.includes("htmx") && filesToRebuild && state.resolvedPaths.htmxDir) {
|
|
3048
|
+
const htmxFrameworkFiles = filesToRebuild.filter((file4) => detectFramework(file4, state.resolvedPaths) === "htmx");
|
|
3049
|
+
if (htmxFrameworkFiles.length > 0) {
|
|
3050
|
+
const htmxPageFiles = htmxFrameworkFiles.filter((f) => f.endsWith(".html"));
|
|
3051
|
+
const isSingle = !config.reactDirectory && !config.svelteDirectory && !config.vueDirectory && !config.htmlDirectory;
|
|
3052
|
+
const outputHtmxPages = isSingle ? resolve17(state.resolvedPaths.buildDir, "pages") : resolve17(state.resolvedPaths.buildDir, basename8(config.htmxDirectory ?? "htmx"), "pages");
|
|
3053
|
+
for (const htmxPageFile of htmxPageFiles) {
|
|
3054
|
+
const htmxPageName = basename8(htmxPageFile);
|
|
3055
|
+
const builtHtmxPagePath = resolve17(outputHtmxPages, htmxPageName);
|
|
3056
|
+
try {
|
|
3057
|
+
const { handleHTMXUpdate: handleHTMXUpdate2 } = await Promise.resolve().then(() => (init_simpleHTMXHMR(), exports_simpleHTMXHMR));
|
|
3058
|
+
const newHTML = await handleHTMXUpdate2(builtHtmxPagePath);
|
|
3059
|
+
if (newHTML) {
|
|
3060
|
+
logger.hmrUpdate(htmxPageFile, "htmx");
|
|
3061
|
+
broadcastToClients(state, {
|
|
3062
|
+
data: {
|
|
3063
|
+
framework: "htmx",
|
|
3064
|
+
html: newHTML,
|
|
3065
|
+
sourceFile: builtHtmxPagePath
|
|
3066
|
+
},
|
|
3067
|
+
type: "htmx-update"
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3070
|
+
} catch {}
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
const updatedModulePaths = [];
|
|
3075
|
+
for (const update of allModuleUpdates) {
|
|
3076
|
+
updatedModulePaths.push(update.sourceFile);
|
|
3077
|
+
for (const modulePath of Object.values(update.modulePaths)) {
|
|
3078
|
+
updatedModulePaths.push(modulePath);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
if (updatedModulePaths.length > 0) {
|
|
3082
|
+
incrementModuleVersions(state.moduleVersions, updatedModulePaths);
|
|
3083
|
+
}
|
|
3084
|
+
if (allModuleUpdates.length > 0) {
|
|
3085
|
+
const updatesByFramework = groupModuleUpdatesByFramework(allModuleUpdates);
|
|
3086
|
+
const serverVersions = serializeModuleVersions(state.moduleVersions);
|
|
3087
|
+
for (const [framework, updates] of updatesByFramework) {
|
|
3088
|
+
const moduleVersions = {};
|
|
3089
|
+
for (const update of updates) {
|
|
3090
|
+
const sourceVersion = state.moduleVersions.get(update.sourceFile);
|
|
3091
|
+
if (sourceVersion !== undefined) {
|
|
3092
|
+
moduleVersions[update.sourceFile] = sourceVersion;
|
|
3093
|
+
}
|
|
3094
|
+
for (const [, path] of Object.entries(update.modulePaths)) {
|
|
3095
|
+
const pathVersion = state.moduleVersions.get(path);
|
|
3096
|
+
if (pathVersion !== undefined) {
|
|
3097
|
+
moduleVersions[path] = pathVersion;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
broadcastToClients(state, {
|
|
3102
|
+
data: {
|
|
3103
|
+
framework,
|
|
3104
|
+
manifest,
|
|
3105
|
+
modules: updates.map((update) => ({
|
|
3106
|
+
componentType: update.componentType,
|
|
3107
|
+
moduleKeys: update.moduleKeys,
|
|
3108
|
+
modulePaths: update.modulePaths,
|
|
3109
|
+
sourceFile: update.sourceFile,
|
|
3110
|
+
version: state.moduleVersions.get(update.sourceFile)
|
|
3111
|
+
})),
|
|
3112
|
+
moduleVersions,
|
|
3113
|
+
serverVersions
|
|
3114
|
+
},
|
|
3115
|
+
message: `${framework} modules updated`,
|
|
3116
|
+
type: "module-update"
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
for (const framework of affectedFrameworks) {
|
|
3122
|
+
broadcastToClients(state, {
|
|
3123
|
+
data: {
|
|
3124
|
+
framework,
|
|
3125
|
+
manifest
|
|
3126
|
+
},
|
|
3127
|
+
message: `${framework} framework updated`,
|
|
3128
|
+
type: "framework-update"
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
onRebuildComplete({ manifest, hmrState: state });
|
|
3132
|
+
await populateAssetStore(state.assetStore, manifest, state.resolvedPaths.buildDir);
|
|
3133
|
+
await cleanStaleAssets(state.assetStore, manifest, state.resolvedPaths.buildDir);
|
|
3134
|
+
return manifest;
|
|
755
3135
|
} catch (error) {
|
|
756
|
-
|
|
757
|
-
|
|
3136
|
+
const errorData = extractBuildErrorDetails(error, affectedFrameworks, state.resolvedPaths);
|
|
3137
|
+
broadcastToClients(state, {
|
|
3138
|
+
data: {
|
|
3139
|
+
affectedFrameworks,
|
|
3140
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3141
|
+
...errorData
|
|
3142
|
+
},
|
|
3143
|
+
message: "Rebuild failed",
|
|
3144
|
+
type: "rebuild-error"
|
|
3145
|
+
});
|
|
3146
|
+
return null;
|
|
3147
|
+
} finally {
|
|
3148
|
+
state.isRebuilding = false;
|
|
3149
|
+
if (state.fileChangeQueue.size > 0) {
|
|
3150
|
+
const pending = Array.from(state.fileChangeQueue.keys());
|
|
3151
|
+
const queuedFiles = [];
|
|
3152
|
+
for (const [, filePaths] of state.fileChangeQueue) {
|
|
3153
|
+
queuedFiles.push(...filePaths);
|
|
3154
|
+
}
|
|
3155
|
+
state.fileChangeQueue.clear();
|
|
3156
|
+
for (const f of pending)
|
|
3157
|
+
state.rebuildQueue.add(f);
|
|
3158
|
+
if (state.rebuildTimeout)
|
|
3159
|
+
clearTimeout(state.rebuildTimeout);
|
|
3160
|
+
state.rebuildTimeout = setTimeout(() => {
|
|
3161
|
+
triggerRebuild(state, config, onRebuildComplete, queuedFiles.length > 0 ? queuedFiles : undefined);
|
|
3162
|
+
}, 50);
|
|
3163
|
+
}
|
|
758
3164
|
}
|
|
759
3165
|
};
|
|
760
3166
|
|
|
761
|
-
// src/core/
|
|
762
|
-
var
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const { default: ImportedPageComponent } = await import(pagePath);
|
|
786
|
-
const app = createSSRApp({
|
|
787
|
-
render: () => h(ImportedPageComponent, maybeProps ?? {})
|
|
788
|
-
});
|
|
789
|
-
const bodyStream = renderVueToWebStream(app);
|
|
790
|
-
const head = `<!DOCTYPE html><html>${headTag}<body><div id="root">`;
|
|
791
|
-
const tail = `</div><script>window.__INITIAL_PROPS__=${JSON.stringify(maybeProps ?? {})}</script><script type="module" src="${indexPath}"></script></body></html>`;
|
|
792
|
-
const stream = new ReadableStream({
|
|
793
|
-
start(controller) {
|
|
794
|
-
controller.enqueue(head);
|
|
795
|
-
const reader = bodyStream.getReader();
|
|
796
|
-
const pumpLoop = () => {
|
|
797
|
-
reader.read().then(({ done, value }) => done ? (controller.enqueue(tail), controller.close()) : (controller.enqueue(value), pumpLoop())).catch((err) => controller.error(err));
|
|
798
|
-
};
|
|
799
|
-
pumpLoop();
|
|
3167
|
+
// src/core/devBuild.ts
|
|
3168
|
+
var devBuild = async (config) => {
|
|
3169
|
+
const cached = globalThis.__hmrDevResult;
|
|
3170
|
+
if (cached) {
|
|
3171
|
+
const serverMtime = statSync(resolve18(Bun.main)).mtimeMs;
|
|
3172
|
+
const lastMtime = globalThis.__hmrServerMtime;
|
|
3173
|
+
globalThis.__hmrServerMtime = serverMtime;
|
|
3174
|
+
if (serverMtime !== lastMtime) {
|
|
3175
|
+
console.log("\x1B[36m[hmr] Server module reloaded\x1B[0m");
|
|
3176
|
+
} else {
|
|
3177
|
+
globalThis.__hmrSkipServerRestart = true;
|
|
3178
|
+
console.log("\x1B[36m[hmr] Hot module update detected\x1B[0m");
|
|
3179
|
+
}
|
|
3180
|
+
return cached;
|
|
3181
|
+
}
|
|
3182
|
+
const state = createHMRState(config);
|
|
3183
|
+
const watchPaths = getWatchPaths(config, state.resolvedPaths);
|
|
3184
|
+
buildInitialDependencyGraph(state.dependencyGraph, watchPaths);
|
|
3185
|
+
console.log("\uD83D\uDD28 Building AbsoluteJS with HMR...");
|
|
3186
|
+
const manifest = await build2({
|
|
3187
|
+
...config,
|
|
3188
|
+
options: {
|
|
3189
|
+
...config.options,
|
|
3190
|
+
preserveIntermediateFiles: true
|
|
800
3191
|
}
|
|
801
3192
|
});
|
|
802
|
-
|
|
803
|
-
|
|
3193
|
+
if (!manifest || Object.keys(manifest).length === 0) {
|
|
3194
|
+
console.log("\u26A0\uFE0F Manifest is empty - this is OK for HTML/HTMX-only projects");
|
|
3195
|
+
}
|
|
3196
|
+
await populateAssetStore(state.assetStore, manifest ?? {}, state.resolvedPaths.buildDir);
|
|
3197
|
+
await cleanStaleAssets(state.assetStore, manifest ?? {}, state.resolvedPaths.buildDir);
|
|
3198
|
+
console.log("\u2705 Build completed successfully");
|
|
3199
|
+
startFileWatching(state, config, (filePath) => {
|
|
3200
|
+
queueFileChange(state, filePath, config, (newBuildResult) => {
|
|
3201
|
+
Object.assign(manifest, newBuildResult.manifest);
|
|
3202
|
+
});
|
|
804
3203
|
});
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
3204
|
+
console.log("\uD83D\uDC40 File watching: Active");
|
|
3205
|
+
console.log("\uD83D\uDD25 HMR: Ready");
|
|
3206
|
+
const result = {
|
|
3207
|
+
hmrState: state,
|
|
3208
|
+
manifest
|
|
3209
|
+
};
|
|
3210
|
+
globalThis.__hmrServerStartup = Date.now().toString();
|
|
3211
|
+
globalThis.__hmrDevResult = result;
|
|
3212
|
+
globalThis.__hmrServerMtime = statSync(resolve18(Bun.main)).mtimeMs;
|
|
3213
|
+
return result;
|
|
810
3214
|
};
|
|
811
3215
|
// src/core/lookup.ts
|
|
812
|
-
var
|
|
813
|
-
|
|
3216
|
+
var isWrapped = (source) => ("manifest" in source) && typeof source.manifest === "object" && !Array.isArray(source.manifest);
|
|
3217
|
+
var asset = (source, name) => {
|
|
3218
|
+
const assetPath = isWrapped(source) ? source.manifest[name] : source[name];
|
|
814
3219
|
if (assetPath === undefined) {
|
|
815
3220
|
throw new Error(`Asset "${name}" not found in manifest.`);
|
|
816
3221
|
}
|
|
817
3222
|
return assetPath;
|
|
818
3223
|
};
|
|
3224
|
+
|
|
3225
|
+
// src/core/index.ts
|
|
3226
|
+
init_pageHandlers();
|
|
3227
|
+
// src/plugins/pageRouter.ts
|
|
3228
|
+
var pageRouterPlugin = () => {
|
|
3229
|
+
console.log("Page Router Plugin Not Implemented Yet");
|
|
3230
|
+
};
|
|
3231
|
+
// src/plugins/hmr.ts
|
|
3232
|
+
var STORE_KEY = "__elysiaStore";
|
|
3233
|
+
var restoreStore = (app) => {
|
|
3234
|
+
const saved = globalThis[STORE_KEY];
|
|
3235
|
+
if (saved) {
|
|
3236
|
+
const store = app.store;
|
|
3237
|
+
for (const key of Object.keys(saved)) {
|
|
3238
|
+
store[key] = saved[key];
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
globalThis[STORE_KEY] = app.store;
|
|
3242
|
+
};
|
|
3243
|
+
var hmr = (hmrState2, manifest) => {
|
|
3244
|
+
return (app) => {
|
|
3245
|
+
restoreStore(app);
|
|
3246
|
+
return app.onBeforeHandle(({ request }) => {
|
|
3247
|
+
const rawUrl = request.url;
|
|
3248
|
+
const qIdx = rawUrl.indexOf("?");
|
|
3249
|
+
const pathEnd = qIdx === -1 ? rawUrl.length : qIdx;
|
|
3250
|
+
const pathStart = rawUrl.indexOf("/", rawUrl.indexOf("//") + 2);
|
|
3251
|
+
const pathname = rawUrl.slice(pathStart, pathEnd);
|
|
3252
|
+
const bytes = lookupAsset(hmrState2.assetStore, pathname);
|
|
3253
|
+
if (bytes) {
|
|
3254
|
+
return new Response(new Uint8Array(bytes).buffer, {
|
|
3255
|
+
headers: {
|
|
3256
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
3257
|
+
"Content-Type": getMimeType(pathname)
|
|
3258
|
+
}
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
}).ws("/hmr", {
|
|
3262
|
+
close: (ws) => handleClientDisconnect(hmrState2, ws),
|
|
3263
|
+
message: (ws, msg) => handleHMRMessage(hmrState2, ws, msg),
|
|
3264
|
+
open: (ws) => handleClientConnect(hmrState2, ws, manifest)
|
|
3265
|
+
}).get("/hmr-status", () => ({
|
|
3266
|
+
connectedClients: hmrState2.connectedClients.size,
|
|
3267
|
+
isRebuilding: hmrState2.isRebuilding,
|
|
3268
|
+
manifestKeys: Object.keys(manifest),
|
|
3269
|
+
rebuildQueue: Array.from(hmrState2.rebuildQueue),
|
|
3270
|
+
timestamp: Date.now()
|
|
3271
|
+
}));
|
|
3272
|
+
};
|
|
3273
|
+
};
|
|
819
3274
|
// src/plugins/networking.ts
|
|
3275
|
+
init_constants();
|
|
820
3276
|
import { argv } from "process";
|
|
821
3277
|
var {env: env3 } = globalThis.Bun;
|
|
822
3278
|
|
|
823
3279
|
// src/utils/networking.ts
|
|
824
3280
|
import os from "os";
|
|
825
|
-
var
|
|
3281
|
+
var getAllNetworkIPs = () => {
|
|
826
3282
|
const interfaces = os.networkInterfaces();
|
|
827
3283
|
const addresses = Object.values(interfaces).flat().filter((iface) => iface !== undefined);
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
3284
|
+
const ipv4Addresses = [];
|
|
3285
|
+
for (const addr of addresses) {
|
|
3286
|
+
if (addr.internal)
|
|
3287
|
+
continue;
|
|
3288
|
+
if (addr.family === "IPv4") {
|
|
3289
|
+
ipv4Addresses.push(addr.address);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return ipv4Addresses;
|
|
3293
|
+
};
|
|
3294
|
+
var getLocalIPAddress = () => {
|
|
3295
|
+
const allIPs = getAllNetworkIPs();
|
|
3296
|
+
if (allIPs.length > 0 && allIPs[0]) {
|
|
3297
|
+
return allIPs[0];
|
|
3298
|
+
}
|
|
831
3299
|
console.warn("No IP address found, falling back to localhost");
|
|
832
3300
|
return "localhost";
|
|
833
3301
|
};
|
|
@@ -853,28 +3321,6 @@ var networking = (app) => app.listen({
|
|
|
853
3321
|
console.log(`Server started on http://${host}:${port}`);
|
|
854
3322
|
}
|
|
855
3323
|
});
|
|
856
|
-
// src/plugins/pageRouter.ts
|
|
857
|
-
var pageRouterPlugin = () => {
|
|
858
|
-
console.log("Page Router Plugin Not Implemented Yet");
|
|
859
|
-
};
|
|
860
|
-
// src/utils/generateHeadElement.ts
|
|
861
|
-
var generateHeadElement = ({
|
|
862
|
-
cssPath,
|
|
863
|
-
title = "AbsoluteJS",
|
|
864
|
-
description = "A page created using AbsoluteJS",
|
|
865
|
-
font,
|
|
866
|
-
icon = "/assets/ico/favicon.ico"
|
|
867
|
-
} = {}) => `<head>
|
|
868
|
-
<meta charset="UTF-8">
|
|
869
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
870
|
-
<title>${title}</title>
|
|
871
|
-
<meta name="description" content="${description}">
|
|
872
|
-
<link rel="icon" href="${icon}" type="image/x-icon">
|
|
873
|
-
${font ? `<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
874
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
875
|
-
<link href="https://fonts.googleapis.com/css2?family=${font}:wght@100..900&display=swap" rel="stylesheet">` : ""}
|
|
876
|
-
${cssPath ? `<link rel="stylesheet" href="${cssPath}" type="text/css">` : ""}
|
|
877
|
-
</head>`;
|
|
878
3324
|
// src/utils/getEnv.ts
|
|
879
3325
|
var {env: env4 } = globalThis.Bun;
|
|
880
3326
|
var getEnv = (key) => {
|
|
@@ -884,21 +3330,108 @@ var getEnv = (key) => {
|
|
|
884
3330
|
}
|
|
885
3331
|
return environmentVariable;
|
|
886
3332
|
};
|
|
3333
|
+
// src/utils/registerClientScript.ts
|
|
3334
|
+
var scriptRegistry = new Map;
|
|
3335
|
+
var requestCounter = 0;
|
|
3336
|
+
var getRequestId = () => `req_${Date.now()}_${++requestCounter}`;
|
|
3337
|
+
var registerClientScript = (script, requestId) => {
|
|
3338
|
+
const id = requestId || globalThis.__absolutejs_requestId || getRequestId();
|
|
3339
|
+
if (!scriptRegistry.has(id)) {
|
|
3340
|
+
scriptRegistry.set(id, new Set);
|
|
3341
|
+
}
|
|
3342
|
+
scriptRegistry.get(id).add(script);
|
|
3343
|
+
return id;
|
|
3344
|
+
};
|
|
3345
|
+
if (typeof globalThis !== "undefined") {
|
|
3346
|
+
globalThis.registerClientScript = registerClientScript;
|
|
3347
|
+
}
|
|
3348
|
+
var getAndClearClientScripts = (requestId) => {
|
|
3349
|
+
const scripts = scriptRegistry.get(requestId);
|
|
3350
|
+
if (!scripts) {
|
|
3351
|
+
return [];
|
|
3352
|
+
}
|
|
3353
|
+
const scriptArray = Array.from(scripts);
|
|
3354
|
+
scriptRegistry.delete(requestId);
|
|
3355
|
+
return scriptArray;
|
|
3356
|
+
};
|
|
3357
|
+
var generateClientScriptCode = (scripts) => {
|
|
3358
|
+
if (scripts.length === 0) {
|
|
3359
|
+
return "";
|
|
3360
|
+
}
|
|
3361
|
+
const scriptCode = scripts.map((script, index) => {
|
|
3362
|
+
const funcString = script.toString();
|
|
3363
|
+
const bodyMatch = funcString.match(/\{([\s\S]*)\}/);
|
|
3364
|
+
if (!bodyMatch || !bodyMatch[1]) {
|
|
3365
|
+
return "";
|
|
3366
|
+
}
|
|
3367
|
+
const body = bodyMatch[1].trim();
|
|
3368
|
+
return `
|
|
3369
|
+
(function() {
|
|
3370
|
+
function executeScript_${index}() {
|
|
3371
|
+
${body}
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
// Try executing immediately if DOM is ready
|
|
3375
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
3376
|
+
executeScript_${index}();
|
|
3377
|
+
} else {
|
|
3378
|
+
document.addEventListener('DOMContentLoaded', executeScript_${index});
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
// Also try with delays to ensure element is available after hydration
|
|
3382
|
+
setTimeout(executeScript_${index}, 100);
|
|
3383
|
+
setTimeout(executeScript_${index}, 300);
|
|
3384
|
+
setTimeout(executeScript_${index}, 500);
|
|
3385
|
+
setTimeout(executeScript_${index}, 1000);
|
|
3386
|
+
|
|
3387
|
+
// Fallback on window load
|
|
3388
|
+
window.addEventListener('load', executeScript_${index});
|
|
3389
|
+
})();`;
|
|
3390
|
+
}).join(`
|
|
3391
|
+
`);
|
|
3392
|
+
return `<script>
|
|
3393
|
+
(function() {
|
|
3394
|
+
${scriptCode}
|
|
3395
|
+
})();
|
|
3396
|
+
</script>`;
|
|
3397
|
+
};
|
|
3398
|
+
var clearAllClientScripts = () => {
|
|
3399
|
+
scriptRegistry.clear();
|
|
3400
|
+
};
|
|
3401
|
+
// src/utils/getRegisterClientScript.ts
|
|
3402
|
+
var getRegisterClientScript = () => {
|
|
3403
|
+
const globalRegister = globalThis.registerClientScript;
|
|
3404
|
+
if (globalRegister && typeof globalRegister === "function") {
|
|
3405
|
+
return globalRegister;
|
|
3406
|
+
}
|
|
3407
|
+
return null;
|
|
3408
|
+
};
|
|
887
3409
|
export {
|
|
888
3410
|
updateAssetPaths,
|
|
3411
|
+
registerClientScript,
|
|
889
3412
|
pageRouterPlugin,
|
|
890
3413
|
networking,
|
|
3414
|
+
isValidHMRClientMessage,
|
|
3415
|
+
hmrState,
|
|
3416
|
+
hmr,
|
|
891
3417
|
handleVuePageRequest,
|
|
892
3418
|
handleSveltePageRequest,
|
|
893
3419
|
handleReactPageRequest,
|
|
894
3420
|
handlePageRequest,
|
|
895
3421
|
handleHTMXPageRequest,
|
|
896
3422
|
handleHTMLPageRequest,
|
|
3423
|
+
getRegisterClientScript,
|
|
897
3424
|
getLocalIPAddress,
|
|
898
3425
|
getEnv,
|
|
3426
|
+
getAndClearClientScripts,
|
|
3427
|
+
getAllNetworkIPs,
|
|
899
3428
|
generateHeadElement,
|
|
900
|
-
|
|
3429
|
+
generateClientScriptCode,
|
|
3430
|
+
devBuild,
|
|
3431
|
+
clearAllClientScripts,
|
|
3432
|
+
build2 as build,
|
|
901
3433
|
asset,
|
|
3434
|
+
WS_READY_STATE_OPEN,
|
|
902
3435
|
UNFOUND_INDEX,
|
|
903
3436
|
TWO_THIRDS,
|
|
904
3437
|
TIME_PRECISION,
|
|
@@ -913,5 +3446,5 @@ export {
|
|
|
913
3446
|
BUN_BUILD_WARNING_SUPPRESSION
|
|
914
3447
|
};
|
|
915
3448
|
|
|
916
|
-
//# debugId=
|
|
3449
|
+
//# debugId=FE1A3F856A389BB564756E2164756E21
|
|
917
3450
|
//# sourceMappingURL=index.js.map
|