Skip to content

Commit 56cd31e

Browse files
committed
fix windows native module staging in before-pack
1 parent b569512 commit 56cd31e

3 files changed

Lines changed: 111 additions & 21 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
asarUnpackGlobs,
4+
buildExternals,
5+
macOnlyNativeModules,
6+
packagedFileGlobs,
7+
requiredNativeModules,
8+
runtimeNativeModules,
9+
watcherPackageFor,
10+
} from "./runtime-dependencies";
11+
12+
describe("watcherPackageFor", () => {
13+
it.each([
14+
["mac", 1, "@parcel/watcher-darwin-x64"],
15+
["mac", 3, "@parcel/watcher-darwin-arm64"],
16+
["windows", 1, "@parcel/watcher-win32-x64"],
17+
["windows", 3, "@parcel/watcher-win32-arm64"],
18+
["linux", 1, "@parcel/watcher-linux-x64-glibc"],
19+
["linux", 3, "@parcel/watcher-linux-arm64-glibc"],
20+
])("maps platform=%s arch=%i to %s", (platform, arch, expected) => {
21+
expect(watcherPackageFor(platform, arch as number)).toBe(expected);
22+
});
23+
24+
it("returns null for an unrecognized platform name", () => {
25+
// electron-builder passes "windows", never "win"; matching "win" was the
26+
// bug that left the Windows watcher binary unstaged.
27+
expect(watcherPackageFor("win", 1)).toBeNull();
28+
expect(watcherPackageFor("darwin", 1)).toBeNull();
29+
});
30+
});
31+
32+
describe("native module globs", () => {
33+
it("collapses the @parcel scope to a single glob", () => {
34+
expect(packagedFileGlobs).toContain("node_modules/@parcel/**/*");
35+
expect(asarUnpackGlobs).toContain("node_modules/@parcel/**");
36+
expect(packagedFileGlobs).not.toContain(
37+
"node_modules/@parcel/watcher/**/*",
38+
);
39+
});
40+
41+
it("emits a per-package glob for unscoped modules", () => {
42+
expect(packagedFileGlobs).toContain("node_modules/node-pty/**/*");
43+
expect(packagedFileGlobs).toContain("node_modules/better-sqlite3/**/*");
44+
});
45+
});
46+
47+
describe("native module list invariants", () => {
48+
it("only marks modules that are actually staged as required", () => {
49+
for (const mod of requiredNativeModules) {
50+
expect(runtimeNativeModules).toContain(mod);
51+
}
52+
});
53+
54+
it("externalizes only modules staged on some platform", () => {
55+
const staged = new Set([...runtimeNativeModules, ...macOnlyNativeModules]);
56+
for (const mod of buildExternals) {
57+
expect(staged.has(mod)).toBe(true);
58+
}
59+
});
60+
});

apps/code/runtime-dependencies.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ export const runtimeNativeModules = [
2929
"is-number",
3030
];
3131

32+
// The base native modules that must exist when packaging; a missing one is a
33+
// broken build, not a warning. before-pack stages these with copyRequiredDep.
34+
export const requiredNativeModules = [
35+
"node-pty",
36+
"@parcel/watcher",
37+
"better-sqlite3",
38+
];
39+
3240
// file-icon (and its p-map dependency) is only used on macOS.
3341
export const macOnlyNativeModules = ["file-icon", "p-map"];
3442

@@ -63,3 +71,34 @@ export const packagedFileGlobs = [
6371
export const asarUnpackGlobs = asarUnpackModules.map(
6472
(name) => `node_modules/${scopeOf(name)}/**`,
6573
);
74+
75+
// Mirrors electron-builder's Arch enum (ia32=0, x64=1, armv7l=2, arm64=3).
76+
const ARCH_X64 = 1;
77+
const ARCH_ARM64 = 3;
78+
79+
// The platform-specific @parcel/watcher prebuild before-pack must stage, keyed
80+
// by electron-builder's platform name and arch. The name is "windows", not
81+
// "win" (electron-builder's Platform.WINDOWS.name); matching "win" silently
82+
// skips staging and ships a broken Windows app. Returns null for an
83+
// unrecognized platform.
84+
export function watcherPackageFor(
85+
platformName: string,
86+
arch: number,
87+
): string | null {
88+
if (platformName === "mac") {
89+
return arch === ARCH_X64
90+
? "@parcel/watcher-darwin-x64"
91+
: "@parcel/watcher-darwin-arm64";
92+
}
93+
if (platformName === "windows") {
94+
return arch === ARCH_ARM64
95+
? "@parcel/watcher-win32-arm64"
96+
: "@parcel/watcher-win32-x64";
97+
}
98+
if (platformName === "linux") {
99+
return arch === ARCH_ARM64
100+
? "@parcel/watcher-linux-arm64-glibc"
101+
: "@parcel/watcher-linux-x64-glibc";
102+
}
103+
return null;
104+
}

apps/code/scripts/before-pack.ts

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
22
import path from "node:path";
33
import {
44
macOnlyNativeModules,
5+
requiredNativeModules,
56
runtimeNativeModules,
7+
watcherPackageFor,
68
} from "../runtime-dependencies";
79

8-
const ARCH_X64 = 1;
9-
const ARCH_ARM64 = 3;
10-
1110
type BeforePackContext = {
1211
packager: { platform: { name: string } };
1312
arch: number;
@@ -66,30 +65,22 @@ export default async function beforePack(context: BeforePackContext) {
6665
console.log(`[before-pack] local node_modules: ${localNodeModules}`);
6766

6867
for (const dep of runtimeNativeModules) {
69-
copyDep(dep, rootNodeModules, localNodeModules);
68+
if (requiredNativeModules.includes(dep)) {
69+
copyRequiredDep(dep, rootNodeModules, localNodeModules);
70+
} else {
71+
copyDep(dep, rootNodeModules, localNodeModules);
72+
}
7073
}
7174

72-
if (platformName === "mac") {
73-
const watcherPkg =
74-
arch === ARCH_X64
75-
? "@parcel/watcher-darwin-x64"
76-
: "@parcel/watcher-darwin-arm64";
75+
const watcherPkg = watcherPackageFor(platformName, arch);
76+
if (watcherPkg) {
7777
copyRequiredDep(watcherPkg, rootNodeModules, localNodeModules);
78+
}
79+
80+
if (platformName === "mac") {
7881
for (const dep of macOnlyNativeModules) {
7982
copyDep(dep, rootNodeModules, localNodeModules);
8083
}
81-
} else if (platformName === "win") {
82-
const watcherPkg =
83-
arch === ARCH_ARM64
84-
? "@parcel/watcher-win32-arm64"
85-
: "@parcel/watcher-win32-x64";
86-
copyRequiredDep(watcherPkg, rootNodeModules, localNodeModules);
87-
} else if (platformName === "linux") {
88-
const watcherPkg =
89-
arch === ARCH_ARM64
90-
? "@parcel/watcher-linux-arm64-glibc"
91-
: "@parcel/watcher-linux-x64-glibc";
92-
copyRequiredDep(watcherPkg, rootNodeModules, localNodeModules);
9384
}
9485

9586
const watcherBuild = path.join(localNodeModules, "@parcel/watcher/build");

0 commit comments

Comments
 (0)