@nativescript/vite 8.0.0-alpha.6 → 8.0.0-alpha.8
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/configuration/angular.js +155 -22
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +9 -12
- package/configuration/base.js.map +1 -1
- package/configuration/typescript.js +1 -1
- package/configuration/typescript.js.map +1 -1
- package/helpers/angular/angular-linker.js +3 -12
- package/helpers/angular/angular-linker.js.map +1 -1
- package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
- package/helpers/angular/inject-component-hmr-registration.js +359 -0
- package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
- package/helpers/global-defines.d.ts +7 -13
- package/helpers/global-defines.js +10 -16
- package/helpers/global-defines.js.map +1 -1
- package/helpers/main-entry.js +6 -7
- package/helpers/main-entry.js.map +1 -1
- package/helpers/ns-core-url.d.ts +5 -6
- package/helpers/ns-core-url.js +5 -6
- package/helpers/ns-core-url.js.map +1 -1
- package/helpers/prelink-angular.js +1 -4
- package/helpers/prelink-angular.js.map +1 -1
- package/hmr/client/css-handler.js +2 -1
- package/hmr/client/css-handler.js.map +1 -1
- package/hmr/client/hmr-pending-overlay.d.ts +1 -1
- package/hmr/client/hmr-pending-overlay.js +1 -1
- package/hmr/client/index.js +27 -26
- package/hmr/client/index.js.map +1 -1
- package/hmr/client/utils.js +27 -25
- package/hmr/client/utils.js.map +1 -1
- package/hmr/entry-runtime.js +11 -29
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/client/index.js +118 -136
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/angular/server/linker.js +1 -4
- package/hmr/frameworks/angular/server/linker.js.map +1 -1
- package/hmr/frameworks/vue/client/index.js +18 -42
- package/hmr/frameworks/vue/client/index.js.map +1 -1
- package/hmr/server/core-sanitize.js +8 -8
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/import-map.js +3 -4
- package/hmr/server/import-map.js.map +1 -1
- package/hmr/server/ns-core-cjs-shape.d.ts +2 -4
- package/hmr/server/ns-core-cjs-shape.js +4 -6
- package/hmr/server/ns-core-cjs-shape.js.map +1 -1
- package/hmr/server/perf-instrumentation.d.ts +5 -9
- package/hmr/server/perf-instrumentation.js +2 -5
- package/hmr/server/perf-instrumentation.js.map +1 -1
- package/hmr/server/vite-plugin.js +3 -1
- package/hmr/server/vite-plugin.js.map +1 -1
- package/hmr/server/websocket-angular-hot-update.d.ts +1 -0
- package/hmr/server/websocket-angular-hot-update.js +42 -30
- package/hmr/server/websocket-angular-hot-update.js.map +1 -1
- package/hmr/server/websocket-core-bridge.js +12 -17
- package/hmr/server/websocket-core-bridge.js.map +1 -1
- package/hmr/server/websocket-hmr-pending.d.ts +1 -1
- package/hmr/server/websocket-hmr-pending.js +1 -1
- package/hmr/server/websocket-module-bindings.js +2 -2
- package/hmr/server/websocket-module-bindings.js.map +1 -1
- package/hmr/server/websocket-ns-m-paths.js +14 -15
- package/hmr/server/websocket-ns-m-paths.js.map +1 -1
- package/hmr/server/websocket-runtime-compat.js +4 -3
- package/hmr/server/websocket-runtime-compat.js.map +1 -1
- package/hmr/server/websocket-served-module-helpers.js +13 -12
- package/hmr/server/websocket-served-module-helpers.js.map +1 -1
- package/hmr/server/websocket-vue-sfc.js +15 -60
- package/hmr/server/websocket-vue-sfc.js.map +1 -1
- package/hmr/server/websocket.js +286 -338
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/runtime/boot-timeline.js +0 -3
- package/hmr/shared/runtime/boot-timeline.js.map +1 -1
- package/hmr/shared/runtime/dev-overlay.js +36 -50
- package/hmr/shared/runtime/dev-overlay.js.map +1 -1
- package/hmr/shared/runtime/module-provenance.js +1 -4
- package/hmr/shared/runtime/module-provenance.js.map +1 -1
- package/hmr/shared/runtime/root-placeholder.js +38 -68
- package/hmr/shared/runtime/root-placeholder.js.map +1 -1
- package/hmr/shared/runtime/session-bootstrap.js +8 -8
- package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
- package/hmr/shared/runtime/vendor-bootstrap.js +1 -9
- package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
- package/hmr/shared/vendor/manifest.js +4 -16
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/package.json +1 -1
- package/runtime/core-aliases-early.js +17 -41
- package/runtime/core-aliases-early.js.map +1 -1
package/hmr/server/websocket.js
CHANGED
|
@@ -80,14 +80,13 @@ const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
|
|
|
80
80
|
const APP_VIRTUAL_WITH_SLASH = `${APP_VIRTUAL_PREFIX}/`;
|
|
81
81
|
const DEFAULT_MAIN_ENTRY = getProjectAppRelativePath('app.ts');
|
|
82
82
|
const DEFAULT_MAIN_ENTRY_VIRTUAL = getProjectAppVirtualPath('app.ts');
|
|
83
|
-
// alpha.59 — Stable URL + Explicit Invalidation:
|
|
84
83
|
// Memoized resolver for the project bootstrap entry as a posix
|
|
85
|
-
// project-relative path (e.g. `/src/main.ts`). This mirrors the
|
|
86
|
-
// the cold-boot wrapper performs (`getPackageJson().main` →
|
|
87
|
-
// project-relative under `/<APP_ROOT_DIR>/`) so the eviction set for
|
|
88
|
-
// always lines up with the URL the runtime actually re-imports.
|
|
89
|
-
// at first call and cached: `package.json` is read at startup
|
|
90
|
-
// changes during a dev session, so it's safe to memoize.
|
|
84
|
+
// project-relative path (e.g. `/src/main.ts`). This mirrors the
|
|
85
|
+
// resolution the cold-boot wrapper performs (`getPackageJson().main` →
|
|
86
|
+
// project-relative under `/<APP_ROOT_DIR>/`) so the eviction set for
|
|
87
|
+
// HMR always lines up with the URL the runtime actually re-imports.
|
|
88
|
+
// Resolved at first call and cached: `package.json` is read at startup
|
|
89
|
+
// and never changes during a dev session, so it's safe to memoize.
|
|
91
90
|
let __ns_bootstrap_entry_rel_cached = null;
|
|
92
91
|
function getBootstrapEntryRelPath() {
|
|
93
92
|
if (__ns_bootstrap_entry_rel_cached)
|
|
@@ -354,11 +353,9 @@ function ensureGuardPlainDynamicImports(code, origin) {
|
|
|
354
353
|
return code;
|
|
355
354
|
}
|
|
356
355
|
}
|
|
357
|
-
//
|
|
358
|
-
// here. Single source of truth now lives in
|
|
356
|
+
// `ensureDynamicHmrImportHelper` lives in
|
|
359
357
|
// `./websocket-served-module-helpers.js`. See that file for the
|
|
360
|
-
// architectural rationale and the current
|
|
361
|
-
// implementation.
|
|
358
|
+
// architectural rationale and the current helper implementation.
|
|
362
359
|
async function expandStarExports(code, server, projectRoot, verbose, sharedTransformer) {
|
|
363
360
|
const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
|
|
364
361
|
let match;
|
|
@@ -390,10 +387,7 @@ async function expandStarExports(code, server, projectRoot, verbose, sharedTrans
|
|
|
390
387
|
if (!names.length)
|
|
391
388
|
return null;
|
|
392
389
|
if (verbose) {
|
|
393
|
-
|
|
394
|
-
console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
|
|
395
|
-
}
|
|
396
|
-
catch { }
|
|
390
|
+
console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
|
|
397
391
|
}
|
|
398
392
|
return { rep, names };
|
|
399
393
|
}
|
|
@@ -456,7 +450,7 @@ function stripViteDynamicImportVirtual(code) {
|
|
|
456
450
|
}
|
|
457
451
|
return code;
|
|
458
452
|
}
|
|
459
|
-
const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);
|
|
453
|
+
const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);console.error(e.message+'\\n'+(e.stack||''));try{g.__NS_REQUIRE_GUARD_LAST__={spec:s,stack:e.stack,label:l,ts:Date.now()};}catch(e3){}}}catch(e1){}return o.apply(this, arguments);};};if(typeof g.require==='function'&&!g.require.__NS_REQ_GUARDED__){var o1=g.require;g.require=mk(o1,'require');g.require.__NS_REQ_GUARDED__=true;}if(typeof g.__nsRequire==='function'&&!g.__nsRequire.__NS_REQ_GUARDED__){var o2=g.__nsRequire;g.__nsRequire=mk(o2,'__nsRequire');g.__nsRequire.__NS_REQ_GUARDED__=true;}g.__NS_REQUIRE_GUARD_INSTALLED__=true;}}catch(e){}})();\n`;
|
|
460
454
|
function shouldRemapImport(spec) {
|
|
461
455
|
if (!spec || typeof spec !== 'string')
|
|
462
456
|
return false;
|
|
@@ -914,14 +908,13 @@ function toNodeModulesHttpModuleId(importPath) {
|
|
|
914
908
|
}
|
|
915
909
|
return `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
916
910
|
}
|
|
917
|
-
//
|
|
918
|
-
// previously lived here as duplicates of the implementations in
|
|
911
|
+
// `rewriteNsMImportPathForHmr` and `getNumericServeVersionTag` live in
|
|
919
912
|
// `./websocket-ns-m-paths.js`. The path rewriter is part of the
|
|
920
|
-
// "Stable URL + Explicit Invalidation" architecture
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
//
|
|
913
|
+
// "Stable URL + Explicit Invalidation" architecture and must be a
|
|
914
|
+
// single source of truth so the canonicalization rules can't drift
|
|
915
|
+
// between modules. They are imported above and re-exported below for
|
|
916
|
+
// tests / external callers that historically reached them through this
|
|
917
|
+
// module.
|
|
925
918
|
function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
926
919
|
if (!spec || typeof spec !== 'string') {
|
|
927
920
|
return null;
|
|
@@ -1966,10 +1959,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
1966
1959
|
if (spec === '@') {
|
|
1967
1960
|
const stub = `/ns/m/__invalid_at__.mjs`;
|
|
1968
1961
|
if (verbose) {
|
|
1969
|
-
|
|
1970
|
-
console.warn(`[rewrite] mapped bare '@' spec to stub: ${stub}`);
|
|
1971
|
-
}
|
|
1972
|
-
catch { }
|
|
1962
|
+
console.warn(`[rewrite] mapped bare '@' spec to stub: ${stub}`);
|
|
1973
1963
|
}
|
|
1974
1964
|
return `${prefix}${stub}${suffix}`;
|
|
1975
1965
|
}
|
|
@@ -2190,10 +2180,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2190
2180
|
result = result.replace(/(import\(\s*['"])@(['"]\s*\))/g, (_m) => {
|
|
2191
2181
|
const stubExpr = `import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href)`;
|
|
2192
2182
|
if (verbose) {
|
|
2193
|
-
|
|
2194
|
-
console.warn(`[rewrite] mapped dynamic import('@') to /ns/m/__invalid_at__.mjs via import.meta.url`);
|
|
2195
|
-
}
|
|
2196
|
-
catch { }
|
|
2183
|
+
console.warn(`[rewrite] mapped dynamic import('@') to /ns/m/__invalid_at__.mjs via import.meta.url`);
|
|
2197
2184
|
}
|
|
2198
2185
|
return stubExpr;
|
|
2199
2186
|
});
|
|
@@ -2202,10 +2189,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2202
2189
|
result = result.replace(/(from\s*['"])@(['"])/g, (_m, p1, p2) => {
|
|
2203
2190
|
const stub = `/ns/m/__invalid_at__.mjs`;
|
|
2204
2191
|
if (verbose) {
|
|
2205
|
-
|
|
2206
|
-
console.warn(`[rewrite] mapped static from '@' to ${stub}`);
|
|
2207
|
-
}
|
|
2208
|
-
catch { }
|
|
2192
|
+
console.warn(`[rewrite] mapped static from '@' to ${stub}`);
|
|
2209
2193
|
}
|
|
2210
2194
|
return `${p1}${stub}${p2}`;
|
|
2211
2195
|
});
|
|
@@ -2213,10 +2197,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2213
2197
|
result = result.replace(/(import\s*(?!\()\s*['"])@(['"])/g, (_m, p1, p2) => {
|
|
2214
2198
|
const stub = `/ns/m/__invalid_at__.mjs`;
|
|
2215
2199
|
if (verbose) {
|
|
2216
|
-
|
|
2217
|
-
console.warn(`[rewrite] mapped side-effect import '@' to ${stub}`);
|
|
2218
|
-
}
|
|
2219
|
-
catch { }
|
|
2200
|
+
console.warn(`[rewrite] mapped side-effect import '@' to ${stub}`);
|
|
2220
2201
|
}
|
|
2221
2202
|
return `${p1}${stub}${p2}`;
|
|
2222
2203
|
});
|
|
@@ -2508,11 +2489,8 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2508
2489
|
const gm = { id, deps: normDeps, hash };
|
|
2509
2490
|
graph.set(id, gm);
|
|
2510
2491
|
if (verbose) {
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
console.log('[hmr-ws][graph] size', graph.size);
|
|
2514
|
-
}
|
|
2515
|
-
catch { }
|
|
2492
|
+
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification, bumpVersion });
|
|
2493
|
+
console.log('[hmr-ws][graph] size', graph.size);
|
|
2516
2494
|
}
|
|
2517
2495
|
if (shouldBroadcastGraphUpsertDelta(classification, options?.emitDeltaOnInsert === true, options?.broadcastDelta !== false)) {
|
|
2518
2496
|
emitDelta([gm], []);
|
|
@@ -2593,7 +2571,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2593
2571
|
}
|
|
2594
2572
|
// bumpVersion: false — the initial walk is a bulk load, not a live
|
|
2595
2573
|
// edit. Keeping graphVersion stable during cold boot avoids double
|
|
2596
|
-
// cache-key drift
|
|
2574
|
+
// cache-key drift.
|
|
2597
2575
|
upsertGraphModule(rel, code, deps, { bumpVersion: false });
|
|
2598
2576
|
}
|
|
2599
2577
|
catch { }
|
|
@@ -2607,10 +2585,12 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2607
2585
|
await walk(pathMod.join(root, 'src'));
|
|
2608
2586
|
}
|
|
2609
2587
|
catch { }
|
|
2610
|
-
// Diagnostic summary
|
|
2611
|
-
//
|
|
2612
|
-
//
|
|
2613
|
-
|
|
2588
|
+
// Diagnostic summary. Gated behind the verbose flag so the
|
|
2589
|
+
// dev console stays quiet on a normal save. Flip
|
|
2590
|
+
// NS_VITE_VERBOSE=1 to surface slow cold-boot walks; a
|
|
2591
|
+
// `bumpedVersion=no` result is the happy path, `yes`
|
|
2592
|
+
// indicates a regression.
|
|
2593
|
+
if (verbose) {
|
|
2614
2594
|
console.info(formatPopulateInitialGraphSummary({
|
|
2615
2595
|
moduleCount: graph.size,
|
|
2616
2596
|
durationMs: Date.now() - tStart,
|
|
@@ -2618,7 +2598,6 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2618
2598
|
bumpedVersion: graphVersion !== versionAtStart,
|
|
2619
2599
|
}));
|
|
2620
2600
|
}
|
|
2621
|
-
catch { }
|
|
2622
2601
|
}
|
|
2623
2602
|
// Kick off `populateInitialGraph` in the background (non-awaited) so /ns/m
|
|
2624
2603
|
// responses are never blocked on a full tree walk. Returns the shared
|
|
@@ -2669,32 +2648,28 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2669
2648
|
// race conditions during HTTP HMR startup, but the shared runner
|
|
2670
2649
|
// already has per-URL coalescing and an async-cached result map,
|
|
2671
2650
|
// so higher fan-out is safe and dramatically reduces cold-boot
|
|
2672
|
-
// time
|
|
2673
|
-
//
|
|
2674
|
-
//
|
|
2651
|
+
// time. We cap at 8 by default to match typical dev machines and
|
|
2652
|
+
// respect Vite's internal worker pool limits. Override via the
|
|
2653
|
+
// `NS_VITE_HMR_TRANSFORM_CONCURRENCY` env var when needed.
|
|
2675
2654
|
const configuredTransformConcurrency = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CONCURRENCY || '', 10);
|
|
2676
2655
|
const transformConcurrency = Number.isFinite(configuredTransformConcurrency) && configuredTransformConcurrency > 0 ? configuredTransformConcurrency : 8;
|
|
2677
|
-
// Keep transformed code cached for longer across HMR updates so
|
|
2678
|
-
// unchanged neighbours of an edited file don't re-run
|
|
2679
|
-
// Angular/TypeScript/Vite transform pipeline. The
|
|
2680
|
-
// explicitly invalidates affected URLs, so a longer TTL
|
|
2681
|
-
//
|
|
2656
|
+
// Keep transformed code cached for longer across HMR updates so
|
|
2657
|
+
// that unchanged neighbours of an edited file don't re-run
|
|
2658
|
+
// through the Angular/TypeScript/Vite transform pipeline. The
|
|
2659
|
+
// HMR flow explicitly invalidates affected URLs, so a longer TTL
|
|
2660
|
+
// is safe. Override with `NS_VITE_HMR_TRANSFORM_CACHE_MS`.
|
|
2682
2661
|
const configuredTransformCacheMs = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CACHE_MS || '', 10);
|
|
2683
2662
|
const transformCacheMs = Number.isFinite(configuredTransformCacheMs) && configuredTransformCacheMs >= 0 ? configuredTransformCacheMs : 60000;
|
|
2684
2663
|
sharedTransformRequest = createSharedTransformRequestRunner((url) => server.transformRequest(url), (url, timeoutMs) => {
|
|
2685
|
-
|
|
2686
|
-
console.warn('[ns:m] slow transformRequest for', url, '(>' + timeoutMs + 'ms)');
|
|
2687
|
-
}
|
|
2688
|
-
catch { }
|
|
2664
|
+
console.warn('[ns:m] slow transformRequest for', url, '(>' + timeoutMs + 'ms)');
|
|
2689
2665
|
}, {
|
|
2690
2666
|
maxConcurrent: transformConcurrency,
|
|
2691
2667
|
resultCacheTtlMs: transformCacheMs,
|
|
2692
2668
|
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot || process.cwd()),
|
|
2693
2669
|
});
|
|
2694
|
-
// Always-on startup banner — prints once per dev server process
|
|
2695
|
-
// anyone investigating perf can immediately see which build
|
|
2696
|
-
// and what knobs are active.
|
|
2697
|
-
// ("Track 1 + 2 — diagnostic instrumentation").
|
|
2670
|
+
// Always-on startup banner — prints once per dev server process
|
|
2671
|
+
// so anyone investigating perf can immediately see which build
|
|
2672
|
+
// is live and what knobs are active.
|
|
2698
2673
|
try {
|
|
2699
2674
|
let pkgVersion = 'unknown';
|
|
2700
2675
|
try {
|
|
@@ -2717,26 +2692,27 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2717
2692
|
}
|
|
2718
2693
|
catch { }
|
|
2719
2694
|
}
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2695
|
+
if (verbose) {
|
|
2696
|
+
console.info(formatServerStartupBanner({
|
|
2697
|
+
version: pkgVersion,
|
|
2698
|
+
transformConcurrency,
|
|
2699
|
+
transformCacheMs,
|
|
2700
|
+
lazyInitialGraph: true,
|
|
2701
|
+
graphVersion,
|
|
2702
|
+
}));
|
|
2703
|
+
}
|
|
2727
2704
|
}
|
|
2728
2705
|
catch { }
|
|
2729
|
-
// Always-on cold-boot request trace. Runs in front of every
|
|
2730
|
-
// middleware so it catches all NS dev routes (/ns/m/*,
|
|
2731
|
-
// /ns/core/*, /__ns_boot__/*, etc.) with a single
|
|
2732
|
-
// itself after an idle window so HMR edits don't
|
|
2733
|
-
// the cold-boot numbers. The idle window is
|
|
2734
|
-
// (5s) because V8's HTTP ESM resolver
|
|
2735
|
-
// while parsing — a too-tight window
|
|
2736
|
-
// wave and under-reporting boot by
|
|
2737
|
-
//
|
|
2738
|
-
//
|
|
2739
|
-
// three, 2026-04").
|
|
2706
|
+
// Always-on cold-boot request trace. Runs in front of every
|
|
2707
|
+
// other middleware so it catches all NS dev routes (/ns/m/*,
|
|
2708
|
+
// /ns/rt/*, /ns/core/*, /__ns_boot__/*, etc.) with a single
|
|
2709
|
+
// hook. Closes itself after an idle window so HMR edits don't
|
|
2710
|
+
// get rolled into the cold-boot numbers. The idle window is
|
|
2711
|
+
// generous by default (5s) because V8's HTTP ESM resolver
|
|
2712
|
+
// pauses between dep levels while parsing — a too-tight window
|
|
2713
|
+
// was closing after the first wave and under-reporting boot by
|
|
2714
|
+
// 100x. Override via `NS_VITE_HMR_BOOT_TRACE_IDLE_MS` when
|
|
2715
|
+
// profiling something tricky.
|
|
2740
2716
|
try {
|
|
2741
2717
|
const configuredIdleMs = Number.parseInt(process.env.NS_VITE_HMR_BOOT_TRACE_IDLE_MS || '', 10);
|
|
2742
2718
|
const idleWindowMs = Number.isFinite(configuredIdleMs) && configuredIdleMs > 0 ? configuredIdleMs : 5000;
|
|
@@ -2746,11 +2722,13 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2746
2722
|
coldBootCounter = createColdBootRequestCounter({
|
|
2747
2723
|
summaryEvery,
|
|
2748
2724
|
idleWindowMs,
|
|
2725
|
+
// Gated on the verbose flag so cold-boot progress and
|
|
2726
|
+
// the final window-closed summary stay quiet by
|
|
2727
|
+
// default. Flip NS_VITE_VERBOSE=1 to surface them.
|
|
2749
2728
|
log: (line) => {
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
catch { }
|
|
2729
|
+
if (!verbose)
|
|
2730
|
+
return;
|
|
2731
|
+
console.info(line);
|
|
2754
2732
|
},
|
|
2755
2733
|
});
|
|
2756
2734
|
}
|
|
@@ -2784,13 +2762,12 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2784
2762
|
// kicked off on the first /ns/m hit, which meant populate was
|
|
2785
2763
|
// competing with the device for the same 8 transform slots
|
|
2786
2764
|
// throughout the first 4-5 seconds of cold boot. Starting at
|
|
2787
|
-
// `configureServer` time gives populate the full app
|
|
2788
|
-
// window (typically 2-3s on simulator) as a head
|
|
2789
|
-
// of its work lands before the device even
|
|
2790
|
-
// NS_VITE_HMR_DISABLE_POPULATE=1 when
|
|
2791
|
-
// is helping or hurting a specific
|
|
2792
|
-
//
|
|
2793
|
-
// three").
|
|
2765
|
+
// `configureServer` time gives populate the full app
|
|
2766
|
+
// build/launch window (typically 2-3s on simulator) as a head
|
|
2767
|
+
// start, so more of its work lands before the device even
|
|
2768
|
+
// connects. Disable via `NS_VITE_HMR_DISABLE_POPULATE=1` when
|
|
2769
|
+
// profiling whether populate is helping or hurting a specific
|
|
2770
|
+
// app.
|
|
2794
2771
|
try {
|
|
2795
2772
|
const disablePopulate = process.env.NS_VITE_HMR_DISABLE_POPULATE === '1' || process.env.NS_VITE_HMR_DISABLE_POPULATE === 'true';
|
|
2796
2773
|
if (disablePopulate) {
|
|
@@ -2870,10 +2847,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2870
2847
|
});
|
|
2871
2848
|
});
|
|
2872
2849
|
wss.on('error', (err) => {
|
|
2873
|
-
|
|
2874
|
-
console.warn('[hmr-ws] server error:', err?.message || String(err));
|
|
2875
|
-
}
|
|
2876
|
-
catch { }
|
|
2850
|
+
console.warn('[hmr-ws] server error:', err?.message || String(err));
|
|
2877
2851
|
});
|
|
2878
2852
|
// Import map endpoint: GET /ns/import-map.json
|
|
2879
2853
|
// Returns the import map + runtime config for __nsConfigureRuntime()
|
|
@@ -3084,12 +3058,12 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3084
3058
|
// That gave deterministic URL tags but blocked the cold boot on a
|
|
3085
3059
|
// full src/ tree walk (hundreds of transformRequest calls, 3-6s).
|
|
3086
3060
|
//
|
|
3087
|
-
//
|
|
3088
|
-
//
|
|
3089
|
-
//
|
|
3090
|
-
// initial population in the background so it doesn't block the
|
|
3091
|
-
// response. `handleHotUpdate` awaits the same promise so
|
|
3092
|
-
// HMR event still sees a fully populated graph.
|
|
3061
|
+
// graphVersion now starts at 1 and stays stable during cold boot
|
|
3062
|
+
// (see `upsertGraphModule`'s bumpVersion option and the inline
|
|
3063
|
+
// comment at the graphVersion declaration). We kick off the
|
|
3064
|
+
// initial population in the background so it doesn't block the
|
|
3065
|
+
// first response. `handleHotUpdate` awaits the same promise so
|
|
3066
|
+
// the first HMR event still sees a fully populated graph.
|
|
3093
3067
|
ensureInitialGraphPopulationStarted(server);
|
|
3094
3068
|
// Cold-boot counter is now hooked via the leading boot-trace
|
|
3095
3069
|
// middleware (see `configureServer` — it records the request
|
|
@@ -3307,7 +3281,9 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3307
3281
|
if (transformed?.code && ACTIVE_STRATEGY?.flavor === 'solid' && (resolvedCandidate || spec || '').includes('@solid-refresh')) {
|
|
3308
3282
|
const PATCH_SENTINEL = '/* __ns_solid_refresh_patched__ */';
|
|
3309
3283
|
const alreadyPatched = transformed.code.includes(PATCH_SENTINEL);
|
|
3310
|
-
|
|
3284
|
+
if (verbose) {
|
|
3285
|
+
console.log('[hmr-ws][solid] @solid-refresh patch check:', { spec: resolvedCandidate || spec, alreadyPatched, codeLen: transformed.code.length });
|
|
3286
|
+
}
|
|
3311
3287
|
if (!alreadyPatched) {
|
|
3312
3288
|
let patchedCode = transformed.code;
|
|
3313
3289
|
// Patch 1: Bypass shouldWarnAndDecline() — the vendor-bundled solid-js
|
|
@@ -3316,7 +3292,9 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3316
3292
|
const declineCheck = 'function shouldWarnAndDecline() {';
|
|
3317
3293
|
if (patchedCode.includes(declineCheck)) {
|
|
3318
3294
|
patchedCode = patchedCode.replace(declineCheck, `${PATCH_SENTINEL}\nfunction shouldWarnAndDecline() { return false; /* NS HMR: always allow refresh */ }\nfunction __original_shouldWarnAndDecline() {`);
|
|
3319
|
-
|
|
3295
|
+
if (verbose) {
|
|
3296
|
+
console.log('[hmr-ws][solid] bypassed shouldWarnAndDecline() for NativeScript HMR');
|
|
3297
|
+
}
|
|
3320
3298
|
}
|
|
3321
3299
|
// Patch 2: Force createMemo path in createProxy.
|
|
3322
3300
|
// Without the 'development' condition, $DEVCOMP is not set on components,
|
|
@@ -3327,10 +3305,17 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3327
3305
|
const proxyCondition = 'if (!s || $DEVCOMP in s) {';
|
|
3328
3306
|
if (patchedCode.includes(proxyCondition)) {
|
|
3329
3307
|
patchedCode = patchedCode.replace(proxyCondition, 'if (true) { /* NS HMR: always use createMemo for reactive HMR updates */');
|
|
3330
|
-
|
|
3308
|
+
if (verbose) {
|
|
3309
|
+
console.log('[hmr-ws][solid] forced createMemo path in createProxy for NativeScript HMR');
|
|
3310
|
+
}
|
|
3331
3311
|
}
|
|
3332
3312
|
// Patch 3: Inline patchRegistry call so updates apply immediately
|
|
3333
3313
|
// on module re-evaluation (accept callbacks are not invoked by the HMR client).
|
|
3314
|
+
// The injected `console.log` helpers run inside the user's runtime
|
|
3315
|
+
// when @solid-refresh re-evaluates a module, so they are a runtime
|
|
3316
|
+
// concern (stripped if the user disables the patch). Keeping them
|
|
3317
|
+
// behind the patch sentinel rather than the dev-server `verbose`
|
|
3318
|
+
// flag is intentional — the patch only runs when Solid HMR fires.
|
|
3334
3319
|
const marker = 'hot.data[SOLID_REFRESH] = hot.data[SOLID_REFRESH] || registry;';
|
|
3335
3320
|
if (patchedCode.includes(marker)) {
|
|
3336
3321
|
const patchCode = [
|
|
@@ -3344,7 +3329,9 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3344
3329
|
`}`,
|
|
3345
3330
|
].join('\n ');
|
|
3346
3331
|
patchedCode = patchedCode.replace(marker, `${patchCode}\n ${marker}`);
|
|
3347
|
-
|
|
3332
|
+
if (verbose) {
|
|
3333
|
+
console.log('[hmr-ws][solid] added inline patchRegistry for NativeScript HMR');
|
|
3334
|
+
}
|
|
3348
3335
|
}
|
|
3349
3336
|
// Work on a copy to avoid mutating Vite's cached TransformResult
|
|
3350
3337
|
transformed = { ...transformed, code: patchedCode };
|
|
@@ -3369,13 +3356,10 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3369
3356
|
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
|
|
3370
3357
|
const deps = Array.from(collectImportDependencies(code, id));
|
|
3371
3358
|
if (verbose) {
|
|
3372
|
-
|
|
3373
|
-
console.log('[hmr-ws][ts-graph] candidate', { id, depsCount: deps.length });
|
|
3374
|
-
}
|
|
3375
|
-
catch { }
|
|
3359
|
+
console.log('[hmr-ws][ts-graph] candidate', { id, depsCount: deps.length });
|
|
3376
3360
|
}
|
|
3377
3361
|
// Serve-time warm-up: no live edit happened, so don't bump
|
|
3378
|
-
// graphVersion.
|
|
3362
|
+
// graphVersion.
|
|
3379
3363
|
upsertGraphModule(id, code, deps, { bumpVersion: false });
|
|
3380
3364
|
}
|
|
3381
3365
|
}
|
|
@@ -3599,19 +3583,20 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3599
3583
|
}
|
|
3600
3584
|
}
|
|
3601
3585
|
catch { }
|
|
3602
|
-
//
|
|
3586
|
+
// `/ns/rt` and `/ns/core` URL versioning.
|
|
3603
3587
|
//
|
|
3604
|
-
//
|
|
3605
|
-
//
|
|
3606
|
-
//
|
|
3607
|
-
// (HMRSupport.mm
|
|
3608
|
-
// version segments to the bare `/ns/rt` and
|
|
3609
|
-
// keys before lookup, so V8 actually saw a
|
|
3610
|
-
// entry — but the server was doing extra
|
|
3611
|
-
// version segment that the runtime then
|
|
3612
|
-
// stripped. Now that
|
|
3613
|
-
// these bridge endpoints don't
|
|
3614
|
-
// anyway), the version segment is
|
|
3588
|
+
// Older versions of the server emitted `/ns/rt/<ver>` and
|
|
3589
|
+
// `/ns/core/<ver>` so V8's HTTP module cache would see a
|
|
3590
|
+
// fresh URL on every save. The runtime canonicalizer
|
|
3591
|
+
// (`CanonicalizeHttpUrlKey` in HMRSupport.mm) collapses
|
|
3592
|
+
// these version segments to the bare `/ns/rt` and
|
|
3593
|
+
// `/ns/core` keys before lookup, so V8 actually saw a
|
|
3594
|
+
// single cache entry — but the server was doing extra
|
|
3595
|
+
// work to inject a version segment that the runtime then
|
|
3596
|
+
// immediately stripped. Now that the runtime supports
|
|
3597
|
+
// explicit eviction (and these bridge endpoints don't
|
|
3598
|
+
// change at HMR time anyway), the version segment is
|
|
3599
|
+
// purely vestigial.
|
|
3615
3600
|
//
|
|
3616
3601
|
// Rather than rip the helpers out (which would touch
|
|
3617
3602
|
// every ensureVersionedImports caller and risk bumping
|
|
@@ -3627,25 +3612,25 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3627
3612
|
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
3628
3613
|
}
|
|
3629
3614
|
catch { }
|
|
3630
|
-
//
|
|
3615
|
+
// `/ns/m` URL finalize step.
|
|
3631
3616
|
//
|
|
3632
|
-
// `rewriteNsMImportPathForHmr`
|
|
3633
|
-
//
|
|
3634
|
-
//
|
|
3635
|
-
//
|
|
3636
|
-
//
|
|
3637
|
-
//
|
|
3638
|
-
//
|
|
3639
|
-
//
|
|
3640
|
-
//
|
|
3641
|
-
//
|
|
3617
|
+
// `rewriteNsMImportPathForHmr` is a canonicalizer: it
|
|
3618
|
+
// strips legacy `__ns_hmr__/<tag>/` segments and adds
|
|
3619
|
+
// `__ns_boot__/b1/` only for boot-tagged requests. The
|
|
3620
|
+
// `ver` parameter is preserved on the signature for API
|
|
3621
|
+
// compatibility but is ignored for app modules (cache
|
|
3622
|
+
// busting is driven by `__nsInvalidateModules`, not URL
|
|
3623
|
+
// versioning). We pass `'v0'` as a stable placeholder —
|
|
3624
|
+
// the canonicalizer emits the same URL regardless of
|
|
3625
|
+
// this value, but a constant placeholder makes the
|
|
3626
|
+
// contract explicit.
|
|
3642
3627
|
//
|
|
3643
3628
|
// SFC URLs (line below, `/ns/sfc/${verTag}/...`) still
|
|
3644
3629
|
// embed a version because the Vue SFC pathway does not
|
|
3645
|
-
// yet have an eviction protocol. The runtime
|
|
3646
|
-
// does NOT strip `/ns/sfc/<ver>/`, so Vue
|
|
3647
|
-
// per-save SFC re-fetches — that's a
|
|
3648
|
-
//
|
|
3630
|
+
// yet have an eviction protocol. The runtime
|
|
3631
|
+
// canonicalizer does NOT strip `/ns/sfc/<ver>/`, so Vue
|
|
3632
|
+
// users still see per-save SFC re-fetches — that's a
|
|
3633
|
+
// known follow-up.
|
|
3649
3634
|
try {
|
|
3650
3635
|
const verTag = (() => {
|
|
3651
3636
|
const numeric = getNumericServeVersionTag(forcedVer, Number(graphVersion || 0));
|
|
@@ -3789,10 +3774,9 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3789
3774
|
// by the serving pipeline. Only warn, don't fatally block the importer.
|
|
3790
3775
|
const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
|
|
3791
3776
|
if (hasCjsPattern) {
|
|
3792
|
-
|
|
3777
|
+
if (verbose) {
|
|
3793
3778
|
console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
|
|
3794
3779
|
}
|
|
3795
|
-
catch { }
|
|
3796
3780
|
continue;
|
|
3797
3781
|
}
|
|
3798
3782
|
const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
|
|
@@ -3806,19 +3790,15 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3806
3790
|
}
|
|
3807
3791
|
}
|
|
3808
3792
|
catch (eLC) {
|
|
3809
|
-
|
|
3793
|
+
if (verbose) {
|
|
3810
3794
|
console.warn('[ns:m][link-check] failed', eLC?.message || eLC);
|
|
3811
3795
|
}
|
|
3812
|
-
catch { }
|
|
3813
3796
|
}
|
|
3814
3797
|
res.statusCode = 200;
|
|
3815
3798
|
res.end(code);
|
|
3816
3799
|
}
|
|
3817
3800
|
catch (e) {
|
|
3818
|
-
|
|
3819
|
-
console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
|
|
3820
|
-
}
|
|
3821
|
-
catch { }
|
|
3801
|
+
console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
|
|
3822
3802
|
res.statusCode = 500;
|
|
3823
3803
|
res.end('export {}\n');
|
|
3824
3804
|
}
|
|
@@ -3851,8 +3831,10 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3851
3831
|
`let __cached_rt = null;\n` +
|
|
3852
3832
|
`let __cached_vm = null;\n` +
|
|
3853
3833
|
`const __RT_REALM_TAG = (globalThis.__NS_RT_REALM__ ||= Math.random().toString(36).slice(2));\n` +
|
|
3854
|
-
//
|
|
3855
|
-
|
|
3834
|
+
// One-shot evaluation marker to confirm the bridge is executed on
|
|
3835
|
+
// device. Gated on __NS_ENV_VERBOSE__ so it stays silent unless
|
|
3836
|
+
// the developer opts in via NS_VITE_VERBOSE / VITE_DEBUG_LOGS.
|
|
3837
|
+
`try { if (!(globalThis.__NS_RT_ONCE__ && globalThis.__NS_RT_ONCE__.eval)) { (globalThis.__NS_RT_ONCE__ ||= {}).eval = true; if (globalThis.__NS_ENV_VERBOSE__) console.log('[ns-rt] evaluated', { rtRealm: __RT_REALM_TAG }); } } catch {}\n` +
|
|
3856
3838
|
`function __ensure(){\n` +
|
|
3857
3839
|
` if (__cached_rt) return __cached_rt;\n` +
|
|
3858
3840
|
` let vm = null;\n` +
|
|
@@ -3944,7 +3926,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3944
3926
|
`export const vShow = (__ensure().vShow);\n` +
|
|
3945
3927
|
`export const createApp = (...a) => (__ensure().createApp)(...a);\n` +
|
|
3946
3928
|
`export const registerElement = (...a) => (__ensure().registerElement)(...a);\n` +
|
|
3947
|
-
`export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) {
|
|
3929
|
+
`export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) { console.error('[ns-rt] $navigateTo app navigator error', e); throw e; } } console.error('[ns-rt] $navigateTo unavailable: app navigator missing'); throw new Error('$navigateTo unavailable: app navigator missing'); } ;\n` +
|
|
3948
3930
|
`export const $navigateBack = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$navigateBack || (vm.default && vm.default.$navigateBack))) || (rt && (rt.$navigateBack || (rt.runtimeHelpers && rt.runtimeHelpers.navigateBack))); let res; try { const via = (impl && (impl === (vm && vm.$navigateBack) || impl === (vm && vm.default && vm.default.$navigateBack))) ? 'vm' : (impl ? 'rt' : 'none'); } catch {} try { if (typeof impl === 'function') res = impl(...a); } catch {} try { const top = (g && g.Frame && g.Frame.topmost && g.Frame.topmost()); if (!res && top && top.canGoBack && top.canGoBack()) { res = top.goBack(); } } catch {} try { const hook = g && (g.__NS_HMR_ON_NAVIGATE_BACK || g.__NS_HMR_ON_BACK || g.__nsAttemptBackRemount); if (typeof hook === 'function') hook(); } catch {} return res; }\n` +
|
|
3949
3931
|
`export const $showModal = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$showModal || (vm.default && vm.default.$showModal))) || (rt && (rt.$showModal || (rt.runtimeHelpers && rt.runtimeHelpers.showModal))); try { if (typeof impl === 'function') return impl(...a); } catch (e) { } return undefined; }\n` +
|
|
3950
3932
|
`export default {\n` +
|
|
@@ -4032,15 +4014,15 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4032
4014
|
});
|
|
4033
4015
|
// 2.6) ESM bridge for @nativescript/core: GET /ns/core[/<ver>][?p=sub/path]
|
|
4034
4016
|
//
|
|
4035
|
-
// Since bundle.mjs no longer bundles @nativescript/core (
|
|
4036
|
-
//
|
|
4037
|
-
//
|
|
4038
|
-
//
|
|
4039
|
-
//
|
|
4040
|
-
//
|
|
4041
|
-
//
|
|
4042
|
-
//
|
|
4043
|
-
//
|
|
4017
|
+
// Since bundle.mjs no longer bundles @nativescript/core (it is
|
|
4018
|
+
// declared external in the rolldown config under HMR), this
|
|
4019
|
+
// endpoint is the ONE place core is evaluated. Every consumer —
|
|
4020
|
+
// bundle.mjs's own `@nativescript/core*` imports (resolved to
|
|
4021
|
+
// full HTTP URLs in the entry virtual module), externalized
|
|
4022
|
+
// vendor packages, HTTP-served app modules — all end up here.
|
|
4023
|
+
// No more proxy bridge, no enumeration, no namespace detection,
|
|
4024
|
+
// no prototype-polluted maps. We just serve Vite's authoritative
|
|
4025
|
+
// transformed module content.
|
|
4044
4026
|
//
|
|
4045
4027
|
// iOS caches by URL path, so each unique URL is evaluated exactly
|
|
4046
4028
|
// once per app lifetime. Every class identity is shared, every
|
|
@@ -4147,16 +4129,15 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4147
4129
|
`const __TEST__ = false;`,
|
|
4148
4130
|
].join('\n');
|
|
4149
4131
|
// Boot-time instrumentation + module self-registration.
|
|
4150
|
-
//
|
|
4151
|
-
// -
|
|
4152
|
-
//
|
|
4153
|
-
//
|
|
4154
|
-
//
|
|
4155
|
-
//
|
|
4156
|
-
//
|
|
4157
|
-
// on mismatch so drift in any emitter surfaces
|
|
4132
|
+
//
|
|
4133
|
+
// - URL canonicalization: the same logical module must
|
|
4134
|
+
// always resolve to byte-identical URLs across every
|
|
4135
|
+
// emitter. The /ns/core handler records the first URL
|
|
4136
|
+
// seen for each canonical sub (or '' for main) in
|
|
4137
|
+
// `globalThis.__NS_CORE_FIRST_URL__` and fails hard on
|
|
4138
|
+
// mismatch so drift in any emitter surfaces
|
|
4158
4139
|
// immediately, before the realm splits.
|
|
4159
|
-
// -
|
|
4140
|
+
// - CJS/ESM boot order: CommonJS
|
|
4160
4141
|
// `require('@nativescript/core/...')` calls from
|
|
4161
4142
|
// vendor install() hooks must resolve to the SAME
|
|
4162
4143
|
// ESM namespace that ran this side-effect preamble.
|
|
@@ -4185,13 +4166,13 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4185
4166
|
` const __nsUrl = ${JSON.stringify(canonicalUrl)};`,
|
|
4186
4167
|
` __nsSeen.push(__nsUrl);`,
|
|
4187
4168
|
` if (typeof __nsFirst[__nsKey] === 'string' && __nsFirst[__nsKey] !== __nsUrl) {`,
|
|
4188
|
-
` throw new Error('[ns-core] URL drift for sub=' + __nsKey + ': first=' + __nsFirst[__nsKey] + ' now=' + __nsUrl
|
|
4169
|
+
` throw new Error('[ns-core] URL drift for sub=' + __nsKey + ': first=' + __nsFirst[__nsKey] + ' now=' + __nsUrl);`,
|
|
4189
4170
|
` }`,
|
|
4190
4171
|
` if (!__nsFirst[__nsKey]) __nsFirst[__nsKey] = __nsUrl;`,
|
|
4191
4172
|
` globalThis.__NS_CORE_EVAL_COUNT__ = (globalThis.__NS_CORE_EVAL_COUNT__ || 0) + 1;`,
|
|
4192
|
-
`} } catch (e) {
|
|
4173
|
+
`} } catch (e) { console.warn('[ns-core] instrumentation failed:', (e && e.message) || e); }`,
|
|
4193
4174
|
].join('\n');
|
|
4194
|
-
//
|
|
4175
|
+
// CJS/ESM interop shape — REGISTRATION side.
|
|
4195
4176
|
//
|
|
4196
4177
|
// The actual shape installer runs earlier in the module
|
|
4197
4178
|
// body (between preamble and selfImport; see
|
|
@@ -4207,9 +4188,6 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4207
4188
|
// registration — the shape function is identity-preserving
|
|
4208
4189
|
// via WeakMap — gives a stable, shared, CJS-compatible
|
|
4209
4190
|
// view without copying on every require.
|
|
4210
|
-
//
|
|
4211
|
-
// See HMR_CORE_REALM_DETERMINISTIC_PLAN.md § "Invariant D"
|
|
4212
|
-
// for the full rationale.
|
|
4213
4191
|
const registrationFooter = [
|
|
4214
4192
|
`try { if (typeof globalThis !== 'undefined') {`,
|
|
4215
4193
|
` const __nsReg = globalThis.__NS_CORE_MODULES__ || (globalThis.__NS_CORE_MODULES__ = Object.create(null));`,
|
|
@@ -4217,7 +4195,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4217
4195
|
` const __nsSelfRaw = (typeof __ns_core_self_ns__ !== 'undefined') ? __ns_core_self_ns__ : { default: undefined };`,
|
|
4218
4196
|
` const __nsSelf = __nsShapeFn(__nsSelfRaw);`,
|
|
4219
4197
|
...registrationKeys.map((k) => ` __nsReg[${k}] = __nsSelf;`),
|
|
4220
|
-
`} } catch (e) {
|
|
4198
|
+
`} } catch (e) { console.warn('[ns-core] self-register failed:', (e && e.message) || e); }`,
|
|
4221
4199
|
].join('\n');
|
|
4222
4200
|
// Bind `import * as __ns_core_self_ns__` to the module's
|
|
4223
4201
|
// own export namespace so the footer can stash it into
|
|
@@ -4265,10 +4243,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4265
4243
|
res.end(moduleCode);
|
|
4266
4244
|
}
|
|
4267
4245
|
catch (e) {
|
|
4268
|
-
|
|
4269
|
-
console.warn('[ns-core-bridge] serve failed:', e?.message);
|
|
4270
|
-
}
|
|
4271
|
-
catch { }
|
|
4246
|
+
console.warn('[ns-core-bridge] serve failed:', e?.message);
|
|
4272
4247
|
next();
|
|
4273
4248
|
}
|
|
4274
4249
|
});
|
|
@@ -4278,14 +4253,11 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4278
4253
|
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4279
4254
|
if (!(urlObj.pathname === '/ns/entry-rt'))
|
|
4280
4255
|
return next();
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
|
|
4286
|
-
}
|
|
4256
|
+
if (verbose) {
|
|
4257
|
+
const ra = req.socket?.remoteAddress;
|
|
4258
|
+
const rp = req.socket?.remotePort;
|
|
4259
|
+
console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
|
|
4287
4260
|
}
|
|
4288
|
-
catch { }
|
|
4289
4261
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4290
4262
|
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4291
4263
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
@@ -4321,7 +4293,8 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4321
4293
|
content = 'export default async function start(){ console.error("[/ns/entry-rt] not found"); }\n';
|
|
4322
4294
|
}
|
|
4323
4295
|
}
|
|
4324
|
-
|
|
4296
|
+
if (verbose)
|
|
4297
|
+
console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
|
|
4325
4298
|
res.statusCode = 200;
|
|
4326
4299
|
res.end(content);
|
|
4327
4300
|
}
|
|
@@ -4529,10 +4502,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4529
4502
|
}
|
|
4530
4503
|
if (!transformed?.code) {
|
|
4531
4504
|
if (verbose) {
|
|
4532
|
-
|
|
4533
|
-
console.warn(`[sfc][serve] transform miss for`, fullSpec);
|
|
4534
|
-
}
|
|
4535
|
-
catch { }
|
|
4505
|
+
console.warn(`[sfc][serve] transform miss for`, fullSpec);
|
|
4536
4506
|
}
|
|
4537
4507
|
// Emit an erroring module to surface the failure at import site with helpful hints
|
|
4538
4508
|
try {
|
|
@@ -4648,10 +4618,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4648
4618
|
}
|
|
4649
4619
|
catch (eTplSelf) {
|
|
4650
4620
|
if (verbose) {
|
|
4651
|
-
|
|
4652
|
-
console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
|
|
4653
|
-
}
|
|
4654
|
-
catch { }
|
|
4621
|
+
console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
|
|
4655
4622
|
}
|
|
4656
4623
|
code = transformed.code || 'export {}\n';
|
|
4657
4624
|
code = processTemplateVariantMinimal(code);
|
|
@@ -4818,10 +4785,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4818
4785
|
}
|
|
4819
4786
|
catch (eTsVar) {
|
|
4820
4787
|
if (verbose) {
|
|
4821
|
-
|
|
4822
|
-
console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
|
|
4823
|
-
}
|
|
4824
|
-
catch { }
|
|
4788
|
+
console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
|
|
4825
4789
|
}
|
|
4826
4790
|
}
|
|
4827
4791
|
}
|
|
@@ -4885,10 +4849,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4885
4849
|
const kind = isVariant ? `variant:${variantType || 'unknown'}` : 'full';
|
|
4886
4850
|
const sig = `// [sfc] kind=${kind} path=${importerPath} len=${code.length} default=${hasDefault} wrapped=${false}\n`;
|
|
4887
4851
|
if (verbose) {
|
|
4888
|
-
|
|
4889
|
-
console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
|
|
4890
|
-
}
|
|
4891
|
-
catch { }
|
|
4852
|
+
console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
|
|
4892
4853
|
}
|
|
4893
4854
|
// Ensure script variants always provide a default export if they declare a component
|
|
4894
4855
|
if (!hasDefault) {
|
|
@@ -4957,16 +4918,10 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4957
4918
|
// 6) Object property forms (rare in template output) render: (...) => {}
|
|
4958
4919
|
const hasRender = /export\s+function\s+render\s*\(/.test(templateCode) || /(?:^|\n)\s*function\s+render\s*\(/.test(templateCode) || /export\s+(?:const|let|var)\s+render\s*=/.test(templateCode) || /(?:^|\n)\s*(?:const|let|var)\s+render\s*=/.test(templateCode) || /\brender\s*[:=]\s*/.test(templateCode) || /export\s*\{\s*render\s*(?:as\s*render)?\s*\}/.test(templateCode);
|
|
4959
4920
|
if (hasRender && verbose) {
|
|
4960
|
-
|
|
4961
|
-
console.log('[sfc-meta] detected render for', base);
|
|
4962
|
-
}
|
|
4963
|
-
catch { }
|
|
4921
|
+
console.log('[sfc-meta] detected render for', base);
|
|
4964
4922
|
}
|
|
4965
4923
|
else if (!hasRender && verbose) {
|
|
4966
|
-
|
|
4967
|
-
console.warn('[sfc-meta] render NOT detected for', base);
|
|
4968
|
-
}
|
|
4969
|
-
catch { }
|
|
4924
|
+
console.warn('[sfc-meta] render NOT detected for', base);
|
|
4970
4925
|
}
|
|
4971
4926
|
const hash = createHash('md5').update(base).digest('hex').slice(0, 8);
|
|
4972
4927
|
const payload = {
|
|
@@ -5100,10 +5055,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5100
5055
|
}
|
|
5101
5056
|
catch (eScript) {
|
|
5102
5057
|
if (verbose) {
|
|
5103
|
-
|
|
5104
|
-
console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
|
|
5105
|
-
}
|
|
5106
|
-
catch { }
|
|
5058
|
+
console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
|
|
5107
5059
|
}
|
|
5108
5060
|
// Retry without inlineTemplate
|
|
5109
5061
|
try {
|
|
@@ -5119,10 +5071,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5119
5071
|
}
|
|
5120
5072
|
catch (eNoInline) {
|
|
5121
5073
|
if (verbose) {
|
|
5122
|
-
|
|
5123
|
-
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5124
|
-
}
|
|
5125
|
-
catch { }
|
|
5074
|
+
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5126
5075
|
}
|
|
5127
5076
|
}
|
|
5128
5077
|
}
|
|
@@ -5153,10 +5102,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5153
5102
|
}
|
|
5154
5103
|
catch (eNoInline) {
|
|
5155
5104
|
if (verbose) {
|
|
5156
|
-
|
|
5157
|
-
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5158
|
-
}
|
|
5159
|
-
catch { }
|
|
5105
|
+
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5160
5106
|
}
|
|
5161
5107
|
}
|
|
5162
5108
|
}
|
|
@@ -5179,20 +5125,14 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5179
5125
|
});
|
|
5180
5126
|
compiledTplCode = (ct && (ct.code || '')) || '';
|
|
5181
5127
|
if (ct?.errors?.length && verbose) {
|
|
5182
|
-
|
|
5183
|
-
console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
|
|
5184
|
-
}
|
|
5185
|
-
catch { }
|
|
5128
|
+
console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
|
|
5186
5129
|
}
|
|
5187
5130
|
}
|
|
5188
5131
|
}
|
|
5189
5132
|
catch (eTpl) {
|
|
5190
5133
|
templateErr = eTpl;
|
|
5191
5134
|
if (verbose) {
|
|
5192
|
-
|
|
5193
|
-
console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
|
|
5194
|
-
}
|
|
5195
|
-
catch { }
|
|
5135
|
+
console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
|
|
5196
5136
|
}
|
|
5197
5137
|
// Fallback: use the variant-transformed template code if available
|
|
5198
5138
|
try {
|
|
@@ -5255,10 +5195,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5255
5195
|
}
|
|
5256
5196
|
catch (eExtract) {
|
|
5257
5197
|
if (verbose) {
|
|
5258
|
-
|
|
5259
|
-
console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
|
|
5260
|
-
}
|
|
5261
|
-
catch { }
|
|
5198
|
+
console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
|
|
5262
5199
|
}
|
|
5263
5200
|
}
|
|
5264
5201
|
}
|
|
@@ -5336,10 +5273,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5336
5273
|
}
|
|
5337
5274
|
catch (eTs) {
|
|
5338
5275
|
if (verbose) {
|
|
5339
|
-
|
|
5340
|
-
console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
|
|
5341
|
-
}
|
|
5342
|
-
catch { }
|
|
5276
|
+
console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
|
|
5343
5277
|
}
|
|
5344
5278
|
}
|
|
5345
5279
|
// Hoist imports + strip residual TS via AST
|
|
@@ -5349,18 +5283,12 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5349
5283
|
importLines = astRes.imports;
|
|
5350
5284
|
scriptTransformed = astRes.body;
|
|
5351
5285
|
if (astRes.diagnostics.length && verbose) {
|
|
5352
|
-
|
|
5353
|
-
console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
|
|
5354
|
-
}
|
|
5355
|
-
catch { }
|
|
5286
|
+
console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
|
|
5356
5287
|
}
|
|
5357
5288
|
}
|
|
5358
5289
|
catch (eAst) {
|
|
5359
5290
|
if (verbose) {
|
|
5360
|
-
|
|
5361
|
-
console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
|
|
5362
|
-
}
|
|
5363
|
-
catch { }
|
|
5291
|
+
console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
|
|
5364
5292
|
}
|
|
5365
5293
|
}
|
|
5366
5294
|
// Ensure renderDecl ends with closing brace ONLY for function declaration forms
|
|
@@ -6054,13 +5982,12 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6054
5982
|
if (isRuntimeGraphExcludedPath(file)) {
|
|
6055
5983
|
return;
|
|
6056
5984
|
}
|
|
6057
|
-
// Always-on update timing
|
|
6058
|
-
//
|
|
6059
|
-
//
|
|
6060
|
-
//
|
|
6061
|
-
//
|
|
6062
|
-
//
|
|
6063
|
-
// without flipping verbose.
|
|
5985
|
+
// Always-on update timing. Captures the four phases (await,
|
|
5986
|
+
// framework, broadcast, total) plus invalidated module count
|
|
5987
|
+
// and recipient count. Emitted at the end of this function via
|
|
5988
|
+
// `emitHmrUpdateSummary()`. Single line, always-on so a
|
|
5989
|
+
// 6-second `.ts` save is immediately visible without flipping
|
|
5990
|
+
// verbose.
|
|
6064
5991
|
const updateRoot = server.config.root || process.cwd();
|
|
6065
5992
|
const updateRel = (() => {
|
|
6066
5993
|
try {
|
|
@@ -6079,21 +6006,19 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6079
6006
|
tEnd: 0,
|
|
6080
6007
|
invalidated: 0,
|
|
6081
6008
|
recipients: 0,
|
|
6082
|
-
//
|
|
6009
|
+
// Narrowing diagnostic — populated by the angular branch when
|
|
6083
6010
|
// the changed file is `.ts`, otherwise remains undefined and is
|
|
6084
|
-
// omitted from the summary line entirely.
|
|
6085
|
-
// HMR_CORE_REALM_DETERMINISTIC_PLAN.md ("Round-eight — surface
|
|
6086
|
-
// narrowing decision").
|
|
6011
|
+
// omitted from the summary line entirely.
|
|
6087
6012
|
narrowed: undefined,
|
|
6088
6013
|
emitted: false,
|
|
6089
6014
|
};
|
|
6090
|
-
//
|
|
6091
|
-
//
|
|
6092
|
-
//
|
|
6093
|
-
//
|
|
6094
|
-
//
|
|
6095
|
-
//
|
|
6096
|
-
//
|
|
6015
|
+
// Broadcast a "pending" notification at the very start of
|
|
6016
|
+
// handleHotUpdate so the client can show the HMR-applying
|
|
6017
|
+
// overlay BEFORE we spend time on graph updates / transforms /
|
|
6018
|
+
// dependency analysis (typically 7–200ms on a warm cache).
|
|
6019
|
+
// Without this, the overlay only appears at `ns:angular-update`
|
|
6020
|
+
// broadcast time and the user perceives a "delayed" reaction
|
|
6021
|
+
// to their save.
|
|
6097
6022
|
//
|
|
6098
6023
|
// Fire-and-forget: a failed pending broadcast must never
|
|
6099
6024
|
// hold up the actual update. The client treats receipt of
|
|
@@ -6140,10 +6065,11 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6140
6065
|
}
|
|
6141
6066
|
catch { }
|
|
6142
6067
|
};
|
|
6143
|
-
//
|
|
6144
|
-
//
|
|
6145
|
-
// completes, we'd lose transitive-importer data. Await
|
|
6146
|
-
// here so the delta computation below always sees a
|
|
6068
|
+
// The first /ns/m request kicks off populateInitialGraph in the
|
|
6069
|
+
// background. If an HMR update races in before that walk
|
|
6070
|
+
// completes, we'd lose transitive-importer data. Await
|
|
6071
|
+
// completion here so the delta computation below always sees a
|
|
6072
|
+
// populated graph.
|
|
6147
6073
|
if (graphInitialPopulationPromise) {
|
|
6148
6074
|
try {
|
|
6149
6075
|
await graphInitialPopulationPromise;
|
|
@@ -6160,6 +6086,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6160
6086
|
flavor: ACTIVE_STRATEGY.flavor,
|
|
6161
6087
|
modules: ctx.modules,
|
|
6162
6088
|
getModuleById: (id) => server.moduleGraph.getModuleById(id),
|
|
6089
|
+
verbose,
|
|
6163
6090
|
});
|
|
6164
6091
|
for (const mod of graphTargets) {
|
|
6165
6092
|
if (!mod?.id)
|
|
@@ -6240,6 +6167,12 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6240
6167
|
getModuleById: (id) => server.moduleGraph.getModuleById(id),
|
|
6241
6168
|
getModulesByFile: (targetFile) => server.moduleGraph.getModulesByFile?.(targetFile),
|
|
6242
6169
|
});
|
|
6170
|
+
if (verbose) {
|
|
6171
|
+
console.info(`[ns-hmr-diag][server] hot-update file=${file} isHtml=${isHtml} isTs=${isTs} ctxModules=${Array.from(ctx.modules || []).length} hotUpdateRoots=${angularHotUpdateRoots.length} (${angularHotUpdateRoots
|
|
6172
|
+
.map((m) => m?.id ?? '(none)')
|
|
6173
|
+
.slice(0, 8)
|
|
6174
|
+
.join(', ')}${angularHotUpdateRoots.length > 8 ? ', …' : ''})`);
|
|
6175
|
+
}
|
|
6243
6176
|
if (!(isHtml || isTs))
|
|
6244
6177
|
return;
|
|
6245
6178
|
updateMetrics.invalidated += angularHotUpdateRoots.length;
|
|
@@ -6259,13 +6192,13 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6259
6192
|
}
|
|
6260
6193
|
}
|
|
6261
6194
|
const angularTransitiveInvalidationRoots = (angularHotUpdateRoots.length ? angularHotUpdateRoots : ctx.modules);
|
|
6262
|
-
//
|
|
6263
|
-
//
|
|
6195
|
+
// Read the source for `.ts/.tsx/.js/.jsx` edits so
|
|
6196
|
+
// `shouldInvalidateAngularTransitiveImporters` can
|
|
6264
6197
|
// distinguish leaf modules (constants/utils) from real
|
|
6265
6198
|
// Angular files. If `ctx.read()` throws (file deleted, race
|
|
6266
6199
|
// against the watcher), `angularChangedSource` stays
|
|
6267
|
-
// undefined and we fall back to the conservative
|
|
6268
|
-
//
|
|
6200
|
+
// undefined and we fall back to the conservative "always
|
|
6201
|
+
// invalidate transitively" behavior.
|
|
6269
6202
|
let angularChangedSource;
|
|
6270
6203
|
if (isTs) {
|
|
6271
6204
|
try {
|
|
@@ -6280,37 +6213,36 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6280
6213
|
file,
|
|
6281
6214
|
source: angularChangedSource,
|
|
6282
6215
|
});
|
|
6283
|
-
//
|
|
6284
|
-
//
|
|
6285
|
-
//
|
|
6286
|
-
//
|
|
6287
|
-
//
|
|
6288
|
-
//
|
|
6289
|
-
//
|
|
6216
|
+
// Surface the narrowing decision on every `.ts` Angular hot
|
|
6217
|
+
// update (HTML routes always invalidate transitively and
|
|
6218
|
+
// aren't subject to narrowing, so we leave them as
|
|
6219
|
+
// `undefined` — the field is omitted from the summary line).
|
|
6220
|
+
// The boolean is the inverse of `angularNeedsTransitive`
|
|
6221
|
+
// because "needs transitive" is the broad (un-narrowed)
|
|
6222
|
+
// behavior.
|
|
6290
6223
|
if (isTs) {
|
|
6291
6224
|
updateMetrics.narrowed = !angularNeedsTransitive;
|
|
6292
6225
|
}
|
|
6293
|
-
//
|
|
6226
|
+
// Stable URL + Explicit Invalidation:
|
|
6294
6227
|
//
|
|
6295
|
-
// Compute the transitive importer closure ONCE here and reuse
|
|
6296
|
-
// for (a) `server.moduleGraph.invalidateModule` (so Vite's
|
|
6228
|
+
// Compute the transitive importer closure ONCE here and reuse
|
|
6229
|
+
// it for (a) `server.moduleGraph.invalidateModule` (so Vite's
|
|
6297
6230
|
// transform pipeline re-runs on next request), (b) the shared
|
|
6298
|
-
// transform-request cache, and (c) the runtime eviction set
|
|
6299
|
-
// broadcast in `ns:angular-update`.
|
|
6300
|
-
// this list twice in adjacent try blocks; consolidating it
|
|
6231
|
+
// transform-request cache, and (c) the runtime eviction set
|
|
6232
|
+
// we broadcast in `ns:angular-update`. Consolidating this
|
|
6301
6233
|
// removes a redundant graph walk and guarantees the three
|
|
6302
6234
|
// consumers see the exact same set of importers (otherwise a
|
|
6303
6235
|
// late module-graph mutation between calls could leave an
|
|
6304
6236
|
// asymmetric narrowed/broad mix).
|
|
6305
6237
|
//
|
|
6306
|
-
//
|
|
6307
|
-
//
|
|
6308
|
-
//
|
|
6309
|
-
// must be re-transformed by Vite?". The runtime, however,
|
|
6310
|
-
// a stricter requirement: ESM live bindings only refresh
|
|
6311
|
-
// the importing module re-evaluates inside V8. A
|
|
6312
|
-
// file with no Angular decorator does NOT need a
|
|
6313
|
-
// re-transform of its importers (their compiled JS is
|
|
6238
|
+
// We separate Vite-transform narrowing from runtime eviction:
|
|
6239
|
+
// `angularNeedsTransitive` answers the question "does the
|
|
6240
|
+
// changed file's symbol shape change such that importers
|
|
6241
|
+
// must be re-transformed by Vite?". The runtime, however,
|
|
6242
|
+
// has a stricter requirement: ESM live bindings only refresh
|
|
6243
|
+
// if the importing module re-evaluates inside V8. A
|
|
6244
|
+
// constants file with no Angular decorator does NOT need a
|
|
6245
|
+
// Vite re-transform of its importers (their compiled JS is
|
|
6314
6246
|
// identical), but its importers still hold stale bindings to
|
|
6315
6247
|
// the OLD constants Module record. After eviction + re-import
|
|
6316
6248
|
// of `main.ts`, V8 sees the cached importers, returns them
|
|
@@ -6318,12 +6250,16 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6318
6250
|
// user-visible symptom: HMR completes successfully, logs are
|
|
6319
6251
|
// clean, but the simulator does not reflect the change.
|
|
6320
6252
|
//
|
|
6321
|
-
// The fix: ALWAYS compute the transitive importer closure
|
|
6322
|
-
// runtime eviction. Only skip Vite's
|
|
6323
|
-
// + transform-cache purge when
|
|
6324
|
-
// false — those are the genuine
|
|
6325
|
-
// re-transform work on the server).
|
|
6326
|
-
// includes importers so V8 re-fetches
|
|
6253
|
+
// The fix: ALWAYS compute the transitive importer closure
|
|
6254
|
+
// for runtime eviction. Only skip Vite's
|
|
6255
|
+
// `moduleGraph.invalidate` + transform-cache purge when
|
|
6256
|
+
// `angularNeedsTransitive` is false — those are the genuine
|
|
6257
|
+
// narrowing wins (saves re-transform work on the server).
|
|
6258
|
+
// The eviction set always includes importers so V8 re-fetches
|
|
6259
|
+
// and re-binds them.
|
|
6260
|
+
if (verbose) {
|
|
6261
|
+
console.info(`[ns-hmr-diag][server] angularNeedsTransitive=${angularNeedsTransitive} (file=${path.basename(file)})`);
|
|
6262
|
+
}
|
|
6327
6263
|
let transitiveImporters = [];
|
|
6328
6264
|
try {
|
|
6329
6265
|
transitiveImporters = collectAngularTransitiveImportersForInvalidation({
|
|
@@ -6331,6 +6267,9 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6331
6267
|
isExcluded: (id) => id.includes('/node_modules/'),
|
|
6332
6268
|
maxDepth: 16,
|
|
6333
6269
|
});
|
|
6270
|
+
if (verbose) {
|
|
6271
|
+
console.info(`[ns-hmr-diag][server] transitiveImporters count=${transitiveImporters.length} firstN=`, transitiveImporters.slice(0, 16).map((m) => m?.id ?? '(none)'));
|
|
6272
|
+
}
|
|
6334
6273
|
if (angularNeedsTransitive) {
|
|
6335
6274
|
updateMetrics.invalidated += transitiveImporters.length;
|
|
6336
6275
|
for (const mod of transitiveImporters) {
|
|
@@ -6348,19 +6287,18 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6348
6287
|
}
|
|
6349
6288
|
}
|
|
6350
6289
|
else if (isTs && typeof angularChangedSource === 'string') {
|
|
6351
|
-
//
|
|
6352
|
-
//
|
|
6353
|
-
//
|
|
6354
|
-
//
|
|
6355
|
-
//
|
|
6356
|
-
//
|
|
6290
|
+
// Surfacing this log unconditionally lets the user
|
|
6291
|
+
// immediately confirm whether narrowing fired for a
|
|
6292
|
+
// given `.ts` edit (the summary line below still
|
|
6293
|
+
// emits `narrowed=yes`/`no`, but having both makes
|
|
6294
|
+
// the decision easier to spot in noisy logs and lets
|
|
6295
|
+
// the user diff scenarios without flipping
|
|
6357
6296
|
// `NS_HMR_VERBOSE=true`).
|
|
6358
6297
|
//
|
|
6359
|
-
//
|
|
6360
|
-
//
|
|
6361
|
-
// registry so live bindings refresh). The
|
|
6362
|
-
//
|
|
6363
|
-
// distinction visible.
|
|
6298
|
+
// Narrowing means "skip Vite re-transform" (the
|
|
6299
|
+
// importers still get evicted from the V8 module
|
|
6300
|
+
// registry so live bindings refresh). The importer
|
|
6301
|
+
// count is appended so the distinction is visible.
|
|
6364
6302
|
console.log(`[hmr-ws][angular] narrowed transitive invalidation (no @Component/@Directive/@Pipe/@Injectable/@NgModule): ${updateRel} — Vite transform skipped, runtime eviction includes ${transitiveImporters.length} importer(s)`);
|
|
6365
6303
|
}
|
|
6366
6304
|
}
|
|
@@ -6369,13 +6307,13 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6369
6307
|
console.warn('[hmr-ws][angular] transitive importer collection failed', error);
|
|
6370
6308
|
}
|
|
6371
6309
|
try {
|
|
6372
|
-
//
|
|
6373
|
-
//
|
|
6374
|
-
// output IS different now). Transitive importers are
|
|
6375
|
-
// purged when narrowing decides their output may
|
|
6376
|
-
// changed; otherwise their cached transforms are
|
|
6377
|
-
// valid (compiled JS is identical even though the
|
|
6378
|
-
// must re-evaluate them to refresh ESM bindings).
|
|
6310
|
+
// Purge shared transform cache for the changed file +
|
|
6311
|
+
// hot-update roots unconditionally (their transform
|
|
6312
|
+
// output IS different now). Transitive importers are
|
|
6313
|
+
// only purged when narrowing decides their output may
|
|
6314
|
+
// have changed; otherwise their cached transforms are
|
|
6315
|
+
// still valid (compiled JS is identical even though the
|
|
6316
|
+
// runtime must re-evaluate them to refresh ESM bindings).
|
|
6379
6317
|
const transformCacheInvalidationUrls = new Set(collectAngularTransformCacheInvalidationUrls({
|
|
6380
6318
|
file,
|
|
6381
6319
|
isTs,
|
|
@@ -6401,19 +6339,19 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6401
6339
|
rememberAngularReloadSuppression(root, file);
|
|
6402
6340
|
const origin = getServerOrigin(server);
|
|
6403
6341
|
const bootstrapEntryRel = getBootstrapEntryRelPath();
|
|
6404
|
-
//
|
|
6342
|
+
// Stable URL + Explicit Invalidation:
|
|
6405
6343
|
//
|
|
6406
6344
|
// `evictPaths` is the canonical list of `/ns/m/<rel>` URLs
|
|
6407
6345
|
// the runtime must drop from `g_moduleRegistry` before
|
|
6408
|
-
// re-importing `importerEntry`.
|
|
6409
|
-
// signaled invalidation by bumping a global
|
|
6410
|
-
// counter and embedding it in every URL —
|
|
6411
|
-
// module registry by full URL, so a v1 →
|
|
6412
|
-
// effectively flushed the entire dependency
|
|
6413
|
-
// the cache and forced the runtime to
|
|
6414
|
-
// every transitively-imported module
|
|
6415
|
-
// HMR cycles, dominated by Vite's
|
|
6416
|
-
// pipeline). The new model:
|
|
6346
|
+
// re-importing `importerEntry`. Older versions of the
|
|
6347
|
+
// server signaled invalidation by bumping a global
|
|
6348
|
+
// `graphVersion` counter and embedding it in every URL —
|
|
6349
|
+
// but V8 keys the module registry by full URL, so a v1 →
|
|
6350
|
+
// v2 bump effectively flushed the entire dependency
|
|
6351
|
+
// graph from the cache and forced the runtime to
|
|
6352
|
+
// re-fetch + re-eval every transitively-imported module
|
|
6353
|
+
// on each save (~3s HMR cycles, dominated by Vite's
|
|
6354
|
+
// single-threaded transform pipeline). The new model:
|
|
6417
6355
|
//
|
|
6418
6356
|
// 1. URLs are stable: `/ns/m/<rel>` everywhere, no `vN`.
|
|
6419
6357
|
// 2. The server walks the inverse-dependency closure and
|
|
@@ -6445,14 +6383,23 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6445
6383
|
});
|
|
6446
6384
|
}
|
|
6447
6385
|
catch (error) {
|
|
6448
|
-
if (verbose)
|
|
6449
|
-
console.warn('[hmr-
|
|
6386
|
+
if (verbose) {
|
|
6387
|
+
console.warn('[ns-hmr-diag][server] eviction set computation failed', error);
|
|
6388
|
+
}
|
|
6450
6389
|
}
|
|
6451
6390
|
if (verbose) {
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6391
|
+
try {
|
|
6392
|
+
const tsRel = rel.replace(/\.(html|htm)$/i, '.ts');
|
|
6393
|
+
const jsRel = rel.replace(/\.(html|htm)$/i, '.js');
|
|
6394
|
+
const containsRelatedTs = evictPaths.some((u) => u.endsWith(tsRel));
|
|
6395
|
+
const containsRelatedJs = evictPaths.some((u) => u.endsWith(jsRel));
|
|
6396
|
+
const sample = evictPaths.slice(0, 32);
|
|
6397
|
+
console.info(`[ns-hmr-diag][server] evict-set count=${evictPaths.length} importerEntry=${bootstrapEntryRel ?? '(none)'} containsRelatedTs=${containsRelatedTs} containsRelatedJs=${containsRelatedJs} firstN=`, sample);
|
|
6398
|
+
if (evictPaths.length > sample.length) {
|
|
6399
|
+
console.info(`[ns-hmr-diag][server] evict-set hidden=${evictPaths.length - sample.length} (showed first ${sample.length})`);
|
|
6400
|
+
}
|
|
6401
|
+
}
|
|
6402
|
+
catch { }
|
|
6456
6403
|
}
|
|
6457
6404
|
const msg = {
|
|
6458
6405
|
type: 'ns:angular-update',
|
|
@@ -6550,7 +6497,8 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6550
6497
|
console.log('[hmr-ws] Not a .vue file, skipping');
|
|
6551
6498
|
return;
|
|
6552
6499
|
}
|
|
6553
|
-
|
|
6500
|
+
if (verbose)
|
|
6501
|
+
console.log('[hmr-ws] Processing .vue file update...');
|
|
6554
6502
|
try {
|
|
6555
6503
|
const root = server.config.root || process.cwd();
|
|
6556
6504
|
let rel = '/' + path.posix.normalize(path.relative(root, file)).split(path.sep).join('/');
|
|
@@ -6694,7 +6642,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
|
|
|
6694
6642
|
if (typeof spec === 'string' && /^(?:https?:)\/\//.test(spec)) {
|
|
6695
6643
|
const err = new Error('[ns-hmr][require-guard] require of URL: ' + spec + ' via ' + label);
|
|
6696
6644
|
const stack = err.stack || '';
|
|
6697
|
-
|
|
6645
|
+
console.error(err.message + '\n' + stack);
|
|
6698
6646
|
try { g.__NS_REQUIRE_GUARD_LAST__ = { spec, stack, label, ts: Date.now() }; } catch {}
|
|
6699
6647
|
}
|
|
6700
6648
|
} catch {}
|