Skip to content

Commit 696d4fc

Browse files
committed
refactor(runtime): reduce surface to a mechanism-only dev-loader contract
The runtime explicitly does not implement HMR policy. import.meta.hot, the hot-data/accept/dispose/prune registries, and dev-session state move to the JS HMR clients (eg, @nativescript/vite); native keeps only the sync HTTP module fetch, prewarm cache + list-mode kickstart, eviction plumbing, and the dev-boot-complete signal. Dev helpers are consolidated under __NS_DEV__. Also fixes dynamic import() error propagation: with top-level-await enabled, Module::Evaluate() returns a promise instead of an empty MaybeLocal on throw, so the previous empty-check never fired and import() resolved a half-evaluated namespace, silently swallowing module evaluation errors. The callback now checks Module::GetStatus() for kErrored (HTTP, blob, and generic paths) and rejects with the module's real exception, matching iOS. Removes the httpModulePrefetch app-config flag and speculative prefetching; list-mode kickstart remains.
1 parent 269fa36 commit 696d4fc

24 files changed

Lines changed: 840 additions & 2718 deletions

test-app/app/src/main/assets/app/mainpage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,6 @@ require('./tests/testQueueMicrotask');
7777
require("./tests/testConcurrentAccess");
7878

7979
require("./tests/testESModules.mjs");
80-
require("./tests/testHmrHotDataExt.mjs");
80+
require("./tests/testNsDevBoundary.mjs");
8181
require("./tests/testHttpCanonicalKey.mjs");
8282
require("./tests/testNodeBuiltinsAndOptionalModules.mjs");

test-app/app/src/main/assets/app/tests/esm/hmr/hot-data-ext.js

Lines changed: 0 additions & 79 deletions
This file was deleted.

test-app/app/src/main/assets/app/tests/esm/hmr/hot-data-ext.mjs

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Fixture for testNsDevBoundary: reports whether the runtime attached a
2+
// native `import.meta.hot` (it must NOT — hot contexts are injected by the
3+
// JS dev client via source rewrite, never by the runtime).
4+
export const hasHot = typeof import.meta.hot !== "undefined";
5+
export const hotValue = import.meta.hot;

test-app/app/src/main/assets/app/tests/testHmrHotDataExt.mjs

Lines changed: 0 additions & 65 deletions
This file was deleted.
Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,77 @@
11
// HTTP canonical-key identity tests.
22
//
33
// Pins the behavior of the native CanonicalizeHttpUrlKey (the loader/registry
4-
// key) via the debug-only __nsCanonicalizeHttpUrlKey diagnostic global. Pure
5-
// string logic — no dev server required. Android does NOT collapse the
6-
// __ns_boot__/__ns_hmr__ virtual prefixes here (that is canonicalHotKey's job),
7-
// which these specs assert explicitly.
4+
// key) via the debug-only __NS_DEV__.canonicalizeHttpUrlKey diagnostic. Pure
5+
// string logic — no dev server required.
6+
//
7+
// Module identity IS the canonical URL: the dev server serves every module
8+
// under one URL and freshness is handled by __NS_DEV__.invalidateModules
9+
// (registry + prewarm-cache evict + fetch nonce), never by URL variation.
10+
// There is deliberately no path-tag vocabulary (__ns_boot__/__ns_hmr__)
11+
// to collapse, and no versioned-bridge-endpoint normalization.
812

913
describe("HTTP canonical key", function () {
14+
function canonFn() {
15+
const dev = globalThis.__NS_DEV__;
16+
return dev && typeof dev.canonicalizeHttpUrlKey === "function"
17+
? dev.canonicalizeHttpUrlKey
18+
: null;
19+
}
1020
function canon(url) {
11-
return globalThis.__nsCanonicalizeHttpUrlKey(url);
21+
return canonFn()(url);
1222
}
1323

1424
it("is available in dev builds", function () {
15-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") {
16-
pending("__nsCanonicalizeHttpUrlKey not available (release build?)");
25+
if (!canonFn()) {
26+
pending("__NS_DEV__.canonicalizeHttpUrlKey not available (release build?)");
1727
return;
1828
}
19-
expect(typeof globalThis.__nsCanonicalizeHttpUrlKey).toBe("function");
29+
expect(typeof canonFn()).toBe("function");
2030
});
2131

2232
it("drops the fragment and unwraps file://http wrappers", function () {
23-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") { pending("release"); return; }
33+
if (!canonFn()) { pending("release"); return; }
2434
expect(canon("http://h/ns/m/foo.js#frag")).toBe("http://h/ns/m/foo.js");
2535
expect(canon("file://http://h/x.js")).toBe("http://h/x.js");
2636
});
2737

28-
it("normalizes versioned bridge endpoints but not deeper paths", function () {
29-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") { pending("release"); return; }
30-
expect(canon("http://h/ns/rt/42")).toBe("http://h/ns/rt");
31-
expect(canon("http://h/ns/core/13")).toBe("http://h/ns/core");
38+
it("does NOT collapse versioned endpoint paths or path tags", function () {
39+
if (!canonFn()) { pending("release"); return; }
40+
// Path is identity — no /ns/rt/<v> → /ns/rt collapse, no
41+
// __ns_hmr__/__ns_boot__ tag folding.
42+
expect(canon("http://h/ns/rt/42")).toBe("http://h/ns/rt/42");
3243
expect(canon("http://h/ns/rt/42/x.js")).toBe("http://h/ns/rt/42/x.js");
33-
});
34-
35-
it("does NOT collapse __ns_hmr__/__ns_boot__ prefixes (Android loader key)", function () {
36-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") { pending("release"); return; }
3744
expect(canon("http://h/ns/m/__ns_hmr__/v7/foo.js"))
3845
.toBe("http://h/ns/m/__ns_hmr__/v7/foo.js");
3946
});
4047

41-
it("strips ?import and sorts remaining query params", function () {
42-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") { pending("release"); return; }
43-
expect(canon("http://h/a?import=1&b=2&a=3")).toBe("http://h/a?a=3&b=2");
44-
expect(canon("http://h/a?b=2&a=1")).toBe("http://h/a?a=1&b=2");
48+
it("strips import/t/v markers and sorts remaining params on dev endpoints", function () {
49+
if (!canonFn()) { pending("release"); return; }
50+
expect(canon("http://h/ns/m/a?import=1&b=2&a=3")).toBe("http://h/ns/m/a?a=3&b=2");
51+
expect(canon("http://h/ns/m/a?b=2&a=1")).toBe("http://h/ns/m/a?a=1&b=2");
4552
expect(canon("http://h/ns/core?import=1")).toBe("http://h/ns/core");
53+
expect(canon("http://h/ns/m/a?t=123&v=abc&x=1")).toBe("http://h/ns/m/a?x=1");
54+
});
55+
56+
it("preserves the query verbatim on non-dev endpoints", function () {
57+
if (!canonFn()) { pending("release"); return; }
58+
// Public-internet module URLs: the query can be part of identity
59+
// (auth, content versioning, routing) — only the fragment is dropped.
60+
expect(canon("http://h/a?import=1&b=2&a=3")).toBe("http://h/a?import=1&b=2&a=3");
61+
expect(canon("https://cdn.example.com/pkg.js?token=x#frag"))
62+
.toBe("https://cdn.example.com/pkg.js?token=x");
63+
});
64+
65+
it("preserves the t param on @ng/component endpoints", function () {
66+
if (!canonFn()) { pending("release"); return; }
67+
// Angular HMR component-update endpoint: `t` identifies a specific
68+
// recompile and must remain a distinct registry entry.
69+
expect(canon("http://h/ns/m/app/@ng/component?c=x&t=111"))
70+
.toBe("http://h/ns/m/app/@ng/component?c=x&t=111");
4671
});
4772

4873
it("leaves a non-http(s) specifier unchanged", function () {
49-
if (typeof globalThis.__nsCanonicalizeHttpUrlKey !== "function") { pending("release"); return; }
74+
if (!canonFn()) { pending("release"); return; }
5075
expect(canon("~/local/foo.js")).toBe("~/local/foo.js");
5176
});
5277
});

test-app/app/src/main/assets/app/tests/testNodeBuiltinsAndOptionalModules.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Tests the resolver paths added in the HMR/ESM hardening port:
1+
// Tests the ESM resolver's synthetic-module paths:
22
// - node: built-in polyfills (in-memory ES modules)
33
// - bare-specifier optional-module placeholders
44
// - ns-vendor:// vendor-registry resolution via configureRuntime importMap
@@ -46,9 +46,10 @@ describe("Node built-in and optional module resolution", function () {
4646
});
4747

4848
it("resolves import-map vendor modules through the explicit vendor registry", async function () {
49-
const configureRuntime = globalThis.__nsConfigureDevRuntime || globalThis.__nsConfigureRuntime;
49+
const dev = globalThis.__NS_DEV__;
50+
const configureRuntime = dev && dev.configureRuntime;
5051
if (typeof configureRuntime !== "function") {
51-
pending("__nsConfigureDevRuntime not available (release build?)");
52+
pending("__NS_DEV__.configureRuntime not available");
5253
return;
5354
}
5455

0 commit comments

Comments
 (0)