@nativescript/vite 8.0.0-alpha.28 → 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 (96) hide show
  1. package/configuration/angular.js +26 -128
  2. package/configuration/angular.js.map +1 -1
  3. package/configuration/base.js.map +1 -1
  4. package/configuration/javascript.js +5 -72
  5. package/configuration/javascript.js.map +1 -1
  6. package/configuration/typescript.js +4 -74
  7. package/configuration/typescript.js.map +1 -1
  8. package/helpers/angular/angular-linker.d.ts +5 -6
  9. package/helpers/angular/angular-linker.js +31 -110
  10. package/helpers/angular/angular-linker.js.map +1 -1
  11. package/helpers/angular/inject-component-hmr-registration.js +2 -70
  12. package/helpers/angular/inject-component-hmr-registration.js.map +1 -1
  13. package/helpers/angular/inject-hmr-vite-ignore.js +2 -69
  14. package/helpers/angular/inject-hmr-vite-ignore.js.map +1 -1
  15. package/helpers/angular/inline-decorator-component-templates.js +1 -170
  16. package/helpers/angular/inline-decorator-component-templates.js.map +1 -1
  17. package/helpers/angular/js-lexer.d.ts +4 -0
  18. package/helpers/angular/js-lexer.js +182 -0
  19. package/helpers/angular/js-lexer.js.map +1 -0
  20. package/helpers/angular/shared-linker.d.ts +31 -3
  21. package/helpers/angular/shared-linker.js +67 -14
  22. package/helpers/angular/shared-linker.js.map +1 -1
  23. package/helpers/angular/synthesize-decorator-ctor-parameters.js +2 -170
  24. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -1
  25. package/helpers/angular/synthesize-injectable-factories.js +1 -174
  26. package/helpers/angular/synthesize-injectable-factories.js.map +1 -1
  27. package/helpers/app-components.d.ts +2 -1
  28. package/helpers/app-components.js.map +1 -1
  29. package/helpers/app-css-state.d.ts +8 -0
  30. package/helpers/app-css-state.js +8 -0
  31. package/helpers/app-css-state.js.map +1 -0
  32. package/helpers/bundler-context.d.ts +11 -0
  33. package/helpers/bundler-context.js +71 -0
  34. package/helpers/bundler-context.js.map +1 -0
  35. package/helpers/dev-host.d.ts +2 -1
  36. package/helpers/dev-host.js.map +1 -1
  37. package/helpers/main-entry.js +2 -3
  38. package/helpers/main-entry.js.map +1 -1
  39. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  40. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  41. package/helpers/nativeclass-transform.js.map +1 -1
  42. package/helpers/platform-types.d.ts +2 -0
  43. package/helpers/platform-types.js +2 -0
  44. package/helpers/platform-types.js.map +1 -0
  45. package/helpers/prelink-angular.js +11 -29
  46. package/helpers/prelink-angular.js.map +1 -1
  47. package/helpers/typescript-check.d.ts +2 -1
  48. package/helpers/typescript-check.js.map +1 -1
  49. package/helpers/workers.js +1 -1
  50. package/helpers/workers.js.map +1 -1
  51. package/hmr/client/css-handler.js +1 -17
  52. package/hmr/client/css-handler.js.map +1 -1
  53. package/hmr/client/utils.d.ts +1 -1
  54. package/hmr/client/utils.js +15 -59
  55. package/hmr/client/utils.js.map +1 -1
  56. package/hmr/frameworks/angular/server/strategy.js +3 -14
  57. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  58. package/hmr/frameworks/solid/server/strategy.js +3 -18
  59. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  60. package/hmr/frameworks/typescript/server/strategy.js +2 -15
  61. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  62. package/hmr/frameworks/vue/client/index.js +0 -154
  63. package/hmr/frameworks/vue/client/index.js.map +1 -1
  64. package/hmr/server/core-sanitize.d.ts +3 -4
  65. package/hmr/server/core-sanitize.js +3 -4
  66. package/hmr/server/core-sanitize.js.map +1 -1
  67. package/hmr/server/framework-strategy.d.ts +9 -19
  68. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  69. package/hmr/server/vendor-bare-module-shims.js +80 -0
  70. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  71. package/hmr/server/websocket-angular-entry.js +1 -1
  72. package/hmr/server/websocket-angular-entry.js.map +1 -1
  73. package/hmr/server/websocket-module-bindings.js +1 -1
  74. package/hmr/server/websocket-module-bindings.js.map +1 -1
  75. package/hmr/server/websocket-served-module-helpers.d.ts +4 -1
  76. package/hmr/server/websocket-served-module-helpers.js +19 -9
  77. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  78. package/hmr/server/websocket-vue-sfc.js +3 -3
  79. package/hmr/server/websocket-vue-sfc.js.map +1 -1
  80. package/hmr/server/websocket.d.ts +10 -33
  81. package/hmr/server/websocket.js +31 -755
  82. package/hmr/server/websocket.js.map +1 -1
  83. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  84. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  85. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  86. package/hmr/shared/runtime/dev-overlay.d.ts +3 -29
  87. package/hmr/shared/runtime/dev-overlay.js +3 -330
  88. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  89. package/hmr/shared/runtime/root-placeholder.js +9 -33
  90. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  91. package/hmr/shared/vendor/manifest.js +5 -82
  92. package/hmr/shared/vendor/manifest.js.map +1 -1
  93. package/package.json +53 -11
  94. package/hmr/server/websocket-ns-m-finalize.d.ts +0 -22
  95. package/hmr/server/websocket-ns-m-finalize.js +0 -88
  96. package/hmr/server/websocket-ns-m-finalize.js.map +0 -1
@@ -32,6 +32,8 @@ 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';
@@ -47,10 +49,11 @@ import { classifyBootRoute, classifyHmrUpdateKind, createColdBootRequestCounter,
47
49
  import { createHmrPendingMessage } from './websocket-hmr-pending.js';
48
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';
49
51
  import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
50
- import { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, extractDirectExportedNames, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
52
+ import { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
51
53
  import { createSharedTransformRequestRunner } from './shared-transform-request.js';
52
54
  import { formatNsMHmrServeTag, getNumericServeVersionTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
53
- 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 };
54
57
  export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
55
58
  export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
56
59
  export { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
@@ -166,9 +169,6 @@ function getHmrSocketRole(client) {
166
169
  }
167
170
  return typeof client.__nsHmrClientRole === 'string' && client.__nsHmrClientRole ? client.__nsHmrClientRole : 'unknown';
168
171
  }
169
- function shouldAllowLocalCoreSanitizerPaths(contextLabel) {
170
- return /\bnode_modules\/@nativescript\/vite\/hmr\/(?:client|frameworks)\//.test(contextLabel);
171
- }
172
172
  export function prepareAngularEntryForDevice(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
173
173
  const rewrittenCode = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp);
174
174
  return rewriteAngularEntryRegisterOnly(rewrittenCode, resolveAngularCoreHmrImportSource(rewrittenCode, httpOrigin));
@@ -177,74 +177,6 @@ const processSfcCode = createProcessSfcCode(processCodeForDevice);
177
177
  // Bare specifiers and special skip patterns (virtual, data:, etc.)
178
178
  const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
179
179
  const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
180
- const MODULE_IMPORT_ANALYSIS_PLUGINS = ['typescript', 'jsx', 'importMeta', 'topLevelAwait', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', 'decorators-legacy'];
181
- function collectTopLevelImportRecords(code) {
182
- if (!code || typeof code !== 'string' || !/\bimport\b/.test(code)) {
183
- return [];
184
- }
185
- try {
186
- const ast = babelParse(code, {
187
- sourceType: 'module',
188
- plugins: MODULE_IMPORT_ANALYSIS_PLUGINS,
189
- });
190
- const body = ast?.program?.body;
191
- if (!Array.isArray(body)) {
192
- return [];
193
- }
194
- return body
195
- .filter((node) => t.isImportDeclaration(node) && typeof node.start === 'number' && typeof node.end === 'number' && typeof node.source?.value === 'string')
196
- .map((node) => ({
197
- start: node.start,
198
- end: node.end,
199
- text: code.slice(node.start, node.end),
200
- source: node.source.value,
201
- hasOnlyNamedSpecifiers: Array.isArray(node.specifiers) && node.specifiers.length > 0 && node.specifiers.every((spec) => t.isImportSpecifier(spec)),
202
- namedBindings: Array.isArray(node.specifiers)
203
- ? node.specifiers
204
- .filter((spec) => t.isImportSpecifier(spec) && typeof spec.start === 'number' && typeof spec.end === 'number')
205
- .map((spec) => ({
206
- importedName: t.isIdentifier(spec.imported) ? spec.imported.name : String(spec.imported?.value || ''),
207
- text: code.slice(spec.start, spec.end),
208
- }))
209
- : [],
210
- }));
211
- }
212
- catch {
213
- return [];
214
- }
215
- }
216
- function hoistTopLevelStaticImports(code) {
217
- const imports = collectTopLevelImportRecords(code);
218
- if (!imports.length) {
219
- return code;
220
- }
221
- let stripped = code;
222
- for (const imp of [...imports].sort((left, right) => right.start - left.start)) {
223
- stripped = stripped.slice(0, imp.start) + stripped.slice(imp.end);
224
- }
225
- const hoisted = [];
226
- const seen = new Set();
227
- for (const imp of imports) {
228
- const text = imp.text.trim();
229
- if (!text || seen.has(text)) {
230
- continue;
231
- }
232
- seen.add(text);
233
- hoisted.push(text);
234
- }
235
- if (!hoisted.length) {
236
- return stripped;
237
- }
238
- return `${hoisted.join('\n')}\n${stripped.replace(/^\s*\n+/, '')}`;
239
- }
240
- // Duplicate of `websocket-served-module-helpers.ts::buildBootProgressSnippet`
241
- // — both copies must stay in lock-step. See the canonical doc there for
242
- // why the snippet must remain fully synchronous (top-level await on a
243
- // boot-tagged module trips the iOS 10 s async-module deadline).
244
- export function buildBootProgressSnippet(bootModuleLabel) {
245
- const normalizedLabel = JSON.stringify(String(bootModuleLabel || '').replace(/\\/g, '/'));
246
- 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');
247
- }
248
180
  function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
249
181
  const imports = collectTopLevelImportRecords(code);
250
182
  if (!imports.length) {
@@ -333,145 +265,6 @@ function guardBareDynamicImports(code) {
333
265
  return code;
334
266
  }
335
267
  }
336
- function stripCoreGlobalsImports(code) {
337
- const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
338
- return code.replace(pattern, '');
339
- }
340
- function ensureVariableDynamicImportHelper(code) {
341
- if (!code.includes('__variableDynamicImportRuntimeHelper')) {
342
- return code;
343
- }
344
- if (PAT.VARIABLE_DYNAMIC_IMPORT_HELPER_PATTERN.test(code)) {
345
- return code;
346
- }
347
- const helper = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n` +
348
- ` try { if (request === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n` +
349
- ` const loader = map && (map[request] || map[request?.replace(/\\\\/g, "/")]);\n` +
350
- ` if (!loader) {\n` +
351
- ` const error = new Error(\"Cannot dynamically import: \" + request);\n` +
352
- ` error.code = 'ERR_MODULE_NOT_FOUND';\n` +
353
- ` return Promise.reject(error);\n` +
354
- ` }\n` +
355
- ` try {\n` +
356
- ` return loader(importMode);\n` +
357
- ` } catch (err) {\n` +
358
- ` return Promise.reject(err);\n` +
359
- ` }\n` +
360
- `};\n`;
361
- return `${helper}${code}`;
362
- }
363
- function ensureGuardPlainDynamicImports(code, origin) {
364
- try {
365
- if (!code || !/\bimport\s*\(/.test(code))
366
- return code;
367
- 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`;
368
- const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
369
- if (replaced !== code) {
370
- return wrapper + replaced;
371
- }
372
- return code;
373
- }
374
- catch {
375
- return code;
376
- }
377
- }
378
- // `ensureDynamicHmrImportHelper` lives in
379
- // `./websocket-served-module-helpers.js`. See that file for the
380
- // architectural rationale and the current helper implementation.
381
- async function expandStarExports(code, server, projectRoot, verbose, sharedTransformer) {
382
- const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
383
- let match;
384
- const replacements = [];
385
- while ((match = STAR_RE.exec(code)) !== null) {
386
- const url = match[2];
387
- if (!url.includes('/node_modules/'))
388
- continue;
389
- replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
390
- }
391
- if (!replacements.length)
392
- return code;
393
- // Pull target URLs through the shared runner when it's available so each
394
- // node_modules path shares the 60s TTL cache with the main /ns/m pipeline
395
- // and respects the global concurrency gate. Fan them out in parallel —
396
- // this block used to be a serial `for await` loop, which dominated cold
397
- // boot on apps with dozens of star-re-exports.
398
- const transformer = sharedTransformer ?? ((url) => server.transformRequest(url));
399
- const resolved = await Promise.all(replacements.map(async (rep) => {
400
- try {
401
- let vitePath = rep.url.replace(/^https?:\/\/[^/]+/, '');
402
- vitePath = vitePath.replace(/^\/ns\/m\//, '/');
403
- vitePath = vitePath.replace(/^\/__ns_boot__\/[^/]+/, '');
404
- vitePath = vitePath.replace(/\/__ns_hmr__\/[^/]+/, '');
405
- const result = await transformer(vitePath);
406
- if (!result?.code)
407
- return null;
408
- const names = extractExportedNames(result.code);
409
- if (!names.length)
410
- return null;
411
- if (verbose) {
412
- console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
413
- }
414
- return { rep, names };
415
- }
416
- catch {
417
- return null;
418
- }
419
- }));
420
- for (const entry of resolved) {
421
- if (!entry)
422
- continue;
423
- const explicit = `export { ${entry.names.join(', ')} } from ${JSON.stringify(entry.rep.url)};`;
424
- code = code.replace(entry.rep.full, explicit);
425
- }
426
- return code;
427
- }
428
- function extractExportedNames(code) {
429
- return extractDirectExportedNames(code);
430
- }
431
- function repairImportEqualsAssignments(code) {
432
- try {
433
- if (!code || typeof code !== 'string')
434
- return code;
435
- code = code.replace(/(^|\n)\s*import\s*\{([^}]+)\}\s*=\s*([^;]+);?/g, (_m, p1, specList, rhs) => {
436
- const cleaned = String(specList)
437
- .split(',')
438
- .map((s) => s.trim())
439
- .filter(Boolean)
440
- .map((seg) => seg.replace(/\s+as\s+/i, ': '))
441
- .join(', ');
442
- return `${p1}const { ${cleaned} } = ${rhs};`;
443
- });
444
- 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});`);
445
- code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => `${p1}const ${id} = ${rhs};`);
446
- }
447
- catch { }
448
- return code;
449
- }
450
- function ensureVersionedRtImports(code, origin, ver) {
451
- if (!code || !origin || !Number.isFinite(ver))
452
- return code;
453
- code = code.replace(/(from\s+["'])(?:https?:\/\/[^"']+)?\/(?:\ns|ns)\/rt(?:\/[\d]+)?(["'])/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
454
- code = code.replace(/(import\(\s*["'])(?:https?:\/\/[^"']+)?\/(?:\@ns|ns)\/rt(?:\/[\d]+)?(["']\s*\))/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
455
- return code;
456
- }
457
- function stripViteDynamicImportVirtual(code) {
458
- if (!/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
459
- return code;
460
- }
461
- const original = code;
462
- code = code.replace(/^[\t ]*import[^\n]*\/@id\/__x00__vite\/dynamic-import-helper[^\n]*$/gm, '');
463
- if (/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
464
- code = code.replace(/\/@id\/__x00__vite\/dynamic-import-helper[^"'`)]*/g, '/__NS_UNUSED_DYNAMIC_IMPORT_HELPER__');
465
- }
466
- if (!/__variableDynamicImportRuntimeHelper/.test(code)) {
467
- 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`;
468
- code = inline + code;
469
- }
470
- if (code !== original) {
471
- code = `// [hmr-sanitize] removed virtual dynamic-import-helper\n${code}`;
472
- }
473
- return code;
474
- }
475
268
  // Detect (and log) `require('http(s)://...')` calls made from CJS shims.
476
269
  // Pattern: HTTP-served ESM modules end up in NS-vite's `__nsRequire`
477
270
  // shim with HTTP URLs as their relative resolution targets. The guard
@@ -557,43 +350,6 @@ function stripImportMetaHotBlocks(code) {
557
350
  }
558
351
  return result;
559
352
  }
560
- // Extract a quick set of export names and whether a default export exists from ESM code.
561
- // This uses conservative regex scanning for metadata only (no code rewriting).
562
- function extractExportMetadata(code) {
563
- const named = new Set();
564
- let hasDefault = /\bexport\s+default\b/.test(code);
565
- try {
566
- // export const foo, export let foo, export function bar, export class Baz
567
- for (const m of code.matchAll(/\bexport\s+(?:const|let|var|function|class)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g)) {
568
- if (m[1])
569
- named.add(m[1]);
570
- }
571
- // export { a, b as c }
572
- for (const m of code.matchAll(/\bexport\s*\{([^}]+)\}/g)) {
573
- const inner = (m[1] || '')
574
- .split(',')
575
- .map((s) => s.trim())
576
- .filter(Boolean);
577
- for (const seg of inner) {
578
- // forms: name or name as alias or default as name
579
- const dm = seg.match(/^([A-Za-z_$][A-Za-z0-9_$]*)(?:\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*))?$/);
580
- if (dm) {
581
- const base = dm[1];
582
- const alias = dm[2];
583
- if (base === 'default') {
584
- hasDefault = true;
585
- continue;
586
- }
587
- named.add(alias || base);
588
- }
589
- }
590
- }
591
- }
592
- catch { }
593
- // Remove default if accidentally included
594
- named.delete('default');
595
- return { hasDefault, named: Array.from(named) };
596
- }
597
353
  function normalizeImportPath(spec, importerDir) {
598
354
  if (!spec)
599
355
  return null;
@@ -793,8 +549,8 @@ function cleanCode(code) {
793
549
  result = result.replace(PAT.VITE_CLIENT_IMPORT, '');
794
550
  result = result.replace(PAT.IMPORT_META_HOT_ASSIGNMENT, '');
795
551
  // Keep import.meta.hot call sites; runtime now provides a stable import.meta.hot.
796
- result = ACTIVE_STRATEGY.preClean(result);
797
- result = ACTIVE_STRATEGY.rewriteFrameworkImports(result);
552
+ result = ACTIVE_STRATEGY.preClean?.(result) ?? result;
553
+ result = ACTIVE_STRATEGY.rewriteFrameworkImports?.(result) ?? result;
798
554
  // Vendor manifest-driven import rewrites
799
555
  // NOTE: Static and side-effect vendor imports are intentionally NOT rewritten here.
800
556
  // They are left as import statements so that ensureNativeScriptModuleBindings()
@@ -822,13 +578,11 @@ function cleanCode(code) {
822
578
  }
823
579
  result = result.replace(PAT.VITE_CLIENT_IMPORT, '').replace(PAT.IMPORT_META_HOT_ASSIGNMENT, '');
824
580
  // Clean up HMR noise
825
- result = ACTIVE_STRATEGY.postClean(result);
581
+ result = ACTIVE_STRATEGY.postClean?.(result) ?? result;
826
582
  result = stripCoreGlobalsImports(result);
827
583
  return result;
828
584
  }
829
- // ============================================================================
830
- // APPLICATION IMPORT HELPERS
831
- // ============================================================================
585
+ // Application import helpers
832
586
  /**
833
587
  * Check if a path is an application module (not node_modules, not vendor, not relative)
834
588
  * This is generic and works for ANY project structure.
@@ -973,197 +727,6 @@ function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
973
727
  }
974
728
  return absolute;
975
729
  }
976
- /**
977
- * After the Angular linker runs on code that Vite has already resolved (bare
978
- * specifiers → full URLs), the linker injects NEW import statements with bare
979
- * specifiers (e.g. `import {Component} from '@angular/core'`). These cause:
980
- * 1. Duplicate-identifier SyntaxErrors (the name was already imported via URL)
981
- * 2. Unresolvable bare specifiers at runtime on device
982
- *
983
- * This function:
984
- * • builds a map packageName → resolvedURL from existing resolved imports
985
- * • collects all binding names already imported per package
986
- * • for each bare-specifier import, removes duplicate bindings
987
- * • rewrites any genuinely-new bindings to use the resolved URL
988
- */
989
- function deduplicateLinkerImports(code) {
990
- if (!code)
991
- return code;
992
- try {
993
- const imports = collectTopLevelImportRecords(code);
994
- if (!imports.length) {
995
- return code;
996
- }
997
- // ── Step 1: collect resolved imports already in the file ──────────
998
- const pkgUrlMap = new Map();
999
- const pkgBindings = new Map();
1000
- for (const imp of imports) {
1001
- const url = imp.source;
1002
- if (!/^https?:\/\//.test(url) && !url.startsWith('/')) {
1003
- continue;
1004
- }
1005
- const nmIdx = url.lastIndexOf('/node_modules/');
1006
- if (nmIdx === -1)
1007
- continue;
1008
- const afterNm = url.substring(nmIdx + '/node_modules/'.length);
1009
- const parts = afterNm.split('/');
1010
- const pkg = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
1011
- if (!pkgUrlMap.has(pkg))
1012
- pkgUrlMap.set(pkg, url);
1013
- if (imp.namedBindings.length) {
1014
- if (!pkgBindings.has(pkg))
1015
- pkgBindings.set(pkg, new Set());
1016
- for (const binding of imp.namedBindings) {
1017
- if (binding.importedName)
1018
- pkgBindings.get(pkg).add(binding.importedName);
1019
- }
1020
- }
1021
- }
1022
- if (pkgUrlMap.size === 0)
1023
- return code;
1024
- // ── Step 2: rewrite bare-specifier imports ───────────────────────
1025
- const edits = [];
1026
- for (const imp of imports) {
1027
- if (!imp.hasOnlyNamedSpecifiers) {
1028
- continue;
1029
- }
1030
- const specifier = imp.source;
1031
- if (specifier.startsWith('/') || specifier.startsWith('.') || specifier.startsWith('http')) {
1032
- continue;
1033
- }
1034
- const parts = specifier.split('/');
1035
- const pkg = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
1036
- const url = pkgUrlMap.get(pkg);
1037
- if (!url) {
1038
- continue;
1039
- }
1040
- const existing = pkgBindings.get(pkg) || new Set();
1041
- const newBindings = imp.namedBindings.filter((binding) => !existing.has(binding.importedName));
1042
- if (newBindings.length === 0) {
1043
- edits.push({ start: imp.start, end: imp.end, text: '' });
1044
- continue;
1045
- }
1046
- if (newBindings.length === imp.namedBindings.length) {
1047
- continue;
1048
- }
1049
- for (const binding of newBindings) {
1050
- existing.add(binding.importedName);
1051
- }
1052
- edits.push({
1053
- start: imp.start,
1054
- end: imp.end,
1055
- text: `import { ${newBindings.map((binding) => binding.text).join(', ')} } from ${JSON.stringify(url)};`,
1056
- });
1057
- }
1058
- if (!edits.length) {
1059
- return code;
1060
- }
1061
- let next = code;
1062
- for (const edit of edits.sort((left, right) => right.start - left.start)) {
1063
- next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
1064
- }
1065
- return next;
1066
- }
1067
- catch {
1068
- return code;
1069
- }
1070
- }
1071
- export function wrapCommonJsModuleForDevice(code, absolutePath) {
1072
- if (!code)
1073
- return code;
1074
- try {
1075
- const hasExportDefault = /\bexport\s+default\b/.test(code) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(code);
1076
- const hasNamedExports = /\bexport\s+(?:const|let|var|function|class|async)\b/.test(code) || /\bexport\s*\{/.test(code);
1077
- const hasCjsExports = /\bmodule\s*\.\s*exports\b/.test(code) || /\bexports\s*\.\s*\w/.test(code);
1078
- if (hasExportDefault || hasNamedExports || !hasCjsExports) {
1079
- return code;
1080
- }
1081
- const namedExports = new Set();
1082
- const exportsRe = /\bexports\s*\.\s*([A-Za-z_$][\w$]*)\s*=/g;
1083
- let em;
1084
- while ((em = exportsRe.exec(code)) !== null) {
1085
- const name = em[1];
1086
- if (name !== '__esModule' && name !== 'default') {
1087
- namedExports.add(name);
1088
- }
1089
- }
1090
- const defPropRe = /Object\s*\.\s*defineProperty\s*\(\s*exports\s*,\s*['"]([^'"]+)['"]/g;
1091
- while ((em = defPropRe.exec(code)) !== null) {
1092
- const name = em[1];
1093
- if (name !== '__esModule' && name !== 'default') {
1094
- namedExports.add(name);
1095
- }
1096
- }
1097
- // Static enumeration only sees `exports.foo = ...` and `Object.defineProperty(exports, 'foo', ...)`.
1098
- // Real-world packages like lodash attach their entire surface to a function inside an IIFE and
1099
- // then `module.exports = thatFunction`. Static analysis returns zero in that case. To handle
1100
- // these modules we ALSO load the package in the dev-server's Node context (only when we have a
1101
- // node_modules path) and merge the runtime keys. See `helpers/cjs-named-exports.ts` for the
1102
- // reasoning and safety boundaries.
1103
- if (absolutePath) {
1104
- try {
1105
- for (const n of getCjsNamedExports(absolutePath)) {
1106
- namedExports.add(n);
1107
- }
1108
- }
1109
- catch {
1110
- /* fall through to whatever we caught statically */
1111
- }
1112
- }
1113
- let suffix = `\nvar __cjs_mod = module.exports;\nexport default __cjs_mod;\n`;
1114
- if (namedExports.size) {
1115
- const entries = Array.from(namedExports);
1116
- const temps = entries.map((name, i) => `var __cjs_e${i} = __cjs_mod[${JSON.stringify(name)}];`);
1117
- const reExports = entries.map((name, i) => `__cjs_e${i} as ${name}`);
1118
- suffix += `${temps.join(' ')}\nexport { ${reExports.join(', ')} };\n`;
1119
- }
1120
- const prelude = `var module = { exports: {} }; var exports = module.exports;\n` +
1121
- `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` +
1122
- `var __ns_cjs_require_kind = (typeof globalThis.__nsBaseRequire === 'function' ? 'base-require' : (typeof globalThis.__nsRequire === 'function' ? 'vendor-require' : 'global-require'));\n` +
1123
- `var require = function(spec) {\n` +
1124
- ` if (!__ns_cjs_require_base) { throw new Error('require is not defined'); }\n` +
1125
- // Resolve relative specifiers against the HTTP-served module's URL
1126
- // before delegating to NS's runtime require. Without this step,
1127
- // \`require('./base64-vlq')\` inside a CJS module served from
1128
- // \`http://.../ns/m/node_modules/source-map-js/lib/source-map-generator.js\`
1129
- // would pass a literal '"./base64-vlq"' to the native require, which
1130
- // has no notion of the current HTTP-module's location and either
1131
- // throws "Module not found" or fetches an arbitrary filesystem path
1132
- // that happens to parse as code (producing misleading syntax errors
1133
- // like "missing ) after argument list" from unrelated modules).
1134
- ` var __nsResolvedSpec = spec;\n` +
1135
- ` try {\n` +
1136
- ` if (typeof spec === 'string' && (spec.indexOf('./') === 0 || spec.indexOf('../') === 0)) {\n` +
1137
- ` var __nsParentUrl = (typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string') ? import.meta.url : null;\n` +
1138
- ` if (__nsParentUrl) {\n` +
1139
- ` var __nsResolvedUrl = new URL(spec, __nsParentUrl);\n` +
1140
- ` // Common Node-style bare extensions: prefer .js if the resolved URL lacks an extension in its last path segment.\n` +
1141
- ` if (!/\\.[A-Za-z0-9]+$/.test(__nsResolvedUrl.pathname.split('/').pop() || '')) {\n` +
1142
- ` __nsResolvedUrl.pathname = __nsResolvedUrl.pathname.replace(/\\/+$/, '') + '.js';\n` +
1143
- ` }\n` +
1144
- ` __nsResolvedSpec = __nsResolvedUrl.href;\n` +
1145
- ` }\n` +
1146
- ` }\n` +
1147
- ` } catch (e) {}\n` +
1148
- ` 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` +
1149
- ` var mod = __ns_cjs_require_base(__nsResolvedSpec);\n` +
1150
- ` try {\n` +
1151
- ` if (mod && (typeof mod === 'object' || typeof mod === 'function') && mod.default !== undefined) {\n` +
1152
- ` var keys = [];\n` +
1153
- ` try { keys = Object.keys(mod); } catch (e) {}\n` +
1154
- ` var defaultOnly = keys.length === 1 && keys[0] === 'default';\n` +
1155
- ` var esModuleOnly = keys.length === 2 && keys.indexOf('default') !== -1 && keys.indexOf('__esModule') !== -1;\n` +
1156
- ` if (mod.__esModule || defaultOnly || esModuleOnly) { return mod.default; }\n` +
1157
- ` }\n` +
1158
- ` } catch (e) {}\n` +
1159
- ` return mod;\n` +
1160
- `};\n`;
1161
- return `${prelude}${code}${suffix}`;
1162
- }
1163
- catch {
1164
- return code;
1165
- }
1166
- }
1167
730
  /**
1168
731
  * Process code for device: inject globals, remove framework imports
1169
732
  */
@@ -1696,198 +1259,6 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
1696
1259
  // Assert that sanitized code no longer contains any Vite optimized deps artifacts
1697
1260
  // or virtual ids that could break the HTTP ESM loader on device. Throws with
1698
1261
  // a helpful diagnostics message if any are found.
1699
- function assertNoOptimizedArtifacts(code, contextLabel) {
1700
- try {
1701
- const offenders = [];
1702
- const lines = code.split('\n');
1703
- const tests = [
1704
- // Allow Vite dev indirections like /@id/ and /.vite/deps when served via HTTP.
1705
- // Only flag clearly invalid virtual placeholders if they surface in output.
1706
- /\b__VITE_PLUGIN__\b/,
1707
- /\b__VITE_PRELOAD__\b/,
1708
- ];
1709
- // Absolute or relative local @nativescript/core usage indicates a split realm risk; fail fast
1710
- const localCore = /(^|[^\w@])(?:\.\.?\/|\/)??@nativescript[\/_-]core\//i;
1711
- for (let i = 0; i < lines.length; i++) {
1712
- const ln = lines[i];
1713
- for (const re of tests) {
1714
- if (re.test(ln)) {
1715
- offenders.push(`${i + 1}: ${ln.substring(0, 200)}`);
1716
- break;
1717
- }
1718
- }
1719
- if (localCore.test(ln)) {
1720
- // Comments can never cause split-realm risk at runtime — skip them.
1721
- // Library authors commonly reference @nativescript/core in comments
1722
- // (e.g. TSDoc /// <reference> directives, module resolution notes).
1723
- const trimmed = ln.trimStart();
1724
- if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
1725
- continue;
1726
- }
1727
- if (shouldAllowLocalCoreSanitizerPaths(contextLabel)) {
1728
- continue;
1729
- }
1730
- offenders.push(`${i + 1}: ${ln.substring(0, 200)} [local-core-path]`);
1731
- }
1732
- if (offenders.length >= 10)
1733
- break;
1734
- }
1735
- if (offenders.length) {
1736
- 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');
1737
- const err = new Error(msg);
1738
- // Attach details for server logs / higher-level handlers
1739
- err.code = 'NS_SANITIZE_FAIL';
1740
- err.offenders = offenders;
1741
- throw err;
1742
- }
1743
- }
1744
- catch (e) {
1745
- // If diagnostics generation itself fails, do not mask the underlying issue
1746
- throw e;
1747
- }
1748
- }
1749
- // Ensure there are no lingering named imports from the unified core bridge.
1750
- // Converts named imports from `/ns/core[/<sub>]` into default import +
1751
- // destructuring. The package-main bridge serves a shape-shifted module whose
1752
- // named exports come from `__ns_core_bridge.default` rather than ESM named
1753
- // exports, so a named-import binding would be `undefined` at evaluation.
1754
- function ensureDestructureCoreImports(code) {
1755
- try {
1756
- let result = code;
1757
- let coreImportCounter = 0;
1758
- const toDestructure = (specList) => specList
1759
- .split(',')
1760
- .map((s) => s.trim())
1761
- .filter(Boolean)
1762
- .map((seg) => {
1763
- const m = seg.split(/\s+as\s+/i);
1764
- return m.length === 2 ? `${m[0].trim()}: ${m[1].trim()}` : seg;
1765
- })
1766
- .join(', ');
1767
- // import { A, B } from '/ns/core[/<sub>]'
1768
- const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[^"']+)?)['"];?\s*/gm;
1769
- result = result.replace(reNamed, (_full, pfx, specList, src) => {
1770
- // Deep subpath URLs serve actual ESM with real named exports — skip.
1771
- if (isDeepCoreSubpath(src))
1772
- return _full;
1773
- const tmp = `__ns_core_ns_re${coreImportCounter > 0 ? `_${coreImportCounter}` : ''}`;
1774
- coreImportCounter++;
1775
- const decl = `const { ${toDestructure(specList)} } = ${tmp};`;
1776
- return `${pfx}import ${tmp} from ${JSON.stringify(src)};\n${decl}\n`;
1777
- });
1778
- // import Default, { A, B } from '/ns/core[/<sub>]'
1779
- const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[^"']+)?)['"];?\s*/gm;
1780
- result = result.replace(reMixed, (_full, pfx, defName, specList, src) => {
1781
- if (isDeepCoreSubpath(src))
1782
- return _full;
1783
- const decl = `const { ${toDestructure(specList)} } = ${defName};`;
1784
- return `${pfx}import ${defName} from ${JSON.stringify(src)};\n${decl}\n`;
1785
- });
1786
- return result;
1787
- }
1788
- catch {
1789
- return code;
1790
- }
1791
- }
1792
- // Converts any named imports from the runtime bridge (/ns/rt[/ver]) into a default import + destructuring.
1793
- // This guarantees helper aliases like _resolveComponent remain bound even if we later normalize imports.
1794
- function ensureDestructureRtImports(code) {
1795
- try {
1796
- let result = code;
1797
- const toDestructure = (specList) => specList
1798
- .split(',')
1799
- .map((s) => s.trim())
1800
- .filter(Boolean)
1801
- .map((seg) => {
1802
- // Preserve alias mapping (e.g., resolveComponent as _resolveComponent)
1803
- const m = seg.split(/\s+as\s+/i);
1804
- return m.length === 2 ? `${m[0].trim()}: ${m[1].trim()}` : seg;
1805
- })
1806
- .join(', ');
1807
- const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)['"];?\s*/gm;
1808
- result = result.replace(reNamed, (_full, pfx, specList, src) => {
1809
- const tmp = `__ns_rt_ns_re`;
1810
- const decl = `const { ${toDestructure(specList)} } = ${tmp};`;
1811
- return `${pfx}import ${tmp} from ${JSON.stringify(src)};\n${decl}\n`;
1812
- });
1813
- const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)['"];?\s*/gm;
1814
- result = result.replace(reMixed, (_full, pfx, defName, specList, _src) => {
1815
- const decl = `const { ${toDestructure(specList)} } = ${defName};`;
1816
- return `${pfx}import ${defName} from ${JSON.stringify(_src)};\n${decl}\n`;
1817
- });
1818
- return result;
1819
- }
1820
- catch {
1821
- return code;
1822
- }
1823
- }
1824
- // Remove overlapping named imports from /ns/rt that duplicate bindings already provided
1825
- // by destructuring a default /ns/rt import (e.g., const { $showModal } = __ns_rt_ns_1;).
1826
- function dedupeRtNamedImportsAgainstDestructures(code) {
1827
- try {
1828
- let result = code;
1829
- // Collect bindings created from any destructure of __ns_rt_ns* temps
1830
- const rtDestructureRE = /(^|\n)\s*const\s*\{([^}]+)\}\s*=\s*(__ns_rt_ns(?:\d+|_re))\s*;?/gm;
1831
- const rtBound = new Set();
1832
- let m;
1833
- while ((m = rtDestructureRE.exec(result)) !== null) {
1834
- const specList = String(m[2] || '');
1835
- specList
1836
- .split(',')
1837
- .map((s) => s.trim())
1838
- .filter(Boolean)
1839
- .forEach((seg) => {
1840
- const bind = seg.includes(':') ? seg.split(':')[1].trim() : seg;
1841
- if (bind)
1842
- rtBound.add(bind);
1843
- });
1844
- }
1845
- if (!rtBound.size)
1846
- return result;
1847
- // For any named import from /ns/rt (versioned or not), drop specifiers that are already bound
1848
- const rtNamedImportRE = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?)["'];?\s*/gm;
1849
- const edits = [];
1850
- while ((m = rtNamedImportRE.exec(result)) !== null) {
1851
- const full = m[0];
1852
- const pfx = m[1] || '';
1853
- const specList = String(m[2] || '');
1854
- const src = m[3];
1855
- const kept = [];
1856
- specList
1857
- .split(',')
1858
- .map((s) => s.trim())
1859
- .filter(Boolean)
1860
- .forEach((seg) => {
1861
- const importedName = seg.split(/\s+as\s+/i)[0].trim();
1862
- if (!rtBound.has(importedName))
1863
- kept.push(seg);
1864
- });
1865
- let replacement = '';
1866
- if (kept.length) {
1867
- replacement = `${pfx}import { ${kept.join(', ')} } from ${JSON.stringify(src)};`;
1868
- }
1869
- else {
1870
- replacement = pfx || '';
1871
- }
1872
- edits.push({
1873
- start: rtNamedImportRE.lastIndex - full.length,
1874
- end: rtNamedImportRE.lastIndex,
1875
- text: replacement,
1876
- });
1877
- }
1878
- if (edits.length) {
1879
- edits
1880
- .sort((a, b) => b.start - a.start)
1881
- .forEach((e) => {
1882
- result = result.slice(0, e.start) + e.text + result.slice(e.end);
1883
- });
1884
- }
1885
- return result;
1886
- }
1887
- catch {
1888
- return code;
1889
- }
1890
- }
1891
1262
  /**
1892
1263
  * THE SINGLE REWRITE FUNCTION - used everywhere for consistency
1893
1264
  */
@@ -2476,9 +1847,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
2476
1847
  });
2477
1848
  return result;
2478
1849
  }
2479
- // ============================================================================
2480
- // PLUGIN
2481
- // ============================================================================
1850
+ // Plugin
2482
1851
  function createHmrWebSocketPlugin(opts) {
2483
1852
  const verbose = !!opts.verbose;
2484
1853
  let wss = null;
@@ -3235,7 +2604,7 @@ function createHmrWebSocketPlugin(opts) {
3235
2604
  try {
3236
2605
  const origin = getServerOrigin(server);
3237
2606
  code = ensureVersionedRtImports(code, origin, graphVersion);
3238
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, graphVersion);
2607
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, origin, graphVersion) ?? code;
3239
2608
  }
3240
2609
  catch { }
3241
2610
  // Compute rel .mjs output path
@@ -3291,7 +2660,7 @@ function createHmrWebSocketPlugin(opts) {
3291
2660
  }
3292
2661
  try {
3293
2662
  depCode = ensureVersionedRtImports(depCode, getServerOrigin(server), graphVersion);
3294
- depCode = ACTIVE_STRATEGY.ensureVersionedImports(depCode, getServerOrigin(server), graphVersion);
2663
+ depCode = ACTIVE_STRATEGY.ensureVersionedImports?.(depCode, getServerOrigin(server), graphVersion) ?? depCode;
3295
2664
  }
3296
2665
  catch { }
3297
2666
  let depRel = depResolved.replace(/^\//, '').replace(/\.(tsx?|jsx?)$/i, '.mjs');
@@ -3788,79 +3157,10 @@ function createHmrWebSocketPlugin(opts) {
3788
3157
  const pkg = bare;
3789
3158
  let code = '';
3790
3159
  if (pkg === 'vue' || pkg === 'nativescript-vue') {
3791
- // Re-export Vue helpers from vendor NativeScript-Vue (fallback to 'vue' if present)
3792
- code = `
3793
- const g = globalThis;
3794
- const reg = g.__nsVendorRegistry;
3795
- const req = reg && g.__nsVendorRequire ? g.__nsVendorRequire : (g.__nsRequire || g.require);
3796
- let mod = reg && reg.get('${pkg === 'vue' ? 'nativescript-vue' : 'nativescript-vue'}');
3797
- if (!mod && req) {
3798
- try { mod = req('${pkg === 'vue' ? 'nativescript-vue' : 'nativescript-vue'}'); } catch {}
3799
- ${pkg === 'vue' ? "if (!mod) { try { mod = req('vue'); } catch {} }" : ''}
3800
- }
3801
- mod = mod || {};
3802
- const v = (mod.default ?? mod);
3803
- export default v;
3804
- export const defineComponent = v.defineComponent;
3805
- export const resolveComponent = v.resolveComponent;
3806
- export const createVNode = v.createVNode;
3807
- export const createTextVNode = v.createTextVNode;
3808
- export const createCommentVNode = v.createCommentVNode;
3809
- export const Fragment = v.Fragment;
3810
- export const withCtx = v.withCtx;
3811
- export const openBlock = v.openBlock;
3812
- export const createBlock = v.createBlock;
3813
- export const createElementVNode = v.createElementVNode || v.createVNode;
3814
- export const createElementBlock = v.createElementBlock || v.createBlock;
3815
- export const renderSlot = v.renderSlot;
3816
- export const mergeProps = v.mergeProps;
3817
- export const toHandlers = v.toHandlers;
3818
- export const renderList = v.renderList;
3819
- export const normalizeProps = v.normalizeProps;
3820
- export const guardReactiveProps = v.guardReactiveProps;
3821
- export const withDirectives = v.withDirectives;
3822
- export const resolveDirective = v.resolveDirective;
3823
- export const withModifiers = v.withModifiers;
3824
- export const withKeys = v.withKeys;
3825
- export const ref = v.ref;
3826
- export const shallowRef = v.shallowRef;
3827
- export const unref = v.unref;
3828
- export const computed = v.computed;
3829
- export const onMounted = v.onMounted;
3830
- export const onBeforeUnmount = v.onBeforeUnmount;
3831
- export const onUnmounted = v.onUnmounted;
3832
- export const watch = v.watch;
3833
- export const nextTick = v.nextTick;
3834
- export const createApp = v.createApp || (vm && vm.createApp);
3835
- export const registerElement = v.registerElement || (vm && vm.registerElement);
3836
- export const normalizeClass = v.normalizeClass;
3837
- export const normalizeStyle = v.normalizeStyle;
3838
- export const toDisplayString = v.toDisplayString;
3839
- `;
3160
+ code = buildVueVendorShim(pkg);
3840
3161
  }
3841
3162
  else if (pkg === 'pinia') {
3842
- // Re-export Pinia APIs from vendor pinia module
3843
- code = `
3844
- const g = globalThis;
3845
- const reg = g.__nsVendorRegistry;
3846
- const req = reg && g.__nsVendorRequire ? g.__nsVendorRequire : (g.__nsRequire || g.require);
3847
- let mod = reg && reg.get('pinia');
3848
- if (!mod && req) { try { mod = req('pinia'); } catch {} }
3849
- mod = mod || {};
3850
- const p = (mod.default ?? mod);
3851
- export default p;
3852
- export const createPinia = p.createPinia;
3853
- export const defineStore = p.defineStore;
3854
- export const storeToRefs = p.storeToRefs;
3855
- export const setActivePinia = p.setActivePinia;
3856
- export const getActivePinia = p.getActivePinia;
3857
- export const mapStores = p.mapStores;
3858
- export const mapState = p.mapState;
3859
- export const mapGetters = p.mapGetters;
3860
- export const mapActions = p.mapActions;
3861
- export const mapWritableState = p.mapWritableState;
3862
- export const piniaSymbol = p.piniaSymbol;
3863
- `;
3163
+ code = buildPiniaVendorShim();
3864
3164
  }
3865
3165
  res.statusCode = 200;
3866
3166
  res.end(code || 'export {}\n');
@@ -4018,7 +3318,7 @@ export const piniaSymbol = p.piniaSymbol;
4018
3318
  try {
4019
3319
  const verNum = 0;
4020
3320
  code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
4021
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
3321
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), verNum) ?? code;
4022
3322
  }
4023
3323
  catch { }
4024
3324
  // `/ns/m` URL finalize step.
@@ -5134,11 +4434,11 @@ export const piniaSymbol = p.piniaSymbol;
5134
4434
  const verNum = Number(verFromPath || '0');
5135
4435
  if (Number.isFinite(verNum) && verNum > 0) {
5136
4436
  code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
5137
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
4437
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), verNum) ?? code;
5138
4438
  }
5139
4439
  else {
5140
4440
  code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
5141
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), graphVersion);
4441
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, getServerOrigin(server), graphVersion) ?? code;
5142
4442
  }
5143
4443
  }
5144
4444
  catch { }
@@ -5845,7 +5145,7 @@ export const piniaSymbol = p.piniaSymbol;
5845
5145
  try {
5846
5146
  const origin = getServerOrigin(server);
5847
5147
  inlineCode2 = ensureVersionedRtImports(inlineCode2, origin, Number(ver));
5848
- inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports(inlineCode2, origin, Number(ver));
5148
+ inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports?.(inlineCode2, origin, Number(ver)) ?? inlineCode2;
5849
5149
  }
5850
5150
  catch { }
5851
5151
  // Normalize imports/helpers via AST to ensure _defineComponent and other helpers are bound once
@@ -5959,7 +5259,7 @@ export const piniaSymbol = p.piniaSymbol;
5959
5259
  try {
5960
5260
  const origin = getServerOrigin(server);
5961
5261
  code = ensureVersionedRtImports(code, origin, Number(ver));
5962
- code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, Number(ver));
5262
+ code = ACTIVE_STRATEGY.ensureVersionedImports?.(code, origin, Number(ver)) ?? code;
5963
5263
  }
5964
5264
  catch { }
5965
5265
  // Inline-template body path already runs processCodeForDevice (AST + sanitizers); no additional _defineComponent fix needed
@@ -6290,7 +5590,7 @@ export const piniaSymbol = p.piniaSymbol;
6290
5590
  }
6291
5591
  // Authoritative "what triggers HMR" gate, applied before the pending
6292
5592
  // overlay broadcast below: react only to files inside the app source
6293
- // dir (`appPath`) or a tsconfig-configured shared library.
5593
+ // dir (`appPath`) or a tsconfig-configured shared library.
6294
5594
  if (!isWithinHmrScope(file, getHmrSourceRootsCached())) {
6295
5595
  if (verbose) {
6296
5596
  console.log(`[ns-hmr][server] ignored change (outside HMR source scope): ${file}`);
@@ -6539,8 +5839,9 @@ export const piniaSymbol = p.piniaSymbol;
6539
5839
  // the framework's own template-update payload.
6540
5840
  if (!file.endsWith('.css')) {
6541
5841
  try {
6542
- const deps = server.__nsAppCssDeps;
6543
- const appCssPath = server.__nsAppCssPath;
5842
+ const appCssState = getAppCssState(server);
5843
+ const deps = appCssState?.deps;
5844
+ const appCssPath = appCssState?.path;
6544
5845
  if (deps && appCssPath) {
6545
5846
  const normalizedFile = path.resolve(file).replace(/\\/g, '/');
6546
5847
  if (deps.has(normalizedFile)) {
@@ -7496,9 +6797,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
7496
6797
  },
7497
6798
  };
7498
6799
  }
7499
- // ----------
7500
6800
  // Framework-specific HMR WebSocket plugins
7501
- // ----------
7502
6801
  export function hmrWebSocketVue(opts) {
7503
6802
  ACTIVE_STRATEGY = resolveFrameworkStrategy('vue');
7504
6803
  return createHmrWebSocketPlugin(opts);
@@ -7516,37 +6815,14 @@ export function hmrWebSocketTypescript(opts) {
7516
6815
  return createHmrWebSocketPlugin(opts);
7517
6816
  }
7518
6817
  /**
7519
- * Get the dev-server origin string baked into every module the rewriter
7520
- * serves to the device (`/ns/core/...`, `/ns/m/...`, etc.).
7521
- *
7522
- * This MUST agree with the origin `dev-host.ts` bakes into `bundle.mjs`
7523
- * itself. If the two disagree (e.g. the bundle imports
7524
- * `http://localhost:5173/ns/core/utils` while the rewriter splices in
7525
- * `http://192.168.0.8:5173/ns/core/utils`) V8's ESM loader keys those
7526
- * as DIFFERENT modules and the app ends up with two side-by-side
7527
- * realms of `@nativescript/core` — a classic singleton-state split.
7528
- * `internal/debug-sessions/LATEST-05-12-2026-HMR_CORE_REALM_SPLIT.md`
7529
- * documents the symptom.
7530
- *
7531
- * Routing all platforms through `resolveDeviceReachableOrigin` keeps
7532
- * them in lock-step:
7533
- *
7534
- * - Android wildcard / loopback → `10.0.2.2` (emulator NAT can't
7535
- * reach the host's LAN IP; physical devices opt in via
7536
- * `NS_HMR_PREFER_LAN_HOST=1`).
7537
- *
7538
- * - iOS / visionOS wildcard → `localhost` (simulator shares the
7539
- * host's network stack; physical devices opt in via the same
7540
- * `NS_HMR_PREFER_LAN_HOST=1` or `NS_HMR_HOST=<lan-ip>` env).
7541
- *
7542
- * - Explicit non-loopback `server.host` (e.g. a developer-set LAN
7543
- * IP) on any platform → trusted verbatim.
7544
- *
7545
- * The old `resolvedUrls.network[0]` preference is intentionally
7546
- * dropped — Vite reports a LAN IP whenever it detects one, but that
7547
- * IP is neither reachable from an Android emulator nor consistent
7548
- * with what `dev-host.ts` selects, so leaning on it created the
7549
- * 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`).
7550
6826
  */
7551
6827
  function getServerOrigin(server) {
7552
6828
  const platform = detectDevHostPlatform();