@nativescript/vite 8.0.0-alpha.13 → 8.0.0-alpha.15

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 (54) hide show
  1. package/helpers/main-entry.d.ts +2 -1
  2. package/helpers/main-entry.js +70 -8
  3. package/helpers/main-entry.js.map +1 -1
  4. package/helpers/ns-core-url.js +23 -0
  5. package/helpers/ns-core-url.js.map +1 -1
  6. package/hmr/client/css-handler.d.ts +1 -0
  7. package/hmr/client/css-handler.js +33 -5
  8. package/hmr/client/css-handler.js.map +1 -1
  9. package/hmr/client/css-update-overlay.d.ts +18 -0
  10. package/hmr/client/css-update-overlay.js +27 -0
  11. package/hmr/client/css-update-overlay.js.map +1 -0
  12. package/hmr/client/index.js +20 -0
  13. package/hmr/client/index.js.map +1 -1
  14. package/hmr/entry-runtime.d.ts +1 -0
  15. package/hmr/entry-runtime.js +95 -11
  16. package/hmr/entry-runtime.js.map +1 -1
  17. package/hmr/helpers/ast-normalizer.js +45 -5
  18. package/hmr/helpers/ast-normalizer.js.map +1 -1
  19. package/hmr/server/core-sanitize.d.ts +23 -0
  20. package/hmr/server/core-sanitize.js +60 -0
  21. package/hmr/server/core-sanitize.js.map +1 -1
  22. package/hmr/server/vite-plugin.js +24 -4
  23. package/hmr/server/vite-plugin.js.map +1 -1
  24. package/hmr/server/websocket-core-bridge.d.ts +10 -0
  25. package/hmr/server/websocket-core-bridge.js +35 -5
  26. package/hmr/server/websocket-core-bridge.js.map +1 -1
  27. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  28. package/hmr/server/websocket-css-hot-update.js +65 -0
  29. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  30. package/hmr/server/websocket-ns-m-finalize.js +2 -4
  31. package/hmr/server/websocket-ns-m-finalize.js.map +1 -1
  32. package/hmr/server/websocket-served-module-helpers.js +19 -6
  33. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  34. package/hmr/server/websocket.d.ts +1 -0
  35. package/hmr/server/websocket.js +120 -67
  36. package/hmr/server/websocket.js.map +1 -1
  37. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  38. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  39. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  40. package/hmr/shared/runtime/boot-progress.d.ts +40 -0
  41. package/hmr/shared/runtime/boot-progress.js +128 -0
  42. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  43. package/hmr/shared/runtime/boot-timeline.d.ts +1 -0
  44. package/hmr/shared/runtime/boot-timeline.js +1 -0
  45. package/hmr/shared/runtime/boot-timeline.js.map +1 -1
  46. package/hmr/shared/runtime/dev-overlay.js +96 -5
  47. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  48. package/hmr/shared/runtime/root-placeholder.js +317 -47
  49. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  50. package/hmr/shared/runtime/session-bootstrap.js +132 -18
  51. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  52. package/hmr/shared/vendor/manifest.js +18 -2
  53. package/hmr/shared/vendor/manifest.js.map +1 -1
  54. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from 'node:module';
2
- import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCoreSpecToBridge, isDeepCoreSubpath, rewriteSpecifiersForDevice } from './core-sanitize.js';
2
+ import { sanitizeStrayCoreReferences, isDeepCoreSubpath, rewriteSpecifiersForDevice } from './core-sanitize.js';
3
3
  import { buildDefaultExportFooter, buildShapeInstallHeader, hasNamespaceReExport, rewriteNamespaceReExportsForShape } from './ns-core-cjs-shape.js';
4
4
  // AST tooling for robust transformations
5
5
  import { parse as babelParse } from '@babel/parser';
@@ -33,10 +33,11 @@ import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
33
33
  import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
34
34
  import { buildRuntimeConfig, generateImportMap } from './import-map.js';
35
35
  import { getCliFlags } from '../../helpers/cli-flags.js';
36
- import { normalizeCoreSub as normalizeCoreSubCanonical } from '../../helpers/ns-core-url.js';
36
+ import { buildCoreUrl, buildCoreUrlPath, normalizeCoreSub as normalizeCoreSubCanonical } from '../../helpers/ns-core-url.js';
37
37
  import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
38
38
  import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
39
39
  import { angularSourceHasSemanticDecorator, canonicalizeTransformRequestCacheKey, collectAngularEvictionUrls, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
40
+ import { collectCssHotUpdatePaths } from './websocket-css-hot-update.js';
40
41
  import { classifyGraphUpsert, shouldBroadcastGraphUpsertDelta, shouldBumpGraphVersion } from './websocket-graph-upsert.js';
41
42
  import { classifyBootRoute, classifyHmrUpdateKind, createColdBootRequestCounter, formatHmrUpdateSummary, formatPopulateInitialGraphSummary, formatServerStartupBanner } from './perf-instrumentation.js';
42
43
  import { createHmrPendingMessage } from './websocket-hmr-pending.js';
@@ -55,6 +56,7 @@ export { rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
55
56
  // without churn while the implementation lives in a focused module.
56
57
  export { formatNsMHmrServeTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
57
58
  export { angularSourceHasSemanticDecorator, canonicalizeTransformRequestCacheKey, collectAngularEvictionUrls, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, createSharedTransformRequestRunner, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload, classifyGraphUpsert, shouldBroadcastGraphUpsertDelta, shouldBumpGraphVersion };
59
+ export { collectCssHotUpdatePaths } from './websocket-css-hot-update.js';
58
60
  const pluginTransformTypescript = (() => {
59
61
  const requireFromHere = createRequire(import.meta.url);
60
62
  const loaded = requireFromHere('@babel/plugin-transform-typescript');
@@ -215,14 +217,13 @@ function hoistTopLevelStaticImports(code) {
215
217
  }
216
218
  return `${hoisted.join('\n')}\n${stripped.replace(/^\s*\n+/, '')}`;
217
219
  }
220
+ // Duplicate of `websocket-served-module-helpers.ts::buildBootProgressSnippet`
221
+ // — both copies must stay in lock-step. See the canonical doc there for
222
+ // why the snippet must remain fully synchronous (top-level await on a
223
+ // boot-tagged module trips the iOS 10 s async-module deadline).
218
224
  export function buildBootProgressSnippet(bootModuleLabel) {
219
225
  const normalizedLabel = JSON.stringify(String(bootModuleLabel || '').replace(/\\/g, '/'));
220
- return [
221
- `const __nsBootGlobal=globalThis;`,
222
- `try{if(!__nsBootGlobal.__NS_HMR_BOOT_COMPLETE__){const __nsBootApi=__nsBootGlobal.__NS_HMR_DEV_OVERLAY__;if(__nsBootApi&&typeof __nsBootApi.setBootStage==='function'){const __nsBootCount=(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__=Number(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__||0)+1);__nsBootGlobal.__NS_HMR_BOOT_LAST_MODULE__=${normalizedLabel};const __nsBootNow=Date.now();const __nsBootLast=Number(__nsBootGlobal.__NS_HMR_BOOT_LAST_PROGRESS_AT__||0);if(__nsBootCount<=8||__nsBootCount%6===0||__nsBootNow-__nsBootLast>90){__nsBootGlobal.__NS_HMR_BOOT_LAST_PROGRESS_AT__=__nsBootNow;const __nsBootProgress=Math.min(94,82+Math.min(10,Math.round((Math.log(__nsBootCount+1)/Math.LN2)*2)));__nsBootApi.setBootStage('importing-main',{detail:'Evaluated '+__nsBootCount+' modules\\n'+__nsBootGlobal.__NS_HMR_BOOT_LAST_MODULE__,attempt:Number(__nsBootGlobal.__NS_HMR_BOOT_MAIN_ATTEMPT__||1),attempts:Number(__nsBootGlobal.__NS_HMR_BOOT_MAIN_ATTEMPTS__||6),progress:__nsBootProgress});}}}}catch(__nsBootErr){}`,
223
- `if(!__nsBootGlobal.__NS_HMR_BOOT_COMPLETE__){const __nsBootCount=Number(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__||0);if(__nsBootCount<=24||__nsBootCount%8===0){await new Promise((resolve)=>setTimeout(resolve,0));}}`,
224
- '',
225
- ].join('\n');
226
+ 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');
226
227
  }
227
228
  function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
228
229
  const imports = collectTopLevelImportRecords(code);
@@ -1171,7 +1172,20 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
1171
1172
  // Minimal process shim — populated with CLI --env.* flags at module load time.
1172
1173
  // In production builds, Vite/Rollup replaces process.env.* statically.
1173
1174
  // In HMR dev mode the code runs as-is on device, so we need the shim.
1174
- `if (typeof process === "undefined") { globalThis.process = { env: ${__processEnvJson} }; } else if (!process.env) { process.env = ${__processEnvJson}; }`,
1175
+ //
1176
+ // IMPORTANT: every check goes through `globalThis.process` (a member
1177
+ // expression), NEVER bare `typeof process` (an identifier reference).
1178
+ // bare identifier resolution
1179
+ // against runtime-added global object properties is not reliable in
1180
+ // V8 module scope. `globalThis.process` is unambiguous: it always
1181
+ // reads the `process` property off the (single) global object.
1182
+ //
1183
+ // The shim is also strictly additive — it only initializes
1184
+ // `globalThis.process` and `globalThis.process.env` if they are
1185
+ // missing. App code that pre-populates `process.env` (e.g. an Azure
1186
+ // App Configuration boot module) is preserved; we never overwrite a
1187
+ // populated env with the bare `{ NODE_ENV: 'development' }` stub.
1188
+ `if (typeof globalThis.process === "undefined" || globalThis.process === null) { globalThis.process = { env: ${__processEnvJson} }; } else if (!globalThis.process.env) { globalThis.process.env = ${__processEnvJson}; }`,
1175
1189
  'const __ANDROID__ = globalThis.__ANDROID__ !== undefined ? globalThis.__ANDROID__ : false;',
1176
1190
  'const __IOS__ = globalThis.__IOS__ !== undefined ? globalThis.__IOS__ : false;',
1177
1191
  'const __VISIONOS__ = globalThis.__VISIONOS__ !== undefined ? globalThis.__VISIONOS__ : false;',
@@ -1423,21 +1437,11 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
1423
1437
  }
1424
1438
  }
1425
1439
  catch { }
1426
- // Normalize stray string-literal side-effect lines that still reference @nativescript/core
1427
- // into proper imports of the unified core bridge. This prevents the local-core-path
1428
- // fast-fail from triggering due to upstream transforms that emitted naked literals.
1429
- try {
1430
- result = normalizeStrayCoreStringLiterals(result);
1431
- }
1432
- catch { }
1433
- try {
1434
- result = fixDanglingCoreFrom(result);
1435
- }
1436
- catch { }
1437
- try {
1438
- result = normalizeAnyCoreSpecToBridge(result);
1439
- }
1440
- catch { }
1440
+ // Apply the three-pass safety net for stray @nativescript/core references
1441
+ // (naked string literals, dangling `from` merges, lingering resolved-path
1442
+ // references). Centralised in core-sanitize.sanitizeStrayCoreReferences so
1443
+ // every NS-M emitter applies the same passes in the same order.
1444
+ result = sanitizeStrayCoreReferences(result);
1441
1445
  result = ensureVariableDynamicImportHelper(result);
1442
1446
  // Normalize any lingering @nativescript/core imports to the /ns/core bridge (non-destructive best-effort)
1443
1447
  try {
@@ -1869,10 +1873,30 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
1869
1873
  try {
1870
1874
  let coreAliasIdx = 0;
1871
1875
  const mkAlias = () => `__NSC${coreAliasIdx++}`;
1872
- const coreUrl = (sub) => {
1873
- const p = (sub || '').replace(/^\//, '');
1874
- return `${httpOriginSafe || ''}/ns/core` + (p ? `?p=${p}` : '');
1875
- };
1876
+ // Use the canonical PATH form `/ns/core/<sub>` (NOT the legacy
1877
+ // `/ns/core?p=<sub>` query form). The iOS HTTP ESM loader caches
1878
+ // module records by URL string if vendor.mjs's external imports
1879
+ // resolve to `/ns/core/<sub>` (via the import map + buildCoreUrl()
1880
+ // canonical generator) and the app's rewritten imports resolve to
1881
+ // `/ns/core?p=<sub>`, V8 creates two distinct module records for
1882
+ // the same logical core subpath. Each gets its own class
1883
+ // identities (TextBase, View, etc.), and side-effect patches
1884
+ // applied to one (e.g. @nativescript-community/text's
1885
+ // `TextBase.prototype.setTextDecorationAndTransform` override
1886
+ // installed when vendor.mjs evaluates `overrideSpanAndFormattedString()`
1887
+ // from `@nativescript-community/ui-label/index-common.js`) are
1888
+ // invisible to the other — manifesting as inconsistent line-height /
1889
+ // letter-spacing rendering between HMR and no-HMR.
1890
+ //
1891
+ // Mirrors `normalizeCoreSub()` (helpers/ns-core-url.ts) so the URL
1892
+ // produced here is byte-identical to what `buildCoreUrl()` produces
1893
+ // for the bundle entry, import map, and external-urls plugin.
1894
+ // Delegate to the ONE canonical URL builder so every emitter (this
1895
+ // rewriter, core-sanitize, the runtime import map, the ns-core-external-urls
1896
+ // build plugin, and main-entry) produces byte-identical URLs for
1897
+ // the same logical core module. Any drift here would re-introduce
1898
+ // the realm-split bug.
1899
+ const coreUrl = (sub) => (httpOriginSafe ? buildCoreUrl(httpOriginSafe, sub) : buildCoreUrlPath(sub));
1876
1900
  // Case 1: import { A, B } from '@nativescript/core[/sub]'
1877
1901
  result = result.replace(/(^|\n)\s*import\s*\{\s*([^}]+?)\s*\}\s*from\s+["']@nativescript\/core([^"'\n]*)["'];?/g, (_m, pfx, names, sub) => {
1878
1902
  const alias = mkAlias();
@@ -3824,16 +3848,14 @@ export const piniaSymbol = p.piniaSymbol;
3824
3848
  code = ensureGuardPlainDynamicImports(code, getServerOrigin(server));
3825
3849
  }
3826
3850
  catch { }
3827
- // Extra hardening: normalize any remaining core references to the unified bridge
3828
- // - Stray string-literals
3829
- // - Dangling `from` merges
3830
- // - Any spec (including /node_modules resolves) that still references '@nativescript/core'
3831
- // Do this right before the final fast-fail assertion. If a rewrite occurred, add a small marker for diagnostics.
3851
+ // Extra hardening before the fast-fail assertion: run the
3852
+ // consolidated stray-core-reference safety net. If any
3853
+ // rewrite occurred, leave a diagnostic marker so the
3854
+ // pipeline review log explains why the served code carries
3855
+ // it.
3832
3856
  try {
3833
3857
  const __before = code;
3834
- code = normalizeStrayCoreStringLiterals(code);
3835
- code = fixDanglingCoreFrom(code);
3836
- code = normalizeAnyCoreSpecToBridge(code);
3858
+ code = sanitizeStrayCoreReferences(code);
3837
3859
  if (code !== __before) {
3838
3860
  code = `// [hmr-sanitize] core-literal->bridge\n` + code;
3839
3861
  }
@@ -4368,6 +4390,23 @@ export const piniaSymbol = p.piniaSymbol;
4368
4390
  const coreRequest = parseCoreBridgeRequest(urlObj.pathname, urlObj.searchParams, Number(graphVersion || 0));
4369
4391
  if (!coreRequest)
4370
4392
  return next();
4393
+ // Non-canonical incoming URL — every emitter is supposed
4394
+ // to canonicalize before hitting the device. Promote the
4395
+ // drift to a 301 redirect so iOS still gets the file at
4396
+ // the canonical URL (no realm split) but the offending
4397
+ // caller is forced to update. We log the offending raw
4398
+ // pathname so the regression source is easy to find.
4399
+ if (coreRequest.canonicalPath) {
4400
+ try {
4401
+ console.warn(`[ns-core-bridge] 301 ${urlObj.pathname}${urlObj.search} → ${coreRequest.canonicalPath} (non-canonical core URL — please update emitter)`);
4402
+ }
4403
+ catch { }
4404
+ res.setHeader('Access-Control-Allow-Origin', '*');
4405
+ res.setHeader('Location', coreRequest.canonicalPath);
4406
+ res.statusCode = 301;
4407
+ res.end();
4408
+ return;
4409
+ }
4371
4410
  res.setHeader('Access-Control-Allow-Origin', '*');
4372
4411
  res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
4373
4412
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
@@ -6460,6 +6499,51 @@ export const piniaSymbol = p.piniaSymbol;
6460
6499
  console.warn('[hmr-ws][v2] failed graph update', e);
6461
6500
  }
6462
6501
  const root = server.config.root || process.cwd();
6502
+ // CSS hot-update — handled BEFORE the project-scope filter
6503
+ // because workspace `@import` deps live outside `<root>/`.
6504
+ // The helper maps in-scope edits to their own path and
6505
+ // out-of-scope edits to `app.css` (Vite re-runs PostCSS
6506
+ // through the `@import` chain on the next fetch).
6507
+ if (file.endsWith('.css')) {
6508
+ const cssPaths = collectCssHotUpdatePaths({
6509
+ file,
6510
+ root,
6511
+ appRootDir: APP_ROOT_DIR,
6512
+ appEntryCss: path.resolve(root, APP_ROOT_DIR, 'app.css'),
6513
+ });
6514
+ if (cssPaths.length > 0) {
6515
+ updateMetrics.tAfterFramework = Date.now();
6516
+ try {
6517
+ const origin = getServerOrigin(server);
6518
+ const timestamp = Date.now();
6519
+ const msg = {
6520
+ type: 'ns:css-updates',
6521
+ origin,
6522
+ updates: cssPaths.map((cssPath) => ({
6523
+ type: 'css-update',
6524
+ path: cssPath,
6525
+ acceptedPath: cssPath,
6526
+ timestamp,
6527
+ })),
6528
+ };
6529
+ wss.clients.forEach((client) => {
6530
+ if (isSocketClientOpen(client)) {
6531
+ client.send(JSON.stringify(msg));
6532
+ updateMetrics.recipients += 1;
6533
+ }
6534
+ });
6535
+ }
6536
+ catch (error) {
6537
+ console.warn('[hmr-ws] CSS update failed:', error);
6538
+ }
6539
+ if (verbose)
6540
+ console.log(`[hmr-ws] Hot update for: ${file} → broadcast CSS paths: ${cssPaths.join(', ')}`);
6541
+ emitHmrUpdateSummary();
6542
+ return;
6543
+ }
6544
+ // CSS without a broadcast target (no appEntryCss
6545
+ // configured) — fall through to the scope filter.
6546
+ }
6463
6547
  const srcDir = `${root}/src`;
6464
6548
  const coreDir = `${root}/core`;
6465
6549
  const appDir = `${root}/${APP_ROOT_DIR}`;
@@ -6471,37 +6555,6 @@ export const piniaSymbol = p.piniaSymbol;
6471
6555
  return;
6472
6556
  if (verbose)
6473
6557
  console.log(`[hmr-ws] Hot update for: ${file}`);
6474
- // Handle CSS updates
6475
- if (file.endsWith('.css')) {
6476
- updateMetrics.tAfterFramework = Date.now();
6477
- try {
6478
- let rel = '/' + path.posix.normalize(path.relative(root, file)).split(path.sep).join('/');
6479
- const origin = getServerOrigin(server);
6480
- const msg = {
6481
- type: 'ns:css-updates',
6482
- origin,
6483
- updates: [
6484
- {
6485
- type: 'css-update',
6486
- path: rel,
6487
- acceptedPath: rel,
6488
- timestamp: Date.now(),
6489
- },
6490
- ],
6491
- };
6492
- wss.clients.forEach((client) => {
6493
- if (isSocketClientOpen(client)) {
6494
- client.send(JSON.stringify(msg));
6495
- updateMetrics.recipients += 1;
6496
- }
6497
- });
6498
- }
6499
- catch (error) {
6500
- console.warn('[hmr-ws] CSS update failed:', error);
6501
- }
6502
- emitHmrUpdateSummary();
6503
- return;
6504
- }
6505
6558
  // Framework-specific hot update handling
6506
6559
  if (ACTIVE_STRATEGY.flavor === 'angular') {
6507
6560
  // For Angular, react to component TS or external template HTML changes under /src