@nativescript/vite 8.0.0-alpha.2 → 8.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configuration/angular.js +45 -8
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +1 -1
- package/configuration/base.js.map +1 -1
- package/helpers/commonjs-plugins.d.ts +5 -2
- package/helpers/commonjs-plugins.js +126 -0
- package/helpers/commonjs-plugins.js.map +1 -1
- package/helpers/main-entry.d.ts +1 -0
- package/helpers/main-entry.js +26 -85
- package/helpers/main-entry.js.map +1 -1
- package/hmr/client/index.js +72 -19
- package/hmr/client/index.js.map +1 -1
- package/hmr/client/utils.js +57 -6
- package/hmr/client/utils.js.map +1 -1
- package/hmr/entry-runtime.js +1 -1
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/client/index.d.ts +2 -1
- package/hmr/frameworks/angular/client/index.js +51 -2
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/angular/server/strategy.js +20 -5
- package/hmr/frameworks/angular/server/strategy.js.map +1 -1
- package/hmr/frameworks/typescript/server/strategy.js +8 -2
- package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
- package/hmr/server/core-sanitize.d.ts +3 -2
- package/hmr/server/core-sanitize.js +34 -17
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/import-map.js +20 -14
- package/hmr/server/import-map.js.map +1 -1
- package/hmr/server/index.d.ts +2 -1
- package/hmr/server/index.js.map +1 -1
- package/hmr/server/runtime-graph-filter.d.ts +5 -0
- package/hmr/server/runtime-graph-filter.js +21 -0
- package/hmr/server/runtime-graph-filter.js.map +1 -0
- package/hmr/server/shared-transform-request.d.ts +12 -0
- package/hmr/server/shared-transform-request.js +137 -0
- package/hmr/server/shared-transform-request.js.map +1 -0
- package/hmr/server/vite-plugin.d.ts +21 -1
- package/hmr/server/vite-plugin.js +443 -22
- package/hmr/server/vite-plugin.js.map +1 -1
- package/hmr/server/websocket-angular-entry.d.ts +2 -0
- package/hmr/server/websocket-angular-entry.js +68 -0
- package/hmr/server/websocket-angular-entry.js.map +1 -0
- package/hmr/server/websocket-angular-hot-update.d.ts +61 -0
- package/hmr/server/websocket-angular-hot-update.js +239 -0
- package/hmr/server/websocket-angular-hot-update.js.map +1 -0
- package/hmr/server/websocket-core-bridge.d.ts +23 -0
- package/hmr/server/websocket-core-bridge.js +360 -0
- package/hmr/server/websocket-core-bridge.js.map +1 -0
- package/hmr/server/websocket-graph-upsert.d.ts +6 -0
- package/hmr/server/websocket-graph-upsert.js +13 -0
- package/hmr/server/websocket-graph-upsert.js.map +1 -0
- package/hmr/server/websocket-module-bindings.d.ts +6 -0
- package/hmr/server/websocket-module-bindings.js +471 -0
- package/hmr/server/websocket-module-bindings.js.map +1 -0
- package/hmr/server/websocket-module-specifiers.d.ts +37 -0
- package/hmr/server/websocket-module-specifiers.js +637 -0
- package/hmr/server/websocket-module-specifiers.js.map +1 -0
- package/hmr/server/websocket.d.ts +21 -74
- package/hmr/server/websocket.js +455 -1386
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/package-classifier.d.ts +9 -0
- package/hmr/shared/package-classifier.js +58 -0
- package/hmr/shared/package-classifier.js.map +1 -0
- package/hmr/shared/runtime/browser-runtime-contract.d.ts +64 -0
- package/hmr/shared/runtime/browser-runtime-contract.js +54 -0
- package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -0
- package/hmr/shared/runtime/dev-overlay.d.ts +1 -1
- package/hmr/shared/runtime/dev-overlay.js +23 -12
- package/hmr/shared/runtime/dev-overlay.js.map +1 -1
- package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
- package/hmr/shared/runtime/root-placeholder.js +430 -52
- package/hmr/shared/runtime/root-placeholder.js.map +1 -1
- package/hmr/shared/runtime/session-bootstrap.d.ts +1 -0
- package/hmr/shared/runtime/session-bootstrap.js +146 -0
- package/hmr/shared/runtime/session-bootstrap.js.map +1 -0
- package/hmr/shared/vendor/manifest.d.ts +2 -0
- package/hmr/shared/vendor/manifest.js +24 -10
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/package.json +7 -1
- package/runtime/core-aliases-early.js +83 -32
- package/runtime/core-aliases-early.js.map +1 -1
package/hmr/server/websocket.js
CHANGED
|
@@ -4,12 +4,11 @@ import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCore
|
|
|
4
4
|
import { parse as babelParse } from '@babel/parser';
|
|
5
5
|
import { genCode } from '../helpers/babel.js';
|
|
6
6
|
import babelCore from '@babel/core';
|
|
7
|
-
import pluginTransformTypescript from '@babel/plugin-transform-typescript';
|
|
8
7
|
import traverse from '@babel/traverse';
|
|
9
8
|
// Ensure traverse callable across CJS/ESM builds
|
|
10
9
|
const babelTraverse = traverse?.default || traverse;
|
|
11
10
|
import * as t from '@babel/types';
|
|
12
|
-
import { existsSync, readFileSync } from 'fs';
|
|
11
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
13
12
|
import { astNormalizeModuleImportsAndHelpers, astVerifyAndAnnotateDuplicates } from '../helpers/ast-normalizer.js';
|
|
14
13
|
import { stripRtCoreSentinel, stripDanglingViteCjsImports } from '../helpers/sanitize.js';
|
|
15
14
|
import { WebSocketServer } from 'ws';
|
|
@@ -17,7 +16,7 @@ import * as path from 'path';
|
|
|
17
16
|
import { createHash } from 'crypto';
|
|
18
17
|
import * as PAT from './constants.js';
|
|
19
18
|
import { getVendorManifest, resolveVendorSpecifier } from '../shared/vendor/registry.js';
|
|
20
|
-
import { getPackageJson, getProjectFilePath } from '../../helpers/project.js';
|
|
19
|
+
import { getPackageJson, getProjectFilePath, getProjectRootPath } from '../../helpers/project.js';
|
|
21
20
|
import { loadPrebuiltVendorManifest } from '../shared/vendor/manifest-loader.js';
|
|
22
21
|
import '../vendor-bootstrap.js';
|
|
23
22
|
import { NS_NATIVE_TAGS } from './compiler.js';
|
|
@@ -32,13 +31,31 @@ import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
|
|
|
32
31
|
import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
|
|
33
32
|
import { buildRuntimeConfig, generateImportMap } from './import-map.js';
|
|
34
33
|
import { getCliFlags } from '../../helpers/cli-flags.js';
|
|
34
|
+
import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
|
|
35
|
+
import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
36
|
+
import { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
|
|
37
|
+
import { classifyGraphUpsert, shouldBroadcastGraphUpsertDelta } from './websocket-graph-upsert.js';
|
|
38
|
+
import { extractVitePrebundleId, filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, getFlattenedManifestMap, isCoreGlobalsReference, isEsmFrameworkPackageSpecifier, isLikelyNativeScriptPluginSpecifier, isLikelyNativeScriptRuntimePluginSpecifier, isNativeScriptCoreModule, isNativeScriptPluginModule, normalizeNativeScriptCoreSpecifier, normalizeNodeModulesSpecifier, resolveCandidateFilePath, resolveInternalRuntimePluginBareSpecifier, resolveNodeModulesPackageBoundary, resolveVendorFromCandidate, resolveVendorRouting, shouldPreserveBareRuntimePluginSubpathImport, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule, viteDepsPathToBareSpecifier, } from './websocket-module-specifiers.js';
|
|
39
|
+
import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
40
|
+
import { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, extractDirectExportedNames, hasModuleDefaultExport, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
|
|
41
|
+
import { createSharedTransformRequestRunner } from './shared-transform-request.js';
|
|
42
|
+
export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
43
|
+
export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
|
|
44
|
+
export { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
|
|
45
|
+
export { rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
46
|
+
export { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, createSharedTransformRequestRunner, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload, classifyGraphUpsert, shouldBroadcastGraphUpsertDelta };
|
|
47
|
+
const pluginTransformTypescript = (() => {
|
|
48
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
49
|
+
const loaded = requireFromHere('@babel/plugin-transform-typescript');
|
|
50
|
+
return loaded?.default || loaded;
|
|
51
|
+
})();
|
|
35
52
|
// Build a serialized process.env object from CLI --env.* flags.
|
|
36
53
|
// This is injected into every HTTP-served module so app code referencing
|
|
37
54
|
// process.env.TEST_ENV (etc.) works on device in HMR dev mode.
|
|
38
55
|
const __processEnvEntries = { NODE_ENV: 'development' };
|
|
39
56
|
try {
|
|
40
57
|
const flags = getCliFlags();
|
|
41
|
-
for (const [k, v] of Object.entries(flags)) {
|
|
58
|
+
for (const [k, v] of Object.entries(flags || {})) {
|
|
42
59
|
// Skip internal NativeScript build flags
|
|
43
60
|
if (['ios', 'android', 'visionos', 'platform', 'hmr', 'verbose'].includes(k))
|
|
44
61
|
continue;
|
|
@@ -60,182 +77,46 @@ const STRATEGY_REGISTRY = new Map([
|
|
|
60
77
|
['typescript', typescriptServerStrategy],
|
|
61
78
|
]);
|
|
62
79
|
function resolveFrameworkStrategy(flavor) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const processSfcCode = createProcessSfcCode(processCodeForDevice);
|
|
67
|
-
// Bare specifiers and special skip patterns (virtual, data:, etc.)
|
|
68
|
-
const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
|
|
69
|
-
const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
|
|
70
|
-
// Minimal helpers to support vendor pre-bundle detection
|
|
71
|
-
function extractVitePrebundleId(spec) {
|
|
72
|
-
const m = spec.match(/\.vite\/deps\/([^?]+?)\.[mc]?js/);
|
|
73
|
-
if (m)
|
|
74
|
-
return m[1];
|
|
75
|
-
const m2 = spec.match(/__x00__([^?]+?)\.[mc]?js/);
|
|
76
|
-
if (m2)
|
|
77
|
-
return m2[1];
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
function getFlattenedManifestMap(manifest) {
|
|
81
|
-
const map = new Map();
|
|
82
|
-
const mods = Object.keys(manifest.modules || {});
|
|
83
|
-
for (const canonical of mods) {
|
|
84
|
-
const flat = canonical.replace(/\./g, '__').replace(/\//g, '_');
|
|
85
|
-
map.set(flat, canonical);
|
|
86
|
-
const alias = manifest.aliases?.[canonical];
|
|
87
|
-
if (alias) {
|
|
88
|
-
const aliasFlat = String(alias).replace(/\./g, '__').replace(/\//g, '_');
|
|
89
|
-
map.set(aliasFlat, canonical);
|
|
90
|
-
}
|
|
80
|
+
const strategy = STRATEGY_REGISTRY.get(flavor);
|
|
81
|
+
if (!strategy) {
|
|
82
|
+
throw new Error(`[ns-hmr] Unsupported framework strategy: ${flavor}`);
|
|
91
83
|
}
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
// NativeScript module detectors
|
|
95
|
-
function isCoreGlobalsReference(spec) {
|
|
96
|
-
return /@nativescript(?:[\/_-])core(?:[\/_-])globals/.test(spec || '');
|
|
97
|
-
}
|
|
98
|
-
function isNativeScriptCoreModule(spec) {
|
|
99
|
-
return /^(?:@nativescript[\/_-]core|@nativescript\/core)(?:\b|\/)/i.test(spec || '');
|
|
100
|
-
}
|
|
101
|
-
function isNativeScriptPluginModule(spec) {
|
|
102
|
-
return /^@nativescript\//i.test(spec || '') && !isNativeScriptCoreModule(spec || '');
|
|
103
|
-
}
|
|
104
|
-
function isLikelyNativeScriptRuntimePluginSpecifier(spec) {
|
|
105
|
-
if (!spec)
|
|
106
|
-
return false;
|
|
107
|
-
const s = spec.replace(PAT.QUERY_PATTERN, '');
|
|
108
|
-
if (/^(?:\.|\/|https?:\/\/)/i.test(s))
|
|
109
|
-
return false;
|
|
110
|
-
if (s.startsWith('@@/'))
|
|
111
|
-
return false;
|
|
112
|
-
if (s.startsWith('~/'))
|
|
113
|
-
return false;
|
|
114
|
-
if (s.startsWith('@/'))
|
|
115
|
-
return false;
|
|
116
|
-
if (/\.vue(?:\?|$)/i.test(s))
|
|
117
|
-
return false;
|
|
118
|
-
const root = extractRootPackageName(s) || s;
|
|
119
|
-
if (!root)
|
|
120
|
-
return false;
|
|
121
|
-
if (isNativeScriptCoreModule(root))
|
|
122
|
-
return false;
|
|
123
|
-
if (/^(?:vue|nativescript-vue)(?:\b|\/)/i.test(root))
|
|
124
|
-
return false;
|
|
125
|
-
if (/^@nativescript\//i.test(root))
|
|
126
|
-
return true;
|
|
127
|
-
if (/^(?:@nativescript-community|@nstudio|@mleleux)\//i.test(root))
|
|
128
|
-
return true;
|
|
129
|
-
return /(?:^|\/)nativescript(?:$|[-_])/i.test(root);
|
|
84
|
+
return strategy;
|
|
130
85
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
function isLikelyNativeScriptPluginSpecifier(spec) {
|
|
135
|
-
if (!spec)
|
|
136
|
-
return false;
|
|
137
|
-
const s = spec.replace(PAT.QUERY_PATTERN, '');
|
|
138
|
-
// Absolute or relative paths are not bare packages
|
|
139
|
-
if (/^(?:\.|\/|https?:\/\/)/i.test(s))
|
|
140
|
-
return false;
|
|
141
|
-
// App alias paths like '@/...' are not vendor packages
|
|
142
|
-
if (s.startsWith('@@/'))
|
|
143
|
-
return false; // extremely rare double '@' alias
|
|
144
|
-
if (s.startsWith('~/'))
|
|
145
|
-
return false; // NativeScript tilde alias (app root)
|
|
146
|
-
if (s.startsWith('@/'))
|
|
147
|
-
return false; // Common Vite alias for src
|
|
148
|
-
// .vue SFCs are not vendor packages
|
|
149
|
-
if (/\.vue(?:\?|$)/i.test(s))
|
|
150
|
-
return false;
|
|
151
|
-
// Exclude core and vue runtime which are handled by dedicated bridges
|
|
152
|
-
if (/^@nativescript\/core(\b|\/)/i.test(s))
|
|
153
|
-
return false;
|
|
154
|
-
if (/^(?:vue|nativescript-vue)(?:\b|\/)/i.test(s))
|
|
86
|
+
let ACTIVE_STRATEGY;
|
|
87
|
+
function isSocketClientOpen(client) {
|
|
88
|
+
if (!client) {
|
|
155
89
|
return false;
|
|
156
|
-
// Treat any other bare package id as device-resolved (require) during HMR
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
export function tryReadRawExplicitJavaScriptModule(spec, projectRoot) {
|
|
160
|
-
if (!spec || !spec.startsWith('/'))
|
|
161
|
-
return null;
|
|
162
|
-
if (spec.startsWith('/@id/') || spec.startsWith('/@fs/'))
|
|
163
|
-
return null;
|
|
164
|
-
if (!/\.js$/i.test(spec) || /\.(?:mjs|cjs)$/i.test(spec))
|
|
165
|
-
return null;
|
|
166
|
-
// NativeScript runtime plugins rely on Vite's transform pipeline to apply
|
|
167
|
-
// platform-aware entry resolution and preserve root-entry routing semantics.
|
|
168
|
-
// Reading their on-disk JS directly recreates raw relative imports such as
|
|
169
|
-
// `./gesturehandler`, which can reintroduce HTTP ESM cycles during HMR.
|
|
170
|
-
try {
|
|
171
|
-
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(spec);
|
|
172
|
-
if (nodeModulesSpecifier) {
|
|
173
|
-
const pkgName = extractRootPackageName(nodeModulesSpecifier);
|
|
174
|
-
if (isLikelyNativeScriptRuntimePluginSpecifier(pkgName)) {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
90
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (!rel || rel.startsWith('..') || path.isAbsolute(rel))
|
|
184
|
-
return null;
|
|
185
|
-
if (!existsSync(absPath))
|
|
186
|
-
return null;
|
|
91
|
+
const openState = typeof client.OPEN === 'number' ? client.OPEN : 1;
|
|
92
|
+
return client.readyState === openState;
|
|
93
|
+
}
|
|
94
|
+
function getHmrSocketRoleFromRequestUrl(requestUrl) {
|
|
187
95
|
try {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
code,
|
|
194
|
-
resolvedId: spec,
|
|
195
|
-
};
|
|
96
|
+
const url = new URL(requestUrl || '/ns-hmr', 'http://localhost');
|
|
97
|
+
return url.searchParams.get('ns_hmr_role') || 'unknown';
|
|
196
98
|
}
|
|
197
99
|
catch {
|
|
198
|
-
return
|
|
100
|
+
return 'unknown';
|
|
199
101
|
}
|
|
200
102
|
}
|
|
201
|
-
function
|
|
202
|
-
if (!
|
|
203
|
-
return
|
|
204
|
-
const [rawPath, rawQuery = ''] = url.split('?', 2);
|
|
205
|
-
let normalizedPath = rawPath;
|
|
206
|
-
const root = projectRoot ? projectRoot.replace(/\\/g, '/') : '';
|
|
207
|
-
if (normalizedPath.startsWith('/@fs/')) {
|
|
208
|
-
const fsPath = normalizedPath.slice('/@fs'.length).replace(/\\/g, '/');
|
|
209
|
-
if (root && fsPath.startsWith(root)) {
|
|
210
|
-
const rel = fsPath.slice(root.length);
|
|
211
|
-
normalizedPath = rel.startsWith('/') ? rel : `/${rel}`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
else if (root && normalizedPath.replace(/\\/g, '/').startsWith(root)) {
|
|
215
|
-
const rel = normalizedPath.replace(/\\/g, '/').slice(root.length);
|
|
216
|
-
normalizedPath = rel.startsWith('/') ? rel : `/${rel}`;
|
|
103
|
+
function getHmrSocketRole(client) {
|
|
104
|
+
if (!client) {
|
|
105
|
+
return 'unknown';
|
|
217
106
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
params.delete('t');
|
|
223
|
-
params.delete('v');
|
|
224
|
-
const kept = Array.from(params.entries()).sort(([leftKey, leftValue], [rightKey, rightValue]) => {
|
|
225
|
-
if (leftKey === rightKey) {
|
|
226
|
-
return leftValue.localeCompare(rightValue);
|
|
227
|
-
}
|
|
228
|
-
return leftKey.localeCompare(rightKey);
|
|
229
|
-
});
|
|
230
|
-
if (!kept.length) {
|
|
231
|
-
return normalizedPath;
|
|
232
|
-
}
|
|
233
|
-
const normalizedQuery = new URLSearchParams();
|
|
234
|
-
for (const [key, value] of kept) {
|
|
235
|
-
normalizedQuery.append(key, value);
|
|
236
|
-
}
|
|
237
|
-
return `${normalizedPath}?${normalizedQuery.toString()}`;
|
|
107
|
+
return typeof client.__nsHmrClientRole === 'string' && client.__nsHmrClientRole ? client.__nsHmrClientRole : 'unknown';
|
|
108
|
+
}
|
|
109
|
+
function shouldAllowLocalCoreSanitizerPaths(contextLabel) {
|
|
110
|
+
return /\bnode_modules\/@nativescript\/vite\/hmr\/(?:client|frameworks)\//.test(contextLabel);
|
|
238
111
|
}
|
|
112
|
+
export function prepareAngularEntryForDevice(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
113
|
+
const rewrittenCode = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp);
|
|
114
|
+
return rewriteAngularEntryRegisterOnly(rewrittenCode, resolveAngularCoreHmrImportSource(rewrittenCode, httpOrigin));
|
|
115
|
+
}
|
|
116
|
+
const processSfcCode = createProcessSfcCode(processCodeForDevice);
|
|
117
|
+
// Bare specifiers and special skip patterns (virtual, data:, etc.)
|
|
118
|
+
const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
|
|
119
|
+
const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
|
|
239
120
|
const MODULE_IMPORT_ANALYSIS_PLUGINS = ['typescript', 'jsx', 'importMeta', 'topLevelAwait', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', 'decorators-legacy'];
|
|
240
121
|
function collectTopLevelImportRecords(code) {
|
|
241
122
|
if (!code || typeof code !== 'string' || !/\bimport\b/.test(code)) {
|
|
@@ -351,953 +232,47 @@ function buildNodeModuleProvenancePrelude(sourceId) {
|
|
|
351
232
|
const viteDepsMatch = cleaned.match(/(?:^|\/)node_modules\/\.vite\/deps\/([^?#]+)/);
|
|
352
233
|
if (viteDepsMatch?.[1]) {
|
|
353
234
|
normalized = `.vite/deps/${viteDepsMatch[1]}`;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (!normalized) {
|
|
357
|
-
return '';
|
|
358
|
-
}
|
|
359
|
-
let packageSpecifier = normalized;
|
|
360
|
-
let via = 'node_modules';
|
|
361
|
-
if (normalized.startsWith('.vite/deps/')) {
|
|
362
|
-
via = 'vite-deps';
|
|
363
|
-
packageSpecifier = viteDepsPathToBareSpecifier(normalized.slice('.vite/deps/'.length)) || normalized;
|
|
364
|
-
}
|
|
365
|
-
const rootPackage = extractRootPackageName(packageSpecifier);
|
|
366
|
-
if (!rootPackage) {
|
|
367
|
-
return '';
|
|
368
|
-
}
|
|
369
|
-
return `try { const __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(${JSON.stringify(rootPackage)}, ${JSON.stringify({ kind: 'http-esm', specifier: packageSpecifier, url: sourceId, via })}); } } catch {}\n`;
|
|
370
|
-
}
|
|
371
|
-
export function collectGraphUpdateModulesForHotUpdate(options) {
|
|
372
|
-
const targets = new Map();
|
|
373
|
-
const addTarget = (mod) => {
|
|
374
|
-
const id = mod?.id?.replace(/\?.*$/, '');
|
|
375
|
-
if (!id)
|
|
376
|
-
return;
|
|
377
|
-
if (!targets.has(id)) {
|
|
378
|
-
targets.set(id, mod);
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
if (options.flavor === 'angular' && /\.(html|htm)$/i.test(options.file)) {
|
|
382
|
-
for (const mod of options.modules || []) {
|
|
383
|
-
for (const importer of mod?.importers || []) {
|
|
384
|
-
const importerId = importer?.id || '';
|
|
385
|
-
if (/\.[cm]?[jt]sx?(?:$|\?)/i.test(importerId)) {
|
|
386
|
-
addTarget(importer);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (!targets.size) {
|
|
391
|
-
addTarget(options.getModuleById(options.file.replace(/\.(html|htm)$/i, '.ts')));
|
|
392
|
-
addTarget(options.getModuleById(options.file.replace(/\.(html|htm)$/i, '.js')));
|
|
393
|
-
}
|
|
394
|
-
return Array.from(targets.values());
|
|
395
|
-
}
|
|
396
|
-
if (!options.file.endsWith('.vue')) {
|
|
397
|
-
addTarget(options.getModuleById(options.file) || options.getModuleById(options.file + '?vue'));
|
|
398
|
-
}
|
|
399
|
-
return Array.from(targets.values());
|
|
400
|
-
}
|
|
401
|
-
export function collectAngularHotUpdateRoots(options) {
|
|
402
|
-
const roots = [];
|
|
403
|
-
const seenIds = new Set();
|
|
404
|
-
const seenObjects = new Set();
|
|
405
|
-
const addRoot = (mod) => {
|
|
406
|
-
if (!mod) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (mod.id) {
|
|
410
|
-
if (seenIds.has(mod.id)) {
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
seenIds.add(mod.id);
|
|
414
|
-
roots.push(mod);
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
if (seenObjects.has(mod)) {
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
seenObjects.add(mod);
|
|
421
|
-
roots.push(mod);
|
|
422
|
-
};
|
|
423
|
-
if (/\.(html|htm)$/i.test(options.file)) {
|
|
424
|
-
for (const mod of collectGraphUpdateModulesForHotUpdate({
|
|
425
|
-
file: options.file,
|
|
426
|
-
flavor: 'angular',
|
|
427
|
-
modules: options.modules,
|
|
428
|
-
getModuleById: options.getModuleById,
|
|
429
|
-
})) {
|
|
430
|
-
addRoot(mod);
|
|
431
|
-
}
|
|
432
|
-
return roots;
|
|
433
|
-
}
|
|
434
|
-
if (!/\.(m|c)?ts$/i.test(options.file)) {
|
|
435
|
-
return roots;
|
|
436
|
-
}
|
|
437
|
-
for (const mod of options.modules || []) {
|
|
438
|
-
addRoot(mod);
|
|
439
|
-
}
|
|
440
|
-
for (const mod of options.getModulesByFile?.(options.file) || []) {
|
|
441
|
-
addRoot(mod);
|
|
442
|
-
}
|
|
443
|
-
if (!roots.length) {
|
|
444
|
-
addRoot(options.getModuleById(options.file));
|
|
445
|
-
}
|
|
446
|
-
return roots;
|
|
447
|
-
}
|
|
448
|
-
export function collectAngularTransitiveImportersForInvalidation(options) {
|
|
449
|
-
const visited = new Set();
|
|
450
|
-
const collected = new Map();
|
|
451
|
-
const isExcluded = options.isExcluded ?? ((id) => id.includes('/node_modules/'));
|
|
452
|
-
const maxDepth = Math.max(1, Math.floor(options.maxDepth ?? 16));
|
|
453
|
-
const normalizeId = (value) => (value ?? '').replace(/\?.*$/, '');
|
|
454
|
-
const walk = (mod, depth) => {
|
|
455
|
-
if (!mod || visited.has(mod)) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
visited.add(mod);
|
|
459
|
-
if (depth >= maxDepth) {
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
const importers = mod.importers;
|
|
463
|
-
if (!importers) {
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
for (const importer of importers) {
|
|
467
|
-
if (!importer)
|
|
468
|
-
continue;
|
|
469
|
-
const importerId = normalizeId(importer.id);
|
|
470
|
-
if (!importerId) {
|
|
471
|
-
walk(importer, depth + 1);
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
if (isExcluded(importerId)) {
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
if (!collected.has(importerId)) {
|
|
478
|
-
collected.set(importerId, importer);
|
|
479
|
-
}
|
|
480
|
-
walk(importer, depth + 1);
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
for (const mod of options.modules || []) {
|
|
484
|
-
walk(mod, 0);
|
|
485
|
-
}
|
|
486
|
-
return Array.from(collected.values());
|
|
487
|
-
}
|
|
488
|
-
export function shouldInvalidateAngularTransitiveImporters(options) {
|
|
489
|
-
if (options.flavor !== 'angular') {
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
return /\.(?:html|htm|(m|c)?[jt]sx?)$/i.test(options.file);
|
|
493
|
-
}
|
|
494
|
-
export function shouldSuppressDefaultViteHotUpdate(options) {
|
|
495
|
-
if (options.flavor !== 'angular') {
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
return /\.(html|htm|ts)$/i.test(options.file);
|
|
499
|
-
}
|
|
500
|
-
export function normalizeHotReloadMatchPath(raw, root) {
|
|
501
|
-
let normalized = String(raw || '')
|
|
502
|
-
.split('?')[0]
|
|
503
|
-
.replace(/\\/g, '/')
|
|
504
|
-
.replace(/^file:\/\//, '');
|
|
505
|
-
if (root) {
|
|
506
|
-
const rootNormalized = root.replace(/\\/g, '/');
|
|
507
|
-
if (normalized.startsWith(rootNormalized)) {
|
|
508
|
-
normalized = normalized.slice(rootNormalized.length);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
if (!normalized.startsWith('/')) {
|
|
512
|
-
normalized = `/${normalized}`;
|
|
513
|
-
}
|
|
514
|
-
return normalized;
|
|
515
|
-
}
|
|
516
|
-
export function shouldSuppressViteFullReloadPayload(options) {
|
|
517
|
-
const { payload, pendingEntries, root } = options;
|
|
518
|
-
const now = options.now ?? Date.now();
|
|
519
|
-
if (!payload || payload.type !== 'full-reload') {
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
const payloadPath = typeof payload.path === 'string' && payload.path !== '*' ? normalizeHotReloadMatchPath(payload.path, root) : null;
|
|
523
|
-
const payloadTriggeredBy = typeof payload.triggeredBy === 'string' ? normalizeHotReloadMatchPath(payload.triggeredBy, root) : null;
|
|
524
|
-
for (const entry of pendingEntries) {
|
|
525
|
-
if (!entry || entry.expiresAt <= now) {
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
if (payloadTriggeredBy === entry.absPath || payloadTriggeredBy === entry.relPath || payloadPath === entry.relPath || payloadPath === entry.absPath) {
|
|
529
|
-
return true;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
export function createSharedTransformRequestRunner(transformRequest, onTimeout, options = {}) {
|
|
535
|
-
const inFlight = new Map();
|
|
536
|
-
const recentResults = new Map();
|
|
537
|
-
const cacheGenerations = new Map();
|
|
538
|
-
const queue = [];
|
|
539
|
-
const maxConcurrent = Math.max(1, Math.floor(options.maxConcurrent ?? 1));
|
|
540
|
-
const resultCacheTtlMs = Math.max(0, Math.floor(options.resultCacheTtlMs ?? 0));
|
|
541
|
-
const getResultCacheKey = options.getResultCacheKey ?? ((url) => url);
|
|
542
|
-
let activeCount = 0;
|
|
543
|
-
const getCacheGeneration = (cacheKey) => cacheGenerations.get(cacheKey) ?? 0;
|
|
544
|
-
const invalidateCacheKey = (cacheKey) => {
|
|
545
|
-
cacheGenerations.set(cacheKey, getCacheGeneration(cacheKey) + 1);
|
|
546
|
-
recentResults.delete(cacheKey);
|
|
547
|
-
};
|
|
548
|
-
const pruneRecentResults = () => {
|
|
549
|
-
if (!recentResults.size) {
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
const now = Date.now();
|
|
553
|
-
for (const [key, entry] of recentResults) {
|
|
554
|
-
if (entry.expiresAt <= now) {
|
|
555
|
-
recentResults.delete(key);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
};
|
|
559
|
-
const rememberRecentResult = (url, result, generation) => {
|
|
560
|
-
if (!result || resultCacheTtlMs <= 0) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const cacheKey = getResultCacheKey(url);
|
|
564
|
-
if (getCacheGeneration(cacheKey) !== generation) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
recentResults.delete(cacheKey);
|
|
568
|
-
recentResults.set(cacheKey, {
|
|
569
|
-
expiresAt: Date.now() + resultCacheTtlMs,
|
|
570
|
-
result,
|
|
571
|
-
});
|
|
572
|
-
if (recentResults.size > 512) {
|
|
573
|
-
const oldestKey = recentResults.keys().next().value;
|
|
574
|
-
if (oldestKey) {
|
|
575
|
-
recentResults.delete(oldestKey);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
};
|
|
579
|
-
const runNext = () => {
|
|
580
|
-
while (activeCount < maxConcurrent) {
|
|
581
|
-
const next = queue.shift();
|
|
582
|
-
if (!next) {
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
activeCount += 1;
|
|
586
|
-
next();
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
const schedule = (task) => {
|
|
590
|
-
let resolveStarted = null;
|
|
591
|
-
const started = new Promise((resolve) => {
|
|
592
|
-
resolveStarted = resolve;
|
|
593
|
-
});
|
|
594
|
-
const execution = new Promise((resolve, reject) => {
|
|
595
|
-
queue.push(() => {
|
|
596
|
-
let started;
|
|
597
|
-
resolveStarted?.();
|
|
598
|
-
try {
|
|
599
|
-
started = Promise.resolve(task());
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
started = Promise.reject(error);
|
|
603
|
-
}
|
|
604
|
-
started.then(resolve, reject).finally(() => {
|
|
605
|
-
activeCount = Math.max(0, activeCount - 1);
|
|
606
|
-
runNext();
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
runNext();
|
|
610
|
-
});
|
|
611
|
-
return { execution, started };
|
|
612
|
-
};
|
|
613
|
-
const withTimeout = (entry, url, timeoutMs) => {
|
|
614
|
-
if (!(timeoutMs > 0)) {
|
|
615
|
-
return entry.execution;
|
|
616
|
-
}
|
|
617
|
-
return entry.started.then(() => new Promise((resolve, reject) => {
|
|
618
|
-
const timer = setTimeout(() => {
|
|
619
|
-
try {
|
|
620
|
-
onTimeout?.(url, timeoutMs);
|
|
621
|
-
}
|
|
622
|
-
catch { }
|
|
623
|
-
}, timeoutMs);
|
|
624
|
-
entry.execution.then(resolve, reject).finally(() => {
|
|
625
|
-
clearTimeout(timer);
|
|
626
|
-
});
|
|
627
|
-
}));
|
|
628
|
-
};
|
|
629
|
-
const runner = ((url, timeoutMs = 120000) => {
|
|
630
|
-
pruneRecentResults();
|
|
631
|
-
const cacheKey = getResultCacheKey(url);
|
|
632
|
-
const generation = getCacheGeneration(cacheKey);
|
|
633
|
-
const recent = recentResults.get(cacheKey);
|
|
634
|
-
if (recent && recent.expiresAt > Date.now()) {
|
|
635
|
-
return Promise.resolve(recent.result);
|
|
636
|
-
}
|
|
637
|
-
const existingExecution = inFlight.get(url);
|
|
638
|
-
if (existingExecution && existingExecution.generation === generation && existingExecution.cacheKey === cacheKey) {
|
|
639
|
-
return withTimeout(existingExecution, url, timeoutMs);
|
|
640
|
-
}
|
|
641
|
-
const scheduled = schedule(async () => {
|
|
642
|
-
const result = await Promise.resolve(transformRequest(url));
|
|
643
|
-
rememberRecentResult(url, result, generation);
|
|
644
|
-
return result;
|
|
645
|
-
});
|
|
646
|
-
let execution;
|
|
647
|
-
execution = scheduled.execution.finally(() => {
|
|
648
|
-
if (inFlight.get(url)?.execution === execution) {
|
|
649
|
-
inFlight.delete(url);
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
const entry = { execution, started: scheduled.started, cacheKey, generation };
|
|
653
|
-
inFlight.set(url, entry);
|
|
654
|
-
return withTimeout(entry, url, timeoutMs);
|
|
655
|
-
});
|
|
656
|
-
runner.invalidate = (url) => {
|
|
657
|
-
invalidateCacheKey(getResultCacheKey(url));
|
|
658
|
-
};
|
|
659
|
-
runner.invalidateMany = (urls) => {
|
|
660
|
-
for (const url of urls || []) {
|
|
661
|
-
runner.invalidate(url);
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
runner.clear = () => {
|
|
665
|
-
recentResults.clear();
|
|
666
|
-
cacheGenerations.clear();
|
|
667
|
-
};
|
|
668
|
-
return runner;
|
|
669
|
-
}
|
|
670
|
-
export function ensureNativeScriptModuleBindings(code, options) {
|
|
671
|
-
// Proceed even if a vendor manifest isn't available; we'll still vendor-bind
|
|
672
|
-
// likely NativeScript plugin-style specifiers (e.g., 'pinia', '@scope/pkg')
|
|
673
|
-
// via require() so device can resolve them from the app bundle.
|
|
674
|
-
const importRegex = /(^|\n)\s*import\s+([\s\S]*?)\s+from\s+["']([^"']+)["'];?/gm;
|
|
675
|
-
const sideEffectRegex = /(^|\n)\s*import\s+["']([^"']+)["'];?/gm;
|
|
676
|
-
// Collect non-vendor imports so we can hoist them above the injected vendor prelude.
|
|
677
|
-
// This ensures any residual ESM imports (like SFCs) remain at the true top-level for parsers
|
|
678
|
-
// that require imports to precede other statements.
|
|
679
|
-
const preservedImports = [];
|
|
680
|
-
const modules = new Map();
|
|
681
|
-
const getModuleBinding = (canonical) => {
|
|
682
|
-
let entry = modules.get(canonical);
|
|
683
|
-
if (!entry) {
|
|
684
|
-
entry = {
|
|
685
|
-
default: new Set(),
|
|
686
|
-
namespace: new Set(),
|
|
687
|
-
named: [],
|
|
688
|
-
sideEffectOnly: false,
|
|
689
|
-
};
|
|
690
|
-
modules.set(canonical, entry);
|
|
691
|
-
}
|
|
692
|
-
return entry;
|
|
693
|
-
};
|
|
694
|
-
const parseNamedImports = (clause, binding) => {
|
|
695
|
-
const inner = clause.replace(/^\{/, '').replace(/\}$/, '');
|
|
696
|
-
inner
|
|
697
|
-
.split(',')
|
|
698
|
-
.map((segment) => segment.trim())
|
|
699
|
-
.filter(Boolean)
|
|
700
|
-
.forEach((segment) => {
|
|
701
|
-
const [imported, local] = segment.split(/\s+as\s+/i).map((s) => s.trim());
|
|
702
|
-
const resolvedImported = imported;
|
|
703
|
-
const resolvedLocal = local || imported;
|
|
704
|
-
if (resolvedImported) {
|
|
705
|
-
binding.named.push({
|
|
706
|
-
imported: resolvedImported,
|
|
707
|
-
local: resolvedLocal,
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
});
|
|
711
|
-
};
|
|
712
|
-
// Handle "import ... from 'x'" forms
|
|
713
|
-
code = code.replace(importRegex, (full, pfx, clause, rawSpec) => {
|
|
714
|
-
// Capture original for potential preservation (strip the leading newline to avoid double spacing when hoisted)
|
|
715
|
-
const original = full.replace(/^\n/, '');
|
|
716
|
-
// Do not touch type-only imports other than hoisting
|
|
717
|
-
if (full.trimStart().startsWith('import type')) {
|
|
718
|
-
preservedImports.push(original);
|
|
719
|
-
return pfx || '';
|
|
720
|
-
}
|
|
721
|
-
const specifier = rawSpec.replace(PAT.QUERY_PATTERN, '');
|
|
722
|
-
let canonical = resolveVendorFromCandidate(specifier);
|
|
723
|
-
const runtimePluginSpecifier = isLikelyNativeScriptRuntimePluginSpecifier(canonical || specifier);
|
|
724
|
-
if (options?.preserveNonPluginVendorImports && !runtimePluginSpecifier) {
|
|
725
|
-
preservedImports.push(original);
|
|
726
|
-
return pfx || '';
|
|
727
|
-
}
|
|
728
|
-
// If not found in vendor manifest, treat well-known NativeScript plugin-style packages
|
|
729
|
-
// as require() based modules so the device can resolve them from the app bundle or vendor.
|
|
730
|
-
if (!canonical && isLikelyNativeScriptPluginSpecifier(specifier)) {
|
|
731
|
-
canonical = specifier;
|
|
732
|
-
}
|
|
733
|
-
// CRITICAL: never vendor-inject @nativescript/core here — preserve for the later core-bridge pass.
|
|
734
|
-
if (canonical && /^@nativescript\/core(\b|\/)/i.test(canonical)) {
|
|
735
|
-
preservedImports.push(original);
|
|
736
|
-
return pfx || '';
|
|
737
|
-
}
|
|
738
|
-
if (!canonical) {
|
|
739
|
-
preservedImports.push(original);
|
|
740
|
-
return pfx || '';
|
|
741
|
-
}
|
|
742
|
-
const binding = getModuleBinding(canonical);
|
|
743
|
-
const trimmed = String(clause).trim();
|
|
744
|
-
if (!trimmed) {
|
|
745
|
-
binding.sideEffectOnly = true;
|
|
746
|
-
return pfx || ''; // erase the import line
|
|
747
|
-
}
|
|
748
|
-
// namespace: import * as ns from 'x'
|
|
749
|
-
if (trimmed.startsWith('*')) {
|
|
750
|
-
const m = trimmed.match(/\*\s+as\s+(\w+)/i);
|
|
751
|
-
if (m?.[1])
|
|
752
|
-
binding.namespace.add(m[1]);
|
|
753
|
-
return pfx || '';
|
|
754
|
-
}
|
|
755
|
-
// named: import { a, b as c } from 'x'
|
|
756
|
-
if (trimmed.startsWith('{')) {
|
|
757
|
-
parseNamedImports(trimmed, binding);
|
|
758
|
-
return pfx || '';
|
|
759
|
-
}
|
|
760
|
-
// default + named: import Default, { a as A } from 'x'
|
|
761
|
-
if (trimmed.includes(',') && trimmed.includes('{')) {
|
|
762
|
-
const [defaultPart, namedPart] = trimmed.split(/,(.+)/, 2);
|
|
763
|
-
const def = defaultPart.trim();
|
|
764
|
-
if (def)
|
|
765
|
-
binding.default.add(def);
|
|
766
|
-
if (namedPart)
|
|
767
|
-
parseNamedImports(namedPart.trim(), binding);
|
|
768
|
-
return pfx || '';
|
|
769
|
-
}
|
|
770
|
-
// default only
|
|
771
|
-
binding.default.add(trimmed);
|
|
772
|
-
return pfx || '';
|
|
773
|
-
});
|
|
774
|
-
// Handle side-effect only imports: import 'x'
|
|
775
|
-
code = code.replace(sideEffectRegex, (full, _pfx, rawSpec) => {
|
|
776
|
-
const original = full.replace(/^\n/, '');
|
|
777
|
-
const specifier = rawSpec.replace(PAT.QUERY_PATTERN, '');
|
|
778
|
-
let canonical = resolveVendorFromCandidate(specifier);
|
|
779
|
-
const runtimePluginSpecifier = isLikelyNativeScriptRuntimePluginSpecifier(canonical || specifier);
|
|
780
|
-
if (options?.preserveNonPluginVendorImports && !runtimePluginSpecifier) {
|
|
781
|
-
preservedImports.push(original);
|
|
782
|
-
return _pfx || '';
|
|
783
|
-
}
|
|
784
|
-
if (!canonical && isLikelyNativeScriptPluginSpecifier(specifier)) {
|
|
785
|
-
canonical = specifier;
|
|
786
|
-
}
|
|
787
|
-
if (canonical && /^@nativescript\/core(\b|\/)/i.test(canonical)) {
|
|
788
|
-
preservedImports.push(original);
|
|
789
|
-
return _pfx || '';
|
|
790
|
-
}
|
|
791
|
-
if (!canonical) {
|
|
792
|
-
preservedImports.push(original);
|
|
793
|
-
return _pfx || '';
|
|
794
|
-
}
|
|
795
|
-
const binding = getModuleBinding(canonical);
|
|
796
|
-
binding.sideEffectOnly = true;
|
|
797
|
-
return _pfx || '';
|
|
798
|
-
});
|
|
799
|
-
// If there are no vendor modules to bind, still hoist preserved imports if any were collected.
|
|
800
|
-
if (!modules.size) {
|
|
801
|
-
if (preservedImports.length) {
|
|
802
|
-
const preserved = preservedImports.join('') + '\n';
|
|
803
|
-
return preserved + code;
|
|
804
|
-
}
|
|
805
|
-
return code;
|
|
806
|
-
}
|
|
807
|
-
let injection = 'const __nsVendorRegistry = (globalThis.__nsVendorRegistry ||= new Map());\n';
|
|
808
|
-
// Soft vendor fallback mode: when a plugin module is not available during HMR, provide a stub so the module can instantiate.
|
|
809
|
-
// Toggle with globalThis.__NS_VENDOR_SOFT__ (default true)
|
|
810
|
-
// Use JS-safe global access (no TS casts) to avoid syntax errors on device
|
|
811
|
-
injection += "const __NS_VENDOR_SOFT__ = (typeof globalThis.__NS_VENDOR_SOFT__ !== 'undefined' ? !!globalThis.__NS_VENDOR_SOFT__ : true);\n";
|
|
812
|
-
// Provide a require fallback that throws lazily so callers can soft-stub in the catch block.
|
|
813
|
-
injection += "const __nsVendorRequire = (typeof globalThis.__nsRequire === 'function' ? globalThis.__nsRequire : (typeof globalThis.require === 'function' ? globalThis.require : (spec => { throw new Error('__nsVendorRequire unavailable'); })));\n";
|
|
814
|
-
// One-time diagnostic if require is missing; avoid spewing on every module
|
|
815
|
-
injection += "try { (globalThis.__NS_VENDOR_ONCE__ ||= { loggedRequireMissing: false }); if (!globalThis.__NS_VENDOR_ONCE__.loggedRequireMissing && typeof __nsVendorRequire !== 'function') { console.warn('[ns-hmr][vendor][require-missing] using soft stubs=', __NS_VENDOR_SOFT__); globalThis.__NS_VENDOR_ONCE__.loggedRequireMissing = true; } } catch {}\n";
|
|
816
|
-
injection += "function __nsMissing(name){ try { const fn = function(){ try { console.warn('[ns-hmr][vendor][stub]', name); } catch {} }; return new Proxy(fn, { get: (_t, p) => __nsMissing(name + '.' + String(p)) }); } catch { return {}; } }\n";
|
|
817
|
-
// Helper utils to simplify robust property/default selection without using optional chaining/nullish
|
|
818
|
-
injection += "function __nsHasInstall(x){ try { return (typeof x === 'function') || (typeof x === 'object' && x && typeof x.install === 'function'); } catch { return false; } }\n";
|
|
819
|
-
injection += "function __nsDefault(mod){ try { return (mod && mod['default'] !== undefined) ? mod['default'] : mod; } catch { return mod; } }\n";
|
|
820
|
-
injection += "function __nsNestedDefault(mod){ try { return (mod && mod.default && (typeof mod.default.default === 'function' || (typeof mod.default.default === 'object' && mod.default.default && typeof mod.default.default.install === 'function'))) ? mod.default.default : undefined; } catch { return undefined; } }\n";
|
|
821
|
-
injection += "function __nsPick(mod, name){ try { if (mod && mod['default'] && mod['default'][name] !== undefined) return mod['default'][name]; } catch {} try { if (mod && mod[name] !== undefined) return mod[name]; } catch {} try { if (mod && typeof mod['default'] === 'function' && mod['default'].name === name) return mod['default']; } catch {} return undefined; }\n";
|
|
822
|
-
let index = 0;
|
|
823
|
-
for (const [canonical, binding] of modules) {
|
|
824
|
-
const cacheKey = JSON.stringify(canonical);
|
|
825
|
-
const moduleVar = `__nsVendorModule_${index++}`;
|
|
826
|
-
injection += `const ${moduleVar} = __nsVendorRegistry.has(${cacheKey}) ? __nsVendorRegistry.get(${cacheKey}) : (() => { try { const mod = __nsVendorRequire(${cacheKey}); __nsVendorRegistry.set(${cacheKey}, mod); return mod; } catch (e) { try { console.error('[ns-hmr][vendor][require-failed]', ${cacheKey}, (e && (e.message||e)) ); } catch {} try { if (__NS_VENDOR_SOFT__) { const stub = __nsMissing(${cacheKey}); __nsVendorRegistry.set(${cacheKey}, stub); return stub; } } catch {} throw e; } })();\n`;
|
|
827
|
-
binding.namespace.forEach((alias) => {
|
|
828
|
-
// For namespace imports, expose both the raw module and a default fallback for interop consumers
|
|
829
|
-
injection += `const ${alias} = ${moduleVar};\n`;
|
|
830
|
-
injection += `(${alias} && typeof ${alias} === 'object' && !('default' in ${alias})) && (${alias}.default = ${alias});\n`;
|
|
831
|
-
});
|
|
832
|
-
if (binding.named.length) {
|
|
833
|
-
// Bind each named import robustly from either default or namespace using helper.
|
|
834
|
-
for (const { imported, local } of binding.named) {
|
|
835
|
-
const localName = local;
|
|
836
|
-
const importedName = imported;
|
|
837
|
-
injection += `const ${localName} = __nsPick(${moduleVar}, ${JSON.stringify(importedName)});\n`;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (binding.default.size) {
|
|
841
|
-
// Create one stable default candidate per module and reuse for all default locals
|
|
842
|
-
const defVar = `${moduleVar}__def`;
|
|
843
|
-
injection += `const ${defVar} = __nsDefault(${moduleVar});\n`;
|
|
844
|
-
binding.default.forEach((localName) => {
|
|
845
|
-
injection += `const ${localName} = (__nsHasInstall(${defVar})
|
|
846
|
-
? ${defVar}
|
|
847
|
-
: (__nsHasInstall(${moduleVar})
|
|
848
|
-
? ${moduleVar}
|
|
849
|
-
: (function(){ const _n = __nsNestedDefault(${moduleVar}); return _n !== undefined ? _n : ${defVar}; })()));\n`;
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
if (binding.sideEffectOnly && !binding.namespace.size && !binding.named.length && !binding.default.size) {
|
|
853
|
-
injection += `void ${moduleVar};\n`;
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
injection += '\n';
|
|
857
|
-
// Hoist preserved non-vendor imports to the very top for maximum ESM compatibility
|
|
858
|
-
const preserved = preservedImports.length ? preservedImports.join('') + '\n' : '';
|
|
859
|
-
return preserved + injection + code;
|
|
860
|
-
}
|
|
861
|
-
// Guard any bare dynamic import(spec) occurring in assembled module code.
|
|
862
|
-
// We cannot override native dynamic import globally; for SFC assembler outputs we inline
|
|
863
|
-
// a tiny helper and rewrite "import(" to "__nsDynImport(" to prevent anomalous specs like '@'.
|
|
864
|
-
function guardBareDynamicImports(code) {
|
|
865
|
-
try {
|
|
866
|
-
if (!code || typeof code !== 'string')
|
|
867
|
-
return code;
|
|
868
|
-
const NEEDLE = /(^|\n)\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*/;
|
|
869
|
-
const hasImportCall = /\bimport\s*\(/.test(code);
|
|
870
|
-
if (!hasImportCall)
|
|
871
|
-
return code;
|
|
872
|
-
const helper = "const __nsDynImport = (spec) => { try { if (!spec || spec === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {} try { return import(spec); } catch (e) { return Promise.reject(e); } };\n";
|
|
873
|
-
// Avoid double injection
|
|
874
|
-
const inject = code.includes('const __nsDynImport =') ? '' : helper;
|
|
875
|
-
// Replace bare import( ... ) that are not part of 'import.meta' or type-only contexts
|
|
876
|
-
// Heuristic: replace 'import(' occurrences; skip 'import.meta'
|
|
877
|
-
const rewritten = code.replace(/\bimport\s*\(/g, '__nsDynImport(');
|
|
878
|
-
if (rewritten === code && !inject)
|
|
879
|
-
return code;
|
|
880
|
-
return inject + rewritten;
|
|
881
|
-
}
|
|
882
|
-
catch {
|
|
883
|
-
return code;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
function ensureDynamicHmrImportHelper(code) {
|
|
887
|
-
try {
|
|
888
|
-
if (!code.includes('__nsDynamicHmrImport('))
|
|
889
|
-
return code;
|
|
890
|
-
if (code.includes('const __nsDynamicHmrImport ='))
|
|
891
|
-
return code;
|
|
892
|
-
const helper = 'const __nsDynamicHmrImport = (spec) => {\n' +
|
|
893
|
-
" const __nsm = '/ns' + '/m';\n" +
|
|
894
|
-
" try { if (!spec || spec === '@') { return import(new URL(__nsm + '/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n" +
|
|
895
|
-
' try {\n' +
|
|
896
|
-
" if (typeof spec === 'string' && spec.startsWith(__nsm + '/')) {\n" +
|
|
897
|
-
" if (spec.includes('/__ns_hmr__/')) { return import(new URL(spec, import.meta.url).href); }\n" +
|
|
898
|
-
' const g = globalThis;\n' +
|
|
899
|
-
" const nonce = typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0;\n" +
|
|
900
|
-
" const tag = nonce ? `n${nonce}` : 'live';\n" +
|
|
901
|
-
" const nextPath = __nsm + '/__ns_hmr__/' + encodeURIComponent(tag) + spec.slice(__nsm.length);\n" +
|
|
902
|
-
" const origin = typeof g.__NS_HTTP_ORIGIN__ === 'string' && /^https?:\\/\\//.test(g.__NS_HTTP_ORIGIN__) ? g.__NS_HTTP_ORIGIN__ : '';\n" +
|
|
903
|
-
' return import(origin ? origin + nextPath : new URL(nextPath, import.meta.url).href);\n' +
|
|
904
|
-
' }\n' +
|
|
905
|
-
' } catch {}\n' +
|
|
906
|
-
' return import(spec);\n' +
|
|
907
|
-
'};\n';
|
|
908
|
-
return helper + code;
|
|
909
|
-
}
|
|
910
|
-
catch {
|
|
911
|
-
return code;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
function normalizeNativeScriptCoreSpecifier(spec) {
|
|
915
|
-
let normalized = spec.replace(/@nativescript[_-]core/gi, '@nativescript/core').replace(/@nativescript\/core\/index\.js$/i, '@nativescript/core/index.js');
|
|
916
|
-
if (normalized.startsWith('/node_modules/')) {
|
|
917
|
-
const idx = normalized.toLowerCase().indexOf('@nativescript/core');
|
|
918
|
-
if (idx !== -1) {
|
|
919
|
-
normalized = normalized.slice(idx);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
if (normalized.toLowerCase().startsWith('@nativescript/core')) {
|
|
923
|
-
normalized = normalized.replace(/\?[^"'`]*$/, '');
|
|
924
|
-
}
|
|
925
|
-
return normalized;
|
|
926
|
-
}
|
|
927
|
-
export function normalizeNodeModulesSpecifier(spec) {
|
|
928
|
-
if (!spec) {
|
|
929
|
-
return null;
|
|
930
|
-
}
|
|
931
|
-
let normalized = spec.replace(/\\/g, '/');
|
|
932
|
-
const idx = normalized.lastIndexOf('/node_modules/');
|
|
933
|
-
if (idx === -1) {
|
|
934
|
-
return null;
|
|
935
|
-
}
|
|
936
|
-
let subPath = normalized.slice(idx + '/node_modules/'.length);
|
|
937
|
-
if (!subPath) {
|
|
938
|
-
return null;
|
|
939
|
-
}
|
|
940
|
-
subPath = subPath.replace(PAT.QUERY_PATTERN, '');
|
|
941
|
-
if (!subPath) {
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
// Skip Vite pre-bundled deps that we already map to vendor
|
|
945
|
-
if (subPath.startsWith('.vite/')) {
|
|
946
|
-
return null;
|
|
947
|
-
}
|
|
948
|
-
return subPath.startsWith('/') ? subPath.slice(1) : subPath;
|
|
949
|
-
}
|
|
950
|
-
export function resolveVendorFromCandidate(specifier) {
|
|
951
|
-
if (!specifier) {
|
|
952
|
-
return null;
|
|
953
|
-
}
|
|
954
|
-
const manifest = getVendorManifest();
|
|
955
|
-
if (!manifest) {
|
|
956
|
-
return null;
|
|
957
|
-
}
|
|
958
|
-
const cleaned = specifier.replace(PAT.QUERY_PATTERN, '');
|
|
959
|
-
const direct = resolveVendorSpecifier(cleaned);
|
|
960
|
-
if (direct) {
|
|
961
|
-
return direct;
|
|
962
|
-
}
|
|
963
|
-
const flattenedId = extractVitePrebundleId(cleaned);
|
|
964
|
-
if (flattenedId) {
|
|
965
|
-
const flattenedMap = getFlattenedManifestMap(manifest);
|
|
966
|
-
const flatMatch = flattenedMap.get(flattenedId);
|
|
967
|
-
if (flatMatch) {
|
|
968
|
-
return flatMatch;
|
|
969
|
-
}
|
|
970
|
-
for (const [flatKey, canonical] of flattenedMap.entries()) {
|
|
971
|
-
if (flattenedId === flatKey) {
|
|
972
|
-
return canonical;
|
|
973
|
-
}
|
|
974
|
-
if (flattenedId.startsWith(`${flatKey}_`)) {
|
|
975
|
-
// The suffix after the flat key represents a subpath.
|
|
976
|
-
// Convert it back: e.g., "_solid" → "solid", "_store_dist_x" → "store/dist/x"
|
|
977
|
-
// Only resolve to the root vendor module if it's a file/dist subpath.
|
|
978
|
-
// Entry-point subpaths (e.g., _solid, _store) have different exports
|
|
979
|
-
// and must NOT be collapsed to the root.
|
|
980
|
-
const flatSuffix = flattenedId.slice(flatKey.length + 1);
|
|
981
|
-
const subpath = flatSuffix.replace(/_/g, '/');
|
|
982
|
-
if (isFileDistSubpath(subpath)) {
|
|
983
|
-
return canonical;
|
|
984
|
-
}
|
|
985
|
-
// Check if there's an alias for this subpath entry
|
|
986
|
-
const aliasKey = `${canonical}/${subpath.split('/')[0]}`;
|
|
987
|
-
if (manifest.aliases?.[aliasKey] && manifest.modules[manifest.aliases[aliasKey]]) {
|
|
988
|
-
return manifest.aliases[aliasKey];
|
|
989
|
-
}
|
|
990
|
-
// Entry-point subpath — don't collapse to root
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
const guessedId = flattenedId.replace(/__/g, '.').replace(/_/g, '/');
|
|
994
|
-
if (guessedId && guessedId !== flattenedId) {
|
|
995
|
-
const guessedCanonical = resolveVendorSpecifier(guessedId);
|
|
996
|
-
if (guessedCanonical) {
|
|
997
|
-
return guessedCanonical;
|
|
998
|
-
}
|
|
999
|
-
const prefix = findVendorPrefix(guessedId, manifest);
|
|
1000
|
-
if (prefix) {
|
|
1001
|
-
return prefix;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
const normalizedCore = normalizeNativeScriptCoreSpecifier(cleaned);
|
|
1006
|
-
if (normalizedCore !== cleaned) {
|
|
1007
|
-
const nsCanonical = resolveVendorSpecifier(normalizedCore);
|
|
1008
|
-
if (nsCanonical) {
|
|
1009
|
-
return nsCanonical;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(cleaned);
|
|
1013
|
-
if (nodeModulesSpecifier) {
|
|
1014
|
-
const canonical = resolveVendorSpecifier(nodeModulesSpecifier);
|
|
1015
|
-
if (canonical) {
|
|
1016
|
-
return canonical;
|
|
1017
|
-
}
|
|
1018
|
-
const prefix = findVendorPrefix(nodeModulesSpecifier, manifest);
|
|
1019
|
-
if (prefix) {
|
|
1020
|
-
return prefix;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
const prefix = findVendorPrefix(cleaned, manifest);
|
|
1024
|
-
if (prefix) {
|
|
1025
|
-
return prefix;
|
|
1026
|
-
}
|
|
1027
|
-
return null;
|
|
1028
|
-
}
|
|
1029
|
-
function resolveCandidateFilePath(candidate, projectRoot) {
|
|
1030
|
-
const cleaned = candidate.replace(PAT.QUERY_PATTERN, '');
|
|
1031
|
-
if (!cleaned)
|
|
1032
|
-
return null;
|
|
1033
|
-
const root = path.resolve(projectRoot);
|
|
1034
|
-
let absPath = null;
|
|
1035
|
-
if (cleaned.startsWith('/@fs/')) {
|
|
1036
|
-
absPath = cleaned.slice('/@fs'.length);
|
|
1037
|
-
}
|
|
1038
|
-
else if (cleaned.includes('/node_modules/')) {
|
|
1039
|
-
absPath = path.resolve(root, `.${cleaned}`);
|
|
1040
|
-
}
|
|
1041
|
-
else if (/^(?:[A-Za-z]:)?\//.test(cleaned)) {
|
|
1042
|
-
absPath = path.resolve(cleaned);
|
|
1043
|
-
}
|
|
1044
|
-
if (!absPath) {
|
|
1045
|
-
return null;
|
|
1046
|
-
}
|
|
1047
|
-
const rel = path.relative(root, absPath);
|
|
1048
|
-
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
1049
|
-
return null;
|
|
1050
|
-
}
|
|
1051
|
-
return existsSync(absPath) ? absPath : null;
|
|
1052
|
-
}
|
|
1053
|
-
export function filterExistingNodeModulesTransformCandidates(spec, candidates, projectRoot) {
|
|
1054
|
-
const cleanedSpec = spec.replace(PAT.QUERY_PATTERN, '');
|
|
1055
|
-
if (!cleanedSpec.includes('/node_modules/')) {
|
|
1056
|
-
return candidates;
|
|
1057
|
-
}
|
|
1058
|
-
return candidates.filter((candidate) => !!resolveCandidateFilePath(candidate, projectRoot));
|
|
1059
|
-
}
|
|
1060
|
-
function findVendorPrefix(specifier, manifest) {
|
|
1061
|
-
const { modules, aliases } = manifest;
|
|
1062
|
-
const keys = Object.keys(modules || {});
|
|
1063
|
-
for (const key of keys) {
|
|
1064
|
-
if (specifier === key) {
|
|
1065
|
-
return key;
|
|
1066
|
-
}
|
|
1067
|
-
if (specifier.startsWith(`${key}/`)) {
|
|
1068
|
-
const subpath = specifier.slice(key.length + 1);
|
|
1069
|
-
// Only match file/dist subpaths (e.g., solid-js/dist/dev.js → solid-js).
|
|
1070
|
-
// Entry-point subpaths (e.g., solid-js/store) are separate packages
|
|
1071
|
-
// and must NOT be collapsed to the root vendor module.
|
|
1072
|
-
if (isFileDistSubpath(subpath)) {
|
|
1073
|
-
return key;
|
|
1074
|
-
}
|
|
1075
|
-
// Check if there's an explicit alias for this subpath
|
|
1076
|
-
const aliasKey = `${key}/${subpath.split('/')[0]}`;
|
|
1077
|
-
if (aliases?.[aliasKey] && modules[aliases[aliasKey]]) {
|
|
1078
|
-
return aliases[aliasKey];
|
|
1079
|
-
}
|
|
1080
|
-
// Entry-point subpath with no alias — don't vendor-resolve
|
|
1081
|
-
continue;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
return null;
|
|
1085
|
-
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Convert a Vite .vite/deps/ filename back to a bare specifier with subpath,
|
|
1088
|
-
* using the vendor manifest to determine where the package name ends and the
|
|
1089
|
-
* subpath begins.
|
|
1090
|
-
*
|
|
1091
|
-
* e.g., "@nativescript_tanstack-router_solid.js" → "@nativescript/tanstack-router/solid"
|
|
1092
|
-
* "solid-js.js" → "solid-js"
|
|
1093
|
-
*
|
|
1094
|
-
* Returns null if no known package prefix matches.
|
|
1095
|
-
*/
|
|
1096
|
-
function viteDepsPathToBareSpecifier(depPath) {
|
|
1097
|
-
const manifest = getVendorManifest();
|
|
1098
|
-
if (!manifest)
|
|
1099
|
-
return null;
|
|
1100
|
-
const flatId = extractVitePrebundleId(`.vite/deps/${depPath}`);
|
|
1101
|
-
if (!flatId)
|
|
1102
|
-
return null;
|
|
1103
|
-
const flatMap = getFlattenedManifestMap(manifest);
|
|
1104
|
-
// Try exact match first
|
|
1105
|
-
if (flatMap.has(flatId)) {
|
|
1106
|
-
return flatMap.get(flatId);
|
|
1107
|
-
}
|
|
1108
|
-
// Try prefix match: find the longest matching vendor flat key
|
|
1109
|
-
let bestKey = '';
|
|
1110
|
-
let bestCanonical = '';
|
|
1111
|
-
for (const [flatKey, canonical] of flatMap.entries()) {
|
|
1112
|
-
if (flatId.startsWith(`${flatKey}_`) && flatKey.length > bestKey.length) {
|
|
1113
|
-
bestKey = flatKey;
|
|
1114
|
-
bestCanonical = canonical;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (bestKey && bestCanonical) {
|
|
1118
|
-
// Convert the suffix back to a subpath
|
|
1119
|
-
const flatSuffix = flatId.slice(bestKey.length + 1);
|
|
1120
|
-
const subpath = flatSuffix.replace(/_/g, '/');
|
|
1121
|
-
return `${bestCanonical}/${subpath}`;
|
|
1122
|
-
}
|
|
1123
|
-
return null;
|
|
1124
|
-
}
|
|
1125
|
-
function isFileDistSubpath(subpath) {
|
|
1126
|
-
const firstSegment = subpath.split('/')[0];
|
|
1127
|
-
// Starts with a known build/dist directory segment → file path
|
|
1128
|
-
const FILE_DIST_DIRS = new Set(['dist', 'src', 'lib', 'build', 'esm', 'cjs', 'es', 'umd', 'module', 'bundle', 'output', '_esm', '_cjs']);
|
|
1129
|
-
if (FILE_DIST_DIRS.has(firstSegment)) {
|
|
1130
|
-
return true;
|
|
1131
|
-
}
|
|
1132
|
-
// Single segment with file extension → file path (e.g., "index.js")
|
|
1133
|
-
if (!subpath.includes('/') && /\.[a-zA-Z0-9]+$/.test(subpath)) {
|
|
1134
|
-
return true;
|
|
1135
|
-
}
|
|
1136
|
-
return false;
|
|
1137
|
-
}
|
|
1138
|
-
// ── Package exports reverse map ──────────────────────────────────────────────
|
|
1139
|
-
// Resolves Vite's resolved file paths back to original bare specifiers using
|
|
1140
|
-
// the package's own package.json exports field. This eliminates the fragile
|
|
1141
|
-
// heuristic that tried to guess main entry vs. subpath from file paths.
|
|
1142
|
-
const _exportsReverseMapCache = new Map();
|
|
1143
|
-
/**
|
|
1144
|
-
* Resolve the concrete file path from a package.json exports condition value.
|
|
1145
|
-
* Handles nested condition objects: { "esm2022": { "default": "./file.mjs" } }
|
|
1146
|
-
*/
|
|
1147
|
-
function resolveExportConditionValue(conditions) {
|
|
1148
|
-
if (typeof conditions === 'string')
|
|
1149
|
-
return conditions;
|
|
1150
|
-
if (typeof conditions !== 'object' || conditions === null)
|
|
1151
|
-
return null;
|
|
1152
|
-
const obj = conditions;
|
|
1153
|
-
for (const key of ['esm2022', 'esm', 'esm2015', 'import', 'module', 'default']) {
|
|
1154
|
-
if (key in obj) {
|
|
1155
|
-
const result = resolveExportConditionValue(obj[key]);
|
|
1156
|
-
if (result)
|
|
1157
|
-
return result;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
for (const val of Object.values(obj)) {
|
|
1161
|
-
if (typeof val === 'string')
|
|
1162
|
-
return val;
|
|
1163
|
-
}
|
|
1164
|
-
return null;
|
|
1165
|
-
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Build a reverse map from file paths → bare specifiers for a package.
|
|
1168
|
-
*
|
|
1169
|
-
* Example for @angular/common:
|
|
1170
|
-
* "fesm2022/common.mjs" → "@angular/common" (exports["."])
|
|
1171
|
-
* "fesm2022/http.mjs" → "@angular/common/http" (exports["./http"])
|
|
1172
|
-
*
|
|
1173
|
-
* For packages without exports field, uses main/module fields.
|
|
1174
|
-
*/
|
|
1175
|
-
function getExportsReverseMap(pkgName, projectRoot) {
|
|
1176
|
-
const cached = _exportsReverseMapCache.get(pkgName);
|
|
1177
|
-
if (cached)
|
|
1178
|
-
return cached;
|
|
1179
|
-
const map = new Map();
|
|
1180
|
-
try {
|
|
1181
|
-
const pkgJsonPath = path.join(projectRoot, 'node_modules', pkgName, 'package.json');
|
|
1182
|
-
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
1183
|
-
if (pkgJson.exports && typeof pkgJson.exports === 'object') {
|
|
1184
|
-
for (const [entryPoint, conditions] of Object.entries(pkgJson.exports)) {
|
|
1185
|
-
// Skip wildcard patterns (./locales/*, etc.) and non-JS exports
|
|
1186
|
-
if (entryPoint.includes('*') || entryPoint.endsWith('.json'))
|
|
1187
|
-
continue;
|
|
1188
|
-
const resolvedPath = resolveExportConditionValue(conditions);
|
|
1189
|
-
if (resolvedPath && typeof resolvedPath === 'string') {
|
|
1190
|
-
const normalized = resolvedPath.replace(/^\.\//, '');
|
|
1191
|
-
const bareSpec = entryPoint === '.' ? pkgName : `${pkgName}/${entryPoint.replace(/^\.\//, '')}`;
|
|
1192
|
-
map.set(normalized, bareSpec);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
// Fallback: main/module field for packages without exports
|
|
1197
|
-
if (map.size === 0) {
|
|
1198
|
-
for (const field of ['module', 'main']) {
|
|
1199
|
-
const value = pkgJson[field];
|
|
1200
|
-
if (value && typeof value === 'string') {
|
|
1201
|
-
const normalized = value.replace(/^\.\//, '');
|
|
1202
|
-
map.set(normalized, pkgName);
|
|
1203
|
-
// Also store without extension for NativeScript platform resolution
|
|
1204
|
-
const withoutExt = normalized.replace(/\.[^.]+$/, '');
|
|
1205
|
-
if (withoutExt !== normalized) {
|
|
1206
|
-
map.set(withoutExt, pkgName);
|
|
1207
|
-
}
|
|
1208
|
-
break;
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
catch {
|
|
1214
|
-
// Package.json not found or unreadable
|
|
235
|
+
}
|
|
1215
236
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Extract the root package name from a node_modules specifier.
|
|
1221
|
-
* "@angular/common/fesm2022/http.mjs" → "@angular/common"
|
|
1222
|
-
* "tslib/tslib.es6.mjs" → "tslib"
|
|
1223
|
-
*/
|
|
1224
|
-
function extractRootPackageName(spec) {
|
|
1225
|
-
if (spec.startsWith('@')) {
|
|
1226
|
-
const parts = spec.split('/');
|
|
1227
|
-
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : spec;
|
|
237
|
+
if (!normalized) {
|
|
238
|
+
return '';
|
|
1228
239
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
*
|
|
1235
|
-
* Returns:
|
|
1236
|
-
* { route: 'vendor', bareSpec: string } — use vendor bridge with this bare specifier
|
|
1237
|
-
* { route: 'http' } — serve via HTTP
|
|
1238
|
-
* null — not a vendor package
|
|
1239
|
-
*/
|
|
1240
|
-
function resolveVendorRouting(nodeModulesSpec, projectRoot) {
|
|
1241
|
-
const pkgName = extractRootPackageName(nodeModulesSpec);
|
|
1242
|
-
const subpath = nodeModulesSpec.slice(pkgName.length).replace(/^\//, '');
|
|
1243
|
-
const pkgBaseName = pkgName.split('/').pop() || '';
|
|
1244
|
-
const isRootLevelMainEntry = (() => {
|
|
1245
|
-
if (!subpath || subpath.includes('/')) {
|
|
1246
|
-
return false;
|
|
1247
|
-
}
|
|
1248
|
-
const withoutExt = subpath.replace(/\.[^.]+$/, '');
|
|
1249
|
-
const withoutPlatform = withoutExt.replace(/\.(ios|android|visionos)$/i, '');
|
|
1250
|
-
return withoutPlatform === 'index' || withoutPlatform === pkgBaseName;
|
|
1251
|
-
})();
|
|
1252
|
-
// Runtime NativeScript plugins must preserve require()-style loading even when
|
|
1253
|
-
// they are not part of the vendor manifest. Many community packages rely on
|
|
1254
|
-
// singleton side effects or tolerate CommonJS circular initialization that
|
|
1255
|
-
// would fail under plain HTTP ESM evaluation.
|
|
1256
|
-
if (isLikelyNativeScriptRuntimePluginSpecifier(pkgName) && (!subpath || isRootLevelMainEntry)) {
|
|
1257
|
-
return { route: 'vendor', bareSpec: pkgName };
|
|
1258
|
-
}
|
|
1259
|
-
// Check if this package is in the vendor manifest
|
|
1260
|
-
const manifest = getVendorManifest();
|
|
1261
|
-
if (!manifest?.modules?.[pkgName]) {
|
|
1262
|
-
return null;
|
|
240
|
+
let packageSpecifier = normalized;
|
|
241
|
+
let via = 'node_modules';
|
|
242
|
+
if (normalized.startsWith('.vite/deps/')) {
|
|
243
|
+
via = 'vite-deps';
|
|
244
|
+
packageSpecifier = viteDepsPathToBareSpecifier(normalized.slice('.vite/deps/'.length)) || normalized;
|
|
1263
245
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
// ensureNativeScriptModuleBindings, so they do not rely on ns-vendor:// ESM
|
|
1268
|
-
// exports directly.
|
|
1269
|
-
if (/\.(ios|android|visionos)\.(js|ts|mjs|mts)$/i.test(nodeModulesSpec) && isLikelyNativeScriptRuntimePluginSpecifier(pkgName) && isRootLevelMainEntry) {
|
|
1270
|
-
return { route: 'vendor', bareSpec: pkgName };
|
|
246
|
+
const rootPackage = resolveNodeModulesPackageBoundary(packageSpecifier, getProjectRootPath()).packageName;
|
|
247
|
+
if (!rootPackage) {
|
|
248
|
+
return '';
|
|
1271
249
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
250
|
+
return `try { const __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(${JSON.stringify(rootPackage)}, ${JSON.stringify({ kind: 'http-esm', specifier: packageSpecifier, url: sourceId, via })}); } } catch {}\n`;
|
|
251
|
+
}
|
|
252
|
+
// Guard any bare dynamic import(spec) occurring in assembled module code.
|
|
253
|
+
// We cannot override native dynamic import globally; for SFC assembler outputs we inline
|
|
254
|
+
// a tiny helper and rewrite "import(" to "__nsDynImport(" to prevent anomalous specs like '@'.
|
|
255
|
+
function guardBareDynamicImports(code) {
|
|
256
|
+
try {
|
|
257
|
+
if (!code || typeof code !== 'string')
|
|
258
|
+
return code;
|
|
259
|
+
const NEEDLE = /(^|\n)\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*/;
|
|
260
|
+
const hasImportCall = /\bimport\s*\(/.test(code);
|
|
261
|
+
if (!hasImportCall)
|
|
262
|
+
return code;
|
|
263
|
+
const helper = "const __nsDynImport = (spec) => { try { if (!spec || spec === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {} try { return import(spec); } catch (e) { return Promise.reject(e); } };\n";
|
|
264
|
+
// Avoid double injection
|
|
265
|
+
const inject = code.includes('const __nsDynImport =') ? '' : helper;
|
|
266
|
+
// Replace bare import( ... ) that are not part of 'import.meta' or type-only contexts
|
|
267
|
+
// Heuristic: replace 'import(' occurrences; skip 'import.meta'
|
|
268
|
+
const rewritten = code.replace(/\bimport\s*\(/g, '__nsDynImport(');
|
|
269
|
+
if (rewritten === code && !inject)
|
|
270
|
+
return code;
|
|
271
|
+
return inject + rewritten;
|
|
1275
272
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
return { route: 'vendor', bareSpec: pkgName };
|
|
1279
|
-
}
|
|
1280
|
-
// Use exports reverse map to detect subpath entries — these MUST go to HTTP
|
|
1281
|
-
// to avoid collapsing e.g. @angular/common/http → @angular/common
|
|
1282
|
-
const reverseMap = getExportsReverseMap(pkgName, projectRoot);
|
|
1283
|
-
const originalSpec = reverseMap.get(subpath);
|
|
1284
|
-
if (originalSpec && originalSpec !== pkgName) {
|
|
1285
|
-
// Subpath entry (e.g., fesm2022/http.mjs → @angular/common/http) → HTTP
|
|
1286
|
-
return { route: 'http' };
|
|
1287
|
-
}
|
|
1288
|
-
// For vendor routing, only use the vendor bridge for root-level main entries
|
|
1289
|
-
// (single-segment paths like "index.js", "tslib.es6.mjs"). Multi-segment
|
|
1290
|
-
// build output paths (fesm2022/core.mjs, dist/index.js) go to HTTP even if
|
|
1291
|
-
// they ARE the main entry — the ns-vendor:// protocol in HMR mode does not
|
|
1292
|
-
// reliably serve all named ES exports for complex packages.
|
|
1293
|
-
if (!subpath.includes('/')) {
|
|
1294
|
-
const lastSegment = subpath.replace(/\.[^.]+$/, '');
|
|
1295
|
-
if (lastSegment === 'index' || lastSegment === pkgBaseName || lastSegment.startsWith(pkgBaseName + '.')) {
|
|
1296
|
-
return { route: 'vendor', bareSpec: pkgName };
|
|
1297
|
-
}
|
|
273
|
+
catch {
|
|
274
|
+
return code;
|
|
1298
275
|
}
|
|
1299
|
-
// Default: HTTP — safe for all module types and preserves all named exports
|
|
1300
|
-
return { route: 'http' };
|
|
1301
276
|
}
|
|
1302
277
|
function stripCoreGlobalsImports(code) {
|
|
1303
278
|
const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
|
|
@@ -1326,18 +301,14 @@ function ensureVariableDynamicImportHelper(code) {
|
|
|
1326
301
|
`};\n`;
|
|
1327
302
|
return `${helper}${code}`;
|
|
1328
303
|
}
|
|
1329
|
-
// Final safety net for plain dynamic import(expressions) that might slip through
|
|
1330
|
-
// Vite's helper transformation. We rewrite occurrences of `import(` to `__ns_import(`
|
|
1331
|
-
// and inject a small wrapper that maps the anomalous request '@' to a harmless stub.
|
|
1332
304
|
function ensureGuardPlainDynamicImports(code, origin) {
|
|
1333
305
|
try {
|
|
1334
306
|
if (!code || !/\bimport\s*\(/.test(code))
|
|
1335
307
|
return code;
|
|
1336
|
-
const
|
|
1337
|
-
// Replace only when `import(` is not part of an identifier or property (no preceding "." or word char)
|
|
308
|
+
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`;
|
|
1338
309
|
const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
|
|
1339
310
|
if (replaced !== code) {
|
|
1340
|
-
return
|
|
311
|
+
return wrapper + replaced;
|
|
1341
312
|
}
|
|
1342
313
|
return code;
|
|
1343
314
|
}
|
|
@@ -1345,26 +316,49 @@ function ensureGuardPlainDynamicImports(code, origin) {
|
|
|
1345
316
|
return code;
|
|
1346
317
|
}
|
|
1347
318
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
319
|
+
function ensureDynamicHmrImportHelper(code) {
|
|
320
|
+
try {
|
|
321
|
+
if (!code.includes('__nsDynamicHmrImport('))
|
|
322
|
+
return code;
|
|
323
|
+
if (code.includes('const __nsDynamicHmrImport ='))
|
|
324
|
+
return code;
|
|
325
|
+
const helper = 'const __nsDynamicHmrImport = (spec) => {\n' +
|
|
326
|
+
" const __nsm = '/ns' + '/m';\n" +
|
|
327
|
+
" const __nsBootPrefix = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' && import.meta.url.includes('/__ns_boot__/b1/') ? '/__ns_boot__/b1' : '';\n" +
|
|
328
|
+
" const __nsImporterTagMatch = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' ? import.meta.url.match(/\\/__ns_hmr__\\/([^/]+)\\//) : null;\n" +
|
|
329
|
+
" const __nsImporterTag = __nsImporterTagMatch && __nsImporterTagMatch[1] ? decodeURIComponent(__nsImporterTagMatch[1]) : '';\n" +
|
|
330
|
+
" try { if (!spec || spec === '@') { return import(new URL(__nsm + '/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n" +
|
|
331
|
+
' try {\n' +
|
|
332
|
+
" if (typeof spec === 'string' && spec.startsWith(__nsm + '/')) {\n" +
|
|
333
|
+
' const g = globalThis;\n' +
|
|
334
|
+
" const graphVersion = typeof g.__NS_HMR_GRAPH_VERSION__ === 'number' ? g.__NS_HMR_GRAPH_VERSION__ : 0;\n" +
|
|
335
|
+
" const nonce = typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0;\n" +
|
|
336
|
+
" const __nsActiveBootPrefix = graphVersion || nonce ? '' : __nsBootPrefix;\n" +
|
|
337
|
+
" if (spec.includes('/__ns_hmr__/')) {\n" +
|
|
338
|
+
" const __preservedSpec = !nonce && __nsBootPrefix && spec.startsWith(__nsm + '/__ns_hmr__/') && !spec.includes('/node_modules/') ? __nsm + __nsBootPrefix + spec.slice(__nsm.length) : spec;\n" +
|
|
339
|
+
' return import(new URL(__preservedSpec, import.meta.url).href);\n' +
|
|
340
|
+
' }\n' +
|
|
341
|
+
" if (spec.startsWith(__nsm + '/node_modules/')) { return import(new URL(spec, import.meta.url).href); }\n" +
|
|
342
|
+
" const tag = nonce ? `n${nonce}` : (graphVersion ? `v${graphVersion}` : (__nsImporterTag || 'live'));\n" +
|
|
343
|
+
" const nextPath = __nsm + __nsActiveBootPrefix + '/__ns_hmr__/' + encodeURIComponent(tag) + spec.slice(__nsm.length);\n" +
|
|
344
|
+
" const origin = typeof g.__NS_HTTP_ORIGIN__ === 'string' && /^https?:\\/\\//.test(g.__NS_HTTP_ORIGIN__) ? g.__NS_HTTP_ORIGIN__ : '';\n" +
|
|
345
|
+
' return import(origin ? origin + nextPath : new URL(nextPath, import.meta.url).href);\n' +
|
|
346
|
+
' }\n' +
|
|
347
|
+
' } catch {}\n' +
|
|
348
|
+
' return import(spec);\n' +
|
|
349
|
+
'};\n';
|
|
350
|
+
return helper + code;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return code;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
1361
356
|
async function expandStarExports(code, server, projectRoot, verbose) {
|
|
1362
357
|
const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
|
|
1363
358
|
let match;
|
|
1364
359
|
const replacements = [];
|
|
1365
360
|
while ((match = STAR_RE.exec(code)) !== null) {
|
|
1366
361
|
const url = match[2];
|
|
1367
|
-
// Only expand node_modules star exports served over HTTP
|
|
1368
362
|
if (!url.includes('/node_modules/'))
|
|
1369
363
|
continue;
|
|
1370
364
|
replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
|
|
@@ -1373,88 +367,33 @@ async function expandStarExports(code, server, projectRoot, verbose) {
|
|
|
1373
367
|
return code;
|
|
1374
368
|
for (const rep of replacements) {
|
|
1375
369
|
try {
|
|
1376
|
-
// Strip HTTP origin to get a Vite-resolvable path
|
|
1377
370
|
let vitePath = rep.url.replace(/^https?:\/\/[^/]+/, '');
|
|
1378
|
-
// Strip /ns/m/ prefix to get the node_modules path
|
|
1379
371
|
vitePath = vitePath.replace(/^\/ns\/m\//, '/');
|
|
1380
|
-
// Strip boot-path prefix used during initial HTTP boot progress tracking.
|
|
1381
372
|
vitePath = vitePath.replace(/^\/__ns_boot__\/[^/]+/, '');
|
|
1382
|
-
// Strip HMR cache-busting path segments
|
|
1383
373
|
vitePath = vitePath.replace(/\/__ns_hmr__\/[^/]+/, '');
|
|
1384
|
-
const
|
|
1385
|
-
if (!
|
|
374
|
+
const result = await server.transformRequest(vitePath);
|
|
375
|
+
if (!result?.code)
|
|
1386
376
|
continue;
|
|
1387
|
-
const names = extractExportedNames(
|
|
377
|
+
const names = extractExportedNames(result.code);
|
|
1388
378
|
if (!names.length)
|
|
1389
379
|
continue;
|
|
1390
|
-
// Replace `export * from "url"` with explicit named re-exports
|
|
1391
380
|
const explicit = `export { ${names.join(', ')} } from ${JSON.stringify(rep.url)};`;
|
|
1392
381
|
code = code.replace(rep.full, explicit);
|
|
1393
382
|
if (verbose) {
|
|
1394
|
-
console.log(`[ns/m] expanded export*
|
|
383
|
+
console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
|
|
1395
384
|
}
|
|
1396
385
|
}
|
|
1397
386
|
catch { }
|
|
1398
387
|
}
|
|
1399
388
|
return code;
|
|
1400
389
|
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Extract named export identifiers from a module's source code.
|
|
1403
|
-
* Handles: export function/class/const/let/var NAME, export { NAME },
|
|
1404
|
-
* export { NAME as ALIAS }, and export * from (recursive marker).
|
|
1405
|
-
* Does NOT follow `export * from` chains — only direct exports.
|
|
1406
|
-
*/
|
|
1407
390
|
function extractExportedNames(code) {
|
|
1408
|
-
|
|
1409
|
-
// export function NAME / export class NAME / export async function NAME
|
|
1410
|
-
const declRe = /\bexport\s+(?:async\s+)?(?:function|class)\s+([A-Za-z_$][\w$]*)/g;
|
|
1411
|
-
let m;
|
|
1412
|
-
while ((m = declRe.exec(code)) !== null) {
|
|
1413
|
-
names.add(m[1]);
|
|
1414
|
-
}
|
|
1415
|
-
// export const/let/var NAME (handles destructuring and multiple declarators)
|
|
1416
|
-
const varRe = /\bexport\s+(?:const|let|var)\s+([^=;{]+)/g;
|
|
1417
|
-
while ((m = varRe.exec(code)) !== null) {
|
|
1418
|
-
// Simple case: `export const foo = ...`
|
|
1419
|
-
const decl = m[1].trim();
|
|
1420
|
-
// Could be `{ a, b }` (destructuring) or `foo, bar` (multiple) or just `foo`
|
|
1421
|
-
if (decl.startsWith('{')) {
|
|
1422
|
-
const inner = decl.replace(/^\{|\}$/g, '');
|
|
1423
|
-
for (const part of inner.split(',')) {
|
|
1424
|
-
const name = part.split(':')[0].trim(); // handle { orig: alias }
|
|
1425
|
-
if (/^[A-Za-z_$][\w$]*$/.test(name))
|
|
1426
|
-
names.add(name);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
else {
|
|
1430
|
-
const name = decl.split(/[\s,=]/)[0].trim();
|
|
1431
|
-
if (/^[A-Za-z_$][\w$]*$/.test(name))
|
|
1432
|
-
names.add(name);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
// export { NAME, NAME as ALIAS, ... } (without `from`)
|
|
1436
|
-
// and export { NAME, ... } from "..." (re-exports)
|
|
1437
|
-
const braceRe = /\bexport\s*\{([^}]+)\}/g;
|
|
1438
|
-
while ((m = braceRe.exec(code)) !== null) {
|
|
1439
|
-
for (const part of m[1].split(',')) {
|
|
1440
|
-
const trimmed = part.trim();
|
|
1441
|
-
// `name as alias` → use alias; `name` → use name
|
|
1442
|
-
const asMatch = trimmed.match(/\S+\s+as\s+(\S+)/);
|
|
1443
|
-
const name = asMatch ? asMatch[1] : trimmed.split(/\s/)[0];
|
|
1444
|
-
if (name && /^[A-Za-z_$][\w$]*$/.test(name) && name !== 'default') {
|
|
1445
|
-
names.add(name);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
return Array.from(names);
|
|
391
|
+
return extractDirectExportedNames(code);
|
|
1450
392
|
}
|
|
1451
|
-
// Heal accidental "import ... = expr" assignments produced by upstream transforms.
|
|
1452
|
-
// These are invalid JS; convert to equivalent const assignments.
|
|
1453
393
|
function repairImportEqualsAssignments(code) {
|
|
1454
394
|
try {
|
|
1455
395
|
if (!code || typeof code !== 'string')
|
|
1456
396
|
return code;
|
|
1457
|
-
// import { a, b as c } = expr; -> const { a, b: c } = expr;
|
|
1458
397
|
code = code.replace(/(^|\n)\s*import\s*\{([^}]+)\}\s*=\s*([^;]+);?/g, (_m, p1, specList, rhs) => {
|
|
1459
398
|
const cleaned = String(specList)
|
|
1460
399
|
.split(',')
|
|
@@ -1464,61 +403,28 @@ function repairImportEqualsAssignments(code) {
|
|
|
1464
403
|
.join(', ');
|
|
1465
404
|
return `${p1}const { ${cleaned} } = ${rhs};`;
|
|
1466
405
|
});
|
|
1467
|
-
|
|
1468
|
-
code = code.replace(/(^|\n)\s*import\s
|
|
1469
|
-
return `${p1}const ${ns} = (${rhs});`;
|
|
1470
|
-
});
|
|
1471
|
-
// import name = expr; -> const name = expr;
|
|
1472
|
-
code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => {
|
|
1473
|
-
return `${p1}const ${id} = ${rhs};`;
|
|
1474
|
-
});
|
|
406
|
+
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});`);
|
|
407
|
+
code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => `${p1}const ${id} = ${rhs};`);
|
|
1475
408
|
}
|
|
1476
409
|
catch { }
|
|
1477
410
|
return code;
|
|
1478
411
|
}
|
|
1479
|
-
// Ensure imports of the NativeScript-Vue runtime bridge '/ns/rt' are versioned to
|
|
1480
|
-
// bust the device HTTP loader cache whenever the HMR graph version increments.
|
|
1481
412
|
function ensureVersionedRtImports(code, origin, ver) {
|
|
1482
413
|
if (!code || !origin || !Number.isFinite(ver))
|
|
1483
414
|
return code;
|
|
1484
|
-
// Static imports: import { ... } from ".../ns/rt" (plus optional version)
|
|
1485
415
|
code = code.replace(/(from\s+["'])(?:https?:\/\/[^"']+)?\/(?:\ns|ns)\/rt(?:\/[\d]+)?(["'])/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
|
|
1486
|
-
// Dynamic imports: import(".../ns/rt") (plus optional version)
|
|
1487
416
|
code = code.replace(/(import\(\s*["'])(?:https?:\/\/[^"']+)?\/(?:\@ns|ns)\/rt(?:\/[\d]+)?(["']\s*\))/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
|
|
1488
417
|
return code;
|
|
1489
418
|
}
|
|
1490
|
-
// Ensure imports of @nativescript/core resolve via the unified /ns/core bridge to keep a single realm
|
|
1491
|
-
function ensureVersionedCoreImports(code, origin, ver) {
|
|
1492
|
-
try {
|
|
1493
|
-
// Static imports already handled in rewriteImports; just ensure absolute origin prefix and version
|
|
1494
|
-
code = code.replace(/(["'])\/ns\/core(\?p=[^"']+)?\1/g, (_m, q, qp) => `${q}/ns/core/${ver}${qp || ''}${q}`);
|
|
1495
|
-
// Dynamic imports already handled in rewriteImports; ensure origin and version
|
|
1496
|
-
code = code.replace(/import\(\s*(["'])\/ns\/core(\?p=[^"']+)?\1\s*\)/g, (_m, q, qp) => `import(${q}/ns/core/${ver}${qp || ''}${q})`);
|
|
1497
|
-
}
|
|
1498
|
-
catch { }
|
|
1499
|
-
return code;
|
|
1500
|
-
}
|
|
1501
|
-
export function buildVersionedCoreSubpathAliasModule(sub, ver) {
|
|
1502
|
-
const normalizedSub = (sub || '').replace(/^\/+/, '');
|
|
1503
|
-
const canonicalUrl = `/ns/core/${ver}?p=${normalizedSub}`;
|
|
1504
|
-
return `import * as __ns_core_alias from ${JSON.stringify(canonicalUrl)};\n` + `export default (__ns_core_alias.default || __ns_core_alias);\n` + `export * from ${JSON.stringify(canonicalUrl)};\n`;
|
|
1505
|
-
}
|
|
1506
|
-
// Hardened removal of Vite's virtual dynamic-import-helper. Some variants (side-effect only
|
|
1507
|
-
// or minified forms) slipped past earlier regexes causing runtime attempts to resolve
|
|
1508
|
-
// /@id/__x00__vite/dynamic-import-helper.js which does not exist in the device mirror.
|
|
1509
|
-
// We aggressively strip any reference and inline a helper if necessary.
|
|
1510
419
|
function stripViteDynamicImportVirtual(code) {
|
|
1511
420
|
if (!/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
1512
421
|
return code;
|
|
1513
422
|
}
|
|
1514
423
|
const original = code;
|
|
1515
|
-
// Remove any import lines referencing the virtual helper (with or without bindings)
|
|
1516
424
|
code = code.replace(/^[\t ]*import[^\n]*\/@id\/__x00__vite\/dynamic-import-helper[^\n]*$/gm, '');
|
|
1517
|
-
// If any raw spec strings remain (e.g. concatenated), neutralize them
|
|
1518
425
|
if (/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
1519
426
|
code = code.replace(/\/@id\/__x00__vite\/dynamic-import-helper[^"'`)]*/g, '/__NS_UNUSED_DYNAMIC_IMPORT_HELPER__');
|
|
1520
427
|
}
|
|
1521
|
-
// Ensure helper present
|
|
1522
428
|
if (!/__variableDynamicImportRuntimeHelper/.test(code)) {
|
|
1523
429
|
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`;
|
|
1524
430
|
code = inline + code;
|
|
@@ -1528,17 +434,7 @@ function stripViteDynamicImportVirtual(code) {
|
|
|
1528
434
|
}
|
|
1529
435
|
return code;
|
|
1530
436
|
}
|
|
1531
|
-
// Small snippet injected into device-delivered modules to capture any require('http(s)://') calls
|
|
1532
437
|
const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);try{console.error(e.message+'\\n'+(e.stack||''));}catch(e2){}try{g.__NS_REQUIRE_GUARD_LAST__={spec:s,stack:e.stack,label:l,ts:Date.now()};}catch(e3){}}}catch(e1){}return o.apply(this, arguments);};};if(typeof g.require==='function'&&!g.require.__NS_REQ_GUARDED__){var o1=g.require;g.require=mk(o1,'require');g.require.__NS_REQ_GUARDED__=true;}if(typeof g.__nsRequire==='function'&&!g.__nsRequire.__NS_REQ_GUARDED__){var o2=g.__nsRequire;g.__nsRequire=mk(o2,'__nsRequire');g.__nsRequire.__NS_REQ_GUARDED__=true;}g.__NS_REQUIRE_GUARD_INSTALLED__=true;}}catch(e){}})();\n`;
|
|
1533
|
-
// ============================================================================
|
|
1534
|
-
// HELPER FUNCTIONS
|
|
1535
|
-
// ============================================================================
|
|
1536
|
-
// Origin invariant: we own both client and server. All URLs must use an explicit
|
|
1537
|
-
// http(s)://host:port origin with no trailing slash. Build it deterministically
|
|
1538
|
-
// where needed; do not post-sanitize.
|
|
1539
|
-
/**
|
|
1540
|
-
* Check if an import spec should be remapped to dep-*.mjs
|
|
1541
|
-
*/
|
|
1542
438
|
function shouldRemapImport(spec) {
|
|
1543
439
|
if (!spec || typeof spec !== 'string')
|
|
1544
440
|
return false;
|
|
@@ -1559,12 +455,9 @@ function shouldRemapImport(spec) {
|
|
|
1559
455
|
}
|
|
1560
456
|
return true;
|
|
1561
457
|
}
|
|
1562
|
-
// (legacy wrapSfcWithStableDefault removed; full SFCs now delegate to /ns/asm)
|
|
1563
458
|
function removeNamedImports(code, names) {
|
|
1564
459
|
const regex = /^(\s*import\s*\{)([^}]*)(\}\s*from\s*['"][^'"]+['"];?)/gm;
|
|
1565
460
|
return code.replace(regex, (_m, p1, specList, p3) => {
|
|
1566
|
-
// Only strip for known globalized framework sources (Vue/Nativescript-Vue).
|
|
1567
|
-
// Keep imports from all other packages (Pinia, third-party libs, app modules) intact.
|
|
1568
461
|
const srcMatch = /from\s*['"]\s*([^'"\s]+)\s*['"]/i.exec(_m);
|
|
1569
462
|
const src = (srcMatch?.[1] || '').toLowerCase();
|
|
1570
463
|
const isVueSource = /^(?:vue|nativescript-vue)(?:\b|\/)/i.test(src);
|
|
@@ -1663,7 +556,7 @@ function normalizeImportPath(spec, importerDir) {
|
|
|
1663
556
|
else if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
1664
557
|
key = path.posix.normalize(path.posix.join(importerDir, spec));
|
|
1665
558
|
if (!key.startsWith('/')) {
|
|
1666
|
-
key =
|
|
559
|
+
key = `/${key}`;
|
|
1667
560
|
}
|
|
1668
561
|
}
|
|
1669
562
|
else {
|
|
@@ -1730,6 +623,70 @@ function findDependencyFileName(depFileMap, key) {
|
|
|
1730
623
|
}
|
|
1731
624
|
return undefined;
|
|
1732
625
|
}
|
|
626
|
+
function isRuntimePluginRootEntrySpecifier(specifier, projectRoot) {
|
|
627
|
+
if (!specifier) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
const cleaned = specifier.replace(PAT.QUERY_PATTERN, '');
|
|
631
|
+
const normalized = normalizeNodeModulesSpecifier(cleaned) || cleaned.replace(/^\/+/, '');
|
|
632
|
+
if (!normalized) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
const { packageName, subpath } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
636
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (!subpath) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (subpath.includes('/')) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const pkgBaseName = packageName.split('/').pop() || '';
|
|
646
|
+
const withoutExt = /(?:\.(?:ios|android|visionos))?\.(?:ts|tsx|js|jsx|mjs|mts|cts)$/i.test(subpath) ? subpath.replace(/\.[^.]+$/, '') : subpath;
|
|
647
|
+
const withoutPlatform = withoutExt.replace(/\.(ios|android|visionos)$/i, '');
|
|
648
|
+
return withoutPlatform === 'index' || withoutPlatform === pkgBaseName;
|
|
649
|
+
}
|
|
650
|
+
function collectMixedRuntimePluginHttpRootPackages(code, projectRoot) {
|
|
651
|
+
const nonRootSubpathPackages = new Set();
|
|
652
|
+
const rootEntryPackages = new Set();
|
|
653
|
+
const visitSpecifier = (rawSpecifier) => {
|
|
654
|
+
if (!rawSpecifier) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const specifier = normalizeNativeScriptCoreSpecifier(rawSpecifier).replace(PAT.QUERY_PATTERN, '');
|
|
658
|
+
if (!specifier) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (/^https?:\/\//.test(specifier) || specifier.startsWith('/ns/')) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (/^(?:\.|\/)/.test(specifier) && !specifier.includes('/node_modules/')) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const normalized = normalizeNodeModulesSpecifier(specifier) || specifier.replace(/^\/+/, '');
|
|
668
|
+
if (!normalized) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
672
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (isRuntimePluginRootEntrySpecifier(normalized, projectRoot)) {
|
|
676
|
+
rootEntryPackages.add(packageName);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
nonRootSubpathPackages.add(packageName);
|
|
680
|
+
};
|
|
681
|
+
for (const pattern of [PAT.IMPORT_PATTERN_1, PAT.IMPORT_PATTERN_2, PAT.IMPORT_PATTERN_3, PAT.IMPORT_PATTERN_SIDE_EFFECT]) {
|
|
682
|
+
pattern.lastIndex = 0;
|
|
683
|
+
let match;
|
|
684
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
685
|
+
visitSpecifier(match[2]);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return new Set(Array.from(nonRootSubpathPackages).filter((packageName) => rootEntryPackages.has(packageName)));
|
|
689
|
+
}
|
|
1733
690
|
function collectImportDependencies(code, importerPath) {
|
|
1734
691
|
const importerDir = path.posix.dirname(importerPath);
|
|
1735
692
|
const deps = new Set();
|
|
@@ -1928,6 +885,59 @@ function toAppModuleBaseId(importPath, projectRoot) {
|
|
|
1928
885
|
const base = projectRelative.replace(/\.mjs$/i, '');
|
|
1929
886
|
return `/${base}`;
|
|
1930
887
|
}
|
|
888
|
+
function toNodeModulesHttpModuleId(importPath) {
|
|
889
|
+
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(importPath);
|
|
890
|
+
if (!nodeModulesSpecifier) {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
return `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
894
|
+
}
|
|
895
|
+
export function rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest) {
|
|
896
|
+
const toHmrServeTag = (value) => {
|
|
897
|
+
const raw = String(value ?? '').trim();
|
|
898
|
+
if (!raw) {
|
|
899
|
+
return 'v0';
|
|
900
|
+
}
|
|
901
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
902
|
+
return raw;
|
|
903
|
+
}
|
|
904
|
+
if (/^\d+$/.test(raw)) {
|
|
905
|
+
return `v${raw}`;
|
|
906
|
+
}
|
|
907
|
+
return raw;
|
|
908
|
+
};
|
|
909
|
+
if (!p || !p.startsWith('/ns/m/')) {
|
|
910
|
+
return p;
|
|
911
|
+
}
|
|
912
|
+
const canonicalNodeModulesPath = p.replace(/^\/ns\/m\/__ns_boot__\/b1\/__ns_hmr__\/[^/]+\/node_modules\//, '/ns/m/node_modules/').replace(/^\/ns\/m\/__ns_hmr__\/[^/]+\/node_modules\//, '/ns/m/node_modules/');
|
|
913
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/node_modules/')) {
|
|
914
|
+
return canonicalNodeModulesPath;
|
|
915
|
+
}
|
|
916
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_boot__/')) {
|
|
917
|
+
return canonicalNodeModulesPath;
|
|
918
|
+
}
|
|
919
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_hmr__/')) {
|
|
920
|
+
return bootTaggedRequest ? `/ns/m/__ns_boot__/b1${canonicalNodeModulesPath.slice('/ns/m'.length)}` : canonicalNodeModulesPath;
|
|
921
|
+
}
|
|
922
|
+
const tag = toHmrServeTag(ver);
|
|
923
|
+
const hmrPrefix = `/ns/m/__ns_hmr__/${tag}`;
|
|
924
|
+
const bootHmrPrefix = `/ns/m/__ns_boot__/b1/__ns_hmr__/${tag}`;
|
|
925
|
+
return (bootTaggedRequest ? bootHmrPrefix : hmrPrefix) + canonicalNodeModulesPath.slice('/ns/m'.length);
|
|
926
|
+
}
|
|
927
|
+
function getNumericServeVersionTag(tag, fallback) {
|
|
928
|
+
const raw = String(tag || '').trim();
|
|
929
|
+
if (!raw) {
|
|
930
|
+
return fallback;
|
|
931
|
+
}
|
|
932
|
+
const versionMatch = raw.match(/^v(\d+)$/);
|
|
933
|
+
if (versionMatch?.[1]) {
|
|
934
|
+
return Number(versionMatch[1]);
|
|
935
|
+
}
|
|
936
|
+
if (/^\d+$/.test(raw)) {
|
|
937
|
+
return Number(raw);
|
|
938
|
+
}
|
|
939
|
+
return fallback;
|
|
940
|
+
}
|
|
1931
941
|
function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
1932
942
|
if (!spec || typeof spec !== 'string') {
|
|
1933
943
|
return null;
|
|
@@ -2109,8 +1119,13 @@ export function wrapCommonJsModuleForDevice(code) {
|
|
|
2109
1119
|
/**
|
|
2110
1120
|
* Process code for device: inject globals, remove framework imports
|
|
2111
1121
|
*/
|
|
2112
|
-
function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = false, isNodeModule = false, sourceId) {
|
|
1122
|
+
function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = false, isNodeModule = false, sourceId, options) {
|
|
2113
1123
|
let result = code;
|
|
1124
|
+
const resolvedSpecifierOverrides = options?.resolvedSpecifierOverrides || getProcessCodeResolvedSpecifierOverrides(sourceId, getProjectRootPath());
|
|
1125
|
+
const bindingOptions = {
|
|
1126
|
+
preserveNonPluginVendorImports: preserveVendorImports,
|
|
1127
|
+
resolvedSpecifierOverrides,
|
|
1128
|
+
};
|
|
2114
1129
|
// Ensure Angular partial declarations are linked before any sanitizers run so runtime never hits the JIT path.
|
|
2115
1130
|
result = linkAngularPartialsIfNeeded(result);
|
|
2116
1131
|
// Post-linker: deduplicate/resolve imports the Angular linker injected with bare specifiers
|
|
@@ -2250,7 +1265,7 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2250
1265
|
// __nsVendorRequire + __nsPick rewrite. Vendor imports stay as bare
|
|
2251
1266
|
// specifiers so the device-side import map resolves them via V8's native
|
|
2252
1267
|
// module system, which correctly handles export * re-exports.
|
|
2253
|
-
result =
|
|
1268
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
2254
1269
|
// Repair any accidental "import ... = expr" assignments that may have slipped in.
|
|
2255
1270
|
try {
|
|
2256
1271
|
result = repairImportEqualsAssignments(result);
|
|
@@ -2346,7 +1361,7 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2346
1361
|
}
|
|
2347
1362
|
// Ensure vendor bindings also apply after potential wrapper injections above
|
|
2348
1363
|
// (idempotent: second pass will be a no-op if imports already consumed).
|
|
2349
|
-
result =
|
|
1364
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
2350
1365
|
try {
|
|
2351
1366
|
result = repairImportEqualsAssignments(result);
|
|
2352
1367
|
}
|
|
@@ -2387,17 +1402,17 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2387
1402
|
result = normalizeStrayCoreStringLiterals(result);
|
|
2388
1403
|
}
|
|
2389
1404
|
catch { }
|
|
1405
|
+
try {
|
|
1406
|
+
result = fixDanglingCoreFrom(result);
|
|
1407
|
+
}
|
|
1408
|
+
catch { }
|
|
1409
|
+
try {
|
|
1410
|
+
result = normalizeAnyCoreSpecToBridge(result);
|
|
1411
|
+
}
|
|
1412
|
+
catch { }
|
|
2390
1413
|
result = ensureVariableDynamicImportHelper(result);
|
|
2391
1414
|
// Normalize any lingering @nativescript/core imports to the /ns/core bridge (non-destructive best-effort)
|
|
2392
1415
|
try {
|
|
2393
|
-
result = result.replace(/from\s+["']@nativescript\/core([^"'\n]*)["']/g, (_m, sub) => {
|
|
2394
|
-
const qp = (sub || '').trim().replace(/^\//, '');
|
|
2395
|
-
return `from "/ns/core${qp ? `?p=${qp}` : ''}"`;
|
|
2396
|
-
});
|
|
2397
|
-
result = result.replace(/import\(\s*["']@nativescript\/core([^"'\n]*)["']\s*\)/g, (_m, sub) => {
|
|
2398
|
-
const qp = (sub || '').trim().replace(/^\//, '');
|
|
2399
|
-
return `import("/ns/core${qp ? `?p=${qp}` : ''}")`;
|
|
2400
|
-
});
|
|
2401
1416
|
// Rewrite named imports from the /ns/core bridge into default import + destructuring.
|
|
2402
1417
|
// This makes `import { Frame } from '@nativescript/core'` work even if the bridge provides only a default export.
|
|
2403
1418
|
{
|
|
@@ -2635,6 +1650,9 @@ function assertNoOptimizedArtifacts(code, contextLabel) {
|
|
|
2635
1650
|
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
2636
1651
|
continue;
|
|
2637
1652
|
}
|
|
1653
|
+
if (shouldAllowLocalCoreSanitizerPaths(contextLabel)) {
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
2638
1656
|
offenders.push(`${i + 1}: ${ln.substring(0, 200)} [local-core-path]`);
|
|
2639
1657
|
}
|
|
2640
1658
|
if (offenders.length >= 10)
|
|
@@ -2799,6 +1817,7 @@ function dedupeRtNamedImportsAgainstDestructures(code) {
|
|
|
2799
1817
|
export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
2800
1818
|
let result = code;
|
|
2801
1819
|
const httpOriginSafe = httpOrigin;
|
|
1820
|
+
const mixedRuntimePluginHttpRootPackages = collectMixedRuntimePluginHttpRootPackages(result, projectRoot);
|
|
2802
1821
|
const isDynamicImportPrefix = (prefix) => /import\(\s*["']?$/.test(prefix.trimStart());
|
|
2803
1822
|
const importerDir = path.posix.dirname(importerPath);
|
|
2804
1823
|
// Determine importer output relative path (project-relative .mjs) to compute relative imports consistently
|
|
@@ -2969,6 +1988,25 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2969
1988
|
return `${prefix}${spec}${suffix}`;
|
|
2970
1989
|
}
|
|
2971
1990
|
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(spec);
|
|
1991
|
+
const normalizedRuntimePluginSpec = nodeModulesSpecifier || spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '');
|
|
1992
|
+
if (normalizedRuntimePluginSpec && mixedRuntimePluginHttpRootPackages.size > 0) {
|
|
1993
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalizedRuntimePluginSpec, projectRoot);
|
|
1994
|
+
if (packageName && mixedRuntimePluginHttpRootPackages.has(packageName)) {
|
|
1995
|
+
const httpNodeModulesSpecifier = nodeModulesSpecifier || normalizedRuntimePluginSpec;
|
|
1996
|
+
const httpSpec = `/ns/m/node_modules/${httpNodeModulesSpecifier}`;
|
|
1997
|
+
if (httpOriginSafe) {
|
|
1998
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
1999
|
+
}
|
|
2000
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
if (shouldPreserveBareRuntimePluginSubpathImport(spec, projectRoot)) {
|
|
2004
|
+
const httpSpec = `/ns/m/node_modules/${spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '')}`;
|
|
2005
|
+
if (httpOriginSafe) {
|
|
2006
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
2007
|
+
}
|
|
2008
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
2009
|
+
}
|
|
2972
2010
|
const candidateNativeScriptSpec = nodeModulesSpecifier ?? spec;
|
|
2973
2011
|
// ── Node modules routing ──────────────────────────────────────
|
|
2974
2012
|
// Uses the package's own package.json exports field to determine
|
|
@@ -3017,7 +2055,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3017
2055
|
return `${prefix}${out}${suffix}`;
|
|
3018
2056
|
}
|
|
3019
2057
|
// Case B: plain .vue module → rewrite to SFC endpoint or local artifact
|
|
3020
|
-
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, ''));
|
|
2058
|
+
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, '')) || '';
|
|
3021
2059
|
if (vueKey) {
|
|
3022
2060
|
if (true) {
|
|
3023
2061
|
const absVue = vueKey.startsWith('/') ? vueKey : '/' + vueKey;
|
|
@@ -3047,6 +2085,17 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3047
2085
|
// Rewrite relative application imports to HTTP for served modules
|
|
3048
2086
|
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
3049
2087
|
const absMaybe = normalizeImportPath(spec, importerDir);
|
|
2088
|
+
const nodeModulesHttpSpec = absMaybe ? toNodeModulesHttpModuleId(absMaybe) : null;
|
|
2089
|
+
if (nodeModulesHttpSpec) {
|
|
2090
|
+
if (isDynamicImportPrefix(prefix)) {
|
|
2091
|
+
if (verbose)
|
|
2092
|
+
console.log(`[rewrite][http] dynamic relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2093
|
+
return `__nsDynamicHmrImport(${JSON.stringify(nodeModulesHttpSpec)})`;
|
|
2094
|
+
}
|
|
2095
|
+
if (verbose)
|
|
2096
|
+
console.log(`[rewrite][http] relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2097
|
+
return `${prefix}${nodeModulesHttpSpec}${suffix}`;
|
|
2098
|
+
}
|
|
3050
2099
|
const baseId = absMaybe ? toAppModuleBaseId(absMaybe, projectRoot) : null; // e.g. /src/foo.mjs
|
|
3051
2100
|
if (baseId) {
|
|
3052
2101
|
const httpSpec = `/ns/m${baseId}`;
|
|
@@ -3201,6 +2250,13 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3201
2250
|
console.log(`[rewrite][http] internal ns import (dynamic) → ${spec} via import.meta.url`);
|
|
3202
2251
|
return expr;
|
|
3203
2252
|
}
|
|
2253
|
+
const nodeModulesHttpSpec = toNodeModulesHttpModuleId(spec);
|
|
2254
|
+
if (nodeModulesHttpSpec) {
|
|
2255
|
+
const expr = `import(new URL('${nodeModulesHttpSpec}', import.meta.url).href)`;
|
|
2256
|
+
if (verbose)
|
|
2257
|
+
console.log(`[rewrite][http] absolute dynamic node_modules import → ${nodeModulesHttpSpec} via import.meta.url (from ${spec})`);
|
|
2258
|
+
return expr;
|
|
2259
|
+
}
|
|
3204
2260
|
const baseId = toAppModuleBaseId(spec, projectRoot);
|
|
3205
2261
|
if (!baseId)
|
|
3206
2262
|
return match;
|
|
@@ -3252,7 +2308,8 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3252
2308
|
// Compute a dependency-closed, topologically sorted list of modules for a given set of changed ids.
|
|
3253
2309
|
// Only include application modules we can serve (e.g., under /src and known .vue/.ts/.js entries in the graph).
|
|
3254
2310
|
function computeTxnOrderForChanged(changedIds) {
|
|
3255
|
-
const
|
|
2311
|
+
const includeAppModule = (id) => matchesRuntimeGraphModuleId(id, APP_VIRTUAL_WITH_SLASH, /\.(ts|js|mjs|tsx|jsx)$/i);
|
|
2312
|
+
const includeExt = (id) => ACTIVE_STRATEGY.matchesFile(id) || includeAppModule(id);
|
|
3256
2313
|
const isApp = (id) => id.startsWith(APP_VIRTUAL_WITH_SLASH);
|
|
3257
2314
|
const roots = changedIds.map(normalizeGraphId).filter((id) => graph.has(id) && (isApp(id) || ACTIVE_STRATEGY.matchesFile(id)) && includeExt(id));
|
|
3258
2315
|
const toVisit = new Set();
|
|
@@ -3391,7 +2448,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3391
2448
|
catch { }
|
|
3392
2449
|
});
|
|
3393
2450
|
}
|
|
3394
|
-
function upsertGraphModule(rawId, code, deps) {
|
|
2451
|
+
function upsertGraphModule(rawId, code, deps, options) {
|
|
3395
2452
|
const id = normalizeGraphId(rawId);
|
|
3396
2453
|
const normDeps = deps
|
|
3397
2454
|
.map((d) => normalizeGraphId(d))
|
|
@@ -3400,19 +2457,23 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3400
2457
|
.sort();
|
|
3401
2458
|
const hash = computeHash(code);
|
|
3402
2459
|
const existing = graph.get(id);
|
|
3403
|
-
|
|
3404
|
-
|
|
2460
|
+
const classification = classifyGraphUpsert(existing, hash, normDeps);
|
|
2461
|
+
if (classification === 'unchanged')
|
|
2462
|
+
return existing;
|
|
3405
2463
|
graphVersion++;
|
|
3406
2464
|
const gm = { id, deps: normDeps, hash };
|
|
3407
2465
|
graph.set(id, gm);
|
|
3408
2466
|
if (verbose) {
|
|
3409
2467
|
try {
|
|
3410
|
-
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion });
|
|
2468
|
+
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification });
|
|
3411
2469
|
console.log('[hmr-ws][graph] size', graph.size);
|
|
3412
2470
|
}
|
|
3413
2471
|
catch { }
|
|
3414
2472
|
}
|
|
3415
|
-
|
|
2473
|
+
if (shouldBroadcastGraphUpsertDelta(classification, options?.emitDeltaOnInsert === true, options?.broadcastDelta !== false)) {
|
|
2474
|
+
emitDelta([gm], []);
|
|
2475
|
+
}
|
|
2476
|
+
return gm;
|
|
3416
2477
|
}
|
|
3417
2478
|
function isTypescriptFlavor() {
|
|
3418
2479
|
try {
|
|
@@ -3449,15 +2510,15 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3449
2510
|
}
|
|
3450
2511
|
async function walk(dir) {
|
|
3451
2512
|
for (const name of fs.readdirSync(dir)) {
|
|
3452
|
-
|
|
3453
|
-
if (name === 'node_modules' || name.startsWith('.'))
|
|
2513
|
+
if (name === 'node_modules' || name.startsWith('.') || shouldSkipRuntimeGraphDirectoryName(name))
|
|
3454
2514
|
continue;
|
|
2515
|
+
const full = pathMod.join(dir, name);
|
|
3455
2516
|
try {
|
|
3456
2517
|
const stat = fs.statSync(full);
|
|
3457
2518
|
if (stat.isDirectory())
|
|
3458
2519
|
await walk(full);
|
|
3459
2520
|
else if (stat.isFile()) {
|
|
3460
|
-
if (/\.(vue|ts|js|mjs|tsx|jsx)
|
|
2521
|
+
if (shouldIncludeRuntimeGraphFile(full, /\.(vue|ts|js|mjs|tsx|jsx)$/i)) {
|
|
3461
2522
|
const rel = '/' + pathMod.relative(root, full).split(pathMod.sep).join('/');
|
|
3462
2523
|
// Transform via Vite to gather deps (ignore failures)
|
|
3463
2524
|
try {
|
|
@@ -3527,7 +2588,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3527
2588
|
}, {
|
|
3528
2589
|
maxConcurrent: transformConcurrency,
|
|
3529
2590
|
resultCacheTtlMs: transformCacheMs,
|
|
3530
|
-
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot),
|
|
2591
|
+
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot || process.cwd()),
|
|
3531
2592
|
});
|
|
3532
2593
|
// Attempt early vendor manifest bootstrap once per server.
|
|
3533
2594
|
if (!vendorBootstrapDone) {
|
|
@@ -3572,14 +2633,26 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3572
2633
|
});
|
|
3573
2634
|
// Additional connection diagnostics
|
|
3574
2635
|
wss.on('connection', (ws, req) => {
|
|
2636
|
+
const role = getHmrSocketRoleFromRequestUrl(req.url);
|
|
2637
|
+
ws.__nsHmrClientRole = role;
|
|
3575
2638
|
try {
|
|
3576
2639
|
if (verbose) {
|
|
3577
2640
|
const ra = req.socket?.remoteAddress;
|
|
3578
2641
|
const rp = req.socket?.remotePort;
|
|
3579
|
-
console.log('[hmr-ws] Client connected', ra + (rp ? ':' + rp : ''));
|
|
2642
|
+
console.log('[hmr-ws] Client connected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
3580
2643
|
}
|
|
3581
2644
|
}
|
|
3582
2645
|
catch { }
|
|
2646
|
+
ws.on('close', () => {
|
|
2647
|
+
try {
|
|
2648
|
+
if (verbose) {
|
|
2649
|
+
const ra = req.socket?.remoteAddress;
|
|
2650
|
+
const rp = req.socket?.remotePort;
|
|
2651
|
+
console.log('[hmr-ws] Client disconnected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
catch { }
|
|
2655
|
+
});
|
|
3583
2656
|
});
|
|
3584
2657
|
wss.on('error', (err) => {
|
|
3585
2658
|
try {
|
|
@@ -3700,18 +2773,6 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3700
2773
|
res.setHeader('Content-Type', 'application/json');
|
|
3701
2774
|
return void res.end(JSON.stringify({ error: e?.message || String(e) }));
|
|
3702
2775
|
}
|
|
3703
|
-
// Optional diagnostics: when ?diag=1, inject simple entry/exit logs to help isolate
|
|
3704
|
-
// execution-time failures on device without changing semantics.
|
|
3705
|
-
try {
|
|
3706
|
-
const wantDiag = urlObj.searchParams.get('diag') === '1';
|
|
3707
|
-
if (wantDiag) {
|
|
3708
|
-
const importerPath = spec.replace(/[?#].*$/, '');
|
|
3709
|
-
const enter = `try { console.log('[sfc][enter]', ${JSON.stringify(importerPath)}, 'hasReq=', (typeof globalThis.__nsRequire==='function'||typeof globalThis.require==='function')); } catch {}`;
|
|
3710
|
-
const exit = `\n;try { console.log('[sfc][loaded]', ${JSON.stringify(importerPath)}); } catch {}`;
|
|
3711
|
-
code = `${enter}\n${code}${exit}`;
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
catch { }
|
|
3715
2776
|
try {
|
|
3716
2777
|
const origin = getServerOrigin(server);
|
|
3717
2778
|
code = ensureVersionedRtImports(code, origin, graphVersion);
|
|
@@ -3812,7 +2873,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3812
2873
|
// Support both query (?path=/abs) and path-style (/ns/m/abs)
|
|
3813
2874
|
let spec = urlObj.searchParams.get('path') || '';
|
|
3814
2875
|
// Optional graph version pin for deterministic boot
|
|
3815
|
-
|
|
2876
|
+
let forcedVer = urlObj.searchParams.get('v');
|
|
3816
2877
|
let bootTaggedRequest = false;
|
|
3817
2878
|
if (!spec) {
|
|
3818
2879
|
const base = '/ns/m';
|
|
@@ -3840,21 +2901,10 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3840
2901
|
// The iOS HTTP ESM loader canonicalizes cache keys by stripping query params,
|
|
3841
2902
|
// so we must carry the cache-buster in the path.
|
|
3842
2903
|
try {
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
if (bootMatch) {
|
|
3848
|
-
bootTaggedRequest = true;
|
|
3849
|
-
spec = bootMatch[1] || '/';
|
|
3850
|
-
changed = true;
|
|
3851
|
-
}
|
|
3852
|
-
const hmrMatch = spec.match(/^\/?__ns_hmr__\/[^\/]+(\/.*)?$/);
|
|
3853
|
-
if (hmrMatch) {
|
|
3854
|
-
spec = hmrMatch[1] || '/';
|
|
3855
|
-
changed = true;
|
|
3856
|
-
}
|
|
3857
|
-
}
|
|
2904
|
+
const decorated = stripDecoratedServePrefixes(spec);
|
|
2905
|
+
spec = decorated.cleanedSpec;
|
|
2906
|
+
bootTaggedRequest = decorated.bootTaggedRequest;
|
|
2907
|
+
forcedVer || (forcedVer = decorated.forcedVer);
|
|
3858
2908
|
}
|
|
3859
2909
|
catch { }
|
|
3860
2910
|
// Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
|
|
@@ -3909,6 +2959,12 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3909
2959
|
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
3910
2960
|
if (spec.startsWith('./'))
|
|
3911
2961
|
spec = spec.slice(1);
|
|
2962
|
+
const blockedNodeModulesReason = getBlockedDeviceNodeModulesReason(spec);
|
|
2963
|
+
if (blockedNodeModulesReason) {
|
|
2964
|
+
res.statusCode = 404;
|
|
2965
|
+
res.end(`// [ns:m] blocked device import\nthrow new Error(${JSON.stringify(`[ns/m] ${blockedNodeModulesReason}`)});\nexport {};\n`);
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
3912
2968
|
if (!spec.startsWith('/'))
|
|
3913
2969
|
spec = '/' + spec;
|
|
3914
2970
|
const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
|
|
@@ -4078,7 +3134,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
4078
3134
|
const id = (resolvedCandidate || spec).replace(/[?#].*$/, '');
|
|
4079
3135
|
// Only track app modules (under APP_VIRTUAL_WITH_SLASH) and ts/js/tsx/jsx/mjs.
|
|
4080
3136
|
const isApp = id.startsWith(APP_VIRTUAL_WITH_SLASH) || id.startsWith('/app/');
|
|
4081
|
-
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id)) {
|
|
3137
|
+
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
|
|
4082
3138
|
const deps = Array.from(collectImportDependencies(code, id));
|
|
4083
3139
|
if (verbose) {
|
|
4084
3140
|
try {
|
|
@@ -4222,17 +3278,14 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4222
3278
|
// persistent hot.data that survives across module re-evaluations.
|
|
4223
3279
|
// cleanCode() strips Vite's __vite__createHotContext assignment, which is
|
|
4224
3280
|
// correct — the runtime's native hot context is better.
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
}
|
|
3281
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3282
|
+
const serverOrigin = getServerOrigin(server);
|
|
3283
|
+
if (ACTIVE_STRATEGY?.flavor === 'angular') {
|
|
3284
|
+
code = prepareAngularEntryForDevice(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
3285
|
+
}
|
|
3286
|
+
else {
|
|
3287
|
+
code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
4233
3288
|
}
|
|
4234
|
-
catch { }
|
|
4235
|
-
code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, server.config?.root || process.cwd(), !!verbose, undefined, getServerOrigin(server), true);
|
|
4236
3289
|
// Expand `export * from "url"` into explicit named re-exports.
|
|
4237
3290
|
// NativeScript's HTTP ESM loader may not propagate star-re-exports across
|
|
4238
3291
|
// HTTP module boundaries (the namespace object gets direct exports but
|
|
@@ -4313,7 +3366,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4313
3366
|
}
|
|
4314
3367
|
catch { }
|
|
4315
3368
|
try {
|
|
4316
|
-
const verNum =
|
|
3369
|
+
const verNum = getNumericServeVersionTag(forcedVer, Number(graphVersion || 0));
|
|
4317
3370
|
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
4318
3371
|
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
4319
3372
|
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
@@ -4323,20 +3376,20 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4323
3376
|
// IMPORTANT: use path prefix (not ?v= query) because the iOS HTTP ESM loader
|
|
4324
3377
|
// strips query params when computing module cache keys, so ?v= doesn't bust the V8 cache.
|
|
4325
3378
|
try {
|
|
4326
|
-
const ver =
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
if (p.startsWith('/ns/m/__ns_hmr__/')) {
|
|
4336
|
-
return bootTaggedRequest ? `/ns/m/__ns_boot__/b1${p.slice('/ns/m'.length)}` : p;
|
|
3379
|
+
const ver = (() => {
|
|
3380
|
+
const raw = String(forcedVer || '').trim();
|
|
3381
|
+
if (raw) {
|
|
3382
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
3383
|
+
return raw;
|
|
3384
|
+
}
|
|
3385
|
+
if (/^\d+$/.test(raw)) {
|
|
3386
|
+
return `v${raw}`;
|
|
3387
|
+
}
|
|
4337
3388
|
}
|
|
4338
|
-
return (
|
|
4339
|
-
};
|
|
3389
|
+
return `v${String(graphVersion || 0)}`;
|
|
3390
|
+
})();
|
|
3391
|
+
const origin = getServerOrigin(server);
|
|
3392
|
+
const rewritePath = (p) => rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest);
|
|
4340
3393
|
// 1) Static imports: import ... from "/ns/m/..."
|
|
4341
3394
|
code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4342
3395
|
// 2) Side-effect imports: import "/ns/m/..."
|
|
@@ -4493,16 +3546,6 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4493
3546
|
}
|
|
4494
3547
|
catch { }
|
|
4495
3548
|
}
|
|
4496
|
-
// Diagnostic: dump served code to terminal when ?__diag=1 is in the original URL
|
|
4497
|
-
try {
|
|
4498
|
-
if (urlObj?.searchParams?.get('__diag') === '1') {
|
|
4499
|
-
const specId = resolvedCandidate || spec;
|
|
4500
|
-
console.log(`\n${'='.repeat(80)}\n[ns:m][DIAG] ${specId}\n${'='.repeat(80)}`);
|
|
4501
|
-
console.log(code);
|
|
4502
|
-
console.log(`${'='.repeat(80)}\n[ns:m][DIAG] END ${specId}\n${'='.repeat(80)}\n`);
|
|
4503
|
-
}
|
|
4504
|
-
}
|
|
4505
|
-
catch { }
|
|
4506
3549
|
res.statusCode = 200;
|
|
4507
3550
|
res.end(code);
|
|
4508
3551
|
}
|
|
@@ -4702,39 +3745,41 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4702
3745
|
server.middlewares.use(async (req, res, next) => {
|
|
4703
3746
|
try {
|
|
4704
3747
|
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4705
|
-
|
|
4706
|
-
if (!
|
|
3748
|
+
const coreRequest = parseCoreBridgeRequest(urlObj.pathname, urlObj.searchParams, Number(graphVersion || 0));
|
|
3749
|
+
if (!coreRequest)
|
|
4707
3750
|
return next();
|
|
4708
3751
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4709
3752
|
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4710
3753
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4711
3754
|
res.setHeader('Pragma', 'no-cache');
|
|
4712
3755
|
res.setHeader('Expires', '0');
|
|
4713
|
-
const
|
|
4714
|
-
const hasExplicitVersion = /^[0-9]+$/.test(afterCore);
|
|
4715
|
-
const ver = /^[0-9]+$/.test(afterCore) ? afterCore : String(graphVersion || 0);
|
|
4716
|
-
// Support both query-based (?p=data/observable/index.js) and
|
|
4717
|
-
// path-based (/ns/core/data/observable/index.js) subpath formats.
|
|
4718
|
-
// The device's HTTP ESM loader may use either depending on the import map.
|
|
4719
|
-
const sub = urlObj.searchParams.get('p') || (afterCore && !/^[0-9]+$/.test(afterCore) ? afterCore : '');
|
|
4720
|
-
const key = sub ? `@nativescript/core/${sub}` : `@nativescript/core`;
|
|
3756
|
+
const { hasExplicitVersion, key, normalizedSub, sub, ver } = coreRequest;
|
|
4721
3757
|
// Any @nativescript/core subpath import (including shallow ones like
|
|
4722
3758
|
// `utils`) may expose exports that are not available from the root
|
|
4723
3759
|
// vendor bundle namespace. Serve the actual transformed module content
|
|
4724
3760
|
// instead of the lightweight proxy bridge.
|
|
4725
3761
|
if (sub) {
|
|
4726
3762
|
try {
|
|
4727
|
-
const
|
|
3763
|
+
const resolvedSubpath = normalizedSub || sub;
|
|
3764
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3765
|
+
const resolveModuleId = async (moduleId) => {
|
|
3766
|
+
const resolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3767
|
+
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3768
|
+
};
|
|
3769
|
+
const resolvedId = await resolveRuntimeCoreModulePath(resolvedSubpath, resolveModuleId);
|
|
3770
|
+
const modulePath = resolvedId || `/node_modules/@nativescript/core/${resolvedSubpath}`;
|
|
3771
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
4728
3772
|
if (!hasExplicitVersion) {
|
|
3773
|
+
if (transformed?.code) {
|
|
3774
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3775
|
+
res.statusCode = 200;
|
|
3776
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver, extractExportedNames(expandedModuleCode), hasModuleDefaultExport(expandedModuleCode)));
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
4729
3779
|
res.statusCode = 200;
|
|
4730
|
-
res.end(buildVersionedCoreSubpathAliasModule(
|
|
3780
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver));
|
|
4731
3781
|
return;
|
|
4732
3782
|
}
|
|
4733
|
-
const coreSpecifier = `@nativescript/core/${normalizedSub}`;
|
|
4734
|
-
const resolved = await server.pluginContainer?.resolveId?.(coreSpecifier, undefined);
|
|
4735
|
-
const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
4736
|
-
const modulePath = resolvedId || `/node_modules/@nativescript/core/${normalizedSub}`;
|
|
4737
|
-
const transformed = await sharedTransformRequest(modulePath);
|
|
4738
3783
|
if (transformed?.code) {
|
|
4739
3784
|
// Minimal pipeline: Vite already produces correct ESM.
|
|
4740
3785
|
// ONLY rewrite specifier strings to device-fetchable URLs.
|
|
@@ -4755,16 +3800,37 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4755
3800
|
}
|
|
4756
3801
|
}
|
|
4757
3802
|
// Main entry or shallow subpath: use proxy bridge
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
3803
|
+
let code = buildVersionedCoreMainBridgeModule(key, ver);
|
|
3804
|
+
if (!sub) {
|
|
3805
|
+
try {
|
|
3806
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3807
|
+
const coreSpecifier = '@nativescript/core';
|
|
3808
|
+
const resolved = await server.pluginContainer?.resolveId?.(coreSpecifier, undefined);
|
|
3809
|
+
const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3810
|
+
const modulePath = resolvedId || '/node_modules/@nativescript/core/index.js';
|
|
3811
|
+
const staticExportNames = collectStaticExportNamesFromFile(modulePath);
|
|
3812
|
+
const staticExportOrigins = await normalizeCoreExportOriginsForRuntime(collectStaticExportOriginsFromFile(modulePath), async (moduleId) => {
|
|
3813
|
+
const nextResolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3814
|
+
return typeof nextResolved === 'string' ? nextResolved : nextResolved?.id || null;
|
|
3815
|
+
}, modulePath);
|
|
3816
|
+
if (staticExportNames.length) {
|
|
3817
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, staticExportNames, staticExportOrigins);
|
|
3818
|
+
}
|
|
3819
|
+
else {
|
|
3820
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3821
|
+
if (transformed?.code) {
|
|
3822
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3823
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, extractExportedNames(expandedModuleCode));
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
catch (e) {
|
|
3828
|
+
try {
|
|
3829
|
+
console.warn('[ns-core-bridge] main bridge export discovery failed:', e?.message);
|
|
3830
|
+
}
|
|
3831
|
+
catch { }
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
4768
3834
|
res.statusCode = 200;
|
|
4769
3835
|
res.end(code);
|
|
4770
3836
|
}
|
|
@@ -5808,7 +4874,6 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5808
4874
|
parts.push(scriptTransformed);
|
|
5809
4875
|
parts.push(renderDecl);
|
|
5810
4876
|
parts.push(`try { if (!__ns_sfc__.render) Object.defineProperty(__ns_sfc__, 'render', { configurable: true, enumerable: true, get(){ const r = (typeof __ns_getRender==='function' ? __ns_getRender() : undefined); Object.defineProperty(__ns_sfc__, 'render', { value: r, writable: true, configurable: true, enumerable: true }); return r; }, set(v){ Object.defineProperty(__ns_sfc__, 'render', { value: v, writable: true, configurable: true, enumerable: true }); } }); } catch(_e){}`);
|
|
5811
|
-
parts.push(`// diagnostic: hadScriptDefaultPre=${hadScriptDefaultPre} triedInlineTemplate=${triedInlineTemplate} renderOk=${renderOk} tplBytes=${compiledTplCode.length} scriptBytes=${(compiledScript || '').length} templateErr=${templateErr ? templateErr?.message : ''}`);
|
|
5812
4877
|
parts.push(`export function render(){ const f = (typeof __ns_getRender==='function' ? __ns_getRender() : (__ns_sfc__ && __ns_sfc__.render)); return typeof f==='function' ? f.apply(this, arguments) : undefined; }`);
|
|
5813
4878
|
parts.push(`export default __ns_sfc__`);
|
|
5814
4879
|
let inlineCode = parts.filter(Boolean).join('\n');
|
|
@@ -5887,7 +4952,6 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5887
4952
|
outParts.push(renderDecl);
|
|
5888
4953
|
}
|
|
5889
4954
|
outParts.push(`try { if (!__ns_sfc__.render) Object.defineProperty(__ns_sfc__, 'render', { configurable: true, enumerable: true, get(){ const r = (typeof __ns_getRender==='function' ? __ns_getRender() : (typeof __ns_render==='function' ? __ns_render : undefined)); Object.defineProperty(__ns_sfc__, 'render', { value: r, writable: true, configurable: true, enumerable: true }); return r; }, set(v){ Object.defineProperty(__ns_sfc__, 'render', { value: v, writable: true, configurable: true, enumerable: true }); } }); } catch(_e){}`);
|
|
5890
|
-
outParts.push(`// diagnostic: hadScriptDefaultPre=${hadScriptDefaultPre} triedInlineTemplate=${triedInlineTemplate} renderOk=${renderOk} tplBytes=${compiledTplCode.length} scriptBytes=${(compiledScript || '').length} templateErr=${templateErr ? templateErr?.message : ''}`);
|
|
5891
4955
|
// Export named render as a function that resolves lazily
|
|
5892
4956
|
outParts.push('export function render(){ const f = (typeof __ns_getRender==="function" ? __ns_getRender() : (typeof __ns_render==="function" ? __ns_render : (__ns_sfc__ && __ns_sfc__.render))); return typeof f === "function" ? f.apply(this, arguments) : undefined; }');
|
|
5893
4957
|
outParts.push('export default __ns_sfc__');
|
|
@@ -6188,12 +5252,11 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6188
5252
|
}
|
|
6189
5253
|
let asm;
|
|
6190
5254
|
if (inlineOk) {
|
|
6191
|
-
const diagLine = `// diagnostic:inlineOk ver=${ver} inlineBlock=${!!(inlineBlock && inlineBlock.trim())} helperBindingsLen=${helperBindings.length} renderDeclLen=${renderDecl.length}`;
|
|
6192
5255
|
if (inlineBlock && inlineBlock.trim()) {
|
|
6193
|
-
asm = [`// [sfc-asm] ${base} (inlined template body)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, inlineBlock, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__
|
|
5256
|
+
asm = [`// [sfc-asm] ${base} (inlined template body)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, inlineBlock, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__;`].join('\n');
|
|
6194
5257
|
}
|
|
6195
5258
|
else {
|
|
6196
|
-
asm = [`// [sfc-asm] ${base} (inlined template)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, helperBindings, renderDecl, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__
|
|
5259
|
+
asm = [`// [sfc-asm] ${base} (inlined template)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, helperBindings, renderDecl, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__;`].filter(Boolean).join('\n');
|
|
6197
5260
|
}
|
|
6198
5261
|
}
|
|
6199
5262
|
else {
|
|
@@ -6465,8 +5528,8 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6465
5528
|
ts: Date.now(),
|
|
6466
5529
|
delta: true,
|
|
6467
5530
|
};
|
|
6468
|
-
wss
|
|
6469
|
-
if (c
|
|
5531
|
+
wss?.clients.forEach((c) => {
|
|
5532
|
+
if (isSocketClientOpen(c)) {
|
|
6470
5533
|
try {
|
|
6471
5534
|
c.send(JSON.stringify(single));
|
|
6472
5535
|
}
|
|
@@ -6554,6 +5617,9 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6554
5617
|
if (!wss) {
|
|
6555
5618
|
return;
|
|
6556
5619
|
}
|
|
5620
|
+
if (isRuntimeGraphExcludedPath(file)) {
|
|
5621
|
+
return;
|
|
5622
|
+
}
|
|
6557
5623
|
// Graph update for this file change (wrapped to avoid aborting rest of handler)
|
|
6558
5624
|
try {
|
|
6559
5625
|
const skipAngularHtmlGraphUpdate = ACTIVE_STRATEGY.flavor === 'angular' && /\.(html|htm)$/i.test(file);
|
|
@@ -6573,7 +5639,10 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6573
5639
|
.filter(Boolean);
|
|
6574
5640
|
const transformed = await server.transformRequest(mod.id);
|
|
6575
5641
|
const code = transformed?.code || '';
|
|
6576
|
-
upsertGraphModule((mod.id || '').replace(/\?.*$/, ''), code, deps
|
|
5642
|
+
upsertGraphModule((mod.id || '').replace(/\?.*$/, ''), code, deps, {
|
|
5643
|
+
emitDeltaOnInsert: true,
|
|
5644
|
+
broadcastDelta: ACTIVE_STRATEGY.flavor !== 'angular',
|
|
5645
|
+
});
|
|
6577
5646
|
}
|
|
6578
5647
|
catch (error) {
|
|
6579
5648
|
if (verbose)
|
|
@@ -6616,7 +5685,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6616
5685
|
],
|
|
6617
5686
|
};
|
|
6618
5687
|
wss.clients.forEach((client) => {
|
|
6619
|
-
if (client
|
|
5688
|
+
if (isSocketClientOpen(client)) {
|
|
6620
5689
|
client.send(JSON.stringify(msg));
|
|
6621
5690
|
}
|
|
6622
5691
|
});
|
|
@@ -6682,27 +5751,20 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6682
5751
|
}
|
|
6683
5752
|
}
|
|
6684
5753
|
try {
|
|
6685
|
-
const
|
|
6686
|
-
|
|
6687
|
-
transformCacheInvalidationUrls.add(file);
|
|
6688
|
-
}
|
|
6689
|
-
for (const mod of angularHotUpdateRoots) {
|
|
6690
|
-
if (mod?.id) {
|
|
6691
|
-
transformCacheInvalidationUrls.add(mod.id);
|
|
6692
|
-
}
|
|
6693
|
-
}
|
|
6694
|
-
if (shouldInvalidateAngularTransitiveImporters({ flavor: ACTIVE_STRATEGY.flavor, file })) {
|
|
6695
|
-
const transitiveImporters = collectAngularTransitiveImportersForInvalidation({
|
|
5754
|
+
const transitiveImporters = shouldInvalidateAngularTransitiveImporters({ flavor: ACTIVE_STRATEGY.flavor, file })
|
|
5755
|
+
? collectAngularTransitiveImportersForInvalidation({
|
|
6696
5756
|
modules: angularTransitiveInvalidationRoots,
|
|
6697
5757
|
isExcluded: (id) => id.includes('/node_modules/'),
|
|
6698
5758
|
maxDepth: 16,
|
|
6699
|
-
})
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
5759
|
+
})
|
|
5760
|
+
: [];
|
|
5761
|
+
const transformCacheInvalidationUrls = new Set(collectAngularTransformCacheInvalidationUrls({
|
|
5762
|
+
file,
|
|
5763
|
+
isTs,
|
|
5764
|
+
hotUpdateRoots: angularHotUpdateRoots,
|
|
5765
|
+
transitiveImporters,
|
|
5766
|
+
projectRoot: server.config.root || process.cwd(),
|
|
5767
|
+
}));
|
|
6706
5768
|
if (transformCacheInvalidationUrls.size) {
|
|
6707
5769
|
sharedTransformRequest.invalidateMany(transformCacheInvalidationUrls);
|
|
6708
5770
|
if (verbose) {
|
|
@@ -6723,10 +5785,18 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6723
5785
|
type: 'ns:angular-update',
|
|
6724
5786
|
origin,
|
|
6725
5787
|
path: rel,
|
|
5788
|
+
version: graphVersion,
|
|
6726
5789
|
timestamp: Date.now(),
|
|
6727
5790
|
};
|
|
5791
|
+
if (verbose) {
|
|
5792
|
+
console.log('[hmr-ws][angular] broadcasting update', Array.from(wss.clients || []).map((client) => ({
|
|
5793
|
+
role: getHmrSocketRole(client),
|
|
5794
|
+
readyState: client.readyState,
|
|
5795
|
+
openState: client.OPEN,
|
|
5796
|
+
})));
|
|
5797
|
+
}
|
|
6728
5798
|
wss.clients.forEach((client) => {
|
|
6729
|
-
if (client
|
|
5799
|
+
if (isSocketClientOpen(client)) {
|
|
6730
5800
|
client.send(JSON.stringify(msg));
|
|
6731
5801
|
}
|
|
6732
5802
|
});
|
|
@@ -6748,10 +5818,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6748
5818
|
// Treat the changed file itself as a graph module with no deps. We only
|
|
6749
5819
|
// care that its hash/identity changes so the client sees a delta and can
|
|
6750
5820
|
// perform a TS root reset. Code is not used for execution here.
|
|
6751
|
-
upsertGraphModule(rel, '', []);
|
|
6752
|
-
const gm = graph.get(normalizeGraphId(rel));
|
|
6753
|
-
if (gm)
|
|
6754
|
-
emitDelta([gm], []);
|
|
5821
|
+
upsertGraphModule(rel, '', [], { emitDeltaOnInsert: true });
|
|
6755
5822
|
}
|
|
6756
5823
|
catch (e) {
|
|
6757
5824
|
if (verbose)
|
|
@@ -6782,7 +5849,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6782
5849
|
if (!existing) {
|
|
6783
5850
|
// Module not in graph yet — force upsert with timestamp-based
|
|
6784
5851
|
// hash so the client sees a change.
|
|
6785
|
-
upsertGraphModule(rel, `/* solid-hmr ${Date.now()} */`, []);
|
|
5852
|
+
upsertGraphModule(rel, `/* solid-hmr ${Date.now()} */`, [], { emitDeltaOnInsert: true });
|
|
6786
5853
|
}
|
|
6787
5854
|
// Log what we're sending so devs can trace the flow on the server side.
|
|
6788
5855
|
if (verbose) {
|
|
@@ -6979,7 +6046,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
|
|
|
6979
6046
|
version: graphVersion,
|
|
6980
6047
|
};
|
|
6981
6048
|
wss.clients.forEach((client) => {
|
|
6982
|
-
if (client
|
|
6049
|
+
if (isSocketClientOpen(client)) {
|
|
6983
6050
|
client.send(JSON.stringify(registryUpdateMsg));
|
|
6984
6051
|
}
|
|
6985
6052
|
});
|
|
@@ -7150,4 +6217,6 @@ function getServerOrigin(server) {
|
|
|
7150
6217
|
// Test-only export: allow unit tests to run the sanitizer on snippets without booting a server
|
|
7151
6218
|
// Safe in production builds; this is a named export that tests can import explicitly.
|
|
7152
6219
|
export const __test_processCodeForDevice = processCodeForDevice;
|
|
6220
|
+
export const __test_resolveVendorRouting = resolveVendorRouting;
|
|
6221
|
+
export const __test_getBlockedDeviceNodeModulesReason = getBlockedDeviceNodeModulesReason;
|
|
7153
6222
|
//# sourceMappingURL=websocket.js.map
|