@nativescript/vite 8.0.0-alpha.27 → 8.0.0-alpha.29

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.
Files changed (103) hide show
  1. package/configuration/angular.js +26 -128
  2. package/configuration/angular.js.map +1 -1
  3. package/configuration/base.js +2 -1
  4. package/configuration/base.js.map +1 -1
  5. package/configuration/javascript.js +5 -72
  6. package/configuration/javascript.js.map +1 -1
  7. package/configuration/typescript.js +4 -74
  8. package/configuration/typescript.js.map +1 -1
  9. package/helpers/angular/angular-linker.d.ts +5 -6
  10. package/helpers/angular/angular-linker.js +31 -110
  11. package/helpers/angular/angular-linker.js.map +1 -1
  12. package/helpers/angular/inject-component-hmr-registration.js +2 -70
  13. package/helpers/angular/inject-component-hmr-registration.js.map +1 -1
  14. package/helpers/angular/inject-hmr-vite-ignore.js +2 -69
  15. package/helpers/angular/inject-hmr-vite-ignore.js.map +1 -1
  16. package/helpers/angular/inline-decorator-component-templates.js +1 -170
  17. package/helpers/angular/inline-decorator-component-templates.js.map +1 -1
  18. package/helpers/angular/js-lexer.d.ts +4 -0
  19. package/helpers/angular/js-lexer.js +182 -0
  20. package/helpers/angular/js-lexer.js.map +1 -0
  21. package/helpers/angular/shared-linker.d.ts +31 -3
  22. package/helpers/angular/shared-linker.js +67 -14
  23. package/helpers/angular/shared-linker.js.map +1 -1
  24. package/helpers/angular/synthesize-decorator-ctor-parameters.js +2 -170
  25. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -1
  26. package/helpers/angular/synthesize-injectable-factories.js +1 -174
  27. package/helpers/angular/synthesize-injectable-factories.js.map +1 -1
  28. package/helpers/app-components.d.ts +2 -1
  29. package/helpers/app-components.js.map +1 -1
  30. package/helpers/app-css-state.d.ts +8 -0
  31. package/helpers/app-css-state.js +8 -0
  32. package/helpers/app-css-state.js.map +1 -0
  33. package/helpers/bundler-context.d.ts +11 -0
  34. package/helpers/bundler-context.js +71 -0
  35. package/helpers/bundler-context.js.map +1 -0
  36. package/helpers/dev-host.d.ts +2 -1
  37. package/helpers/dev-host.js.map +1 -1
  38. package/helpers/hmr-scope.d.ts +26 -0
  39. package/helpers/hmr-scope.js +67 -0
  40. package/helpers/hmr-scope.js.map +1 -0
  41. package/helpers/main-entry.js +2 -3
  42. package/helpers/main-entry.js.map +1 -1
  43. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  44. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  45. package/helpers/nativeclass-transform.js.map +1 -1
  46. package/helpers/platform-types.d.ts +2 -0
  47. package/helpers/platform-types.js +2 -0
  48. package/helpers/platform-types.js.map +1 -0
  49. package/helpers/prelink-angular.js +11 -29
  50. package/helpers/prelink-angular.js.map +1 -1
  51. package/helpers/ts-config-paths.d.ts +14 -0
  52. package/helpers/ts-config-paths.js +26 -0
  53. package/helpers/ts-config-paths.js.map +1 -1
  54. package/helpers/typescript-check.d.ts +2 -1
  55. package/helpers/typescript-check.js.map +1 -1
  56. package/helpers/workers.js +1 -1
  57. package/helpers/workers.js.map +1 -1
  58. package/hmr/client/css-handler.js +1 -17
  59. package/hmr/client/css-handler.js.map +1 -1
  60. package/hmr/client/utils.d.ts +1 -1
  61. package/hmr/client/utils.js +15 -59
  62. package/hmr/client/utils.js.map +1 -1
  63. package/hmr/frameworks/angular/server/strategy.js +3 -14
  64. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  65. package/hmr/frameworks/solid/server/strategy.js +3 -18
  66. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  67. package/hmr/frameworks/typescript/server/strategy.js +2 -15
  68. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  69. package/hmr/frameworks/vue/client/index.js +0 -154
  70. package/hmr/frameworks/vue/client/index.js.map +1 -1
  71. package/hmr/server/core-sanitize.d.ts +3 -4
  72. package/hmr/server/core-sanitize.js +3 -4
  73. package/hmr/server/core-sanitize.js.map +1 -1
  74. package/hmr/server/framework-strategy.d.ts +9 -19
  75. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  76. package/hmr/server/vendor-bare-module-shims.js +80 -0
  77. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  78. package/hmr/server/websocket-angular-entry.js +1 -1
  79. package/hmr/server/websocket-angular-entry.js.map +1 -1
  80. package/hmr/server/websocket-module-bindings.js +1 -1
  81. package/hmr/server/websocket-module-bindings.js.map +1 -1
  82. package/hmr/server/websocket-served-module-helpers.d.ts +4 -1
  83. package/hmr/server/websocket-served-module-helpers.js +19 -9
  84. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  85. package/hmr/server/websocket-vue-sfc.js +3 -3
  86. package/hmr/server/websocket-vue-sfc.js.map +1 -1
  87. package/hmr/server/websocket.d.ts +10 -33
  88. package/hmr/server/websocket.js +57 -754
  89. package/hmr/server/websocket.js.map +1 -1
  90. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  91. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  92. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  93. package/hmr/shared/runtime/dev-overlay.d.ts +3 -29
  94. package/hmr/shared/runtime/dev-overlay.js +3 -330
  95. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  96. package/hmr/shared/runtime/root-placeholder.js +9 -33
  97. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  98. package/hmr/shared/vendor/manifest.js +5 -82
  99. package/hmr/shared/vendor/manifest.js.map +1 -1
  100. package/package.json +53 -11
  101. package/hmr/server/websocket-ns-m-finalize.d.ts +0 -22
  102. package/hmr/server/websocket-ns-m-finalize.js +0 -88
  103. package/hmr/server/websocket-ns-m-finalize.js.map +0 -1
@@ -32,11 +32,15 @@ import { typescriptServerStrategy } from '../frameworks/typescript/server/strate
32
32
  import { buildInlineTemplateBlock, createProcessSfcCode, extractTemplateRender, processTemplateVariantMinimal } from '../frameworks/vue/server/sfc-transforms.js';
33
33
  import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
34
34
  import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
35
+ import { getAppCssState } from '../../helpers/app-css-state.js';
36
+ import { buildVueVendorShim, buildPiniaVendorShim } from './vendor-bare-module-shims.js';
35
37
  import { buildRuntimeConfig, generateImportMap } from './import-map.js';
36
38
  import { getCliFlags } from '../../helpers/cli-flags.js';
37
39
  import { buildCoreUrl, buildCoreUrlPath, normalizeCoreSub as normalizeCoreSubCanonical } from '../../helpers/ns-core-url.js';
38
40
  import { resolveDeviceReachableOrigin } from '../../helpers/dev-host.js';
39
41
  import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
42
+ import { getHmrSourceRoots, isWithinHmrScope } from '../../helpers/hmr-scope.js';
43
+ import { getTsConfigData } from '../../helpers/ts-config-paths.js';
40
44
  import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
41
45
  import { angularSourceHasSemanticDecorator, canonicalizeTransformRequestCacheKey, collectAngularEvictionUrls, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
42
46
  import { collectCssHotUpdatePaths } from './websocket-css-hot-update.js';
@@ -45,10 +49,11 @@ import { classifyBootRoute, classifyHmrUpdateKind, createColdBootRequestCounter,
45
49
  import { createHmrPendingMessage } from './websocket-hmr-pending.js';
46
50
  import { extractVitePrebundleId, filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, getFlattenedManifestMap, isCoreGlobalsReference, isEsmFrameworkPackageSpecifier, isLikelyNativeScriptPluginSpecifier, isLikelyNativeScriptRuntimePluginSpecifier, isNativeScriptCoreModule, isNativeScriptPluginModule, normalizeNativeScriptCoreSpecifier, normalizeNodeModulesSpecifier, resolveCandidateFilePath, resolveInternalRuntimePluginBareSpecifier, resolveNodeModulesPackageBoundary, resolveVendorFromCandidate, resolveVendorRouting, rewriteFsAbsoluteToNsM, shouldPreserveBareRuntimePluginSubpathImport, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule, viteDepsPathToBareSpecifier, } from './websocket-module-specifiers.js';
47
51
  import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
48
- import { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, extractDirectExportedNames, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
52
+ import { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
49
53
  import { createSharedTransformRequestRunner } from './shared-transform-request.js';
50
54
  import { formatNsMHmrServeTag, getNumericServeVersionTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
51
- import { ensureDynamicHmrImportHelper } from './websocket-served-module-helpers.js';
55
+ import { assertNoOptimizedArtifacts, buildBootProgressSnippet, collectTopLevelImportRecords, deduplicateLinkerImports, dedupeRtNamedImportsAgainstDestructures, ensureDestructureCoreImports, ensureDestructureRtImports, ensureDynamicHmrImportHelper, ensureGuardPlainDynamicImports, ensureVariableDynamicImportHelper, ensureVersionedRtImports, expandStarExports, extractExportMetadata, hoistTopLevelStaticImports, MODULE_IMPORT_ANALYSIS_PLUGINS, repairImportEqualsAssignments, stripCoreGlobalsImports, stripViteDynamicImportVirtual, wrapCommonJsModuleForDevice, } from './websocket-served-module-helpers.js';
56
+ export { buildBootProgressSnippet, wrapCommonJsModuleForDevice };
52
57
  export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
53
58
  export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
54
59
  export { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
@@ -81,6 +86,22 @@ catch { }
81
86
  const __processEnvJson = JSON.stringify(__processEnvEntries);
82
87
  const { parse, compileTemplate, compileScript } = vueSfcCompiler;
83
88
  const APP_ROOT_DIR = getProjectAppPath();
89
+ // Absolute directories HMR is allowed to react to: the app source dir
90
+ // (`nativescript.config.ts` > `appPath`) plus tsconfig-configured shared
91
+ // libraries. Computed once per process (tsconfig data is itself memoized) and
92
+ // used to scope `handleHotUpdate` so non-source changes.
93
+ let _hmrSourceRoots = null;
94
+ function getHmrSourceRootsCached() {
95
+ if (_hmrSourceRoots)
96
+ return _hmrSourceRoots;
97
+ let tsConfig = {};
98
+ try {
99
+ tsConfig = getTsConfigData({ platform: '' });
100
+ }
101
+ catch { }
102
+ _hmrSourceRoots = getHmrSourceRoots(tsConfig);
103
+ return _hmrSourceRoots;
104
+ }
84
105
  const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
85
106
  const APP_VIRTUAL_WITH_SLASH = `${APP_VIRTUAL_PREFIX}/`;
86
107
  const DEFAULT_MAIN_ENTRY = getProjectAppRelativePath('app.ts');
@@ -148,9 +169,6 @@ function getHmrSocketRole(client) {
148
169
  }
149
170
  return typeof client.__nsHmrClientRole === 'string' && client.__nsHmrClientRole ? client.__nsHmrClientRole : 'unknown';
150
171
  }
151
- function shouldAllowLocalCoreSanitizerPaths(contextLabel) {
152
- return /\bnode_modules\/@nativescript\/vite\/hmr\/(?:client|frameworks)\//.test(contextLabel);
153
- }
154
172
  export function prepareAngularEntryForDevice(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
155
173
  const rewrittenCode = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp);
156
174
  return rewriteAngularEntryRegisterOnly(rewrittenCode, resolveAngularCoreHmrImportSource(rewrittenCode, httpOrigin));
@@ -159,74 +177,6 @@ const processSfcCode = createProcessSfcCode(processCodeForDevice);
159
177
  // Bare specifiers and special skip patterns (virtual, data:, etc.)
160
178
  const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
161
179
  const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
162
- const MODULE_IMPORT_ANALYSIS_PLUGINS = ['typescript', 'jsx', 'importMeta', 'topLevelAwait', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', 'decorators-legacy'];
163
- function collectTopLevelImportRecords(code) {
164
- if (!code || typeof code !== 'string' || !/\bimport\b/.test(code)) {
165
- return [];
166
- }
167
- try {
168
- const ast = babelParse(code, {
169
- sourceType: 'module',
170
- plugins: MODULE_IMPORT_ANALYSIS_PLUGINS,
171
- });
172
- const body = ast?.program?.body;
173
- if (!Array.isArray(body)) {
174
- return [];
175
- }
176
- return body
177
- .filter((node) => t.isImportDeclaration(node) && typeof node.start === 'number' && typeof node.end === 'number' && typeof node.source?.value === 'string')
178
- .map((node) => ({
179
- start: node.start,
180
- end: node.end,
181
- text: code.slice(node.start, node.end),
182
- source: node.source.value,
183
- hasOnlyNamedSpecifiers: Array.isArray(node.specifiers) && node.specifiers.length > 0 && node.specifiers.every((spec) => t.isImportSpecifier(spec)),
184
- namedBindings: Array.isArray(node.specifiers)
185
- ? node.specifiers
186
- .filter((spec) => t.isImportSpecifier(spec) && typeof spec.start === 'number' && typeof spec.end === 'number')
187
- .map((spec) => ({
188
- importedName: t.isIdentifier(spec.imported) ? spec.imported.name : String(spec.imported?.value || ''),
189
- text: code.slice(spec.start, spec.end),
190
- }))
191
- : [],
192
- }));
193
- }
194
- catch {
195
- return [];
196
- }
197
- }
198
- function hoistTopLevelStaticImports(code) {
199
- const imports = collectTopLevelImportRecords(code);
200
- if (!imports.length) {
201
- return code;
202
- }
203
- let stripped = code;
204
- for (const imp of [...imports].sort((left, right) => right.start - left.start)) {
205
- stripped = stripped.slice(0, imp.start) + stripped.slice(imp.end);
206
- }
207
- const hoisted = [];
208
- const seen = new Set();
209
- for (const imp of imports) {
210
- const text = imp.text.trim();
211
- if (!text || seen.has(text)) {
212
- continue;
213
- }
214
- seen.add(text);
215
- hoisted.push(text);
216
- }
217
- if (!hoisted.length) {
218
- return stripped;
219
- }
220
- return `${hoisted.join('\n')}\n${stripped.replace(/^\s*\n+/, '')}`;
221
- }
222
- // Duplicate of `websocket-served-module-helpers.ts::buildBootProgressSnippet`
223
- // — both copies must stay in lock-step. See the canonical doc there for
224
- // why the snippet must remain fully synchronous (top-level await on a
225
- // boot-tagged module trips the iOS 10 s async-module deadline).
226
- export function buildBootProgressSnippet(bootModuleLabel) {
227
- const normalizedLabel = JSON.stringify(String(bootModuleLabel || '').replace(/\\/g, '/'));
228
- return [`const __nsBootGlobal=globalThis;`, `try{if(!__nsBootGlobal.__NS_HMR_BOOT_COMPLETE__){__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__=Number(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__||0)+1;__nsBootGlobal.__NS_HMR_BOOT_LAST_MODULE__=${normalizedLabel};}}catch(__nsBootErr){}`, ''].join('\n');
229
- }
230
180
  function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
231
181
  const imports = collectTopLevelImportRecords(code);
232
182
  if (!imports.length) {
@@ -315,145 +265,6 @@ function guardBareDynamicImports(code) {
315
265
  return code;
316
266
  }
317
267
  }
318
- function stripCoreGlobalsImports(code) {
319
- const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
320
- return code.replace(pattern, '');
321
- }
322
- function ensureVariableDynamicImportHelper(code) {
323
- if (!code.includes('__variableDynamicImportRuntimeHelper')) {
324
- return code;
325
- }
326
- if (PAT.VARIABLE_DYNAMIC_IMPORT_HELPER_PATTERN.test(code)) {
327
- return code;
328
- }
329
- const helper = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n` +
330
- ` try { if (request === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n` +
331
- ` const loader = map && (map[request] || map[request?.replace(/\\\\/g, "/")]);\n` +
332
- ` if (!loader) {\n` +
333
- ` const error = new Error(\"Cannot dynamically import: \" + request);\n` +
334
- ` error.code = 'ERR_MODULE_NOT_FOUND';\n` +
335
- ` return Promise.reject(error);\n` +
336
- ` }\n` +
337
- ` try {\n` +
338
- ` return loader(importMode);\n` +
339
- ` } catch (err) {\n` +
340
- ` return Promise.reject(err);\n` +
341
- ` }\n` +
342
- `};\n`;
343
- return `${helper}${code}`;
344
- }
345
- function ensureGuardPlainDynamicImports(code, origin) {
346
- try {
347
- if (!code || !/\bimport\s*\(/.test(code))
348
- return code;
349
- const wrapper = `const __ns_import = (s) => { try { if (s === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {} return import(s); }\n`;
350
- const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
351
- if (replaced !== code) {
352
- return wrapper + replaced;
353
- }
354
- return code;
355
- }
356
- catch {
357
- return code;
358
- }
359
- }
360
- // `ensureDynamicHmrImportHelper` lives in
361
- // `./websocket-served-module-helpers.js`. See that file for the
362
- // architectural rationale and the current helper implementation.
363
- async function expandStarExports(code, server, projectRoot, verbose, sharedTransformer) {
364
- const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
365
- let match;
366
- const replacements = [];
367
- while ((match = STAR_RE.exec(code)) !== null) {
368
- const url = match[2];
369
- if (!url.includes('/node_modules/'))
370
- continue;
371
- replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
372
- }
373
- if (!replacements.length)
374
- return code;
375
- // Pull target URLs through the shared runner when it's available so each
376
- // node_modules path shares the 60s TTL cache with the main /ns/m pipeline
377
- // and respects the global concurrency gate. Fan them out in parallel —
378
- // this block used to be a serial `for await` loop, which dominated cold
379
- // boot on apps with dozens of star-re-exports.
380
- const transformer = sharedTransformer ?? ((url) => server.transformRequest(url));
381
- const resolved = await Promise.all(replacements.map(async (rep) => {
382
- try {
383
- let vitePath = rep.url.replace(/^https?:\/\/[^/]+/, '');
384
- vitePath = vitePath.replace(/^\/ns\/m\//, '/');
385
- vitePath = vitePath.replace(/^\/__ns_boot__\/[^/]+/, '');
386
- vitePath = vitePath.replace(/\/__ns_hmr__\/[^/]+/, '');
387
- const result = await transformer(vitePath);
388
- if (!result?.code)
389
- return null;
390
- const names = extractExportedNames(result.code);
391
- if (!names.length)
392
- return null;
393
- if (verbose) {
394
- console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
395
- }
396
- return { rep, names };
397
- }
398
- catch {
399
- return null;
400
- }
401
- }));
402
- for (const entry of resolved) {
403
- if (!entry)
404
- continue;
405
- const explicit = `export { ${entry.names.join(', ')} } from ${JSON.stringify(entry.rep.url)};`;
406
- code = code.replace(entry.rep.full, explicit);
407
- }
408
- return code;
409
- }
410
- function extractExportedNames(code) {
411
- return extractDirectExportedNames(code);
412
- }
413
- function repairImportEqualsAssignments(code) {
414
- try {
415
- if (!code || typeof code !== 'string')
416
- return code;
417
- code = code.replace(/(^|\n)\s*import\s*\{([^}]+)\}\s*=\s*([^;]+);?/g, (_m, p1, specList, rhs) => {
418
- const cleaned = String(specList)
419
- .split(',')
420
- .map((s) => s.trim())
421
- .filter(Boolean)
422
- .map((seg) => seg.replace(/\s+as\s+/i, ': '))
423
- .join(', ');
424
- return `${p1}const { ${cleaned} } = ${rhs};`;
425
- });
426
- code = code.replace(/(^|\n)\s*import\s*\*\s*as\s*([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, ns, rhs) => `${p1}const ${ns} = (${rhs});`);
427
- code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => `${p1}const ${id} = ${rhs};`);
428
- }
429
- catch { }
430
- return code;
431
- }
432
- function ensureVersionedRtImports(code, origin, ver) {
433
- if (!code || !origin || !Number.isFinite(ver))
434
- return code;
435
- code = code.replace(/(from\s+["'])(?:https?:\/\/[^"']+)?\/(?:\ns|ns)\/rt(?:\/[\d]+)?(["'])/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
436
- code = code.replace(/(import\(\s*["'])(?:https?:\/\/[^"']+)?\/(?:\@ns|ns)\/rt(?:\/[\d]+)?(["']\s*\))/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
437
- return code;
438
- }
439
- function stripViteDynamicImportVirtual(code) {
440
- if (!/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
441
- return code;
442
- }
443
- const original = code;
444
- code = code.replace(/^[\t ]*import[^\n]*\/@id\/__x00__vite\/dynamic-import-helper[^\n]*$/gm, '');
445
- if (/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
446
- code = code.replace(/\/@id\/__x00__vite\/dynamic-import-helper[^"'`)]*/g, '/__NS_UNUSED_DYNAMIC_IMPORT_HELPER__');
447
- }
448
- if (!/__variableDynamicImportRuntimeHelper/.test(code)) {
449
- const inline = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n try { if (request === '@') { return import('/ns/m/__invalid_at__.mjs'); } } catch {}\n const loader = map && (map[request] || map[request?.replace(/\\\\/g, '/')]);\n if (!loader) { const e = new Error('Cannot dynamically import: ' + request); /*@ts-ignore*/ e.code = 'ERR_MODULE_NOT_FOUND'; return Promise.reject(e); }\n try { return loader(importMode); } catch (e) { return Promise.reject(e); }\n};\n`;
450
- code = inline + code;
451
- }
452
- if (code !== original) {
453
- code = `// [hmr-sanitize] removed virtual dynamic-import-helper\n${code}`;
454
- }
455
- return code;
456
- }
457
268
  // Detect (and log) `require('http(s)://...')` calls made from CJS shims.
458
269
  // Pattern: HTTP-served ESM modules end up in NS-vite's `__nsRequire`
459
270
  // shim with HTTP URLs as their relative resolution targets. The guard
@@ -539,43 +350,6 @@ function stripImportMetaHotBlocks(code) {
539
350
  }
540
351
  return result;
541
352
  }
542
- // Extract a quick set of export names and whether a default export exists from ESM code.
543
- // This uses conservative regex scanning for metadata only (no code rewriting).
544
- function extractExportMetadata(code) {
545
- const named = new Set();
546
- let hasDefault = /\bexport\s+default\b/.test(code);
547
- try {
548
- // export const foo, export let foo, export function bar, export class Baz
549
- for (const m of code.matchAll(/\bexport\s+(?:const|let|var|function|class)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g)) {
550
- if (m[1])
551
- named.add(m[1]);
552
- }
553
- // export { a, b as c }
554
- for (const m of code.matchAll(/\bexport\s*\{([^}]+)\}/g)) {
555
- const inner = (m[1] || '')
556
- .split(',')
557
- .map((s) => s.trim())
558
- .filter(Boolean);
559
- for (const seg of inner) {
560
- // forms: name or name as alias or default as name
561
- const dm = seg.match(/^([A-Za-z_$][A-Za-z0-9_$]*)(?:\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*))?$/);
562
- if (dm) {
563
- const base = dm[1];
564
- const alias = dm[2];
565
- if (base === 'default') {
566
- hasDefault = true;
567
- continue;
568
- }
569
- named.add(alias || base);
570
- }
571
- }
572
- }
573
- }
574
- catch { }
575
- // Remove default if accidentally included
576
- named.delete('default');
577
- return { hasDefault, named: Array.from(named) };
578
- }
579
353
  function normalizeImportPath(spec, importerDir) {
580
354
  if (!spec)
581
355
  return null;
@@ -775,8 +549,8 @@ function cleanCode(code) {
775
549
  result = result.replace(PAT.VITE_CLIENT_IMPORT, '');
776
550
  result = result.replace(PAT.IMPORT_META_HOT_ASSIGNMENT, '');
777
551
  // Keep import.meta.hot call sites; runtime now provides a stable import.meta.hot.
778
- result = ACTIVE_STRATEGY.preClean(result);
779
- result = ACTIVE_STRATEGY.rewriteFrameworkImports(result);
552
+ result = ACTIVE_STRATEGY.preClean?.(result) ?? result;
553
+ result = ACTIVE_STRATEGY.rewriteFrameworkImports?.(result) ?? result;
780
554
  // Vendor manifest-driven import rewrites
781
555
  // NOTE: Static and side-effect vendor imports are intentionally NOT rewritten here.
782
556
  // They are left as import statements so that ensureNativeScriptModuleBindings()
@@ -804,13 +578,11 @@ function cleanCode(code) {
804
578
  }
805
579
  result = result.replace(PAT.VITE_CLIENT_IMPORT, '').replace(PAT.IMPORT_META_HOT_ASSIGNMENT, '');
806
580
  // Clean up HMR noise
807
- result = ACTIVE_STRATEGY.postClean(result);
581
+ result = ACTIVE_STRATEGY.postClean?.(result) ?? result;
808
582
  result = stripCoreGlobalsImports(result);
809
583
  return result;
810
584
  }
811
- // ============================================================================
812
- // APPLICATION IMPORT HELPERS
813
- // ============================================================================
585
+ // Application import helpers
814
586
  /**
815
587
  * Check if a path is an application module (not node_modules, not vendor, not relative)
816
588
  * This is generic and works for ANY project structure.
@@ -955,197 +727,6 @@ function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
955
727
  }
956
728
  return absolute;
957
729
  }
958
- /**
959
- * After the Angular linker runs on code that Vite has already resolved (bare
960
- * specifiers → full URLs), the linker injects NEW import statements with bare
961
- * specifiers (e.g. `import {Component} from '@angular/core'`). These cause:
962
- * 1. Duplicate-identifier SyntaxErrors (the name was already imported via URL)
963
- * 2. Unresolvable bare specifiers at runtime on device
964
- *
965
- * This function:
966
- * • builds a map packageName → resolvedURL from existing resolved imports
967
- * • collects all binding names already imported per package
968
- * • for each bare-specifier import, removes duplicate bindings
969
- * • rewrites any genuinely-new bindings to use the resolved URL
970
- */
971
- function deduplicateLinkerImports(code) {
972
- if (!code)
973
- return code;
974
- try {
975
- const imports = collectTopLevelImportRecords(code);
976
- if (!imports.length) {
977
- return code;
978
- }
979
- // ── Step 1: collect resolved imports already in the file ──────────
980
- const pkgUrlMap = new Map();
981
- const pkgBindings = new Map();
982
- for (const imp of imports) {
983
- const url = imp.source;
984
- if (!/^https?:\/\//.test(url) && !url.startsWith('/')) {
985
- continue;
986
- }
987
- const nmIdx = url.lastIndexOf('/node_modules/');
988
- if (nmIdx === -1)
989
- continue;
990
- const afterNm = url.substring(nmIdx + '/node_modules/'.length);
991
- const parts = afterNm.split('/');
992
- const pkg = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
993
- if (!pkgUrlMap.has(pkg))
994
- pkgUrlMap.set(pkg, url);
995
- if (imp.namedBindings.length) {
996
- if (!pkgBindings.has(pkg))
997
- pkgBindings.set(pkg, new Set());
998
- for (const binding of imp.namedBindings) {
999
- if (binding.importedName)
1000
- pkgBindings.get(pkg).add(binding.importedName);
1001
- }
1002
- }
1003
- }
1004
- if (pkgUrlMap.size === 0)
1005
- return code;
1006
- // ── Step 2: rewrite bare-specifier imports ───────────────────────
1007
- const edits = [];
1008
- for (const imp of imports) {
1009
- if (!imp.hasOnlyNamedSpecifiers) {
1010
- continue;
1011
- }
1012
- const specifier = imp.source;
1013
- if (specifier.startsWith('/') || specifier.startsWith('.') || specifier.startsWith('http')) {
1014
- continue;
1015
- }
1016
- const parts = specifier.split('/');
1017
- const pkg = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
1018
- const url = pkgUrlMap.get(pkg);
1019
- if (!url) {
1020
- continue;
1021
- }
1022
- const existing = pkgBindings.get(pkg) || new Set();
1023
- const newBindings = imp.namedBindings.filter((binding) => !existing.has(binding.importedName));
1024
- if (newBindings.length === 0) {
1025
- edits.push({ start: imp.start, end: imp.end, text: '' });
1026
- continue;
1027
- }
1028
- if (newBindings.length === imp.namedBindings.length) {
1029
- continue;
1030
- }
1031
- for (const binding of newBindings) {
1032
- existing.add(binding.importedName);
1033
- }
1034
- edits.push({
1035
- start: imp.start,
1036
- end: imp.end,
1037
- text: `import { ${newBindings.map((binding) => binding.text).join(', ')} } from ${JSON.stringify(url)};`,
1038
- });
1039
- }
1040
- if (!edits.length) {
1041
- return code;
1042
- }
1043
- let next = code;
1044
- for (const edit of edits.sort((left, right) => right.start - left.start)) {
1045
- next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
1046
- }
1047
- return next;
1048
- }
1049
- catch {
1050
- return code;
1051
- }
1052
- }
1053
- export function wrapCommonJsModuleForDevice(code, absolutePath) {
1054
- if (!code)
1055
- return code;
1056
- try {
1057
- const hasExportDefault = /\bexport\s+default\b/.test(code) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(code);
1058
- const hasNamedExports = /\bexport\s+(?:const|let|var|function|class|async)\b/.test(code) || /\bexport\s*\{/.test(code);
1059
- const hasCjsExports = /\bmodule\s*\.\s*exports\b/.test(code) || /\bexports\s*\.\s*\w/.test(code);
1060
- if (hasExportDefault || hasNamedExports || !hasCjsExports) {
1061
- return code;
1062
- }
1063
- const namedExports = new Set();
1064
- const exportsRe = /\bexports\s*\.\s*([A-Za-z_$][\w$]*)\s*=/g;
1065
- let em;
1066
- while ((em = exportsRe.exec(code)) !== null) {
1067
- const name = em[1];
1068
- if (name !== '__esModule' && name !== 'default') {
1069
- namedExports.add(name);
1070
- }
1071
- }
1072
- const defPropRe = /Object\s*\.\s*defineProperty\s*\(\s*exports\s*,\s*['"]([^'"]+)['"]/g;
1073
- while ((em = defPropRe.exec(code)) !== null) {
1074
- const name = em[1];
1075
- if (name !== '__esModule' && name !== 'default') {
1076
- namedExports.add(name);
1077
- }
1078
- }
1079
- // Static enumeration only sees `exports.foo = ...` and `Object.defineProperty(exports, 'foo', ...)`.
1080
- // Real-world packages like lodash attach their entire surface to a function inside an IIFE and
1081
- // then `module.exports = thatFunction`. Static analysis returns zero in that case. To handle
1082
- // these modules we ALSO load the package in the dev-server's Node context (only when we have a
1083
- // node_modules path) and merge the runtime keys. See `helpers/cjs-named-exports.ts` for the
1084
- // reasoning and safety boundaries.
1085
- if (absolutePath) {
1086
- try {
1087
- for (const n of getCjsNamedExports(absolutePath)) {
1088
- namedExports.add(n);
1089
- }
1090
- }
1091
- catch {
1092
- /* fall through to whatever we caught statically */
1093
- }
1094
- }
1095
- let suffix = `\nvar __cjs_mod = module.exports;\nexport default __cjs_mod;\n`;
1096
- if (namedExports.size) {
1097
- const entries = Array.from(namedExports);
1098
- const temps = entries.map((name, i) => `var __cjs_e${i} = __cjs_mod[${JSON.stringify(name)}];`);
1099
- const reExports = entries.map((name, i) => `__cjs_e${i} as ${name}`);
1100
- suffix += `${temps.join(' ')}\nexport { ${reExports.join(', ')} };\n`;
1101
- }
1102
- const prelude = `var module = { exports: {} }; var exports = module.exports;\n` +
1103
- `var __ns_cjs_require_base = (typeof globalThis.__nsBaseRequire === 'function' ? globalThis.__nsBaseRequire : (typeof globalThis.__nsRequire === 'function' ? globalThis.__nsRequire : (typeof globalThis.require === 'function' ? globalThis.require : undefined)));\n` +
1104
- `var __ns_cjs_require_kind = (typeof globalThis.__nsBaseRequire === 'function' ? 'base-require' : (typeof globalThis.__nsRequire === 'function' ? 'vendor-require' : 'global-require'));\n` +
1105
- `var require = function(spec) {\n` +
1106
- ` if (!__ns_cjs_require_base) { throw new Error('require is not defined'); }\n` +
1107
- // Resolve relative specifiers against the HTTP-served module's URL
1108
- // before delegating to NS's runtime require. Without this step,
1109
- // \`require('./base64-vlq')\` inside a CJS module served from
1110
- // \`http://.../ns/m/node_modules/source-map-js/lib/source-map-generator.js\`
1111
- // would pass a literal '"./base64-vlq"' to the native require, which
1112
- // has no notion of the current HTTP-module's location and either
1113
- // throws "Module not found" or fetches an arbitrary filesystem path
1114
- // that happens to parse as code (producing misleading syntax errors
1115
- // like "missing ) after argument list" from unrelated modules).
1116
- ` var __nsResolvedSpec = spec;\n` +
1117
- ` try {\n` +
1118
- ` if (typeof spec === 'string' && (spec.indexOf('./') === 0 || spec.indexOf('../') === 0)) {\n` +
1119
- ` var __nsParentUrl = (typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string') ? import.meta.url : null;\n` +
1120
- ` if (__nsParentUrl) {\n` +
1121
- ` var __nsResolvedUrl = new URL(spec, __nsParentUrl);\n` +
1122
- ` // Common Node-style bare extensions: prefer .js if the resolved URL lacks an extension in its last path segment.\n` +
1123
- ` if (!/\\.[A-Za-z0-9]+$/.test(__nsResolvedUrl.pathname.split('/').pop() || '')) {\n` +
1124
- ` __nsResolvedUrl.pathname = __nsResolvedUrl.pathname.replace(/\\/+$/, '') + '.js';\n` +
1125
- ` }\n` +
1126
- ` __nsResolvedSpec = __nsResolvedUrl.href;\n` +
1127
- ` }\n` +
1128
- ` }\n` +
1129
- ` } catch (e) {}\n` +
1130
- ` try { var __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(String(__nsResolvedSpec), { kind: __ns_cjs_require_kind, specifier: String(spec), url: __nsResolvedSpec !== spec ? __nsResolvedSpec : undefined, via: 'cjs-wrapper', parent: (typeof import.meta !== 'undefined' && import.meta && import.meta.url) ? import.meta.url : undefined }); } } catch (e) {}\n` +
1131
- ` var mod = __ns_cjs_require_base(__nsResolvedSpec);\n` +
1132
- ` try {\n` +
1133
- ` if (mod && (typeof mod === 'object' || typeof mod === 'function') && mod.default !== undefined) {\n` +
1134
- ` var keys = [];\n` +
1135
- ` try { keys = Object.keys(mod); } catch (e) {}\n` +
1136
- ` var defaultOnly = keys.length === 1 && keys[0] === 'default';\n` +
1137
- ` var esModuleOnly = keys.length === 2 && keys.indexOf('default') !== -1 && keys.indexOf('__esModule') !== -1;\n` +
1138
- ` if (mod.__esModule || defaultOnly || esModuleOnly) { return mod.default; }\n` +
1139
- ` }\n` +
1140
- ` } catch (e) {}\n` +
1141
- ` return mod;\n` +
1142
- `};\n`;
1143
- return `${prelude}${code}${suffix}`;
1144
- }
1145
- catch {
1146
- return code;
1147
- }
1148
- }
1149
730
  /**
1150
731
  * Process code for device: inject globals, remove framework imports
1151
732
  */
@@ -1678,198 +1259,6 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
1678
1259
  // Assert that sanitized code no longer contains any Vite optimized deps artifacts
1679
1260
  // or virtual ids that could break the HTTP ESM loader on device. Throws with
1680
1261
  // a helpful diagnostics message if any are found.
1681
- function assertNoOptimizedArtifacts(code, contextLabel) {
1682
- try {
1683
- const offenders = [];
1684
- const lines = code.split('\n');
1685
- const tests = [
1686
- // Allow Vite dev indirections like /@id/ and /.vite/deps when served via HTTP.
1687
- // Only flag clearly invalid virtual placeholders if they surface in output.
1688
- /\b__VITE_PLUGIN__\b/,
1689
- /\b__VITE_PRELOAD__\b/,
1690
- ];
1691
- // Absolute or relative local @nativescript/core usage indicates a split realm risk; fail fast
1692
- const localCore = /(^|[^\w@])(?:\.\.?\/|\/)??@nativescript[\/_-]core\//i;
1693
- for (let i = 0; i < lines.length; i++) {
1694
- const ln = lines[i];
1695
- for (const re of tests) {
1696
- if (re.test(ln)) {
1697
- offenders.push(`${i + 1}: ${ln.substring(0, 200)}`);
1698
- break;
1699
- }
1700
- }
1701
- if (localCore.test(ln)) {
1702
- // Comments can never cause split-realm risk at runtime — skip them.
1703
- // Library authors commonly reference @nativescript/core in comments
1704
- // (e.g. TSDoc /// <reference> directives, module resolution notes).
1705
- const trimmed = ln.trimStart();
1706
- if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
1707
- continue;
1708
- }
1709
- if (shouldAllowLocalCoreSanitizerPaths(contextLabel)) {
1710
- continue;
1711
- }
1712
- offenders.push(`${i + 1}: ${ln.substring(0, 200)} [local-core-path]`);
1713
- }
1714
- if (offenders.length >= 10)
1715
- break;
1716
- }
1717
- if (offenders.length) {
1718
- const msg = `[sanitize-fail] Optimized deps/virtual id artifacts detected in ${contextLabel}. These cannot be evaluated by the device HTTP ESM loader. Offending lines (first ${Math.min(5, offenders.length)} shown):\n` + offenders.slice(0, 5).join('\n');
1719
- const err = new Error(msg);
1720
- // Attach details for server logs / higher-level handlers
1721
- err.code = 'NS_SANITIZE_FAIL';
1722
- err.offenders = offenders;
1723
- throw err;
1724
- }
1725
- }
1726
- catch (e) {
1727
- // If diagnostics generation itself fails, do not mask the underlying issue
1728
- throw e;
1729
- }
1730
- }
1731
- // Ensure there are no lingering named imports from the unified core bridge.
1732
- // Converts named imports from `/ns/core[/<sub>]` into default import +
1733
- // destructuring. The package-main bridge serves a shape-shifted module whose
1734
- // named exports come from `__ns_core_bridge.default` rather than ESM named
1735
- // exports, so a named-import binding would be `undefined` at evaluation.
1736
- function ensureDestructureCoreImports(code) {
1737
- try {
1738
- let result = code;
1739
- let coreImportCounter = 0;
1740
- const toDestructure = (specList) => specList
1741
- .split(',')
1742
- .map((s) => s.trim())
1743
- .filter(Boolean)
1744
- .map((seg) => {
1745
- const m = seg.split(/\s+as\s+/i);
1746
- return m.length === 2 ? `${m[0].trim()}: ${m[1].trim()}` : seg;
1747
- })
1748
- .join(', ');
1749
- // import { A, B } from '/ns/core[/<sub>]'
1750
- const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[^"']+)?)['"];?\s*/gm;
1751
- result = result.replace(reNamed, (_full, pfx, specList, src) => {
1752
- // Deep subpath URLs serve actual ESM with real named exports — skip.
1753
- if (isDeepCoreSubpath(src))
1754
- return _full;
1755
- const tmp = `__ns_core_ns_re${coreImportCounter > 0 ? `_${coreImportCounter}` : ''}`;
1756
- coreImportCounter++;
1757
- const decl = `const { ${toDestructure(specList)} } = ${tmp};`;
1758
- return `${pfx}import ${tmp} from ${JSON.stringify(src)};\n${decl}\n`;
1759
- });
1760
- // import Default, { A, B } from '/ns/core[/<sub>]'
1761
- const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[^"']+)?)['"];?\s*/gm;
1762
- result = result.replace(reMixed, (_full, pfx, defName, specList, src) => {
1763
- if (isDeepCoreSubpath(src))
1764
- return _full;
1765
- const decl = `const { ${toDestructure(specList)} } = ${defName};`;
1766
- return `${pfx}import ${defName} from ${JSON.stringify(src)};\n${decl}\n`;
1767
- });
1768
- return result;
1769
- }
1770
- catch {
1771
- return code;
1772
- }
1773
- }
1774
- // Converts any named imports from the runtime bridge (/ns/rt[/ver]) into a default import + destructuring.
1775
- // This guarantees helper aliases like _resolveComponent remain bound even if we later normalize imports.
1776
- function ensureDestructureRtImports(code) {
1777
- try {
1778
- let result = code;
1779
- const toDestructure = (specList) => specList
1780
- .split(',')
1781
- .map((s) => s.trim())
1782
- .filter(Boolean)
1783
- .map((seg) => {
1784
- // Preserve alias mapping (e.g., resolveComponent as _resolveComponent)
1785
- const m = seg.split(/\s+as\s+/i);
1786
- return m.length === 2 ? `${m[0].trim()}: ${m[1].trim()}` : seg;
1787
- })
1788
- .join(', ');
1789
- const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)['"];?\s*/gm;
1790
- result = result.replace(reNamed, (_full, pfx, specList, src) => {
1791
- const tmp = `__ns_rt_ns_re`;
1792
- const decl = `const { ${toDestructure(specList)} } = ${tmp};`;
1793
- return `${pfx}import ${tmp} from ${JSON.stringify(src)};\n${decl}\n`;
1794
- });
1795
- const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)['"];?\s*/gm;
1796
- result = result.replace(reMixed, (_full, pfx, defName, specList, _src) => {
1797
- const decl = `const { ${toDestructure(specList)} } = ${defName};`;
1798
- return `${pfx}import ${defName} from ${JSON.stringify(_src)};\n${decl}\n`;
1799
- });
1800
- return result;
1801
- }
1802
- catch {
1803
- return code;
1804
- }
1805
- }
1806
- // Remove overlapping named imports from /ns/rt that duplicate bindings already provided
1807
- // by destructuring a default /ns/rt import (e.g., const { $showModal } = __ns_rt_ns_1;).
1808
- function dedupeRtNamedImportsAgainstDestructures(code) {
1809
- try {
1810
- let result = code;
1811
- // Collect bindings created from any destructure of __ns_rt_ns* temps
1812
- const rtDestructureRE = /(^|\n)\s*const\s*\{([^}]+)\}\s*=\s*(__ns_rt_ns(?:\d+|_re))\s*;?/gm;
1813
- const rtBound = new Set();
1814
- let m;
1815
- while ((m = rtDestructureRE.exec(result)) !== null) {
1816
- const specList = String(m[2] || '');
1817
- specList
1818
- .split(',')
1819
- .map((s) => s.trim())
1820
- .filter(Boolean)
1821
- .forEach((seg) => {
1822
- const bind = seg.includes(':') ? seg.split(':')[1].trim() : seg;
1823
- if (bind)
1824
- rtBound.add(bind);
1825
- });
1826
- }
1827
- if (!rtBound.size)
1828
- return result;
1829
- // For any named import from /ns/rt (versioned or not), drop specifiers that are already bound
1830
- const rtNamedImportRE = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)["'];?\s*/gm;
1831
- const edits = [];
1832
- while ((m = rtNamedImportRE.exec(result)) !== null) {
1833
- const full = m[0];
1834
- const pfx = m[1] || '';
1835
- const specList = String(m[2] || '');
1836
- const src = m[3];
1837
- const kept = [];
1838
- specList
1839
- .split(',')
1840
- .map((s) => s.trim())
1841
- .filter(Boolean)
1842
- .forEach((seg) => {
1843
- const importedName = seg.split(/\s+as\s+/i)[0].trim();
1844
- if (!rtBound.has(importedName))
1845
- kept.push(seg);
1846
- });
1847
- let replacement = '';
1848
- if (kept.length) {
1849
- replacement = `${pfx}import { ${kept.join(', ')} } from ${JSON.stringify(src)};`;
1850
- }
1851
- else {
1852
- replacement = pfx || '';
1853
- }
1854
- edits.push({
1855
- start: rtNamedImportRE.lastIndex - full.length,
1856
- end: rtNamedImportRE.lastIndex,
1857
- text: replacement,
1858
- });
1859
- }
1860
- if (edits.length) {
1861
- edits
1862
- .sort((a, b) => b.start - a.start)
1863
- .forEach((e) => {
1864
- result = result.slice(0, e.start) + e.text + result.slice(e.end);
1865
- });
1866
- }
1867
- return result;
1868
- }
1869
- catch {
1870
- return code;
1871
- }
1872
- }
1873
1262
  /**
1874
1263
  * THE SINGLE REWRITE FUNCTION - used everywhere for consistency
1875
1264
  */
@@ -2458,9 +1847,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
2458
1847
  });
2459
1848
  return result;
2460
1849
  }
2461
- // ============================================================================
2462
- // PLUGIN
2463
- // ============================================================================
1850
+ // Plugin
2464
1851
  function createHmrWebSocketPlugin(opts) {
2465
1852
  const verbose = !!opts.verbose;
2466
1853
  let wss = null;
@@ -3217,7 +2604,7 @@ function createHmrWebSocketPlugin(opts) {
3217
2604
  try {
3218
2605
  const origin = getServerOrigin(server);
3219
2606
  code = ensureVersionedRtImports(code, origin, graphVersion);
3220
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, graphVersion);
2607
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, origin, graphVersion) ?? code;
3221
2608
  }
3222
2609
  catch { }
3223
2610
  // Compute rel .mjs output path
@@ -3273,7 +2660,7 @@ function createHmrWebSocketPlugin(opts) {
3273
2660
  }
3274
2661
  try {
3275
2662
  depCode = ensureVersionedRtImports(depCode, getServerOrigin(server), graphVersion);
3276
- depCode = ACTIVE_STRATEGY.ensureVersionedImports(depCode, getServerOrigin(server), graphVersion);
2663
+ depCode = ACTIVE_STRATEGY.ensureVersionedImports?.(depCode, getServerOrigin(server), graphVersion) ?? depCode;
3277
2664
  }
3278
2665
  catch { }
3279
2666
  let depRel = depResolved.replace(/^\//, '').replace(/\.(tsx?|jsx?)$/i, '.mjs');
@@ -3770,79 +3157,10 @@ function createHmrWebSocketPlugin(opts) {
3770
3157
  const pkg = bare;
3771
3158
  let code = '';
3772
3159
  if (pkg === 'vue' || pkg === 'nativescript-vue') {
3773
- // Re-export Vue helpers from vendor NativeScript-Vue (fallback to 'vue' if present)
3774
- code = `
3775
- const g = globalThis;
3776
- const reg = g.__nsVendorRegistry;
3777
- const req = reg && g.__nsVendorRequire ? g.__nsVendorRequire : (g.__nsRequire || g.require);
3778
- let mod = reg && reg.get('${pkg === 'vue' ? 'nativescript-vue' : 'nativescript-vue'}');
3779
- if (!mod && req) {
3780
- try { mod = req('${pkg === 'vue' ? 'nativescript-vue' : 'nativescript-vue'}'); } catch {}
3781
- ${pkg === 'vue' ? "if (!mod) { try { mod = req('vue'); } catch {} }" : ''}
3782
- }
3783
- mod = mod || {};
3784
- const v = (mod.default ?? mod);
3785
- export default v;
3786
- export const defineComponent = v.defineComponent;
3787
- export const resolveComponent = v.resolveComponent;
3788
- export const createVNode = v.createVNode;
3789
- export const createTextVNode = v.createTextVNode;
3790
- export const createCommentVNode = v.createCommentVNode;
3791
- export const Fragment = v.Fragment;
3792
- export const withCtx = v.withCtx;
3793
- export const openBlock = v.openBlock;
3794
- export const createBlock = v.createBlock;
3795
- export const createElementVNode = v.createElementVNode || v.createVNode;
3796
- export const createElementBlock = v.createElementBlock || v.createBlock;
3797
- export const renderSlot = v.renderSlot;
3798
- export const mergeProps = v.mergeProps;
3799
- export const toHandlers = v.toHandlers;
3800
- export const renderList = v.renderList;
3801
- export const normalizeProps = v.normalizeProps;
3802
- export const guardReactiveProps = v.guardReactiveProps;
3803
- export const withDirectives = v.withDirectives;
3804
- export const resolveDirective = v.resolveDirective;
3805
- export const withModifiers = v.withModifiers;
3806
- export const withKeys = v.withKeys;
3807
- export const ref = v.ref;
3808
- export const shallowRef = v.shallowRef;
3809
- export const unref = v.unref;
3810
- export const computed = v.computed;
3811
- export const onMounted = v.onMounted;
3812
- export const onBeforeUnmount = v.onBeforeUnmount;
3813
- export const onUnmounted = v.onUnmounted;
3814
- export const watch = v.watch;
3815
- export const nextTick = v.nextTick;
3816
- export const createApp = v.createApp || (vm && vm.createApp);
3817
- export const registerElement = v.registerElement || (vm && vm.registerElement);
3818
- export const normalizeClass = v.normalizeClass;
3819
- export const normalizeStyle = v.normalizeStyle;
3820
- export const toDisplayString = v.toDisplayString;
3821
- `;
3160
+ code = buildVueVendorShim(pkg);
3822
3161
  }
3823
3162
  else if (pkg === 'pinia') {
3824
- // Re-export Pinia APIs from vendor pinia module
3825
- code = `
3826
- const g = globalThis;
3827
- const reg = g.__nsVendorRegistry;
3828
- const req = reg && g.__nsVendorRequire ? g.__nsVendorRequire : (g.__nsRequire || g.require);
3829
- let mod = reg && reg.get('pinia');
3830
- if (!mod && req) { try { mod = req('pinia'); } catch {} }
3831
- mod = mod || {};
3832
- const p = (mod.default ?? mod);
3833
- export default p;
3834
- export const createPinia = p.createPinia;
3835
- export const defineStore = p.defineStore;
3836
- export const storeToRefs = p.storeToRefs;
3837
- export const setActivePinia = p.setActivePinia;
3838
- export const getActivePinia = p.getActivePinia;
3839
- export const mapStores = p.mapStores;
3840
- export const mapState = p.mapState;
3841
- export const mapGetters = p.mapGetters;
3842
- export const mapActions = p.mapActions;
3843
- export const mapWritableState = p.mapWritableState;
3844
- export const piniaSymbol = p.piniaSymbol;
3845
- `;
3163
+ code = buildPiniaVendorShim();
3846
3164
  }
3847
3165
  res.statusCode = 200;
3848
3166
  res.end(code || 'export {}\n');
@@ -4000,7 +3318,7 @@ export const piniaSymbol = p.piniaSymbol;
4000
3318
  try {
4001
3319
  const verNum = 0;
4002
3320
  code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
4003
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
3321
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), verNum) ?? code;
4004
3322
  }
4005
3323
  catch { }
4006
3324
  // `/ns/m` URL finalize step.
@@ -5116,11 +4434,11 @@ export const piniaSymbol = p.piniaSymbol;
5116
4434
  const verNum = Number(verFromPath || '0');
5117
4435
  if (Number.isFinite(verNum) && verNum > 0) {
5118
4436
  code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
5119
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
4437
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), verNum) ?? code;
5120
4438
  }
5121
4439
  else {
5122
4440
  code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
5123
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), graphVersion);
4441
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), graphVersion) ?? code;
5124
4442
  }
5125
4443
  }
5126
4444
  catch { }
@@ -5827,7 +5145,7 @@ export const piniaSymbol = p.piniaSymbol;
5827
5145
  try {
5828
5146
  const origin = getServerOrigin(server);
5829
5147
  inlineCode2 = ensureVersionedRtImports(inlineCode2, origin, Number(ver));
5830
- inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports(inlineCode2, origin, Number(ver));
5148
+ inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports?.(inlineCode2, origin, Number(ver)) ?? inlineCode2;
5831
5149
  }
5832
5150
  catch { }
5833
5151
  // Normalize imports/helpers via AST to ensure _defineComponent and other helpers are bound once
@@ -5941,7 +5259,7 @@ export const piniaSymbol = p.piniaSymbol;
5941
5259
  try {
5942
5260
  const origin = getServerOrigin(server);
5943
5261
  code = ensureVersionedRtImports(code, origin, Number(ver));
5944
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, Number(ver));
5262
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, origin, Number(ver)) ?? code;
5945
5263
  }
5946
5264
  catch { }
5947
5265
  // Inline-template body path already runs processCodeForDevice (AST + sanitizers); no additional _defineComponent fix needed
@@ -6270,6 +5588,15 @@ export const piniaSymbol = p.piniaSymbol;
6270
5588
  if (isRuntimeGraphExcludedPath(file)) {
6271
5589
  return;
6272
5590
  }
5591
+ // Authoritative "what triggers HMR" gate, applied before the pending
5592
+ // overlay broadcast below: react only to files inside the app source
5593
+ // dir (`appPath`) or a tsconfig-configured shared library.
5594
+ if (!isWithinHmrScope(file, getHmrSourceRootsCached())) {
5595
+ if (verbose) {
5596
+ console.log(`[ns-hmr][server] ignored change (outside HMR source scope): ${file}`);
5597
+ }
5598
+ return;
5599
+ }
6273
5600
  // Always-on update timing. Captures the four phases (await,
6274
5601
  // framework, broadcast, total) plus invalidated module count
6275
5602
  // and recipient count. Emitted at the end of this function via
@@ -6512,8 +5839,9 @@ export const piniaSymbol = p.piniaSymbol;
6512
5839
  // the framework's own template-update payload.
6513
5840
  if (!file.endsWith('.css')) {
6514
5841
  try {
6515
- const deps = server.__nsAppCssDeps;
6516
- const appCssPath = server.__nsAppCssPath;
5842
+ const appCssState = getAppCssState(server);
5843
+ const deps = appCssState?.deps;
5844
+ const appCssPath = appCssState?.path;
6517
5845
  if (deps && appCssPath) {
6518
5846
  const normalizedFile = path.resolve(file).replace(/\\/g, '/');
6519
5847
  if (deps.has(normalizedFile)) {
@@ -7469,9 +6797,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
7469
6797
  },
7470
6798
  };
7471
6799
  }
7472
- // ----------
7473
6800
  // Framework-specific HMR WebSocket plugins
7474
- // ----------
7475
6801
  export function hmrWebSocketVue(opts) {
7476
6802
  ACTIVE_STRATEGY = resolveFrameworkStrategy('vue');
7477
6803
  return createHmrWebSocketPlugin(opts);
@@ -7489,37 +6815,14 @@ export function hmrWebSocketTypescript(opts) {
7489
6815
  return createHmrWebSocketPlugin(opts);
7490
6816
  }
7491
6817
  /**
7492
- * Get the dev-server origin string baked into every module the rewriter
7493
- * serves to the device (`/ns/core/...`, `/ns/m/...`, etc.).
7494
- *
7495
- * This MUST agree with the origin `dev-host.ts` bakes into `bundle.mjs`
7496
- * itself. If the two disagree (e.g. the bundle imports
7497
- * `http://localhost:5173/ns/core/utils` while the rewriter splices in
7498
- * `http://192.168.0.8:5173/ns/core/utils`) V8's ESM loader keys those
7499
- * as DIFFERENT modules and the app ends up with two side-by-side
7500
- * realms of `@nativescript/core` — a classic singleton-state split.
7501
- * `internal/debug-sessions/LATEST-05-12-2026-HMR_CORE_REALM_SPLIT.md`
7502
- * documents the symptom.
7503
- *
7504
- * Routing all platforms through `resolveDeviceReachableOrigin` keeps
7505
- * them in lock-step:
7506
- *
7507
- * - Android wildcard / loopback → `10.0.2.2` (emulator NAT can't
7508
- * reach the host's LAN IP; physical devices opt in via
7509
- * `NS_HMR_PREFER_LAN_HOST=1`).
7510
- *
7511
- * - iOS / visionOS wildcard → `localhost` (simulator shares the
7512
- * host's network stack; physical devices opt in via the same
7513
- * `NS_HMR_PREFER_LAN_HOST=1` or `NS_HMR_HOST=<lan-ip>` env).
7514
- *
7515
- * - Explicit non-loopback `server.host` (e.g. a developer-set LAN
7516
- * IP) on any platform → trusted verbatim.
7517
- *
7518
- * The old `resolvedUrls.network[0]` preference is intentionally
7519
- * dropped — Vite reports a LAN IP whenever it detects one, but that
7520
- * IP is neither reachable from an Android emulator nor consistent
7521
- * with what `dev-host.ts` selects, so leaning on it created the
7522
- * realm-split risk above.
6818
+ * Dev-server origin baked into every module served to the device
6819
+ * (`/ns/core/...`, `/ns/m/...`). MUST match the origin `dev-host.ts` bakes
6820
+ * into `bundle.mjs`; if they disagree V8 keys them as different modules and
6821
+ * the app ends up with two `@nativescript/core` realms (a singleton-state
6822
+ * split). `resolveDeviceReachableOrigin` keeps every platform in lock-step:
6823
+ * Android wildcard/loopback -> `10.0.2.2`, iOS/visionOS wildcard ->
6824
+ * `localhost`, explicit non-loopback `server.host` -> trusted verbatim
6825
+ * (physical devices opt into LAN via `NS_HMR_PREFER_LAN_HOST`/`NS_HMR_HOST`).
7523
6826
  */
7524
6827
  function getServerOrigin(server) {
7525
6828
  const platform = detectDevHostPlatform();