Skip to content

Commit 37b9d25

Browse files
committed
fix(pack): align node target watcher detection
1 parent b06dd1d commit 37b9d25

5 files changed

Lines changed: 42 additions & 6 deletions

File tree

packages/pack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@hono/node-ws": "^1.3.0",
4343
"@swc/helpers": "0.5.15",
4444
"@utoo/pack-shared": "*",
45+
"browserslist": "^4.28.5",
4546
"domparser-rs": "^0.0.7",
4647
"find-up": "4.1.0",
4748
"get-port": "5.1.1",

packages/pack/src/__test__/serveServerOutputsHmrChild.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ async function main() {
9494
scenario === "dist-root"
9595
? {
9696
entry: [{ import: "./src/index.js", name: "main" }],
97-
target: "node",
97+
target: "current node",
9898
output: { path: "./dist/node", clean: true },
9999
}
100100
: {

packages/pack/src/core/hmr.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { processHtmlEntry } from "../utils/htmlEntry";
2727
import { acquirePersistentCacheLock } from "../utils/lockfile";
2828
import { normalizePath } from "../utils/normalizePath";
2929
import { useWorkerThreads } from "../utils/runtimePluginStratety";
30+
import { isNodeTarget } from "../utils/target";
3031
import { validateEntryPaths } from "../utils/validateEntry";
3132
import { consumeHmrSubscription } from "./hmrSubscription";
3233
import { projectFactory } from "./project";
@@ -178,11 +179,7 @@ export async function createHotReloader(
178179
bundleOptions.config.server?.entry ||
179180
bundleOptions.config.server?.function,
180181
),
181-
// The Rust config treats the common `node`/`node <version>` forms as Node.
182-
// Other browserslist queries remain Web targets.
183-
nodeTarget: /^node(?:\s|$)/i.test(
184-
bundleOptions.config.target?.trim() ?? "",
185-
),
182+
nodeTarget: isNodeTarget(bundleOptions.config.target),
186183
});
187184
const persistentCaching = isPersistentCachingEnabled(
188185
bundleOptions.config.persistentCaching,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { describe, expect, it } from "vitest";
2+
import { isNodeTarget } from "./target";
3+
4+
describe("isNodeTarget", () => {
5+
it.each(["node", "current node", "maintained node versions", "node >= 20"])(
6+
"recognizes the Node target %s",
7+
(target) => {
8+
expect(isNodeTarget(target)).toBe(true);
9+
},
10+
);
11+
12+
it.each([undefined, "web", "last 1 Chrome versions"])(
13+
"keeps the web target %s on the client watcher",
14+
(target) => {
15+
expect(isNodeTarget(target)).toBe(false);
16+
},
17+
);
18+
19+
it("matches the first resolved distribution for mixed queries", () => {
20+
expect(isNodeTarget("last 1 Chrome versions, current node")).toBe(false);
21+
});
22+
});

packages/pack/src/utils/target.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import browserslist from "browserslist";
2+
3+
export function isNodeTarget(target?: string) {
4+
if (target === undefined) return false;
5+
6+
try {
7+
const [distribution] = browserslist(target.split(","), {
8+
ignoreUnknownVersions: true,
9+
});
10+
return distribution?.startsWith("node ") ?? false;
11+
} catch {
12+
// Match Config::platform(): `node` is also the explicit fallback when the
13+
// target isn't a valid Browserslist query.
14+
return target === "node";
15+
}
16+
}

0 commit comments

Comments
 (0)