@absolutejs/absolute 0.20.0-beta.122 → 0.20.0-beta.123
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/CHANGELOG.md +22 -0
- package/changelog.json +41 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +107 -42
- package/dist/build.js.map +4 -4
- package/dist/cli/{dev-pdefc9ac.js → dev-s7kct3xq.js} +6 -1
- package/dist/cli/index-hty8111z.js +410 -0
- package/dist/cli/index.js +6 -2
- package/dist/cli/patchedBun-zjtrn2s4.js +38 -0
- package/dist/dev/client/handlers/react.ts +31 -39
- package/dist/dev/client/hmrClient.ts +3 -0
- package/dist/dev/serverBootstrap.js +11 -0
- package/dist/index.js +109 -44
- package/dist/index.js.map +4 -4
- package/dist/mobile/remoteMacAgentEntry.js +1 -1
- package/dist/src/cli/patchedBun.d.ts +84 -0
- package/dist/src/dev/hotRejectionGuard.d.ts +1 -0
- package/dist/src/dev/moduleServer.d.ts +10 -3
- package/dist/src/mobile/emulatorDoctor.d.ts +1 -1
- package/dist/types/globals.d.ts +10 -0
- package/package.json +1 -1
- package/dist/cli/{mobile-00bqw12c.js → mobile-6kdsrhxy.js} +3 -3
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
scanListeners
|
|
14
14
|
} from "./index-fhaf3tdk.js";
|
|
15
|
+
import {
|
|
16
|
+
resolveDevBunExecutable
|
|
17
|
+
} from "./index-hty8111z.js";
|
|
15
18
|
import {
|
|
16
19
|
stripStringsAndComments
|
|
17
20
|
} from "./index-fc9vxw56.js";
|
|
@@ -1504,6 +1507,8 @@ var dev = async (serverEntry, configPath, options = {}) => {
|
|
|
1504
1507
|
let selectedRemoteMacProfile;
|
|
1505
1508
|
let expoDevProject;
|
|
1506
1509
|
markBoot("dev command entered");
|
|
1510
|
+
const bunExecutable = await resolveDevBunExecutable();
|
|
1511
|
+
markBoot("dev bun runtime selected");
|
|
1507
1512
|
let prescanConfig;
|
|
1508
1513
|
try {
|
|
1509
1514
|
const config = await loadConfig(configPath);
|
|
@@ -2499,7 +2504,7 @@ var dev = async (serverEntry, configPath, options = {}) => {
|
|
|
2499
2504
|
markBoot("dev child spawn requested");
|
|
2500
2505
|
const prescanForSpawn = prescan;
|
|
2501
2506
|
prescan = null;
|
|
2502
|
-
const proc = nodeSpawn(
|
|
2507
|
+
const proc = nodeSpawn(bunExecutable, [
|
|
2503
2508
|
"--hot",
|
|
2504
2509
|
"--no-clear-screen",
|
|
2505
2510
|
...heapSnapshotEnabled ? ["--preload", heapPreloadPath] : [],
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/cli/patchedBun.ts
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
4
|
+
import {
|
|
5
|
+
chmodSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
renameSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
writeFileSync
|
|
12
|
+
} from "fs";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
import { dirname, join } from "path";
|
|
15
|
+
import { createInterface } from "readline/promises";
|
|
16
|
+
import { inflateRawSync } from "zlib";
|
|
17
|
+
var PATCHED_BUN_RELEASE = {
|
|
18
|
+
bunVersion: "1.4.0",
|
|
19
|
+
issue: "https://github.com/oven-sh/bun/issues/32919",
|
|
20
|
+
repo: "absolutejs/patched-bun",
|
|
21
|
+
sha256: {
|
|
22
|
+
"bun-darwin-aarch64": "940d8c7eadb9fb4f39f06b0a086bbb6851d1bf3bc164bd3ba3a6966f8229f53c",
|
|
23
|
+
"bun-darwin-x64": "847f215cdd147431d025ffec94063467d1e4e80a5e4c311efff4570e04fa06ed",
|
|
24
|
+
"bun-freebsd-aarch64": "6d1c6c51cb782a1f9d2f4e2481aa7b856eff05be430094fa3b8fb86adf4067a3",
|
|
25
|
+
"bun-freebsd-x64": "c5395cd411bf3df75c3943e6ebb7722b5dd369df9a97c622449d36129cc6277a",
|
|
26
|
+
"bun-linux-aarch64": "fd5931bf59ff2bbaeab5fa37a1b003a3f10ada6b7fee2d3d8f15e606adad61f9",
|
|
27
|
+
"bun-linux-aarch64-android": "ab560ea72357554c93b4b0390b869ec62769c9b5f676ab196ecb3b0e328d7126",
|
|
28
|
+
"bun-linux-aarch64-musl": "b9c3f9936f4c7b6b0b749090f70806a1f73ca48d5a9762709ef9a4e9a5b0fed8",
|
|
29
|
+
"bun-linux-x64": "d36584f9aa71773fada3831ddf01a34c6089988988b900fd968586db099a0567",
|
|
30
|
+
"bun-linux-x64-android": "1a9712c77516fc224ed3307e083a12ea6e4f1a2956e920b9590bf12e4f59304e",
|
|
31
|
+
"bun-linux-x64-musl": "061a8bd6afad866f807d93125508c303f16c2dbf000387c91d22dd777c072951",
|
|
32
|
+
"bun-windows-aarch64": "91ed75e937b191eaf18bb361040acc24cd363b38f7bdaab5228fadd813f2a9ea",
|
|
33
|
+
"bun-windows-x64": "f7a7a22349dc7d4df0347da3f3058fe746763e22950bc77f4ae8021dab75a7b3"
|
|
34
|
+
},
|
|
35
|
+
tag: "bun-v1.4.0-absolute.2"
|
|
36
|
+
};
|
|
37
|
+
var HOURS_PER_DAY = 24;
|
|
38
|
+
var MS_PER_HOUR = 3600000;
|
|
39
|
+
var ASK_LATER_MS = HOURS_PER_DAY * MS_PER_HOUR;
|
|
40
|
+
var PREFERENCE_FILE = "patched-bun.json";
|
|
41
|
+
var EXECUTABLE_MODE = 493;
|
|
42
|
+
var VERSION_PARTS = 3;
|
|
43
|
+
var PROBE_TIMEOUT_MS = 30000;
|
|
44
|
+
var MUSL_LOADERS = ["/lib/ld-musl-x86_64.so.1", "/lib/ld-musl-aarch64.so.1"];
|
|
45
|
+
var DEPENDENCY_FIELDS = [
|
|
46
|
+
"dependencies",
|
|
47
|
+
"devDependencies",
|
|
48
|
+
"peerDependencies"
|
|
49
|
+
];
|
|
50
|
+
var ZIP = {
|
|
51
|
+
directoryEntry: 33639248,
|
|
52
|
+
endDirectoryOffset: 16,
|
|
53
|
+
endEntryCount: 10,
|
|
54
|
+
endOfDirectory: 101010256,
|
|
55
|
+
endRecordSize: 22,
|
|
56
|
+
entryCommentLength: 32,
|
|
57
|
+
entryCompressedSize: 20,
|
|
58
|
+
entryExtraLength: 30,
|
|
59
|
+
entryHeaderSize: 46,
|
|
60
|
+
entryLocalOffset: 42,
|
|
61
|
+
entryMethod: 10,
|
|
62
|
+
entryNameLength: 28,
|
|
63
|
+
localExtraLength: 28,
|
|
64
|
+
localHeader: 67324752,
|
|
65
|
+
localHeaderSize: 30,
|
|
66
|
+
localNameLength: 26,
|
|
67
|
+
methodDeflate: 8,
|
|
68
|
+
methodStored: 0
|
|
69
|
+
};
|
|
70
|
+
var isAsset = (name) => Object.hasOwn(PATCHED_BUN_RELEASE.sha256, name);
|
|
71
|
+
var detectLibc = () => {
|
|
72
|
+
if (process.platform === "android")
|
|
73
|
+
return "android";
|
|
74
|
+
if (MUSL_LOADERS.some((loader) => existsSync(loader)))
|
|
75
|
+
return "musl";
|
|
76
|
+
return "glibc";
|
|
77
|
+
};
|
|
78
|
+
var absoluteHome = (home) => join(home, ".absolutejs");
|
|
79
|
+
var compareVersions = (left, right) => {
|
|
80
|
+
const parse = (version) => version.split("-")[0]?.split(".").map(Number) ?? [];
|
|
81
|
+
const leftParts = parse(left);
|
|
82
|
+
const rightParts = parse(right);
|
|
83
|
+
const differences = Array.from({ length: VERSION_PARTS }, (_, index) => (leftParts[index] ?? 0) - (rightParts[index] ?? 0));
|
|
84
|
+
return differences.find((difference) => difference !== 0) ?? 0;
|
|
85
|
+
};
|
|
86
|
+
var preferencePath = (home) => join(absoluteHome(home), PREFERENCE_FILE);
|
|
87
|
+
var installedPatchedBun = (home = homedir()) => {
|
|
88
|
+
const asset = patchedBunAsset();
|
|
89
|
+
if (!asset)
|
|
90
|
+
return null;
|
|
91
|
+
const path = patchedBunPath(asset, home);
|
|
92
|
+
return existsSync(path) ? path : null;
|
|
93
|
+
};
|
|
94
|
+
var patchedBunAsset = (platform = process.platform, arch = process.arch, libc = detectLibc()) => {
|
|
95
|
+
const cpu = { arm64: "aarch64", x64: "x64" }[arch];
|
|
96
|
+
const system = {
|
|
97
|
+
android: "linux",
|
|
98
|
+
darwin: "darwin",
|
|
99
|
+
freebsd: "freebsd",
|
|
100
|
+
linux: "linux",
|
|
101
|
+
win32: "windows"
|
|
102
|
+
}[platform];
|
|
103
|
+
if (!cpu || !system)
|
|
104
|
+
return null;
|
|
105
|
+
const abi = system === "linux" && libc !== "glibc" ? `-${libc}` : "";
|
|
106
|
+
const name = `bun-${system}-${cpu}${abi}`;
|
|
107
|
+
return isAsset(name) ? name : null;
|
|
108
|
+
};
|
|
109
|
+
var patchedBunPath = (asset, home = homedir()) => join(absoluteHome(home), "bun", PATCHED_BUN_RELEASE.tag, asset, asset.startsWith("bun-windows") ? "bun.exe" : "bun");
|
|
110
|
+
var runtimeSupportsReactFastRefresh = () => {
|
|
111
|
+
const options = { loader: "tsx", reactFastRefresh: true };
|
|
112
|
+
try {
|
|
113
|
+
return new Bun.Transpiler(options).transformSync("export function Probe(){return null;}").includes("$RefreshReg$");
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var parsePreference = (parsed) => {
|
|
119
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
120
|
+
return null;
|
|
121
|
+
if (!("decision" in parsed))
|
|
122
|
+
return null;
|
|
123
|
+
if (parsed.decision === "never") {
|
|
124
|
+
const never = { decision: "never" };
|
|
125
|
+
return never;
|
|
126
|
+
}
|
|
127
|
+
if (parsed.decision !== "later" || !("askAfter" in parsed) || typeof parsed.askAfter !== "string")
|
|
128
|
+
return null;
|
|
129
|
+
const later = { askAfter: parsed.askAfter, decision: "later" };
|
|
130
|
+
return later;
|
|
131
|
+
};
|
|
132
|
+
var patchedBunOfferState = (context) => {
|
|
133
|
+
if (context.supported)
|
|
134
|
+
return "runtime-supports-refresh";
|
|
135
|
+
if (context.installed)
|
|
136
|
+
return "installed";
|
|
137
|
+
if (!context.usesReact)
|
|
138
|
+
return "not-a-react-project";
|
|
139
|
+
if (!context.asset)
|
|
140
|
+
return "unsupported-platform";
|
|
141
|
+
if (compareVersions(context.bunVersion, PATCHED_BUN_RELEASE.bunVersion) > 0)
|
|
142
|
+
return "runtime-newer";
|
|
143
|
+
if (context.preference?.decision === "never")
|
|
144
|
+
return "declined";
|
|
145
|
+
if (context.preference?.decision === "later" && new Date(context.preference.askAfter) > context.now)
|
|
146
|
+
return "asked-recently";
|
|
147
|
+
if (context.ci || !context.interactive)
|
|
148
|
+
return "non-interactive";
|
|
149
|
+
return "offer";
|
|
150
|
+
};
|
|
151
|
+
var readPatchedBunPreference = (home = homedir()) => {
|
|
152
|
+
try {
|
|
153
|
+
return parsePreference(JSON.parse(readFileSync(preferencePath(home), "utf-8")));
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var findEndOfDirectory = (view) => {
|
|
159
|
+
for (let offset = view.byteLength - ZIP.endRecordSize;offset >= 0; offset--) {
|
|
160
|
+
if (view.getUint32(offset, true) === ZIP.endOfDirectory)
|
|
161
|
+
return offset;
|
|
162
|
+
}
|
|
163
|
+
throw new Error("Not a zip archive.");
|
|
164
|
+
};
|
|
165
|
+
var inflateEntry = (data, method) => {
|
|
166
|
+
if (method === ZIP.methodStored)
|
|
167
|
+
return data;
|
|
168
|
+
if (method === ZIP.methodDeflate)
|
|
169
|
+
return new Uint8Array(inflateRawSync(data));
|
|
170
|
+
throw new Error(`Unsupported zip compression method ${method}.`);
|
|
171
|
+
};
|
|
172
|
+
var readEntryData = (archive, view, entry) => {
|
|
173
|
+
if (view.getUint32(entry.localOffset, true) !== ZIP.localHeader)
|
|
174
|
+
throw new Error("Corrupt zip entry.");
|
|
175
|
+
const dataStart = entry.localOffset + ZIP.localHeaderSize + view.getUint16(entry.localOffset + ZIP.localNameLength, true) + view.getUint16(entry.localOffset + ZIP.localExtraLength, true);
|
|
176
|
+
return inflateEntry(archive.subarray(dataStart, dataStart + entry.compressedSize), entry.method);
|
|
177
|
+
};
|
|
178
|
+
var readZipEntry = (archive, entryName) => {
|
|
179
|
+
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
180
|
+
const end = findEndOfDirectory(view);
|
|
181
|
+
const entries = view.getUint16(end + ZIP.endEntryCount, true);
|
|
182
|
+
const decoder = new TextDecoder;
|
|
183
|
+
let cursor = view.getUint32(end + ZIP.endDirectoryOffset, true);
|
|
184
|
+
for (let index = 0;index < entries; index++) {
|
|
185
|
+
if (view.getUint32(cursor, true) !== ZIP.directoryEntry)
|
|
186
|
+
throw new Error("Corrupt zip directory.");
|
|
187
|
+
const nameLength = view.getUint16(cursor + ZIP.entryNameLength, true);
|
|
188
|
+
const nameStart = cursor + ZIP.entryHeaderSize;
|
|
189
|
+
const name = decoder.decode(archive.subarray(nameStart, nameStart + nameLength));
|
|
190
|
+
const entry = {
|
|
191
|
+
compressedSize: view.getUint32(cursor + ZIP.entryCompressedSize, true),
|
|
192
|
+
localOffset: view.getUint32(cursor + ZIP.entryLocalOffset, true),
|
|
193
|
+
method: view.getUint16(cursor + ZIP.entryMethod, true)
|
|
194
|
+
};
|
|
195
|
+
if (name === entryName)
|
|
196
|
+
return readEntryData(archive, view, entry);
|
|
197
|
+
cursor += ZIP.entryHeaderSize + nameLength + view.getUint16(cursor + ZIP.entryExtraLength, true) + view.getUint16(cursor + ZIP.entryCommentLength, true);
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`${entryName} is not in the archive.`);
|
|
200
|
+
};
|
|
201
|
+
var writePatchedBunPreference = (preference, home = homedir()) => {
|
|
202
|
+
const path = preferencePath(home);
|
|
203
|
+
if (preference === null) {
|
|
204
|
+
rmSync(path, { force: true });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
208
|
+
writeFileSync(path, `${JSON.stringify(preference, null, "\t")}
|
|
209
|
+
`);
|
|
210
|
+
};
|
|
211
|
+
var sha256 = (bytes) => new Bun.CryptoHasher("sha256").update(bytes).digest("hex");
|
|
212
|
+
var projectUsesReact = (projectDir) => {
|
|
213
|
+
const hasReact = (deps) => typeof deps === "object" && deps !== null && Object.hasOwn(deps, "react");
|
|
214
|
+
try {
|
|
215
|
+
const manifest = JSON.parse(readFileSync(join(projectDir, "package.json"), "utf-8"));
|
|
216
|
+
if (typeof manifest !== "object" || manifest === null)
|
|
217
|
+
return false;
|
|
218
|
+
return DEPENDENCY_FIELDS.some((field) => hasReact(Reflect.get(manifest, field)));
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
var PATCHED_BUN_EXPLANATION = [
|
|
224
|
+
"React edits reload the page and lose component state on this Bun.",
|
|
225
|
+
`Stock Bun ignores \`reactFastRefresh\` on Bun.Transpiler (${PATCHED_BUN_RELEASE.issue}),`,
|
|
226
|
+
"so React Fast Refresh cannot keep state across edits. Until Bun ships the fix,",
|
|
227
|
+
`AbsoluteJS publishes Bun ${PATCHED_BUN_RELEASE.bunVersion} with only that fix: https://github.com/${PATCHED_BUN_RELEASE.repo}`,
|
|
228
|
+
"It installs to ~/.absolutejs/bun and only `absolute dev` uses it; your `bun` is unchanged."
|
|
229
|
+
];
|
|
230
|
+
var installPatchedBun = async (options = {}) => {
|
|
231
|
+
const asset = options.asset ?? patchedBunAsset();
|
|
232
|
+
if (!asset)
|
|
233
|
+
throw new Error(`No patched Bun build for ${process.platform}-${process.arch}.`);
|
|
234
|
+
const home = options.home ?? homedir();
|
|
235
|
+
const log = options.log ?? console.log;
|
|
236
|
+
const url = patchedBunDownloadUrl(asset);
|
|
237
|
+
log(`Downloading ${url}`);
|
|
238
|
+
const response = await (options.fetchImpl ?? fetch)(url);
|
|
239
|
+
if (!response.ok)
|
|
240
|
+
throw new Error(`Download failed: ${url} answered ${response.status}.`);
|
|
241
|
+
const archive = new Uint8Array(await response.arrayBuffer());
|
|
242
|
+
const expected = (options.checksums ?? PATCHED_BUN_RELEASE.sha256)[asset];
|
|
243
|
+
const actual = sha256(archive);
|
|
244
|
+
if (actual !== expected)
|
|
245
|
+
throw new Error(`${asset}.zip does not match its pinned SHA-256 (expected ${expected}, got ${actual}); not installing it.`);
|
|
246
|
+
const target = patchedBunPath(asset, home);
|
|
247
|
+
const binary = readZipEntry(archive, `${asset}/${target.endsWith(".exe") ? "bun.exe" : "bun"}`);
|
|
248
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
249
|
+
const staging = `${target}.${process.pid}.tmp`;
|
|
250
|
+
writeFileSync(staging, binary);
|
|
251
|
+
chmodSync(staging, EXECUTABLE_MODE);
|
|
252
|
+
renameSync(staging, target);
|
|
253
|
+
log(`Installed patched Bun ${PATCHED_BUN_RELEASE.bunVersion} at ${target}`);
|
|
254
|
+
return target;
|
|
255
|
+
};
|
|
256
|
+
var parsePatchedBunChoice = (answer) => {
|
|
257
|
+
const normalized = answer.trim().toLowerCase();
|
|
258
|
+
const choices = [
|
|
259
|
+
{
|
|
260
|
+
choice: "install",
|
|
261
|
+
matches: normalized === "" || normalized === "1" || normalized.startsWith("i")
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
choice: "later",
|
|
265
|
+
matches: normalized === "2" || normalized.startsWith("l")
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
choice: "never",
|
|
269
|
+
matches: normalized === "3" || normalized.startsWith("d") || normalized.startsWith("n")
|
|
270
|
+
}
|
|
271
|
+
];
|
|
272
|
+
return choices.find(({ matches }) => matches)?.choice ?? null;
|
|
273
|
+
};
|
|
274
|
+
var patchedBunDownloadUrl = (asset) => `https://github.com/${PATCHED_BUN_RELEASE.repo}/releases/download/${PATCHED_BUN_RELEASE.tag}/${asset}.zip`;
|
|
275
|
+
var verifyPatchedBun = (path) => {
|
|
276
|
+
const probe = spawnSync(path, [
|
|
277
|
+
"-e",
|
|
278
|
+
'console.log(Bun.version, new Bun.Transpiler({loader:"tsx",reactFastRefresh:true}).transformSync("export function P(){return null}").includes("$RefreshReg$"))'
|
|
279
|
+
], { encoding: "utf-8", timeout: PROBE_TIMEOUT_MS });
|
|
280
|
+
return probe.status === 0 && probe.stdout.trim() === `${PATCHED_BUN_RELEASE.bunVersion} true`;
|
|
281
|
+
};
|
|
282
|
+
var askUntilAnswered = async (prompt) => {
|
|
283
|
+
const choice = parsePatchedBunChoice(await prompt.question("Choose [1-3] (default 1): "));
|
|
284
|
+
return choice ?? askUntilAnswered(prompt);
|
|
285
|
+
};
|
|
286
|
+
var askForChoice = async () => {
|
|
287
|
+
const prompt = createInterface({
|
|
288
|
+
input: process.stdin,
|
|
289
|
+
output: process.stdout
|
|
290
|
+
});
|
|
291
|
+
try {
|
|
292
|
+
console.log(`
|
|
293
|
+
${PATCHED_BUN_EXPLANATION.join(`
|
|
294
|
+
`)}
|
|
295
|
+
`);
|
|
296
|
+
console.log(` 1) Install now
|
|
297
|
+
2) Ask me later
|
|
298
|
+
3) Don't ask again
|
|
299
|
+
`);
|
|
300
|
+
return await askUntilAnswered(prompt);
|
|
301
|
+
} finally {
|
|
302
|
+
prompt.close();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
var installVerified = async (home) => {
|
|
306
|
+
const path = await installPatchedBun({ home });
|
|
307
|
+
if (!verifyPatchedBun(path)) {
|
|
308
|
+
rmSync(path, { force: true });
|
|
309
|
+
throw new Error("the installed binary did not pass its check; removed it");
|
|
310
|
+
}
|
|
311
|
+
writePatchedBunPreference(null, home);
|
|
312
|
+
return path;
|
|
313
|
+
};
|
|
314
|
+
var recordDecline = (choice, home) => {
|
|
315
|
+
if (choice === "never") {
|
|
316
|
+
writePatchedBunPreference({ decision: "never" }, home);
|
|
317
|
+
console.log("OK, not asking again. `absolute bun-patch install` installs it any time; `absolute bun-patch reset` asks again.");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
writePatchedBunPreference({
|
|
321
|
+
askAfter: new Date(Date.now() + ASK_LATER_MS).toISOString(),
|
|
322
|
+
decision: "later"
|
|
323
|
+
}, home);
|
|
324
|
+
console.log("OK, asking again tomorrow. `absolute bun-patch install` installs it any time.");
|
|
325
|
+
};
|
|
326
|
+
var resolveDevBunExecutable = async (projectDir = process.cwd(), home = homedir()) => {
|
|
327
|
+
if (process.env.ABSOLUTE_PATCHED_BUN === "0")
|
|
328
|
+
return "bun";
|
|
329
|
+
const installed = installedPatchedBun(home);
|
|
330
|
+
if (installed) {
|
|
331
|
+
console.log(`Using AbsoluteJS patched Bun ${PATCHED_BUN_RELEASE.bunVersion} for React Fast Refresh (${installed}).`);
|
|
332
|
+
return installed;
|
|
333
|
+
}
|
|
334
|
+
const state = patchedBunOfferState({
|
|
335
|
+
asset: patchedBunAsset(),
|
|
336
|
+
bunVersion: Bun.version,
|
|
337
|
+
ci: Boolean(process.env.CI),
|
|
338
|
+
installed: false,
|
|
339
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
340
|
+
now: new Date,
|
|
341
|
+
preference: readPatchedBunPreference(home),
|
|
342
|
+
supported: runtimeSupportsReactFastRefresh(),
|
|
343
|
+
usesReact: projectUsesReact(projectDir)
|
|
344
|
+
});
|
|
345
|
+
if (state === "non-interactive")
|
|
346
|
+
console.log(`React edits will reload the page: stock Bun lacks the reactFastRefresh fix (${PATCHED_BUN_RELEASE.issue}). Run \`absolute bun-patch install\` to use AbsoluteJS's patched Bun.`);
|
|
347
|
+
if (state !== "offer")
|
|
348
|
+
return "bun";
|
|
349
|
+
const choice = await askForChoice();
|
|
350
|
+
if (choice !== "install") {
|
|
351
|
+
recordDecline(choice, home);
|
|
352
|
+
return "bun";
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
return await installVerified(home);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error(`Could not install the patched Bun (${error instanceof Error ? error.message : String(error)}). Continuing with your bun; React edits will reload the page.`);
|
|
358
|
+
return "bun";
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
var describePreference = (preference) => {
|
|
362
|
+
if (preference === null)
|
|
363
|
+
return "will ask";
|
|
364
|
+
if (preference.decision === "never")
|
|
365
|
+
return "won't ask again";
|
|
366
|
+
return `asking again after ${preference.askAfter}`;
|
|
367
|
+
};
|
|
368
|
+
var describeInstalled = (installed) => {
|
|
369
|
+
if (!installed)
|
|
370
|
+
return "no";
|
|
371
|
+
if (verifyPatchedBun(installed))
|
|
372
|
+
return installed;
|
|
373
|
+
return `${installed} (FAILED its check; run \`absolute bun-patch install\`)`;
|
|
374
|
+
};
|
|
375
|
+
var printStatus = (home) => {
|
|
376
|
+
const supported = runtimeSupportsReactFastRefresh();
|
|
377
|
+
console.log(`Release: ${PATCHED_BUN_RELEASE.tag} (${PATCHED_BUN_RELEASE.issue})`);
|
|
378
|
+
console.log(`Platform build: ${patchedBunAsset() ?? "none for this platform"}`);
|
|
379
|
+
console.log(`Your bun: ${Bun.version} (${supported ? "already has the fix" : "stock, no fix"})`);
|
|
380
|
+
console.log(`Installed: ${describeInstalled(installedPatchedBun(home))}`);
|
|
381
|
+
console.log(`Prompt: ${describePreference(readPatchedBunPreference(home))}`);
|
|
382
|
+
};
|
|
383
|
+
var bunPatch = async (args, home = homedir()) => {
|
|
384
|
+
const [action = "status"] = args;
|
|
385
|
+
if (action === "status") {
|
|
386
|
+
printStatus(home);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (action === "install") {
|
|
390
|
+
await installVerified(home);
|
|
391
|
+
console.log("`absolute dev` will use it from now on.");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (action === "remove") {
|
|
395
|
+
rmSync(join(absoluteHome(home), "bun"), {
|
|
396
|
+
force: true,
|
|
397
|
+
recursive: true
|
|
398
|
+
});
|
|
399
|
+
console.log("Removed the patched Bun. `absolute dev` uses your bun again.");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (action === "reset") {
|
|
403
|
+
writePatchedBunPreference(null, home);
|
|
404
|
+
console.log("`absolute dev` will offer the patched Bun again.");
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
throw new Error(`Unknown bun-patch action "${action}". Use status, install, remove or reset.`);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export { PATCHED_BUN_RELEASE, installedPatchedBun, patchedBunAsset, patchedBunPath, runtimeSupportsReactFastRefresh, patchedBunOfferState, readPatchedBunPreference, readZipEntry, writePatchedBunPreference, PATCHED_BUN_EXPLANATION, installPatchedBun, parsePatchedBunChoice, patchedBunDownloadUrl, verifyPatchedBun, resolveDevBunExecutable, bunPatch };
|
package/dist/cli/index.js
CHANGED
|
@@ -58,13 +58,16 @@ if (command === "dev") {
|
|
|
58
58
|
}
|
|
59
59
|
const positionalArgs = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile" && arg !== "--eager");
|
|
60
60
|
const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
|
|
61
|
-
const { dev } = await import("./dev-
|
|
61
|
+
const { dev } = await import("./dev-s7kct3xq.js");
|
|
62
62
|
await dev(serverEntry, configPath, {
|
|
63
63
|
androidDevice,
|
|
64
64
|
eager: args.includes("--eager"),
|
|
65
65
|
iosDevice,
|
|
66
66
|
mobile: !args.includes("--no-mobile")
|
|
67
67
|
});
|
|
68
|
+
} else if (command === "bun-patch") {
|
|
69
|
+
const { bunPatch } = await import("./patchedBun-zjtrn2s4.js");
|
|
70
|
+
await bunPatch(args);
|
|
68
71
|
} else if (command === "start") {
|
|
69
72
|
sendTelemetryEvent("cli:command", { command });
|
|
70
73
|
const outdir = parseNamedArg("--outdir");
|
|
@@ -208,7 +211,7 @@ if (command === "dev") {
|
|
|
208
211
|
sendTelemetryEvent("cli:command", {
|
|
209
212
|
command: `mobile:${workspaceCommand ?? "unknown"}`
|
|
210
213
|
});
|
|
211
|
-
const { runMobile } = await import("./mobile-
|
|
214
|
+
const { runMobile } = await import("./mobile-6kdsrhxy.js");
|
|
212
215
|
try {
|
|
213
216
|
await runMobile(args);
|
|
214
217
|
} catch (error) {
|
|
@@ -267,6 +270,7 @@ if (command === "dev") {
|
|
|
267
270
|
console.error(" routes [--json] List every route (pages + API) of a running dev server");
|
|
268
271
|
console.error(" typecheck Run type checkers for all frameworks");
|
|
269
272
|
console.error(" migrate Report what moving this project to AbsoluteJS involves");
|
|
273
|
+
console.error(" bun-patch [status|install|remove|reset] Bun with the React Fast Refresh fix (oven-sh/bun#32919)");
|
|
270
274
|
console.error(" telemetry Manage anonymous telemetry");
|
|
271
275
|
console.error(" tunnel-relay Run the public reverse-tunnel relay (for webhook dev)");
|
|
272
276
|
process.exit(1);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
PATCHED_BUN_EXPLANATION,
|
|
4
|
+
PATCHED_BUN_RELEASE,
|
|
5
|
+
bunPatch,
|
|
6
|
+
installPatchedBun,
|
|
7
|
+
installedPatchedBun,
|
|
8
|
+
parsePatchedBunChoice,
|
|
9
|
+
patchedBunAsset,
|
|
10
|
+
patchedBunDownloadUrl,
|
|
11
|
+
patchedBunOfferState,
|
|
12
|
+
patchedBunPath,
|
|
13
|
+
readPatchedBunPreference,
|
|
14
|
+
readZipEntry,
|
|
15
|
+
resolveDevBunExecutable,
|
|
16
|
+
runtimeSupportsReactFastRefresh,
|
|
17
|
+
verifyPatchedBun,
|
|
18
|
+
writePatchedBunPreference
|
|
19
|
+
} from "./index-hty8111z.js";
|
|
20
|
+
import"./index-mywsk201.js";
|
|
21
|
+
export {
|
|
22
|
+
PATCHED_BUN_EXPLANATION,
|
|
23
|
+
PATCHED_BUN_RELEASE,
|
|
24
|
+
bunPatch,
|
|
25
|
+
installPatchedBun,
|
|
26
|
+
installedPatchedBun,
|
|
27
|
+
parsePatchedBunChoice,
|
|
28
|
+
patchedBunAsset,
|
|
29
|
+
patchedBunDownloadUrl,
|
|
30
|
+
patchedBunOfferState,
|
|
31
|
+
patchedBunPath,
|
|
32
|
+
readPatchedBunPreference,
|
|
33
|
+
readZipEntry,
|
|
34
|
+
resolveDevBunExecutable,
|
|
35
|
+
runtimeSupportsReactFastRefresh,
|
|
36
|
+
verifyPatchedBun,
|
|
37
|
+
writePatchedBunPreference
|
|
38
|
+
};
|
|
@@ -7,7 +7,6 @@ import type {} from '../../../types/globals';
|
|
|
7
7
|
import { hideErrorOverlay } from '../errorOverlay';
|
|
8
8
|
import { detectCurrentFramework } from '../frameworkDetect';
|
|
9
9
|
import { sendAbsoluteHmrTiming } from '../hmrTiming';
|
|
10
|
-
import { swapCSSStylesheet } from '../cssUtils';
|
|
11
10
|
|
|
12
11
|
const reloadReactPage = () => {
|
|
13
12
|
const url = new URL(window.location.href);
|
|
@@ -21,7 +20,9 @@ export const handleReactUpdate = (message: {
|
|
|
21
20
|
hasCSSChanges?: boolean;
|
|
22
21
|
hasComponentChanges?: boolean;
|
|
23
22
|
manifest?: Record<string, string>;
|
|
23
|
+
moduleUrls?: string[];
|
|
24
24
|
pageModuleUrl?: string;
|
|
25
|
+
pageModuleUrls?: Record<string, string>;
|
|
25
26
|
primarySource?: string;
|
|
26
27
|
serverDuration?: number;
|
|
27
28
|
};
|
|
@@ -30,30 +31,26 @@ export const handleReactUpdate = (message: {
|
|
|
30
31
|
const currentFramework = detectCurrentFramework();
|
|
31
32
|
if (currentFramework !== 'react') return;
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
message.data.manifest && message.data.manifest.ReactExampleCSS;
|
|
37
|
-
if (!hasComponentChanges && hasCSSChanges && cssPath) {
|
|
38
|
-
const clientStart = performance.now();
|
|
39
|
-
void reloadReactCSS(cssPath).then((applied) => {
|
|
40
|
-
sendAbsoluteHmrTiming({
|
|
41
|
-
clientStart,
|
|
42
|
-
kind: 'css',
|
|
43
|
-
outcome: applied ? 'applied' : 'failed',
|
|
44
|
-
serverMs: message.data.serverDuration,
|
|
45
|
-
updateId: message.timestamp
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
34
|
+
// BUN-REACT-REFRESH-LEGACY: the remount/reload fallback for a dev server on
|
|
35
|
+
// stock Bun, which cannot emit React Fast Refresh registrations. Remove it,
|
|
36
|
+
// and the fastRefreshSupported message field, once the minimum Bun has the fix.
|
|
51
37
|
if (message.data.fastRefreshSupported === false) {
|
|
52
|
-
|
|
38
|
+
// The remount re-renders the whole page, so it needs this page's
|
|
39
|
+
// module; a changed child's module would be rendered as the page.
|
|
40
|
+
const componentKey = window.__REACT_COMPONENT_KEY__;
|
|
41
|
+
const pageUrls = message.data.pageModuleUrls;
|
|
42
|
+
if (pageUrls && componentKey && !pageUrls[componentKey]) {
|
|
43
|
+
// This edit does not reach the page on screen.
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const remountUrl =
|
|
47
|
+
pageUrls && componentKey
|
|
48
|
+
? pageUrls[componentKey]
|
|
49
|
+
: message.data.pageModuleUrl;
|
|
53
50
|
const remount = window.__ABS_REACT_REMOUNT__;
|
|
54
|
-
if (
|
|
51
|
+
if (remountUrl && remount) {
|
|
55
52
|
applyRemountImport(
|
|
56
|
-
|
|
53
|
+
remountUrl,
|
|
57
54
|
remount,
|
|
58
55
|
message.data.serverDuration,
|
|
59
56
|
message.timestamp
|
|
@@ -75,10 +72,15 @@ export const handleReactUpdate = (message: {
|
|
|
75
72
|
const refreshRuntime = window.$RefreshRuntime$;
|
|
76
73
|
const { serverDuration } = message.data;
|
|
77
74
|
const { pageModuleUrl } = message.data;
|
|
75
|
+
const fallbackUrls = pageModuleUrl ? [pageModuleUrl] : [];
|
|
76
|
+
const moduleUrls =
|
|
77
|
+
message.data.moduleUrls && message.data.moduleUrls.length > 0
|
|
78
|
+
? message.data.moduleUrls
|
|
79
|
+
: fallbackUrls;
|
|
78
80
|
|
|
79
|
-
if (
|
|
81
|
+
if (moduleUrls.length > 0 && refreshRuntime) {
|
|
80
82
|
applyRefreshImport(
|
|
81
|
-
|
|
83
|
+
moduleUrls,
|
|
82
84
|
refreshRuntime,
|
|
83
85
|
serverDuration,
|
|
84
86
|
message.timestamp
|
|
@@ -117,13 +119,16 @@ const finishUpdate = (
|
|
|
117
119
|
};
|
|
118
120
|
|
|
119
121
|
const applyRefreshImport = (
|
|
120
|
-
|
|
122
|
+
moduleUrls: string[],
|
|
121
123
|
refreshRuntime: { performReactRefresh: () => unknown },
|
|
122
124
|
serverDuration?: number,
|
|
123
125
|
updateId?: number
|
|
124
126
|
) => {
|
|
125
127
|
const clientStart = performance.now();
|
|
126
|
-
|
|
128
|
+
const stamp = Date.now();
|
|
129
|
+
// Every edited module has to run before the refresh, or a page that
|
|
130
|
+
// now renders a new child refreshes without it.
|
|
131
|
+
Promise.all(moduleUrls.map((url) => import(`${url}?t=${stamp}`)))
|
|
127
132
|
.then(() => {
|
|
128
133
|
refreshRuntime.performReactRefresh();
|
|
129
134
|
finishUpdate(clientStart, serverDuration, updateId);
|
|
@@ -175,16 +180,3 @@ const applyRemountImport = (
|
|
|
175
180
|
reloadReactPage();
|
|
176
181
|
});
|
|
177
182
|
};
|
|
178
|
-
|
|
179
|
-
const reloadReactCSS = (cssPath: string) =>
|
|
180
|
-
swapCSSStylesheet(cssPath, (href) => {
|
|
181
|
-
const hrefBase = (href.split('?')[0] ?? '').split('/').pop() ?? '';
|
|
182
|
-
const cssPathBase =
|
|
183
|
-
(cssPath.split('?')[0] ?? '').split('/').pop() ?? '';
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
hrefBase === cssPathBase ||
|
|
187
|
-
href.includes('react-example') ||
|
|
188
|
-
cssPathBase.includes(hrefBase)
|
|
189
|
-
);
|
|
190
|
-
});
|
|
@@ -200,6 +200,7 @@ type HMRMessage = {
|
|
|
200
200
|
affectedFrameworks?: string[];
|
|
201
201
|
column?: number;
|
|
202
202
|
error?: string;
|
|
203
|
+
// BUN-REACT-REFRESH-LEGACY: false only on stock Bun (see handlers/react.ts).
|
|
203
204
|
fastRefreshSupported?: boolean;
|
|
204
205
|
file?: string;
|
|
205
206
|
framework?: string;
|
|
@@ -209,8 +210,10 @@ type HMRMessage = {
|
|
|
209
210
|
line?: number;
|
|
210
211
|
lineText?: string;
|
|
211
212
|
manifest?: Record<string, string>;
|
|
213
|
+
moduleUrls?: string[];
|
|
212
214
|
moduleVersions?: Record<string, number>;
|
|
213
215
|
pageModuleUrl?: string;
|
|
216
|
+
pageModuleUrls?: Record<string, string>;
|
|
214
217
|
primarySource?: string;
|
|
215
218
|
scriptUrl?: string;
|
|
216
219
|
serverDuration?: number;
|
|
@@ -633,6 +633,16 @@ var markBootAt = (label, epochMs) => {
|
|
|
633
633
|
};
|
|
634
634
|
var processStartEpochMs = () => Date.now() - process.uptime() * MILLISECONDS_IN_A_SECOND;
|
|
635
635
|
|
|
636
|
+
// src/dev/hotRejectionGuard.ts
|
|
637
|
+
var installHotRejectionGuard = () => {
|
|
638
|
+
if (globalThis.__absoluteHotRejectionGuard)
|
|
639
|
+
return;
|
|
640
|
+
globalThis.__absoluteHotRejectionGuard = true;
|
|
641
|
+
process.on("unhandledRejection", (reason) => {
|
|
642
|
+
console.error(reason);
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
|
|
636
646
|
// src/dev/importCost/hint.ts
|
|
637
647
|
var ENTRY_GRAPH_START = "server entry import start";
|
|
638
648
|
var ENTRY_GRAPH_END = "framework runtime imported";
|
|
@@ -733,6 +743,7 @@ var isStaleAbsoluteServerEntryCopy = (name, currentPid = process.pid, ownerIsAli
|
|
|
733
743
|
adoptParentBootMarks();
|
|
734
744
|
markBootAt("child process start", processStartEpochMs());
|
|
735
745
|
markBoot("bootstrap imports evaluated");
|
|
746
|
+
installHotRejectionGuard();
|
|
736
747
|
var originalEntry = process.env.ABSOLUTE_SERVER_ENTRY;
|
|
737
748
|
if (!originalEntry) {
|
|
738
749
|
throw new Error("ABSOLUTE_SERVER_ENTRY is required by the AbsoluteJS dev bootstrap");
|