@nativescript/vite 8.0.0-alpha.67 → 8.0.0-alpha.69

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 (186) hide show
  1. package/configuration/angular.js +78 -182
  2. package/configuration/angular.js.map +1 -1
  3. package/configuration/base.js +10 -139
  4. package/configuration/base.js.map +1 -1
  5. package/helpers/app-css-refresh.d.ts +31 -0
  6. package/helpers/app-css-refresh.js +43 -0
  7. package/helpers/app-css-refresh.js.map +1 -0
  8. package/helpers/app-css-state.d.ts +9 -0
  9. package/helpers/app-css-state.js.map +1 -1
  10. package/helpers/main-entry.js +27 -24
  11. package/helpers/main-entry.js.map +1 -1
  12. package/helpers/ns-core-external-urls.d.ts +32 -0
  13. package/helpers/ns-core-external-urls.js +107 -0
  14. package/helpers/ns-core-external-urls.js.map +1 -0
  15. package/helpers/typescript-check.js +2 -2
  16. package/helpers/typescript-check.js.map +1 -1
  17. package/helpers/workers.d.ts +0 -8
  18. package/helpers/workers.js +43 -381
  19. package/helpers/workers.js.map +1 -1
  20. package/hmr/client/css-handler.js +14 -6
  21. package/hmr/client/css-handler.js.map +1 -1
  22. package/hmr/client/framework-client-strategy.d.ts +17 -9
  23. package/hmr/client/framework-client-strategy.js +3 -4
  24. package/hmr/client/framework-client-strategy.js.map +1 -1
  25. package/hmr/client/hmr-pending-overlay.js +2 -21
  26. package/hmr/client/hmr-pending-overlay.js.map +1 -1
  27. package/hmr/client/index.d.ts +9 -1
  28. package/hmr/client/index.js +34 -2621
  29. package/hmr/client/index.js.map +1 -1
  30. package/hmr/client/overlay-driver.d.ts +10 -0
  31. package/hmr/client/overlay-driver.js +33 -0
  32. package/hmr/client/overlay-driver.js.map +1 -0
  33. package/hmr/client/protocol-dispatch.d.ts +8 -0
  34. package/hmr/client/protocol-dispatch.js +308 -0
  35. package/hmr/client/protocol-dispatch.js.map +1 -0
  36. package/hmr/client/root-reset.d.ts +15 -0
  37. package/hmr/client/root-reset.js +358 -0
  38. package/hmr/client/root-reset.js.map +1 -0
  39. package/hmr/client/strategy-loader.d.ts +24 -0
  40. package/hmr/client/strategy-loader.js +83 -0
  41. package/hmr/client/strategy-loader.js.map +1 -0
  42. package/hmr/client/update-pipeline.d.ts +27 -0
  43. package/hmr/client/update-pipeline.js +464 -0
  44. package/hmr/client/update-pipeline.js.map +1 -0
  45. package/hmr/client/utils.d.ts +7 -0
  46. package/hmr/client/utils.js +12 -2
  47. package/hmr/client/utils.js.map +1 -1
  48. package/hmr/client/ws-transport.d.ts +21 -0
  49. package/hmr/client/ws-transport.js +344 -0
  50. package/hmr/client/ws-transport.js.map +1 -0
  51. package/hmr/frameworks/angular/build/angular-linker.js +4 -0
  52. package/hmr/frameworks/angular/build/angular-linker.js.map +1 -1
  53. package/hmr/frameworks/angular/build/linker-stats.d.ts +18 -0
  54. package/hmr/frameworks/angular/build/linker-stats.js +29 -0
  55. package/hmr/frameworks/angular/build/linker-stats.js.map +1 -0
  56. package/hmr/frameworks/angular/build/ts-fallback-transform.d.ts +5 -0
  57. package/hmr/frameworks/angular/build/ts-fallback-transform.js +138 -0
  58. package/hmr/frameworks/angular/build/ts-fallback-transform.js.map +1 -0
  59. package/hmr/frameworks/angular/build/worker-url-preserve.d.ts +5 -0
  60. package/hmr/frameworks/angular/build/worker-url-preserve.js +140 -0
  61. package/hmr/frameworks/angular/build/worker-url-preserve.js.map +1 -0
  62. package/hmr/frameworks/angular/client/index.js +28 -42
  63. package/hmr/frameworks/angular/client/index.js.map +1 -1
  64. package/hmr/frameworks/angular/server/ng-component-route.d.ts +43 -0
  65. package/hmr/frameworks/angular/server/ng-component-route.js +113 -0
  66. package/hmr/frameworks/angular/server/ng-component-route.js.map +1 -0
  67. package/hmr/frameworks/angular/server/strategy.js +22 -11
  68. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  69. package/hmr/frameworks/angular/server/websocket-angular-hot-update.d.ts +12 -0
  70. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +51 -9
  71. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -1
  72. package/hmr/frameworks/react/client/strategy.d.ts +12 -0
  73. package/hmr/frameworks/react/client/strategy.js +27 -0
  74. package/hmr/frameworks/react/client/strategy.js.map +1 -0
  75. package/hmr/frameworks/solid/client/strategy.d.ts +7 -0
  76. package/hmr/frameworks/solid/client/strategy.js +391 -0
  77. package/hmr/frameworks/solid/client/strategy.js.map +1 -0
  78. package/hmr/frameworks/solid/server/strategy.js +2 -0
  79. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  80. package/hmr/frameworks/typescript/client/strategy.d.ts +7 -0
  81. package/hmr/frameworks/typescript/client/strategy.js +418 -0
  82. package/hmr/frameworks/typescript/client/strategy.js.map +1 -0
  83. package/hmr/frameworks/typescript/server/strategy.js +2 -0
  84. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  85. package/hmr/frameworks/vue/client/index.d.ts +1 -1
  86. package/hmr/frameworks/vue/client/index.js +44 -323
  87. package/hmr/frameworks/vue/client/index.js.map +1 -1
  88. package/hmr/frameworks/vue/client/navigate-app.d.ts +3 -0
  89. package/hmr/frameworks/vue/client/navigate-app.js +178 -0
  90. package/hmr/frameworks/vue/client/navigate-app.js.map +1 -0
  91. package/hmr/frameworks/vue/client/strategy.d.ts +1 -1
  92. package/hmr/frameworks/vue/client/strategy.js +9 -14
  93. package/hmr/frameworks/vue/client/strategy.js.map +1 -1
  94. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +2 -20
  95. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -1
  96. package/hmr/frameworks/vue/server/strategy.js +3 -0
  97. package/hmr/frameworks/vue/server/strategy.js.map +1 -1
  98. package/hmr/server/content-css-update.d.ts +54 -0
  99. package/hmr/server/content-css-update.js +78 -0
  100. package/hmr/server/content-css-update.js.map +1 -0
  101. package/hmr/server/css-connect-sync.d.ts +28 -0
  102. package/hmr/server/css-connect-sync.js +59 -0
  103. package/hmr/server/css-connect-sync.js.map +1 -0
  104. package/hmr/server/css-update-message.d.ts +23 -0
  105. package/hmr/server/css-update-message.js +41 -0
  106. package/hmr/server/css-update-message.js.map +1 -0
  107. package/hmr/server/framework-strategy.d.ts +42 -0
  108. package/hmr/server/socket-utils.d.ts +10 -0
  109. package/hmr/server/socket-utils.js +14 -0
  110. package/hmr/server/socket-utils.js.map +1 -0
  111. package/hmr/server/vite-plugin.js +35 -0
  112. package/hmr/server/vite-plugin.js.map +1 -1
  113. package/hmr/server/websocket-hmr-pending.d.ts +0 -8
  114. package/hmr/server/websocket-hmr-pending.js +0 -22
  115. package/hmr/server/websocket-hmr-pending.js.map +1 -1
  116. package/hmr/server/websocket-hot-update.d.ts +3 -0
  117. package/hmr/server/websocket-hot-update.js +30 -99
  118. package/hmr/server/websocket-hot-update.js.map +1 -1
  119. package/hmr/server/websocket-module-bindings.js +23 -5
  120. package/hmr/server/websocket-module-bindings.js.map +1 -1
  121. package/hmr/server/websocket-module-specifiers.d.ts +0 -5
  122. package/hmr/server/websocket-module-specifiers.js +0 -28
  123. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  124. package/hmr/server/websocket-ns-m-paths.d.ts +18 -0
  125. package/hmr/server/websocket-ns-m-paths.js +79 -0
  126. package/hmr/server/websocket-ns-m-paths.js.map +1 -1
  127. package/hmr/server/websocket-ns-m-request.d.ts +2 -1
  128. package/hmr/server/websocket-ns-m-request.js +3 -2
  129. package/hmr/server/websocket-ns-m-request.js.map +1 -1
  130. package/hmr/server/websocket-ns-m.js +100 -374
  131. package/hmr/server/websocket-ns-m.js.map +1 -1
  132. package/hmr/server/websocket-served-module-helpers.d.ts +7 -0
  133. package/hmr/server/websocket-served-module-helpers.js +93 -69
  134. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  135. package/hmr/server/websocket.d.ts +0 -1
  136. package/hmr/server/websocket.js +41 -35
  137. package/hmr/server/websocket.js.map +1 -1
  138. package/hmr/shared/ns-globals.d.ts +0 -1
  139. package/hmr/shared/ns-globals.js.map +1 -1
  140. package/hmr/shared/protocol.d.ts +12 -0
  141. package/hmr/shared/protocol.js.map +1 -1
  142. package/hmr/shared/runtime/dev-overlay-ios.d.ts +87 -0
  143. package/hmr/shared/runtime/dev-overlay-ios.js +521 -0
  144. package/hmr/shared/runtime/dev-overlay-ios.js.map +1 -0
  145. package/hmr/shared/runtime/dev-overlay-shared.d.ts +58 -0
  146. package/hmr/shared/runtime/dev-overlay-shared.js +116 -0
  147. package/hmr/shared/runtime/dev-overlay-shared.js.map +1 -0
  148. package/hmr/shared/runtime/dev-overlay-tree.d.ts +59 -0
  149. package/hmr/shared/runtime/dev-overlay-tree.js +712 -0
  150. package/hmr/shared/runtime/dev-overlay-tree.js.map +1 -0
  151. package/hmr/shared/runtime/dev-overlay.d.ts +15 -107
  152. package/hmr/shared/runtime/dev-overlay.js +19 -1336
  153. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  154. package/hmr/shared/runtime/overlay-flag.d.ts +9 -0
  155. package/hmr/shared/runtime/overlay-flag.js +34 -0
  156. package/hmr/shared/runtime/overlay-flag.js.map +1 -0
  157. package/hmr/shared/runtime/root-placeholder.js +3 -2
  158. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  159. package/hmr/shared/runtime/session-bootstrap.js +2 -1
  160. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  161. package/hmr/shared/runtime/vendor-resolve.d.ts +22 -0
  162. package/hmr/shared/runtime/vendor-resolve.js +45 -0
  163. package/hmr/shared/runtime/vendor-resolve.js.map +1 -0
  164. package/hmr/shared/vendor/manifest-collect.js +0 -3
  165. package/hmr/shared/vendor/manifest-collect.js.map +1 -1
  166. package/package.json +1 -3
  167. package/runtime/core-aliases-early.js +3 -2
  168. package/runtime/core-aliases-early.js.map +1 -1
  169. package/helpers/module-runner-patch.d.ts +0 -3
  170. package/helpers/module-runner-patch.js +0 -63
  171. package/helpers/module-runner-patch.js.map +0 -1
  172. package/hmr/server/websocket-css-hot-update.d.ts +0 -33
  173. package/hmr/server/websocket-css-hot-update.js +0 -65
  174. package/hmr/server/websocket-css-hot-update.js.map +0 -1
  175. package/hmr/shared/runtime/http-only-boot.d.ts +0 -2
  176. package/hmr/shared/runtime/http-only-boot.js +0 -172
  177. package/hmr/shared/runtime/http-only-boot.js.map +0 -1
  178. package/hmr/vendor-bootstrap.d.ts +0 -1
  179. package/hmr/vendor-bootstrap.js +0 -30
  180. package/hmr/vendor-bootstrap.js.map +0 -1
  181. package/shims/react-reconciler-constants.d.ts +0 -14
  182. package/shims/react-reconciler-constants.js +0 -21
  183. package/shims/react-reconciler-constants.js.map +0 -1
  184. package/shims/react-reconciler.d.ts +0 -8
  185. package/shims/react-reconciler.js +0 -15
  186. package/shims/react-reconciler.js.map +0 -1
@@ -1,14 +1,13 @@
1
- import * as path from 'path';
2
1
  import { parse as babelParse } from '@babel/parser';
3
2
  import traverse from '@babel/traverse';
4
3
  import { sanitizeStrayCoreReferences } from './core-sanitize.js';
5
4
  import { getMonorepoWorkspaceRoot } from '../../helpers/project.js';
6
5
  import { isRuntimeGraphExcludedPath } from './runtime-graph-filter.js';
7
6
  import { buildPiniaVendorShim, buildVueVendorShim } from './vendor-bare-module-shims.js';
8
- import { canonicalizeNsMImportPath } from './websocket-ns-m-paths.js';
7
+ import { collapseLegacyNsMTags } from './websocket-ns-m-paths.js';
8
+ import { createNsMRequestContext, resolveNsMTransformedModule } from './websocket-ns-m-request.js';
9
9
  import { setDeviceModuleHeaders } from './route-helpers.js';
10
10
  import { CSS_MODULE_RE, buildCssRegisterSnippetFromVar, normalizeCssForDevice } from './css-device-module.js';
11
- import { filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, resolveCandidateFilePath, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
12
11
  import { assertNoOptimizedArtifacts, buildBootProgressSnippet, canonicalizeRtImports, classifyServedModule, dedupeRtNamedImportsAgainstDestructures, deduplicateLinkerImports, ensureDestructureCoreImports, ensureGuardPlainDynamicImports, ensureVariableDynamicImportHelper, expandStarExports, hoistTopLevelStaticImports, MODULE_IMPORT_ANALYSIS_PLUGINS, wrapCommonJsModuleForDevice } from './websocket-served-module-helpers.js';
13
12
  import { cleanCode, collectImportDependencies, isWorkerEntryModuleId, processCodeForDevice, rewriteImports } from './websocket-device-transform.js';
14
13
  import { REQUIRE_GUARD_SNIPPET } from './require-guard.js';
@@ -16,10 +15,6 @@ import { getServerOrigin } from './server-origin.js';
16
15
  import { createNsMResponseMemo } from './ns-m-response-memo.js';
17
16
  import { getSharedDepsBundleService, registerDepsBundleRoute } from './deps-bundle.js';
18
17
  const babelTraverse = traverse?.default || traverse;
19
- // Minimal valid ESM body for "no component update available": the iOS HTTP
20
- // ESM loader (`LoadHttpModuleForUrl`) rejects empty bodies even on HTTP 200,
21
- // so an actual empty response would crash the component's `_HmrLoad` import.
22
- const NG_COMPONENT_NO_UPDATE_STUB = '// [ns:m] no Angular component update for this request — substituted with valid empty module to satisfy iOS HTTP loader (rejects empty bodies)\nexport {};\n';
23
18
  /**
24
19
  * Registers the `/ns/m/*` device module server: the HTTP endpoint every
25
20
  * NativeScript device uses to fetch app/source modules (and AnalogJS Angular
@@ -53,109 +48,19 @@ export function registerNsModuleServerRoute(server, options) {
53
48
  const sharedTransformRequest = options.sharedTransformRequest;
54
49
  const ensureInitialGraphPopulationStarted = options.ensureInitialGraphPopulationStarted;
55
50
  const strategy = options.getStrategy();
56
- // Delegate AnalogJS Angular component live-reload endpoints.
57
- //
58
- // Angular 21's `ɵɵgetReplaceMetadataURL` (in @angular/core
59
- // _debug_node-chunk.mjs) builds the metadata-replacement URL as
60
- // `new URL('./@ng/component?c=<id>&t=<ts>', import.meta.url).href`.
61
- // Because `import.meta.url` for a NS-served module is
62
- // `http://host:port/ns/m/<project-relative>/component.ts`, the
63
- // resolved metadata URL ends up *nested* under the component's
64
- // directory: `/ns/m/<dir>/@ng/component?c=...&t=...`.
65
- //
66
- // AnalogJS's `liveReloadPlugin` registers a middleware that matches
67
- // `/@ng/component` anywhere in `req.url` and returns either an empty
68
- // module body (no HMR update available) or the metadata-replacement
69
- // code (after a save invalidates the file). Without this delegation
70
- // the NS `/ns/m/` middleware would treat the path as a file lookup,
71
- // fail to resolve `@ng/component` against disk, and respond with
72
- // 404 — which surfaces as `HTTP fetch/compile failed` at the
73
- // component's own `_HmrLoad(Date.now())` call on initial boot and
74
- // blocks Angular component bootstrapping.
75
- //
76
- // Calling `next()` here lets AnalogJS's middleware (or any other
77
- // middleware later in the chain) handle the request. Analog's
78
- // middleware reads only the `?c=` query string and is pathname-
79
- // agnostic, so we don't need to rewrite `req.url` for it to work.
80
- //
81
- // HOWEVER: AnalogJS responds with an EMPTY body (`res.end('')`)
82
- // for non-invalidated component IDs (initial boot, before any
83
- // file save). The iOS HTTP ESM loader's
84
- // `LoadHttpModuleForUrl` (ModuleInternalCallbacks.mm) treats an
85
- // empty body as a fetch failure (`body.empty() → reject`), even
86
- // when the HTTP status is 200 OK. That bubbles up as
87
- // `HTTP fetch/compile failed` at the device's `__ns_import(...)`
88
- // inside each component's `_HmrLoad(Date.now())` and crashes
89
- // Angular's component bootstrap. To make Analog's empty
90
- // "no-update" response acceptable to the iOS loader, we wrap
91
- // `res.write` / `res.end` and substitute a minimal valid ESM
92
- // module body (`export {}`) when downstream writes nothing.
93
- // Non-empty bodies (real HMR update payloads after a save)
94
- // pass through unchanged.
95
- if (urlObj.pathname.includes('/@ng/component')) {
96
- // Boot-fetch gate: every compiled component runs
97
- // `<Class>_HmrLoad(Date.now())` on evaluation, so every cold
98
- // boot hits this route for every component. Analog serves a
99
- // REAL metadata-replacement payload whenever the component's
100
- // module was invalidated at any point in the dev server's
101
- // lifetime (Vite's `lastInvalidationTimestamp` never resets),
102
- // and a boot-time `ɵɵreplaceMetadata` on the root component
103
- // tears down the PageRouterOutlet frame — the "relaunch after
104
- // an HMR edit → white screen" failure. Only fetches echoing a
105
- // forwarded `angular:component-update` broadcast's exact
106
- // `(id, t)` pair may reach Analog; a fresh boot already
107
- // evaluates the latest transformed source and never needs a
108
- // replacement payload.
109
- const isLiveUpdateFetch = options.isLiveAngularComponentUpdateFetch;
110
- if (isLiveUpdateFetch) {
111
- const componentId = urlObj.searchParams.get('c') || '';
112
- const timestampRaw = urlObj.searchParams.get('t');
113
- // `Number(null)` is 0 — require an actual `t` param.
114
- const timestamp = timestampRaw ? Number(timestampRaw) : NaN;
115
- if (!componentId || !Number.isFinite(timestamp) || !isLiveUpdateFetch(componentId, timestamp)) {
116
- res.statusCode = 200;
117
- res.setHeader('Content-Type', 'text/javascript');
118
- res.setHeader('Cache-Control', 'no-cache');
119
- res.end(NG_COMPONENT_NO_UPDATE_STUB);
120
- return;
121
- }
122
- }
123
- const chunks = [];
124
- const origEnd = res.end.bind(res);
125
- let ended = false;
126
- const captureChunk = (chunk) => {
127
- if (chunk == null)
128
- return;
129
- if (typeof chunk === 'string') {
130
- chunks.push(chunk);
131
- }
132
- else if (Buffer.isBuffer(chunk)) {
133
- chunks.push(chunk.toString('utf8'));
134
- }
135
- else {
136
- chunks.push(String(chunk));
137
- }
138
- };
139
- res.write = function (chunk, ..._args) {
140
- captureChunk(chunk);
141
- return true;
142
- };
143
- res.end = function (chunk, ..._args) {
144
- if (ended)
145
- return true;
146
- ended = true;
147
- captureChunk(chunk);
148
- let body = chunks.join('');
149
- if (body.length === 0) {
150
- body = NG_COMPONENT_NO_UPDATE_STUB;
151
- }
152
- try {
153
- res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'));
154
- }
155
- catch { }
156
- return origEnd(body);
157
- };
158
- return next();
51
+ // Framework request interception, BEFORE spec resolution. Angular
52
+ // uses this to delegate AnalogJS `/@ng/component` live-reload
53
+ // fetches (with the boot-fetch gate and iOS empty-body guard —
54
+ // see frameworks/angular/server/ng-component-route.ts). Must run
55
+ // first: an intercepted request must not kick off graph
56
+ // population or the transform pipeline.
57
+ if (strategy?.interceptModuleRequest?.({
58
+ urlObj,
59
+ res,
60
+ next,
61
+ isLiveComponentUpdateFetch: options.isLiveAngularComponentUpdateFetch,
62
+ })) {
63
+ return;
159
64
  }
160
65
  // graphVersion starts at 1 and stays stable during cold boot (see
161
66
  // `upsertGraphModule`'s bumpVersion option and the inline comment
@@ -170,56 +75,28 @@ export function registerNsModuleServerRoute(server, options) {
170
75
  // this handler would miss the round-trip timing and per-route
171
76
  // breakdowns.
172
77
  setDeviceModuleHeaders(res);
173
- // Support both query (?path=/abs) and path-style (/ns/m/abs)
174
- let spec = urlObj.searchParams.get('path') || '';
175
- if (!spec) {
176
- const base = '/ns/m';
177
- const rest = urlObj.pathname.slice(base.length);
178
- if (rest && rest !== '/')
179
- spec = rest;
180
- }
181
- // Special-case stub for anomalous '@' imports emitted as '/__invalid_at__.mjs'
182
- if (spec === '/__invalid_at__.mjs' || spec === '__invalid_at__.mjs') {
183
- res.statusCode = 200;
184
- res.end("// invalid '@' import stub\nexport {}\n");
185
- return;
186
- }
187
- if (!spec) {
188
- res.statusCode = 200;
189
- res.end('export {}\n');
190
- return;
191
- }
78
+ // Inbound spec normalization is owned by createNsMRequestContext
79
+ // (websocket-ns-m-request.ts): query/path-style spec extraction,
80
+ // the anomalous-'@' and empty-spec stubs, legacy boot/HMR tag
81
+ // stripping, absolute-filesystem → project-relative mapping,
82
+ // `@/` aliasing, the blocked-device-import gate, and candidate
83
+ // expansion. Its specs pin all of it.
192
84
  const serverRoot = (server.config?.root || process.cwd());
193
85
  const monorepoWorkspaceRoot = getMonorepoWorkspaceRoot(serverRoot);
194
- spec = spec.replace(/[?#].*$/, '');
195
- // Tolerate LEGACY path-based boot/HMR prefixes from stale cached
196
- // device code (`/ns/m/__ns_boot__/b1/…`, `/ns/m/__ns_hmr__/<tag>/…`).
197
- // Current servers never emit them — freshness is eviction-driven.
198
- try {
199
- spec = stripDecoratedServePrefixes(spec).cleanedSpec;
200
- }
201
- catch { }
202
- // Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
203
- try {
204
- const toPosix = (p) => p.replace(/\\/g, '/');
205
- const rootPosix = toPosix(serverRoot);
206
- const specPosix = toPosix(spec);
207
- // If spec is an absolute path under the project root, convert to '/'+relative
208
- const isAbsFs = /^\//.test(specPosix) || /^[A-Za-z]:\//.test(spec); // posix or win drive
209
- if (isAbsFs) {
210
- let rel = specPosix.startsWith(rootPosix) ? specPosix.slice(rootPosix.length) : require('path').posix.relative(rootPosix, specPosix);
211
- if (!rel.startsWith('..')) {
212
- if (!rel.startsWith('/'))
213
- rel = '/' + rel;
214
- // Ensure leading '/src' style when path maps into src
215
- spec = rel;
216
- }
217
- }
86
+ const contextResult = createNsMRequestContext(req.url || '', serverRoot, APP_VIRTUAL_WITH_SLASH, monorepoWorkspaceRoot);
87
+ if (contextResult.kind === 'next')
88
+ return next();
89
+ if (contextResult.kind === 'response') {
90
+ res.statusCode = contextResult.statusCode;
91
+ res.end(contextResult.code);
92
+ return;
218
93
  }
219
- catch { }
220
- // Serve Vite virtual modules (/@id/ prefix). These are internal
221
- // virtual modules (e.g., \0nsvite:nsconfig-json for ~/package.json)
222
- // that don't exist on disk. Decode the ID and load via plugin container.
94
+ const requestContext = contextResult.value;
95
+ const spec = requestContext.spec;
96
+ // Serve Vite virtual modules (/@id/ prefix) directly — these are
97
+ // internal virtual modules (e.g., \0nsvite:nsconfig-json for
98
+ // ~/package.json) that don't exist on disk and must NOT run
99
+ // through the device post-processing pipeline below.
223
100
  if (spec.startsWith('/@id/')) {
224
101
  try {
225
102
  // First try Vite's transform pipeline directly
@@ -247,18 +124,6 @@ export function registerNsModuleServerRoute(server, options) {
247
124
  }
248
125
  catch { }
249
126
  }
250
- if (spec.startsWith('@/'))
251
- spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
252
- if (spec.startsWith('./'))
253
- spec = spec.slice(1);
254
- const blockedNodeModulesReason = getBlockedDeviceNodeModulesReason(spec);
255
- if (blockedNodeModulesReason) {
256
- res.statusCode = 404;
257
- res.end(`// [ns:m] blocked device import\nthrow new Error(${JSON.stringify(`[ns/m] ${blockedNodeModulesReason}`)});\nexport {};\n`);
258
- return;
259
- }
260
- if (!spec.startsWith('/'))
261
- spec = '/' + spec;
262
127
  // Worker-realm serves must NOT take the deps-bundle bridge: the
263
128
  // bundle evaluates the full dependency closure in the worker realm,
264
129
  // where Angular's injectables reject the entry evaluation
@@ -314,17 +179,6 @@ export function registerNsModuleServerRoute(server, options) {
314
179
  }
315
180
  // Fall through to default handling if inline compile produced nothing.
316
181
  }
317
- const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
318
- const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
319
- const candidates = [...(hasExt ? [spec] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
320
- const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, serverRoot, monorepoWorkspaceRoot);
321
- let transformed = null;
322
- let resolvedCandidate = null;
323
- const rawExplicitModule = tryReadRawExplicitJavaScriptModule(spec, serverRoot);
324
- if (rawExplicitModule) {
325
- transformed = { code: rawExplicitModule.code };
326
- resolvedCandidate = rawExplicitModule.resolvedId;
327
- }
328
182
  // Queue and dedupe transformRequest calls so heavy app graphs do not
329
183
  // overwhelm Vite with concurrent work. Slow-transform warnings start only
330
184
  // when the transform actually begins executing, and requests stay pending
@@ -333,102 +187,38 @@ export function registerNsModuleServerRoute(server, options) {
333
187
  const r = await sharedTransformRequest(url, timeoutMs);
334
188
  // A type-only .ts module legitimately transforms to EMPTY code
335
189
  // (e.g. a workspace lib's `interfaces.ts` holding only `export
336
- // type ...`). Every candidate gate below tests `r?.code`
337
- // truthiness, so '' would cascade through all fallbacks into a
338
- // 404 "transform miss" — and one 404 in the entry graph fails
339
- // the whole dev-session boot. Normalize to the canonical empty
340
- // ESM module instead.
190
+ // type ...`). Every candidate gate in the resolution pipeline
191
+ // tests `r?.code` truthiness, so '' would cascade through all
192
+ // fallbacks into a 404 "transform miss" — and one 404 in the
193
+ // entry graph fails the whole dev-session boot. Normalize to
194
+ // the canonical empty ESM module instead.
341
195
  if (r && typeof r.code === 'string' && r.code.trim() === '') {
342
196
  return { ...r, code: 'export {}\n' };
343
197
  }
344
198
  return r;
345
199
  };
346
- if (!transformed?.code) {
347
- for (const cand of transformCandidates) {
348
- try {
349
- const r = await transformWithTimeout(cand);
350
- if (r?.code) {
351
- transformed = r;
352
- resolvedCandidate = cand;
353
- break;
354
- }
355
- }
356
- catch { }
357
- }
358
- }
359
- // Fallback 1: ask Vite to resolve the id, then transform the resolved id (handles aliases and virtual ids)
360
- if (!transformed?.code) {
200
+ const resolvePluginContainerId = async (id) => {
361
201
  try {
362
- const rid = await server.pluginContainer?.resolveId?.(spec, undefined);
363
- const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
364
- if (ridStr) {
365
- const r = await transformWithTimeout(ridStr);
366
- if (r?.code) {
367
- transformed = r;
368
- resolvedCandidate = ridStr;
369
- }
370
- }
202
+ const resolved = await server.pluginContainer?.resolveId?.(id, undefined);
203
+ return typeof resolved === 'string' ? resolved : resolved?.id || null;
371
204
  }
372
- catch { }
373
- }
374
- // Fallback 1b: if spec is a /node_modules/ path, extract bare specifier
375
- // and try resolveId with that. This handles package.json "exports" field
376
- // resolution (e.g., solid-js/jsx-runtime → solid-js/dist/solid.js).
377
- if (!transformed?.code && spec.includes('/node_modules/')) {
378
- try {
379
- const nmIdx = spec.lastIndexOf('/node_modules/');
380
- const bare = spec.slice(nmIdx + '/node_modules/'.length);
381
- if (bare && !bare.startsWith('.')) {
382
- const rid = await server.pluginContainer?.resolveId?.(bare, undefined);
383
- const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
384
- if (ridStr) {
385
- const r = await transformWithTimeout(ridStr);
386
- if (r?.code) {
387
- transformed = r;
388
- resolvedCandidate = ridStr;
389
- }
390
- }
391
- }
392
- }
393
- catch { }
394
- }
395
- // Fallback 2: try /@fs absolute path under project root (Vite file system alias).
396
- // In a monorepo with hoisted node_modules the file may live above
397
- // `serverRoot`, so try the workspace root next.
398
- if (!transformed?.code) {
399
- try {
400
- const toPosix = (p) => p.replace(/\\/g, '/');
401
- const rootsToTry = [serverRoot, ...(monorepoWorkspaceRoot && path.resolve(monorepoWorkspaceRoot) !== path.resolve(serverRoot) ? [monorepoWorkspaceRoot] : [])];
402
- for (const root of rootsToTry) {
403
- const rootPosix = toPosix(root).replace(/\/$/, '');
404
- const absPosix = `${rootPosix}${spec.startsWith('/') ? '' : '/'}${spec}`;
405
- const fsId = `/@fs${absPosix}`;
406
- if (resolveCandidateFilePath(fsId, serverRoot, monorepoWorkspaceRoot)) {
407
- const r = await transformWithTimeout(fsId);
408
- if (r?.code) {
409
- transformed = r;
410
- resolvedCandidate = fsId;
411
- break;
412
- }
413
- }
414
- }
205
+ catch {
206
+ return null;
415
207
  }
416
- catch { }
417
- }
418
- // Fallback 3: try adding ?import to hint Vite's transform pipeline
419
- if (!transformed?.code) {
420
- for (const cand of transformCandidates) {
421
- try {
422
- const r = await transformWithTimeout(`${cand}${cand.includes('?') ? '&' : '?'}import`);
423
- if (r?.code) {
424
- transformed = r;
425
- resolvedCandidate = `${cand}?import`;
426
- break;
427
- }
428
- }
429
- catch { }
430
- }
431
- }
208
+ };
209
+ const loadVirtualId = (id) => server.pluginContainer.load(id);
210
+ // Canonical "spec → transformed module" resolution
211
+ // (websocket-ns-m-request.ts): raw explicit JS read → transform
212
+ // candidates → resolveId → bare node_modules specifier →
213
+ // /@fs project+workspace roots → ?import hint.
214
+ const resolution = await resolveNsMTransformedModule({
215
+ context: requestContext,
216
+ transformRequest: transformWithTimeout,
217
+ resolveId: resolvePluginContainerId,
218
+ loadVirtualId,
219
+ });
220
+ let transformed = resolution.transformed;
221
+ const resolvedCandidate = resolution.resolvedCandidate;
432
222
  // Solid HMR: patch the served `@solid-refresh` runtime so $$refreshESM
433
223
  // applies registry updates inline on module re-evaluation. The full
434
224
  // patch (and its server-side diagnostics) lives in the Solid strategy's
@@ -490,35 +280,20 @@ export function registerNsModuleServerRoute(server, options) {
490
280
  res.end(code || 'export {}\n');
491
281
  return;
492
282
  }
493
- // Generic bare module resolution via Vite plugin container
494
- if (isBare) {
495
- try {
496
- const resolved = await server.pluginContainer?.resolveId?.(spec, undefined);
497
- const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
498
- if (resolvedId) {
499
- const r = await transformWithTimeout(resolvedId);
500
- if (r?.code) {
501
- transformed = r;
502
- resolvedCandidate = resolvedId;
503
- }
504
- }
505
- }
506
- catch { }
283
+ // (Bare specifiers already went through resolveId in the
284
+ // resolution pipeline above — reaching here means a true miss.)
285
+ // Emit a module that throws with context for easier on-device debugging
286
+ try {
287
+ const tried = Array.from(new Set(requestContext.transformCandidates.length > 0 ? requestContext.transformCandidates : requestContext.candidates)).slice(0, 12);
288
+ const out = `// [ns:m] transform miss path=${spec} tried=${tried.length}\n` + `throw new Error(${JSON.stringify(`[ns/m] transform failed for ${spec} (tried ${tried.length} candidates).`)});\nexport {};\n`;
289
+ res.statusCode = 404;
290
+ res.end(out);
291
+ return;
507
292
  }
508
- if (!transformed?.code) {
509
- // Emit a module that throws with context for easier on-device debugging
510
- try {
511
- const tried = Array.from(new Set(transformCandidates.length > 0 ? transformCandidates : candidates)).slice(0, 12);
512
- const out = `// [ns:m] transform miss path=${spec} tried=${tried.length}\n` + `throw new Error(${JSON.stringify(`[ns/m] transform failed for ${spec} (tried ${tried.length} candidates).`)});\nexport {};\n`;
513
- res.statusCode = 404;
514
- res.end(out);
515
- return;
516
- }
517
- catch {
518
- res.statusCode = 404;
519
- res.end('export {}\n');
520
- return;
521
- }
293
+ catch {
294
+ res.statusCode = 404;
295
+ res.end('export {}\n');
296
+ return;
522
297
  }
523
298
  }
524
299
  // Final-response memo (see ns-m-response-memo.ts): identical
@@ -634,16 +409,10 @@ export function registerNsModuleServerRoute(server, options) {
634
409
  res.statusCode = 500;
635
410
  return void res.end(`throw new Error(${JSON.stringify(e?.message || String(e))});\nexport {};`);
636
411
  }
637
- // Defensive export normalization: if a module defines `routes` and only exports it named,
638
- // add a default export alias so both `import { routes }` and `import routes` work.
412
+ // Framework export normalization (e.g. the Vue/Solid/TS `routes`
413
+ // default-export alias — see FrameworkServerStrategy.normalizeServedExports).
639
414
  try {
640
- if (!/\bexport\s+default\b/.test(code)) {
641
- const hasNamedRoutes = /\bexport\s*\{\s*routes\s*\}/.test(code);
642
- const hasConstRoutes = /\bconst\s+routes\s*=/.test(code) || /\bvar\s+routes\s*=/.test(code) || /\blet\s+routes\s*=/.test(code);
643
- if (hasNamedRoutes && hasConstRoutes) {
644
- code += `\nexport default routes;\n`;
645
- }
646
- }
415
+ code = strategy?.normalizeServedExports?.(code) ?? code;
647
416
  }
648
417
  catch { }
649
418
  // Every emitted endpoint URL is CANONICAL (unversioned).
@@ -659,39 +428,13 @@ export function registerNsModuleServerRoute(server, options) {
659
428
  code = strategy.canonicalizeFrameworkImports?.(code, getServerOrigin(server)) ?? code;
660
429
  }
661
430
  catch { }
662
- // `/ns/m` URL finalize step.
663
- //
664
- // `canonicalizeNsMImportPath` strips any `__ns_hmr__/<tag>/`
665
- // and `__ns_boot__/b1/` segments so every emitted URL is the
666
- // canonical stable form (cache busting is driven by
667
- // `__NS_DEV__.invalidateModules` + the runtime's eviction nonce, not
668
- // URL decoration).
431
+ // `/ns/m` URL finalize step: collapse every emitted /ns/m URL form
432
+ // (static/side-effect/dynamic imports, new URL(...) shapes, SFC
433
+ // URLs) to the canonical stable form. Cache busting is driven by
434
+ // `__NS_DEV__.invalidateModules` + the runtime's eviction nonce,
435
+ // not URL decoration — see websocket-ns-m-paths.ts.
669
436
  try {
670
- const origin = getServerOrigin(server);
671
- const rewritePath = (p) => canonicalizeNsMImportPath(p);
672
- // /ns/m URL forms — all collapse to canonical stable
673
- // URLs via the `rewriteNsMImportPathForHmr` rewriter.
674
- // 1) Static imports: import ... from "/ns/m/..."
675
- code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
676
- // 2) Side-effect imports: import "/ns/m/..."
677
- code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
678
- // 3) Dynamic imports: import("/ns/m/...")
679
- code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
680
- // 4) new URL("/ns/m/...", import.meta.url)
681
- code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
682
- // 5) __ns_import(new URL('/ns/m/...', import.meta.url).href)
683
- code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\)\.href)/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
684
- // 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → canonical stable URL.
685
- try {
686
- code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${rewritePath(p1)}`)}`);
687
- }
688
- catch { }
689
- // 7) SFC URLs (Vue) — canonical, like everything else. Freshness
690
- // is driven by the Vue client evicting the SFC artifact URL set.
691
- try {
692
- code = code.replace(/new\s+URL\(\s*["']\/ns\/sfc(\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}/ns/sfc${p1}`)}`);
693
- }
694
- catch { }
437
+ code = collapseLegacyNsMTags(code, 'outbound-served-code', getServerOrigin(server));
695
438
  }
696
439
  catch { }
697
440
  // Final guard: eliminate any lingering named imports from /ns/core to avoid
@@ -754,45 +497,28 @@ export function registerNsModuleServerRoute(server, options) {
754
497
  continue;
755
498
  }
756
499
  else if (u.pathname.startsWith('/ns/m')) {
757
- // Resolve to local project path and transform with same candidate logic as /ns/m handler
758
- let local = u.pathname.replace(/^\/ns\/m/, '');
500
+ // Resolve the import target through the SAME
501
+ // canonical pipeline the /ns/m route itself uses
502
+ // (websocket-ns-m-request.ts) instead of a third
503
+ // hand-rolled copy of candidate expansion.
759
504
  try {
760
- // Normalize project-relative path
761
- if (local.startsWith('@/'))
762
- local = APP_VIRTUAL_WITH_SLASH + local.slice(2);
763
- if (local.startsWith('./'))
764
- local = local.slice(1);
765
- if (!local.startsWith('/'))
766
- local = '/' + local;
767
- const hasExt = /(\.ts|\.tsx|\.js|\.jsx|\.mjs|\.mts|\.cts|\.vue)$/i.test(local);
768
- const baseNoExt = hasExt ? local.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : local;
769
- const cands = [...(hasExt ? [local] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
770
- let t = null;
771
- for (const cand of cands) {
772
- try {
773
- const r = await server.transformRequest(cand);
774
- if (r?.code) {
775
- t = r;
776
- break;
777
- }
778
- }
779
- catch { }
780
- if (t?.code)
781
- break;
782
- }
783
- if (!t?.code) {
784
- try {
785
- const rid = await server.pluginContainer?.resolveId?.(local, undefined);
786
- const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
787
- if (ridStr) {
788
- const r2 = await server.transformRequest(ridStr);
789
- if (r2?.code)
790
- t = r2;
791
- }
792
- }
793
- catch { }
505
+ const targetContextResult = createNsMRequestContext(u.pathname, serverRoot, APP_VIRTUAL_WITH_SLASH, monorepoWorkspaceRoot);
506
+ if (targetContextResult.kind === 'context') {
507
+ const targetResolution = await resolveNsMTransformedModule({
508
+ context: targetContextResult.value,
509
+ transformRequest: async (url) => {
510
+ try {
511
+ return await server.transformRequest(url);
512
+ }
513
+ catch {
514
+ return null;
515
+ }
516
+ },
517
+ resolveId: resolvePluginContainerId,
518
+ loadVirtualId,
519
+ });
520
+ targetCode = targetResolution.transformed?.code || '';
794
521
  }
795
- targetCode = t?.code || '';
796
522
  }
797
523
  catch { }
798
524
  }