@nativescript/vite 8.0.0-alpha.2 → 8.0.0-alpha.4
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-ns-m-finalize.d.ts +32 -0
- package/hmr/server/websocket-ns-m-finalize.js +73 -0
- package/hmr/server/websocket-ns-m-finalize.js.map +1 -0
- package/hmr/server/websocket-ns-m-paths.d.ts +3 -0
- package/hmr/server/websocket-ns-m-paths.js +47 -0
- package/hmr/server/websocket-ns-m-paths.js.map +1 -0
- package/hmr/server/websocket-ns-m-request.d.ts +35 -0
- package/hmr/server/websocket-ns-m-request.js +203 -0
- package/hmr/server/websocket-ns-m-request.js.map +1 -0
- package/hmr/server/websocket-runtime-compat.d.ts +19 -0
- package/hmr/server/websocket-runtime-compat.js +286 -0
- package/hmr/server/websocket-runtime-compat.js.map +1 -0
- package/hmr/server/websocket-txn.d.ts +6 -0
- package/hmr/server/websocket-txn.js +45 -0
- package/hmr/server/websocket-txn.js.map +1 -0
- package/hmr/server/websocket-vendor-unifier.d.ts +10 -0
- package/hmr/server/websocket-vendor-unifier.js +51 -0
- package/hmr/server/websocket-vendor-unifier.js.map +1 -0
- package/hmr/server/websocket-vue-sfc.d.ts +35 -0
- package/hmr/server/websocket-vue-sfc.js +1116 -0
- package/hmr/server/websocket-vue-sfc.js.map +1 -0
- package/hmr/server/websocket.d.ts +21 -74
- package/hmr/server/websocket.js +578 -3439
- 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
|
@@ -3,42 +3,58 @@ import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCore
|
|
|
3
3
|
// AST tooling for robust transformations
|
|
4
4
|
import { parse as babelParse } from '@babel/parser';
|
|
5
5
|
import { genCode } from '../helpers/babel.js';
|
|
6
|
-
import babelCore from '@babel/core';
|
|
7
|
-
import pluginTransformTypescript from '@babel/plugin-transform-typescript';
|
|
8
6
|
import traverse from '@babel/traverse';
|
|
9
7
|
// Ensure traverse callable across CJS/ESM builds
|
|
10
8
|
const babelTraverse = traverse?.default || traverse;
|
|
11
9
|
import * as t from '@babel/types';
|
|
12
|
-
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
13
11
|
import { astNormalizeModuleImportsAndHelpers, astVerifyAndAnnotateDuplicates } from '../helpers/ast-normalizer.js';
|
|
14
|
-
import {
|
|
12
|
+
import { stripDanglingViteCjsImports } from '../helpers/sanitize.js';
|
|
15
13
|
import { WebSocketServer } from 'ws';
|
|
16
14
|
import * as path from 'path';
|
|
17
15
|
import { createHash } from 'crypto';
|
|
18
16
|
import * as PAT from './constants.js';
|
|
19
17
|
import { getVendorManifest, resolveVendorSpecifier } from '../shared/vendor/registry.js';
|
|
20
|
-
import {
|
|
18
|
+
import { getProjectRootPath } from '../../helpers/project.js';
|
|
21
19
|
import { loadPrebuiltVendorManifest } from '../shared/vendor/manifest-loader.js';
|
|
22
20
|
import '../vendor-bootstrap.js';
|
|
23
|
-
import { NS_NATIVE_TAGS } from './compiler.js';
|
|
24
|
-
import { vueSfcCompiler } from '../frameworks/vue/server/compiler.js';
|
|
25
21
|
import { linkAngularPartialsIfNeeded } from '../frameworks/angular/server/linker.js';
|
|
26
22
|
import { vueServerStrategy } from '../frameworks/vue/server/strategy.js';
|
|
27
23
|
import { angularServerStrategy } from '../frameworks/angular/server/strategy.js';
|
|
28
24
|
import { solidServerStrategy } from '../frameworks/solid/server/strategy.js';
|
|
29
25
|
import { typescriptServerStrategy } from '../frameworks/typescript/server/strategy.js';
|
|
30
|
-
import {
|
|
31
|
-
import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
|
|
26
|
+
import { createProcessSfcCode } from '../frameworks/vue/server/sfc-transforms.js';
|
|
32
27
|
import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
|
|
33
28
|
import { buildRuntimeConfig, generateImportMap } from './import-map.js';
|
|
34
29
|
import { getCliFlags } from '../../helpers/cli-flags.js';
|
|
30
|
+
import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
|
|
31
|
+
import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
32
|
+
import { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
|
|
33
|
+
import { classifyGraphUpsert, shouldBroadcastGraphUpsertDelta } from './websocket-graph-upsert.js';
|
|
34
|
+
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';
|
|
35
|
+
import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
36
|
+
import { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, extractDirectExportedNames, hasModuleDefaultExport, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
|
|
37
|
+
import { finalizeNsMServedModule } from './websocket-ns-m-finalize.js';
|
|
38
|
+
import { createNsMRequestContext, resolveNsMTransformedModule } from './websocket-ns-m-request.js';
|
|
39
|
+
import { getNumericServeVersionTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
|
|
40
|
+
import { registerRuntimeCompatHandlers } from './websocket-runtime-compat.js';
|
|
41
|
+
import { registerTxnHandler } from './websocket-txn.js';
|
|
42
|
+
import { registerVendorUnifierHandler } from './websocket-vendor-unifier.js';
|
|
43
|
+
import { registerVueSfcHandlers } from './websocket-vue-sfc.js';
|
|
44
|
+
import { createSharedTransformRequestRunner } from './shared-transform-request.js';
|
|
45
|
+
export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
46
|
+
export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
|
|
47
|
+
export { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
|
|
48
|
+
export { rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
|
|
49
|
+
export { rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
50
|
+
export { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, createSharedTransformRequestRunner, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload, classifyGraphUpsert, shouldBroadcastGraphUpsertDelta };
|
|
35
51
|
// Build a serialized process.env object from CLI --env.* flags.
|
|
36
52
|
// This is injected into every HTTP-served module so app code referencing
|
|
37
53
|
// process.env.TEST_ENV (etc.) works on device in HMR dev mode.
|
|
38
54
|
const __processEnvEntries = { NODE_ENV: 'development' };
|
|
39
55
|
try {
|
|
40
56
|
const flags = getCliFlags();
|
|
41
|
-
for (const [k, v] of Object.entries(flags)) {
|
|
57
|
+
for (const [k, v] of Object.entries(flags || {})) {
|
|
42
58
|
// Skip internal NativeScript build flags
|
|
43
59
|
if (['ios', 'android', 'visionos', 'platform', 'hmr', 'verbose'].includes(k))
|
|
44
60
|
continue;
|
|
@@ -47,7 +63,6 @@ try {
|
|
|
47
63
|
}
|
|
48
64
|
catch { }
|
|
49
65
|
const __processEnvJson = JSON.stringify(__processEnvEntries);
|
|
50
|
-
const { parse, compileTemplate, compileScript } = vueSfcCompiler;
|
|
51
66
|
const APP_ROOT_DIR = getProjectAppPath();
|
|
52
67
|
const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
|
|
53
68
|
const APP_VIRTUAL_WITH_SLASH = `${APP_VIRTUAL_PREFIX}/`;
|
|
@@ -60,182 +75,46 @@ const STRATEGY_REGISTRY = new Map([
|
|
|
60
75
|
['typescript', typescriptServerStrategy],
|
|
61
76
|
]);
|
|
62
77
|
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
|
-
}
|
|
78
|
+
const strategy = STRATEGY_REGISTRY.get(flavor);
|
|
79
|
+
if (!strategy) {
|
|
80
|
+
throw new Error(`[ns-hmr] Unsupported framework strategy: ${flavor}`);
|
|
91
81
|
}
|
|
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);
|
|
82
|
+
return strategy;
|
|
130
83
|
}
|
|
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))
|
|
84
|
+
let ACTIVE_STRATEGY;
|
|
85
|
+
function isSocketClientOpen(client) {
|
|
86
|
+
if (!client) {
|
|
155
87
|
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
88
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (!rel || rel.startsWith('..') || path.isAbsolute(rel))
|
|
184
|
-
return null;
|
|
185
|
-
if (!existsSync(absPath))
|
|
186
|
-
return null;
|
|
89
|
+
const openState = typeof client.OPEN === 'number' ? client.OPEN : 1;
|
|
90
|
+
return client.readyState === openState;
|
|
91
|
+
}
|
|
92
|
+
function getHmrSocketRoleFromRequestUrl(requestUrl) {
|
|
187
93
|
try {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
code,
|
|
194
|
-
resolvedId: spec,
|
|
195
|
-
};
|
|
94
|
+
const url = new URL(requestUrl || '/ns-hmr', 'http://localhost');
|
|
95
|
+
return url.searchParams.get('ns_hmr_role') || 'unknown';
|
|
196
96
|
}
|
|
197
97
|
catch {
|
|
198
|
-
return
|
|
98
|
+
return 'unknown';
|
|
199
99
|
}
|
|
200
100
|
}
|
|
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}`;
|
|
217
|
-
}
|
|
218
|
-
if (!rawQuery) {
|
|
219
|
-
return normalizedPath;
|
|
220
|
-
}
|
|
221
|
-
const params = new URLSearchParams(rawQuery);
|
|
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);
|
|
101
|
+
function getHmrSocketRole(client) {
|
|
102
|
+
if (!client) {
|
|
103
|
+
return 'unknown';
|
|
236
104
|
}
|
|
237
|
-
return
|
|
105
|
+
return typeof client.__nsHmrClientRole === 'string' && client.__nsHmrClientRole ? client.__nsHmrClientRole : 'unknown';
|
|
238
106
|
}
|
|
107
|
+
function shouldAllowLocalCoreSanitizerPaths(contextLabel) {
|
|
108
|
+
return /\bnode_modules\/@nativescript\/vite\/hmr\/(?:client|frameworks)\//.test(contextLabel);
|
|
109
|
+
}
|
|
110
|
+
export function prepareAngularEntryForDevice(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
111
|
+
const rewrittenCode = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp);
|
|
112
|
+
return rewriteAngularEntryRegisterOnly(rewrittenCode, resolveAngularCoreHmrImportSource(rewrittenCode, httpOrigin));
|
|
113
|
+
}
|
|
114
|
+
const processSfcCode = createProcessSfcCode(processCodeForDevice);
|
|
115
|
+
// Bare specifiers and special skip patterns (virtual, data:, etc.)
|
|
116
|
+
const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
|
|
117
|
+
const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
|
|
239
118
|
const MODULE_IMPORT_ANALYSIS_PLUGINS = ['typescript', 'jsx', 'importMeta', 'topLevelAwait', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', 'decorators-legacy'];
|
|
240
119
|
function collectTopLevelImportRecords(code) {
|
|
241
120
|
if (!code || typeof code !== 'string' || !/\bimport\b/.test(code)) {
|
|
@@ -362,502 +241,12 @@ function buildNodeModuleProvenancePrelude(sourceId) {
|
|
|
362
241
|
via = 'vite-deps';
|
|
363
242
|
packageSpecifier = viteDepsPathToBareSpecifier(normalized.slice('.vite/deps/'.length)) || normalized;
|
|
364
243
|
}
|
|
365
|
-
const rootPackage =
|
|
244
|
+
const rootPackage = resolveNodeModulesPackageBoundary(packageSpecifier, getProjectRootPath()).packageName;
|
|
366
245
|
if (!rootPackage) {
|
|
367
246
|
return '';
|
|
368
247
|
}
|
|
369
248
|
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
249
|
}
|
|
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
250
|
// Guard any bare dynamic import(spec) occurring in assembled module code.
|
|
862
251
|
// We cannot override native dynamic import globally; for SFC assembler outputs we inline
|
|
863
252
|
// a tiny helper and rewrite "import(" to "__nsDynImport(" to prevent anomalous specs like '@'.
|
|
@@ -883,6 +272,48 @@ function guardBareDynamicImports(code) {
|
|
|
883
272
|
return code;
|
|
884
273
|
}
|
|
885
274
|
}
|
|
275
|
+
function stripCoreGlobalsImports(code) {
|
|
276
|
+
const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
|
|
277
|
+
return code.replace(pattern, '');
|
|
278
|
+
}
|
|
279
|
+
function ensureVariableDynamicImportHelper(code) {
|
|
280
|
+
if (!code.includes('__variableDynamicImportRuntimeHelper')) {
|
|
281
|
+
return code;
|
|
282
|
+
}
|
|
283
|
+
if (PAT.VARIABLE_DYNAMIC_IMPORT_HELPER_PATTERN.test(code)) {
|
|
284
|
+
return code;
|
|
285
|
+
}
|
|
286
|
+
const helper = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n` +
|
|
287
|
+
` try { if (request === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n` +
|
|
288
|
+
` const loader = map && (map[request] || map[request?.replace(/\\\\/g, "/")]);\n` +
|
|
289
|
+
` if (!loader) {\n` +
|
|
290
|
+
` const error = new Error(\"Cannot dynamically import: \" + request);\n` +
|
|
291
|
+
` error.code = 'ERR_MODULE_NOT_FOUND';\n` +
|
|
292
|
+
` return Promise.reject(error);\n` +
|
|
293
|
+
` }\n` +
|
|
294
|
+
` try {\n` +
|
|
295
|
+
` return loader(importMode);\n` +
|
|
296
|
+
` } catch (err) {\n` +
|
|
297
|
+
` return Promise.reject(err);\n` +
|
|
298
|
+
` }\n` +
|
|
299
|
+
`};\n`;
|
|
300
|
+
return `${helper}${code}`;
|
|
301
|
+
}
|
|
302
|
+
function ensureGuardPlainDynamicImports(code, origin) {
|
|
303
|
+
try {
|
|
304
|
+
if (!code || !/\bimport\s*\(/.test(code))
|
|
305
|
+
return code;
|
|
306
|
+
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`;
|
|
307
|
+
const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
|
|
308
|
+
if (replaced !== code) {
|
|
309
|
+
return wrapper + replaced;
|
|
310
|
+
}
|
|
311
|
+
return code;
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return code;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
886
317
|
function ensureDynamicHmrImportHelper(code) {
|
|
887
318
|
try {
|
|
888
319
|
if (!code.includes('__nsDynamicHmrImport('))
|
|
@@ -891,14 +322,23 @@ function ensureDynamicHmrImportHelper(code) {
|
|
|
891
322
|
return code;
|
|
892
323
|
const helper = 'const __nsDynamicHmrImport = (spec) => {\n' +
|
|
893
324
|
" const __nsm = '/ns' + '/m';\n" +
|
|
325
|
+
" const __nsBootPrefix = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' && import.meta.url.includes('/__ns_boot__/b1/') ? '/__ns_boot__/b1' : '';\n" +
|
|
326
|
+
" const __nsImporterTagMatch = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' ? import.meta.url.match(/\\/__ns_hmr__\\/([^/]+)\\//) : null;\n" +
|
|
327
|
+
" const __nsImporterTag = __nsImporterTagMatch && __nsImporterTagMatch[1] ? decodeURIComponent(__nsImporterTagMatch[1]) : '';\n" +
|
|
894
328
|
" try { if (!spec || spec === '@') { return import(new URL(__nsm + '/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n" +
|
|
895
329
|
' try {\n' +
|
|
896
330
|
" 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
331
|
' const g = globalThis;\n' +
|
|
332
|
+
" const graphVersion = typeof g.__NS_HMR_GRAPH_VERSION__ === 'number' ? g.__NS_HMR_GRAPH_VERSION__ : 0;\n" +
|
|
899
333
|
" const nonce = typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0;\n" +
|
|
900
|
-
" const
|
|
901
|
-
"
|
|
334
|
+
" const __nsActiveBootPrefix = graphVersion || nonce ? '' : __nsBootPrefix;\n" +
|
|
335
|
+
" if (spec.includes('/__ns_hmr__/')) {\n" +
|
|
336
|
+
" const __preservedSpec = !nonce && __nsBootPrefix && spec.startsWith(__nsm + '/__ns_hmr__/') && !spec.includes('/node_modules/') ? __nsm + __nsBootPrefix + spec.slice(__nsm.length) : spec;\n" +
|
|
337
|
+
' return import(new URL(__preservedSpec, import.meta.url).href);\n' +
|
|
338
|
+
' }\n' +
|
|
339
|
+
" if (spec.startsWith(__nsm + '/node_modules/')) { return import(new URL(spec, import.meta.url).href); }\n" +
|
|
340
|
+
" const tag = nonce ? `n${nonce}` : (graphVersion ? `v${graphVersion}` : (__nsImporterTag || 'live'));\n" +
|
|
341
|
+
" const nextPath = __nsm + __nsActiveBootPrefix + '/__ns_hmr__/' + encodeURIComponent(tag) + spec.slice(__nsm.length);\n" +
|
|
902
342
|
" const origin = typeof g.__NS_HTTP_ORIGIN__ === 'string' && /^https?:\\/\\//.test(g.__NS_HTTP_ORIGIN__) ? g.__NS_HTTP_ORIGIN__ : '';\n" +
|
|
903
343
|
' return import(origin ? origin + nextPath : new URL(nextPath, import.meta.url).href);\n' +
|
|
904
344
|
' }\n' +
|
|
@@ -911,550 +351,47 @@ function ensureDynamicHmrImportHelper(code) {
|
|
|
911
351
|
return code;
|
|
912
352
|
}
|
|
913
353
|
}
|
|
914
|
-
function
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
|
1215
|
-
}
|
|
1216
|
-
_exportsReverseMapCache.set(pkgName, map);
|
|
1217
|
-
return map;
|
|
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;
|
|
1228
|
-
}
|
|
1229
|
-
return spec.split('/')[0];
|
|
1230
|
-
}
|
|
1231
|
-
/**
|
|
1232
|
-
* Determine whether a vendor package import should go through the vendor bridge
|
|
1233
|
-
* (bare specifier) or HTTP (full URL), using the package's exports map.
|
|
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;
|
|
1263
|
-
}
|
|
1264
|
-
// Platform-specific NativeScript plugin main entries should still use the
|
|
1265
|
-
// vendor bridge so device require() preserves singleton side effects.
|
|
1266
|
-
// Their named/default bindings are synthesized later by
|
|
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 };
|
|
1271
|
-
}
|
|
1272
|
-
// Other platform-specific files still go via HTTP.
|
|
1273
|
-
if (/\.(ios|android|visionos)\.(js|ts|mjs|mts)$/.test(nodeModulesSpec)) {
|
|
1274
|
-
return { route: 'http' };
|
|
1275
|
-
}
|
|
1276
|
-
// No subpath → bare package specifier → vendor bridge
|
|
1277
|
-
if (!subpath) {
|
|
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
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
// Default: HTTP — safe for all module types and preserves all named exports
|
|
1300
|
-
return { route: 'http' };
|
|
1301
|
-
}
|
|
1302
|
-
function stripCoreGlobalsImports(code) {
|
|
1303
|
-
const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
|
|
1304
|
-
return code.replace(pattern, '');
|
|
1305
|
-
}
|
|
1306
|
-
function ensureVariableDynamicImportHelper(code) {
|
|
1307
|
-
if (!code.includes('__variableDynamicImportRuntimeHelper')) {
|
|
1308
|
-
return code;
|
|
1309
|
-
}
|
|
1310
|
-
if (PAT.VARIABLE_DYNAMIC_IMPORT_HELPER_PATTERN.test(code)) {
|
|
1311
|
-
return code;
|
|
1312
|
-
}
|
|
1313
|
-
const helper = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n` +
|
|
1314
|
-
` try { if (request === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n` +
|
|
1315
|
-
` const loader = map && (map[request] || map[request?.replace(/\\\\/g, "/")]);\n` +
|
|
1316
|
-
` if (!loader) {\n` +
|
|
1317
|
-
` const error = new Error(\"Cannot dynamically import: \" + request);\n` +
|
|
1318
|
-
` error.code = 'ERR_MODULE_NOT_FOUND';\n` +
|
|
1319
|
-
` return Promise.reject(error);\n` +
|
|
1320
|
-
` }\n` +
|
|
1321
|
-
` try {\n` +
|
|
1322
|
-
` return loader(importMode);\n` +
|
|
1323
|
-
` } catch (err) {\n` +
|
|
1324
|
-
` return Promise.reject(err);\n` +
|
|
1325
|
-
` }\n` +
|
|
1326
|
-
`};\n`;
|
|
1327
|
-
return `${helper}${code}`;
|
|
1328
|
-
}
|
|
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
|
-
function ensureGuardPlainDynamicImports(code, origin) {
|
|
1333
|
-
try {
|
|
1334
|
-
if (!code || !/\bimport\s*\(/.test(code))
|
|
1335
|
-
return code;
|
|
1336
|
-
const w = `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`;
|
|
1337
|
-
// Replace only when `import(` is not part of an identifier or property (no preceding "." or word char)
|
|
1338
|
-
const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
|
|
1339
|
-
if (replaced !== code) {
|
|
1340
|
-
return w + replaced;
|
|
1341
|
-
}
|
|
1342
|
-
return code;
|
|
1343
|
-
}
|
|
1344
|
-
catch {
|
|
1345
|
-
return code;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Expand `export * from "url"` into explicit named re-exports.
|
|
1350
|
-
*
|
|
1351
|
-
* NativeScript's HTTP ESM loader on iOS/Android may not correctly propagate
|
|
1352
|
-
* `export * from` re-exports across HTTP module boundaries — the importing
|
|
1353
|
-
* module's namespace object gets only direct exports, missing re-exported
|
|
1354
|
-
* names. This function resolves each `export * from` by fetching the target
|
|
1355
|
-
* module, scanning for its named exports, and replacing the star export with
|
|
1356
|
-
* explicit `export { name1, name2, ... } from "url"`.
|
|
1357
|
-
*
|
|
1358
|
-
* Only expands star exports pointing to node_modules HTTP URLs to avoid
|
|
1359
|
-
* unnecessary work for app source files (which are typically not re-exported).
|
|
1360
|
-
*/
|
|
1361
|
-
async function expandStarExports(code, server, projectRoot, verbose) {
|
|
1362
|
-
const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
|
|
1363
|
-
let match;
|
|
1364
|
-
const replacements = [];
|
|
1365
|
-
while ((match = STAR_RE.exec(code)) !== null) {
|
|
1366
|
-
const url = match[2];
|
|
1367
|
-
// Only expand node_modules star exports served over HTTP
|
|
1368
|
-
if (!url.includes('/node_modules/'))
|
|
1369
|
-
continue;
|
|
1370
|
-
replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
|
|
354
|
+
async function expandStarExports(code, server, projectRoot, verbose) {
|
|
355
|
+
const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
|
|
356
|
+
let match;
|
|
357
|
+
const replacements = [];
|
|
358
|
+
while ((match = STAR_RE.exec(code)) !== null) {
|
|
359
|
+
const url = match[2];
|
|
360
|
+
if (!url.includes('/node_modules/'))
|
|
361
|
+
continue;
|
|
362
|
+
replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
|
|
1371
363
|
}
|
|
1372
364
|
if (!replacements.length)
|
|
1373
365
|
return code;
|
|
1374
366
|
for (const rep of replacements) {
|
|
1375
367
|
try {
|
|
1376
|
-
// Strip HTTP origin to get a Vite-resolvable path
|
|
1377
368
|
let vitePath = rep.url.replace(/^https?:\/\/[^/]+/, '');
|
|
1378
|
-
// Strip /ns/m/ prefix to get the node_modules path
|
|
1379
369
|
vitePath = vitePath.replace(/^\/ns\/m\//, '/');
|
|
1380
|
-
// Strip boot-path prefix used during initial HTTP boot progress tracking.
|
|
1381
370
|
vitePath = vitePath.replace(/^\/__ns_boot__\/[^/]+/, '');
|
|
1382
|
-
// Strip HMR cache-busting path segments
|
|
1383
371
|
vitePath = vitePath.replace(/\/__ns_hmr__\/[^/]+/, '');
|
|
1384
|
-
const
|
|
1385
|
-
if (!
|
|
372
|
+
const result = await server.transformRequest(vitePath);
|
|
373
|
+
if (!result?.code)
|
|
1386
374
|
continue;
|
|
1387
|
-
const names = extractExportedNames(
|
|
375
|
+
const names = extractExportedNames(result.code);
|
|
1388
376
|
if (!names.length)
|
|
1389
377
|
continue;
|
|
1390
|
-
// Replace `export * from "url"` with explicit named re-exports
|
|
1391
378
|
const explicit = `export { ${names.join(', ')} } from ${JSON.stringify(rep.url)};`;
|
|
1392
379
|
code = code.replace(rep.full, explicit);
|
|
1393
380
|
if (verbose) {
|
|
1394
|
-
console.log(`[ns/m] expanded export*
|
|
381
|
+
console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
|
|
1395
382
|
}
|
|
1396
383
|
}
|
|
1397
384
|
catch { }
|
|
1398
385
|
}
|
|
1399
386
|
return code;
|
|
1400
387
|
}
|
|
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
388
|
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);
|
|
389
|
+
return extractDirectExportedNames(code);
|
|
1450
390
|
}
|
|
1451
|
-
// Heal accidental "import ... = expr" assignments produced by upstream transforms.
|
|
1452
|
-
// These are invalid JS; convert to equivalent const assignments.
|
|
1453
391
|
function repairImportEqualsAssignments(code) {
|
|
1454
392
|
try {
|
|
1455
393
|
if (!code || typeof code !== 'string')
|
|
1456
394
|
return code;
|
|
1457
|
-
// import { a, b as c } = expr; -> const { a, b: c } = expr;
|
|
1458
395
|
code = code.replace(/(^|\n)\s*import\s*\{([^}]+)\}\s*=\s*([^;]+);?/g, (_m, p1, specList, rhs) => {
|
|
1459
396
|
const cleaned = String(specList)
|
|
1460
397
|
.split(',')
|
|
@@ -1464,61 +401,28 @@ function repairImportEqualsAssignments(code) {
|
|
|
1464
401
|
.join(', ');
|
|
1465
402
|
return `${p1}const { ${cleaned} } = ${rhs};`;
|
|
1466
403
|
});
|
|
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
|
-
});
|
|
404
|
+
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});`);
|
|
405
|
+
code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => `${p1}const ${id} = ${rhs};`);
|
|
1475
406
|
}
|
|
1476
407
|
catch { }
|
|
1477
408
|
return code;
|
|
1478
409
|
}
|
|
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
410
|
function ensureVersionedRtImports(code, origin, ver) {
|
|
1482
411
|
if (!code || !origin || !Number.isFinite(ver))
|
|
1483
412
|
return code;
|
|
1484
|
-
// Static imports: import { ... } from ".../ns/rt" (plus optional version)
|
|
1485
413
|
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
414
|
code = code.replace(/(import\(\s*["'])(?:https?:\/\/[^"']+)?\/(?:\@ns|ns)\/rt(?:\/[\d]+)?(["']\s*\))/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
|
|
1488
415
|
return code;
|
|
1489
416
|
}
|
|
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
417
|
function stripViteDynamicImportVirtual(code) {
|
|
1511
418
|
if (!/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
1512
419
|
return code;
|
|
1513
420
|
}
|
|
1514
421
|
const original = code;
|
|
1515
|
-
// Remove any import lines referencing the virtual helper (with or without bindings)
|
|
1516
422
|
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
423
|
if (/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
1519
424
|
code = code.replace(/\/@id\/__x00__vite\/dynamic-import-helper[^"'`)]*/g, '/__NS_UNUSED_DYNAMIC_IMPORT_HELPER__');
|
|
1520
425
|
}
|
|
1521
|
-
// Ensure helper present
|
|
1522
426
|
if (!/__variableDynamicImportRuntimeHelper/.test(code)) {
|
|
1523
427
|
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
428
|
code = inline + code;
|
|
@@ -1528,17 +432,7 @@ function stripViteDynamicImportVirtual(code) {
|
|
|
1528
432
|
}
|
|
1529
433
|
return code;
|
|
1530
434
|
}
|
|
1531
|
-
// Small snippet injected into device-delivered modules to capture any require('http(s)://') calls
|
|
1532
435
|
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
436
|
function shouldRemapImport(spec) {
|
|
1543
437
|
if (!spec || typeof spec !== 'string')
|
|
1544
438
|
return false;
|
|
@@ -1559,12 +453,9 @@ function shouldRemapImport(spec) {
|
|
|
1559
453
|
}
|
|
1560
454
|
return true;
|
|
1561
455
|
}
|
|
1562
|
-
// (legacy wrapSfcWithStableDefault removed; full SFCs now delegate to /ns/asm)
|
|
1563
456
|
function removeNamedImports(code, names) {
|
|
1564
457
|
const regex = /^(\s*import\s*\{)([^}]*)(\}\s*from\s*['"][^'"]+['"];?)/gm;
|
|
1565
458
|
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
459
|
const srcMatch = /from\s*['"]\s*([^'"\s]+)\s*['"]/i.exec(_m);
|
|
1569
460
|
const src = (srcMatch?.[1] || '').toLowerCase();
|
|
1570
461
|
const isVueSource = /^(?:vue|nativescript-vue)(?:\b|\/)/i.test(src);
|
|
@@ -1663,7 +554,7 @@ function normalizeImportPath(spec, importerDir) {
|
|
|
1663
554
|
else if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
1664
555
|
key = path.posix.normalize(path.posix.join(importerDir, spec));
|
|
1665
556
|
if (!key.startsWith('/')) {
|
|
1666
|
-
key =
|
|
557
|
+
key = `/${key}`;
|
|
1667
558
|
}
|
|
1668
559
|
}
|
|
1669
560
|
else {
|
|
@@ -1730,6 +621,70 @@ function findDependencyFileName(depFileMap, key) {
|
|
|
1730
621
|
}
|
|
1731
622
|
return undefined;
|
|
1732
623
|
}
|
|
624
|
+
function isRuntimePluginRootEntrySpecifier(specifier, projectRoot) {
|
|
625
|
+
if (!specifier) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
const cleaned = specifier.replace(PAT.QUERY_PATTERN, '');
|
|
629
|
+
const normalized = normalizeNodeModulesSpecifier(cleaned) || cleaned.replace(/^\/+/, '');
|
|
630
|
+
if (!normalized) {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
const { packageName, subpath } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
634
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
if (!subpath) {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
if (subpath.includes('/')) {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
const pkgBaseName = packageName.split('/').pop() || '';
|
|
644
|
+
const withoutExt = /(?:\.(?:ios|android|visionos))?\.(?:ts|tsx|js|jsx|mjs|mts|cts)$/i.test(subpath) ? subpath.replace(/\.[^.]+$/, '') : subpath;
|
|
645
|
+
const withoutPlatform = withoutExt.replace(/\.(ios|android|visionos)$/i, '');
|
|
646
|
+
return withoutPlatform === 'index' || withoutPlatform === pkgBaseName;
|
|
647
|
+
}
|
|
648
|
+
function collectMixedRuntimePluginHttpRootPackages(code, projectRoot) {
|
|
649
|
+
const nonRootSubpathPackages = new Set();
|
|
650
|
+
const rootEntryPackages = new Set();
|
|
651
|
+
const visitSpecifier = (rawSpecifier) => {
|
|
652
|
+
if (!rawSpecifier) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const specifier = normalizeNativeScriptCoreSpecifier(rawSpecifier).replace(PAT.QUERY_PATTERN, '');
|
|
656
|
+
if (!specifier) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (/^https?:\/\//.test(specifier) || specifier.startsWith('/ns/')) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (/^(?:\.|\/)/.test(specifier) && !specifier.includes('/node_modules/')) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const normalized = normalizeNodeModulesSpecifier(specifier) || specifier.replace(/^\/+/, '');
|
|
666
|
+
if (!normalized) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
670
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (isRuntimePluginRootEntrySpecifier(normalized, projectRoot)) {
|
|
674
|
+
rootEntryPackages.add(packageName);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
nonRootSubpathPackages.add(packageName);
|
|
678
|
+
};
|
|
679
|
+
for (const pattern of [PAT.IMPORT_PATTERN_1, PAT.IMPORT_PATTERN_2, PAT.IMPORT_PATTERN_3, PAT.IMPORT_PATTERN_SIDE_EFFECT]) {
|
|
680
|
+
pattern.lastIndex = 0;
|
|
681
|
+
let match;
|
|
682
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
683
|
+
visitSpecifier(match[2]);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return new Set(Array.from(nonRootSubpathPackages).filter((packageName) => rootEntryPackages.has(packageName)));
|
|
687
|
+
}
|
|
1733
688
|
function collectImportDependencies(code, importerPath) {
|
|
1734
689
|
const importerDir = path.posix.dirname(importerPath);
|
|
1735
690
|
const deps = new Set();
|
|
@@ -1928,6 +883,13 @@ function toAppModuleBaseId(importPath, projectRoot) {
|
|
|
1928
883
|
const base = projectRelative.replace(/\.mjs$/i, '');
|
|
1929
884
|
return `/${base}`;
|
|
1930
885
|
}
|
|
886
|
+
function toNodeModulesHttpModuleId(importPath) {
|
|
887
|
+
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(importPath);
|
|
888
|
+
if (!nodeModulesSpecifier) {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
return `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
892
|
+
}
|
|
1931
893
|
function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
1932
894
|
if (!spec || typeof spec !== 'string') {
|
|
1933
895
|
return null;
|
|
@@ -2109,8 +1071,13 @@ export function wrapCommonJsModuleForDevice(code) {
|
|
|
2109
1071
|
/**
|
|
2110
1072
|
* Process code for device: inject globals, remove framework imports
|
|
2111
1073
|
*/
|
|
2112
|
-
function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = false, isNodeModule = false, sourceId) {
|
|
1074
|
+
function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = false, isNodeModule = false, sourceId, options) {
|
|
2113
1075
|
let result = code;
|
|
1076
|
+
const resolvedSpecifierOverrides = options?.resolvedSpecifierOverrides || getProcessCodeResolvedSpecifierOverrides(sourceId, getProjectRootPath());
|
|
1077
|
+
const bindingOptions = {
|
|
1078
|
+
preserveNonPluginVendorImports: preserveVendorImports,
|
|
1079
|
+
resolvedSpecifierOverrides,
|
|
1080
|
+
};
|
|
2114
1081
|
// Ensure Angular partial declarations are linked before any sanitizers run so runtime never hits the JIT path.
|
|
2115
1082
|
result = linkAngularPartialsIfNeeded(result);
|
|
2116
1083
|
// Post-linker: deduplicate/resolve imports the Angular linker injected with bare specifiers
|
|
@@ -2250,7 +1217,7 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2250
1217
|
// __nsVendorRequire + __nsPick rewrite. Vendor imports stay as bare
|
|
2251
1218
|
// specifiers so the device-side import map resolves them via V8's native
|
|
2252
1219
|
// module system, which correctly handles export * re-exports.
|
|
2253
|
-
result =
|
|
1220
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
2254
1221
|
// Repair any accidental "import ... = expr" assignments that may have slipped in.
|
|
2255
1222
|
try {
|
|
2256
1223
|
result = repairImportEqualsAssignments(result);
|
|
@@ -2346,7 +1313,7 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2346
1313
|
}
|
|
2347
1314
|
// Ensure vendor bindings also apply after potential wrapper injections above
|
|
2348
1315
|
// (idempotent: second pass will be a no-op if imports already consumed).
|
|
2349
|
-
result =
|
|
1316
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
2350
1317
|
try {
|
|
2351
1318
|
result = repairImportEqualsAssignments(result);
|
|
2352
1319
|
}
|
|
@@ -2387,17 +1354,17 @@ function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = fa
|
|
|
2387
1354
|
result = normalizeStrayCoreStringLiterals(result);
|
|
2388
1355
|
}
|
|
2389
1356
|
catch { }
|
|
1357
|
+
try {
|
|
1358
|
+
result = fixDanglingCoreFrom(result);
|
|
1359
|
+
}
|
|
1360
|
+
catch { }
|
|
1361
|
+
try {
|
|
1362
|
+
result = normalizeAnyCoreSpecToBridge(result);
|
|
1363
|
+
}
|
|
1364
|
+
catch { }
|
|
2390
1365
|
result = ensureVariableDynamicImportHelper(result);
|
|
2391
1366
|
// Normalize any lingering @nativescript/core imports to the /ns/core bridge (non-destructive best-effort)
|
|
2392
1367
|
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
1368
|
// Rewrite named imports from the /ns/core bridge into default import + destructuring.
|
|
2402
1369
|
// This makes `import { Frame } from '@nativescript/core'` work even if the bridge provides only a default export.
|
|
2403
1370
|
{
|
|
@@ -2635,6 +1602,9 @@ function assertNoOptimizedArtifacts(code, contextLabel) {
|
|
|
2635
1602
|
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
2636
1603
|
continue;
|
|
2637
1604
|
}
|
|
1605
|
+
if (shouldAllowLocalCoreSanitizerPaths(contextLabel)) {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
2638
1608
|
offenders.push(`${i + 1}: ${ln.substring(0, 200)} [local-core-path]`);
|
|
2639
1609
|
}
|
|
2640
1610
|
if (offenders.length >= 10)
|
|
@@ -2799,6 +1769,7 @@ function dedupeRtNamedImportsAgainstDestructures(code) {
|
|
|
2799
1769
|
export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
2800
1770
|
let result = code;
|
|
2801
1771
|
const httpOriginSafe = httpOrigin;
|
|
1772
|
+
const mixedRuntimePluginHttpRootPackages = collectMixedRuntimePluginHttpRootPackages(result, projectRoot);
|
|
2802
1773
|
const isDynamicImportPrefix = (prefix) => /import\(\s*["']?$/.test(prefix.trimStart());
|
|
2803
1774
|
const importerDir = path.posix.dirname(importerPath);
|
|
2804
1775
|
// Determine importer output relative path (project-relative .mjs) to compute relative imports consistently
|
|
@@ -2969,6 +1940,25 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2969
1940
|
return `${prefix}${spec}${suffix}`;
|
|
2970
1941
|
}
|
|
2971
1942
|
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(spec);
|
|
1943
|
+
const normalizedRuntimePluginSpec = nodeModulesSpecifier || spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '');
|
|
1944
|
+
if (normalizedRuntimePluginSpec && mixedRuntimePluginHttpRootPackages.size > 0) {
|
|
1945
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalizedRuntimePluginSpec, projectRoot);
|
|
1946
|
+
if (packageName && mixedRuntimePluginHttpRootPackages.has(packageName)) {
|
|
1947
|
+
const httpNodeModulesSpecifier = nodeModulesSpecifier || normalizedRuntimePluginSpec;
|
|
1948
|
+
const httpSpec = `/ns/m/node_modules/${httpNodeModulesSpecifier}`;
|
|
1949
|
+
if (httpOriginSafe) {
|
|
1950
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
1951
|
+
}
|
|
1952
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
if (shouldPreserveBareRuntimePluginSubpathImport(spec, projectRoot)) {
|
|
1956
|
+
const httpSpec = `/ns/m/node_modules/${spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '')}`;
|
|
1957
|
+
if (httpOriginSafe) {
|
|
1958
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
1959
|
+
}
|
|
1960
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
1961
|
+
}
|
|
2972
1962
|
const candidateNativeScriptSpec = nodeModulesSpecifier ?? spec;
|
|
2973
1963
|
// ── Node modules routing ──────────────────────────────────────
|
|
2974
1964
|
// Uses the package's own package.json exports field to determine
|
|
@@ -3017,7 +2007,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3017
2007
|
return `${prefix}${out}${suffix}`;
|
|
3018
2008
|
}
|
|
3019
2009
|
// Case B: plain .vue module → rewrite to SFC endpoint or local artifact
|
|
3020
|
-
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, ''));
|
|
2010
|
+
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, '')) || '';
|
|
3021
2011
|
if (vueKey) {
|
|
3022
2012
|
if (true) {
|
|
3023
2013
|
const absVue = vueKey.startsWith('/') ? vueKey : '/' + vueKey;
|
|
@@ -3047,6 +2037,17 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3047
2037
|
// Rewrite relative application imports to HTTP for served modules
|
|
3048
2038
|
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
3049
2039
|
const absMaybe = normalizeImportPath(spec, importerDir);
|
|
2040
|
+
const nodeModulesHttpSpec = absMaybe ? toNodeModulesHttpModuleId(absMaybe) : null;
|
|
2041
|
+
if (nodeModulesHttpSpec) {
|
|
2042
|
+
if (isDynamicImportPrefix(prefix)) {
|
|
2043
|
+
if (verbose)
|
|
2044
|
+
console.log(`[rewrite][http] dynamic relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2045
|
+
return `__nsDynamicHmrImport(${JSON.stringify(nodeModulesHttpSpec)})`;
|
|
2046
|
+
}
|
|
2047
|
+
if (verbose)
|
|
2048
|
+
console.log(`[rewrite][http] relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2049
|
+
return `${prefix}${nodeModulesHttpSpec}${suffix}`;
|
|
2050
|
+
}
|
|
3050
2051
|
const baseId = absMaybe ? toAppModuleBaseId(absMaybe, projectRoot) : null; // e.g. /src/foo.mjs
|
|
3051
2052
|
if (baseId) {
|
|
3052
2053
|
const httpSpec = `/ns/m${baseId}`;
|
|
@@ -3201,6 +2202,13 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
3201
2202
|
console.log(`[rewrite][http] internal ns import (dynamic) → ${spec} via import.meta.url`);
|
|
3202
2203
|
return expr;
|
|
3203
2204
|
}
|
|
2205
|
+
const nodeModulesHttpSpec = toNodeModulesHttpModuleId(spec);
|
|
2206
|
+
if (nodeModulesHttpSpec) {
|
|
2207
|
+
const expr = `import(new URL('${nodeModulesHttpSpec}', import.meta.url).href)`;
|
|
2208
|
+
if (verbose)
|
|
2209
|
+
console.log(`[rewrite][http] absolute dynamic node_modules import → ${nodeModulesHttpSpec} via import.meta.url (from ${spec})`);
|
|
2210
|
+
return expr;
|
|
2211
|
+
}
|
|
3204
2212
|
const baseId = toAppModuleBaseId(spec, projectRoot);
|
|
3205
2213
|
if (!baseId)
|
|
3206
2214
|
return match;
|
|
@@ -3252,7 +2260,8 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3252
2260
|
// Compute a dependency-closed, topologically sorted list of modules for a given set of changed ids.
|
|
3253
2261
|
// Only include application modules we can serve (e.g., under /src and known .vue/.ts/.js entries in the graph).
|
|
3254
2262
|
function computeTxnOrderForChanged(changedIds) {
|
|
3255
|
-
const
|
|
2263
|
+
const includeAppModule = (id) => matchesRuntimeGraphModuleId(id, APP_VIRTUAL_WITH_SLASH, /\.(ts|js|mjs|tsx|jsx)$/i);
|
|
2264
|
+
const includeExt = (id) => ACTIVE_STRATEGY.matchesFile(id) || includeAppModule(id);
|
|
3256
2265
|
const isApp = (id) => id.startsWith(APP_VIRTUAL_WITH_SLASH);
|
|
3257
2266
|
const roots = changedIds.map(normalizeGraphId).filter((id) => graph.has(id) && (isApp(id) || ACTIVE_STRATEGY.matchesFile(id)) && includeExt(id));
|
|
3258
2267
|
const toVisit = new Set();
|
|
@@ -3391,7 +2400,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3391
2400
|
catch { }
|
|
3392
2401
|
});
|
|
3393
2402
|
}
|
|
3394
|
-
function upsertGraphModule(rawId, code, deps) {
|
|
2403
|
+
function upsertGraphModule(rawId, code, deps, options) {
|
|
3395
2404
|
const id = normalizeGraphId(rawId);
|
|
3396
2405
|
const normDeps = deps
|
|
3397
2406
|
.map((d) => normalizeGraphId(d))
|
|
@@ -3400,19 +2409,23 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3400
2409
|
.sort();
|
|
3401
2410
|
const hash = computeHash(code);
|
|
3402
2411
|
const existing = graph.get(id);
|
|
3403
|
-
|
|
3404
|
-
|
|
2412
|
+
const classification = classifyGraphUpsert(existing, hash, normDeps);
|
|
2413
|
+
if (classification === 'unchanged')
|
|
2414
|
+
return existing;
|
|
3405
2415
|
graphVersion++;
|
|
3406
2416
|
const gm = { id, deps: normDeps, hash };
|
|
3407
2417
|
graph.set(id, gm);
|
|
3408
2418
|
if (verbose) {
|
|
3409
2419
|
try {
|
|
3410
|
-
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion });
|
|
2420
|
+
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification });
|
|
3411
2421
|
console.log('[hmr-ws][graph] size', graph.size);
|
|
3412
2422
|
}
|
|
3413
2423
|
catch { }
|
|
3414
2424
|
}
|
|
3415
|
-
|
|
2425
|
+
if (shouldBroadcastGraphUpsertDelta(classification, options?.emitDeltaOnInsert === true, options?.broadcastDelta !== false)) {
|
|
2426
|
+
emitDelta([gm], []);
|
|
2427
|
+
}
|
|
2428
|
+
return gm;
|
|
3416
2429
|
}
|
|
3417
2430
|
function isTypescriptFlavor() {
|
|
3418
2431
|
try {
|
|
@@ -3449,15 +2462,15 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3449
2462
|
}
|
|
3450
2463
|
async function walk(dir) {
|
|
3451
2464
|
for (const name of fs.readdirSync(dir)) {
|
|
3452
|
-
|
|
3453
|
-
if (name === 'node_modules' || name.startsWith('.'))
|
|
2465
|
+
if (name === 'node_modules' || name.startsWith('.') || shouldSkipRuntimeGraphDirectoryName(name))
|
|
3454
2466
|
continue;
|
|
2467
|
+
const full = pathMod.join(dir, name);
|
|
3455
2468
|
try {
|
|
3456
2469
|
const stat = fs.statSync(full);
|
|
3457
2470
|
if (stat.isDirectory())
|
|
3458
2471
|
await walk(full);
|
|
3459
2472
|
else if (stat.isFile()) {
|
|
3460
|
-
if (/\.(vue|ts|js|mjs|tsx|jsx)
|
|
2473
|
+
if (shouldIncludeRuntimeGraphFile(full, /\.(vue|ts|js|mjs|tsx|jsx)$/i)) {
|
|
3461
2474
|
const rel = '/' + pathMod.relative(root, full).split(pathMod.sep).join('/');
|
|
3462
2475
|
// Transform via Vite to gather deps (ignore failures)
|
|
3463
2476
|
try {
|
|
@@ -3527,7 +2540,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3527
2540
|
}, {
|
|
3528
2541
|
maxConcurrent: transformConcurrency,
|
|
3529
2542
|
resultCacheTtlMs: transformCacheMs,
|
|
3530
|
-
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot),
|
|
2543
|
+
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot || process.cwd()),
|
|
3531
2544
|
});
|
|
3532
2545
|
// Attempt early vendor manifest bootstrap once per server.
|
|
3533
2546
|
if (!vendorBootstrapDone) {
|
|
@@ -3572,14 +2585,26 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3572
2585
|
});
|
|
3573
2586
|
// Additional connection diagnostics
|
|
3574
2587
|
wss.on('connection', (ws, req) => {
|
|
2588
|
+
const role = getHmrSocketRoleFromRequestUrl(req.url);
|
|
2589
|
+
ws.__nsHmrClientRole = role;
|
|
3575
2590
|
try {
|
|
3576
2591
|
if (verbose) {
|
|
3577
2592
|
const ra = req.socket?.remoteAddress;
|
|
3578
2593
|
const rp = req.socket?.remotePort;
|
|
3579
|
-
console.log('[hmr-ws] Client connected', ra + (rp ? ':' + rp : ''));
|
|
2594
|
+
console.log('[hmr-ws] Client connected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
3580
2595
|
}
|
|
3581
2596
|
}
|
|
3582
2597
|
catch { }
|
|
2598
|
+
ws.on('close', () => {
|
|
2599
|
+
try {
|
|
2600
|
+
if (verbose) {
|
|
2601
|
+
const ra = req.socket?.remoteAddress;
|
|
2602
|
+
const rp = req.socket?.remotePort;
|
|
2603
|
+
console.log('[hmr-ws] Client disconnected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
catch { }
|
|
2607
|
+
});
|
|
3583
2608
|
});
|
|
3584
2609
|
wss.on('error', (err) => {
|
|
3585
2610
|
try {
|
|
@@ -3700,18 +2725,6 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3700
2725
|
res.setHeader('Content-Type', 'application/json');
|
|
3701
2726
|
return void res.end(JSON.stringify({ error: e?.message || String(e) }));
|
|
3702
2727
|
}
|
|
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
2728
|
try {
|
|
3716
2729
|
const origin = getServerOrigin(server);
|
|
3717
2730
|
code = ensureVersionedRtImports(code, origin, graphVersion);
|
|
@@ -3809,119 +2822,21 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3809
2822
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3810
2823
|
res.setHeader('Pragma', 'no-cache');
|
|
3811
2824
|
res.setHeader('Expires', '0');
|
|
3812
|
-
// Support both query (?path=/abs) and path-style (/ns/m/abs)
|
|
3813
|
-
let spec = urlObj.searchParams.get('path') || '';
|
|
3814
|
-
// Optional graph version pin for deterministic boot
|
|
3815
|
-
const forcedVer = urlObj.searchParams.get('v');
|
|
3816
|
-
let bootTaggedRequest = false;
|
|
3817
|
-
if (!spec) {
|
|
3818
|
-
const base = '/ns/m';
|
|
3819
|
-
let rest = urlObj.pathname.slice(base.length);
|
|
3820
|
-
if (rest && rest !== '/')
|
|
3821
|
-
spec = rest;
|
|
3822
|
-
}
|
|
3823
|
-
// Special-case stub for anomalous '@' imports emitted as '/__invalid_at__.mjs'
|
|
3824
|
-
if (spec === '/__invalid_at__.mjs' || spec === '__invalid_at__.mjs') {
|
|
3825
|
-
res.statusCode = 200;
|
|
3826
|
-
res.end("// invalid '@' import stub\nexport {}\n");
|
|
3827
|
-
return;
|
|
3828
|
-
}
|
|
3829
|
-
if (!spec) {
|
|
3830
|
-
res.statusCode = 200;
|
|
3831
|
-
res.end('export {}\n');
|
|
3832
|
-
return;
|
|
3833
|
-
}
|
|
3834
2825
|
const serverRoot = (server.config?.root || process.cwd());
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
try {
|
|
3843
|
-
let changed = true;
|
|
3844
|
-
while (changed) {
|
|
3845
|
-
changed = false;
|
|
3846
|
-
const bootMatch = spec.match(/^\/?__ns_boot__\/[^\/]+(\/.*)?$/);
|
|
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
|
-
}
|
|
3858
|
-
}
|
|
3859
|
-
catch { }
|
|
3860
|
-
// Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
|
|
3861
|
-
try {
|
|
3862
|
-
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
3863
|
-
const rootPosix = toPosix(serverRoot);
|
|
3864
|
-
const specPosix = toPosix(spec);
|
|
3865
|
-
// If spec is an absolute path under the project root, convert to '/'+relative
|
|
3866
|
-
const isAbsFs = /^\//.test(specPosix) || /^[A-Za-z]:\//.test(spec); // posix or win drive
|
|
3867
|
-
if (isAbsFs) {
|
|
3868
|
-
let rel = specPosix.startsWith(rootPosix) ? specPosix.slice(rootPosix.length) : require('path').posix.relative(rootPosix, specPosix);
|
|
3869
|
-
if (!rel.startsWith('..')) {
|
|
3870
|
-
if (!rel.startsWith('/'))
|
|
3871
|
-
rel = '/' + rel;
|
|
3872
|
-
// Ensure leading '/src' style when path maps into src
|
|
3873
|
-
spec = rel;
|
|
3874
|
-
}
|
|
3875
|
-
}
|
|
2826
|
+
const requestContextResult = createNsMRequestContext(req.url || '', serverRoot, APP_VIRTUAL_WITH_SLASH);
|
|
2827
|
+
if (requestContextResult.kind === 'next')
|
|
2828
|
+
return next();
|
|
2829
|
+
if (requestContextResult.kind === 'response') {
|
|
2830
|
+
res.statusCode = requestContextResult.statusCode;
|
|
2831
|
+
res.end(requestContextResult.code);
|
|
2832
|
+
return;
|
|
3876
2833
|
}
|
|
3877
|
-
|
|
2834
|
+
let { spec, forcedVer, bootTaggedRequest, transformCandidates, candidates } = requestContextResult.value;
|
|
3878
2835
|
// Serve Vite virtual modules (/@id/ prefix). These are internal
|
|
3879
2836
|
// virtual modules (e.g., \0nsvite:nsconfig-json for ~/package.json)
|
|
3880
2837
|
// that don't exist on disk. Decode the ID and load via plugin container.
|
|
3881
|
-
if (spec.startsWith('/@id/')) {
|
|
3882
|
-
try {
|
|
3883
|
-
// First try Vite's transform pipeline directly
|
|
3884
|
-
const vr = await sharedTransformRequest(spec);
|
|
3885
|
-
if (vr?.code) {
|
|
3886
|
-
res.statusCode = 200;
|
|
3887
|
-
res.end(vr.code);
|
|
3888
|
-
return;
|
|
3889
|
-
}
|
|
3890
|
-
}
|
|
3891
|
-
catch { }
|
|
3892
|
-
try {
|
|
3893
|
-
// Fallback: decode the virtual module ID (__x00__ → \0) and
|
|
3894
|
-
// load through the plugin container directly
|
|
3895
|
-
const rawId = spec.slice('/@id/'.length).replace(/__x00__/g, '\0');
|
|
3896
|
-
const loadResult = await server.pluginContainer.load(rawId);
|
|
3897
|
-
if (loadResult) {
|
|
3898
|
-
const code = typeof loadResult === 'string' ? loadResult : loadResult.code;
|
|
3899
|
-
if (code) {
|
|
3900
|
-
res.statusCode = 200;
|
|
3901
|
-
res.end(code);
|
|
3902
|
-
return;
|
|
3903
|
-
}
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
catch { }
|
|
3907
|
-
}
|
|
3908
|
-
if (spec.startsWith('@/'))
|
|
3909
|
-
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
3910
|
-
if (spec.startsWith('./'))
|
|
3911
|
-
spec = spec.slice(1);
|
|
3912
|
-
if (!spec.startsWith('/'))
|
|
3913
|
-
spec = '/' + spec;
|
|
3914
|
-
const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
|
|
3915
|
-
const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
|
|
3916
|
-
const candidates = [...(hasExt ? [spec] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
|
|
3917
|
-
const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, serverRoot);
|
|
3918
2838
|
let transformed = null;
|
|
3919
2839
|
let resolvedCandidate = null;
|
|
3920
|
-
const rawExplicitModule = tryReadRawExplicitJavaScriptModule(spec, serverRoot);
|
|
3921
|
-
if (rawExplicitModule) {
|
|
3922
|
-
transformed = { code: rawExplicitModule.code };
|
|
3923
|
-
resolvedCandidate = rawExplicitModule.resolvedId;
|
|
3924
|
-
}
|
|
3925
2840
|
// Queue and dedupe transformRequest calls so heavy app graphs do not
|
|
3926
2841
|
// overwhelm Vite with concurrent work. Slow-transform warnings start only
|
|
3927
2842
|
// when the transform actually begins executing, and requests stay pending
|
|
@@ -3929,86 +2844,15 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
3929
2844
|
const transformWithTimeout = (url, timeoutMs = 120000) => {
|
|
3930
2845
|
return sharedTransformRequest(url, timeoutMs);
|
|
3931
2846
|
};
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
}
|
|
3942
|
-
catch { }
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
3945
|
-
// Fallback 1: ask Vite to resolve the id, then transform the resolved id (handles aliases and virtual ids)
|
|
3946
|
-
if (!transformed?.code) {
|
|
3947
|
-
try {
|
|
3948
|
-
const rid = await server.pluginContainer?.resolveId?.(spec, undefined);
|
|
3949
|
-
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3950
|
-
if (ridStr) {
|
|
3951
|
-
const r = await transformWithTimeout(ridStr);
|
|
3952
|
-
if (r?.code) {
|
|
3953
|
-
transformed = r;
|
|
3954
|
-
resolvedCandidate = ridStr;
|
|
3955
|
-
}
|
|
3956
|
-
}
|
|
3957
|
-
}
|
|
3958
|
-
catch { }
|
|
3959
|
-
}
|
|
3960
|
-
// Fallback 1b: if spec is a /node_modules/ path, extract bare specifier
|
|
3961
|
-
// and try resolveId with that. This handles package.json "exports" field
|
|
3962
|
-
// resolution (e.g., solid-js/jsx-runtime → solid-js/dist/solid.js).
|
|
3963
|
-
if (!transformed?.code && spec.includes('/node_modules/')) {
|
|
3964
|
-
try {
|
|
3965
|
-
const nmIdx = spec.lastIndexOf('/node_modules/');
|
|
3966
|
-
const bare = spec.slice(nmIdx + '/node_modules/'.length);
|
|
3967
|
-
if (bare && !bare.startsWith('.')) {
|
|
3968
|
-
const rid = await server.pluginContainer?.resolveId?.(bare, undefined);
|
|
3969
|
-
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3970
|
-
if (ridStr) {
|
|
3971
|
-
const r = await sharedTransformRequest(ridStr);
|
|
3972
|
-
if (r?.code) {
|
|
3973
|
-
transformed = r;
|
|
3974
|
-
resolvedCandidate = ridStr;
|
|
3975
|
-
}
|
|
3976
|
-
}
|
|
3977
|
-
}
|
|
3978
|
-
}
|
|
3979
|
-
catch { }
|
|
3980
|
-
}
|
|
3981
|
-
// Fallback 2: try /@fs absolute path under project root (Vite file system alias)
|
|
3982
|
-
if (!transformed?.code) {
|
|
3983
|
-
try {
|
|
3984
|
-
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
3985
|
-
const rootPosix = toPosix(serverRoot).replace(/\/$/, '');
|
|
3986
|
-
const absPosix = `${rootPosix}${spec.startsWith('/') ? '' : '/'}${spec}`;
|
|
3987
|
-
const fsId = `/@fs${absPosix}`;
|
|
3988
|
-
if (resolveCandidateFilePath(fsId, serverRoot)) {
|
|
3989
|
-
const r = await transformWithTimeout(fsId);
|
|
3990
|
-
if (r?.code) {
|
|
3991
|
-
transformed = r;
|
|
3992
|
-
resolvedCandidate = fsId;
|
|
3993
|
-
}
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
catch { }
|
|
3997
|
-
}
|
|
3998
|
-
// Fallback 3: try adding ?import to hint Vite's transform pipeline
|
|
3999
|
-
if (!transformed?.code) {
|
|
4000
|
-
for (const cand of transformCandidates) {
|
|
4001
|
-
try {
|
|
4002
|
-
const r = await transformWithTimeout(`${cand}${cand.includes('?') ? '&' : '?'}import`);
|
|
4003
|
-
if (r?.code) {
|
|
4004
|
-
transformed = r;
|
|
4005
|
-
resolvedCandidate = `${cand}?import`;
|
|
4006
|
-
break;
|
|
4007
|
-
}
|
|
4008
|
-
}
|
|
4009
|
-
catch { }
|
|
4010
|
-
}
|
|
4011
|
-
}
|
|
2847
|
+
({ transformed, resolvedCandidate } = await resolveNsMTransformedModule({
|
|
2848
|
+
context: requestContextResult.value,
|
|
2849
|
+
transformRequest: transformWithTimeout,
|
|
2850
|
+
resolveId: async (id) => {
|
|
2851
|
+
const resolved = await server.pluginContainer?.resolveId?.(id, undefined);
|
|
2852
|
+
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
2853
|
+
},
|
|
2854
|
+
loadVirtualId: async (id) => await server.pluginContainer.load(id),
|
|
2855
|
+
}));
|
|
4012
2856
|
// Solid HMR: patch @@solid-refresh's $$refreshESM to do inline patching
|
|
4013
2857
|
// during module re-evaluation instead of deferring to hot.accept() callback.
|
|
4014
2858
|
// In NativeScript's HTTP ESM environment, accept callbacks are registered
|
|
@@ -4078,7 +2922,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
4078
2922
|
const id = (resolvedCandidate || spec).replace(/[?#].*$/, '');
|
|
4079
2923
|
// Only track app modules (under APP_VIRTUAL_WITH_SLASH) and ts/js/tsx/jsx/mjs.
|
|
4080
2924
|
const isApp = id.startsWith(APP_VIRTUAL_WITH_SLASH) || id.startsWith('/app/');
|
|
4081
|
-
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id)) {
|
|
2925
|
+
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
|
|
4082
2926
|
const deps = Array.from(collectImportDependencies(code, id));
|
|
4083
2927
|
if (verbose) {
|
|
4084
2928
|
try {
|
|
@@ -4211,172 +3055,47 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4211
3055
|
}
|
|
4212
3056
|
}
|
|
4213
3057
|
}
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
code = cleanCode(code);
|
|
4218
|
-
const isNodeMod = /(?:^|\/)node_modules\//.test(resolvedCandidate || spec || '');
|
|
4219
|
-
code = processCodeForDevice(code, false, true, isNodeMod, resolvedCandidate || spec);
|
|
4220
|
-
// Solid HMR: The NativeScript iOS/Android runtime provides import.meta.hot
|
|
4221
|
-
// natively (via InitializeImportMetaHot in HMRSupport.mm) with C++-backed
|
|
4222
|
-
// persistent hot.data that survives across module re-evaluations.
|
|
4223
|
-
// cleanCode() strips Vite's __vite__createHotContext assignment, which is
|
|
4224
|
-
// correct — the runtime's native hot context is better.
|
|
4225
|
-
// We inject a diagnostic log to trace hot.data state during development.
|
|
4226
|
-
try {
|
|
4227
|
-
if (ACTIVE_STRATEGY?.flavor === 'solid' && /\.(tsx|jsx)$/i.test(resolvedCandidate || spec)) {
|
|
4228
|
-
const moduleId = (resolvedCandidate || spec).replace(/[?#].*$/, '');
|
|
4229
|
-
// Diagnostic: log import.meta.hot state on device to trace solid-refresh flow
|
|
4230
|
-
code = `try{if(typeof import.meta!=='undefined'&&import.meta.hot){var _hd=import.meta.hot.data;var _sr=_hd&&_hd['solid-refresh'];console.log('[solid-hmr][native-hot]',${JSON.stringify(moduleId)},'hasHot=true','hasData=',!!_hd,'hasSolidRefresh=',!!_sr,'dataKeys=',_hd?Object.keys(_hd):[]);}else{console.log('[solid-hmr][native-hot]',${JSON.stringify(moduleId)},'hasHot=',!!(typeof import.meta!=='undefined'&&import.meta.hot));}}catch(e){console.log('[solid-hmr][native-hot] error',e);}\n` + code;
|
|
4231
|
-
console.log('[hmr-ws][solid] diagnostic injected for', moduleId, '(using runtime native import.meta.hot)');
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
catch { }
|
|
4235
|
-
code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, server.config?.root || process.cwd(), !!verbose, undefined, getServerOrigin(server), true);
|
|
4236
|
-
// Expand `export * from "url"` into explicit named re-exports.
|
|
4237
|
-
// NativeScript's HTTP ESM loader may not propagate star-re-exports across
|
|
4238
|
-
// HTTP module boundaries (the namespace object gets direct exports but
|
|
4239
|
-
// misses re-exported names). By expanding to `export { a, b } from "url"`,
|
|
4240
|
-
// the engine sees explicit named exports and resolves them correctly.
|
|
4241
|
-
try {
|
|
4242
|
-
code = await expandStarExports(code, server, server.config?.root || process.cwd(), verbose);
|
|
4243
|
-
}
|
|
4244
|
-
catch (e) {
|
|
4245
|
-
if (verbose)
|
|
4246
|
-
console.warn('[ns/m] export* expansion failed:', e?.message);
|
|
4247
|
-
}
|
|
4248
|
-
// Dedupe any /ns/rt named imports that duplicate destructured bindings off default /ns/rt
|
|
4249
|
-
try {
|
|
4250
|
-
code = dedupeRtNamedImportsAgainstDestructures(code);
|
|
4251
|
-
}
|
|
4252
|
-
catch { }
|
|
4253
|
-
code = ensureVariableDynamicImportHelper(code);
|
|
4254
|
-
// Final safety: guard any plain dynamic import(...) occurrences to reroute anomalous '@' specs
|
|
4255
|
-
try {
|
|
4256
|
-
code = ensureGuardPlainDynamicImports(code, getServerOrigin(server));
|
|
4257
|
-
}
|
|
4258
|
-
catch { }
|
|
4259
|
-
// Extra hardening: normalize any remaining core references to the unified bridge
|
|
4260
|
-
// - Stray string-literals
|
|
4261
|
-
// - Dangling `from` merges
|
|
4262
|
-
// - Any spec (including /node_modules resolves) that still references '@nativescript/core'
|
|
4263
|
-
// Do this right before the final fast-fail assertion. If a rewrite occurred, add a small marker for diagnostics.
|
|
4264
|
-
try {
|
|
4265
|
-
const __before = code;
|
|
4266
|
-
code = normalizeStrayCoreStringLiterals(code);
|
|
4267
|
-
code = fixDanglingCoreFrom(code);
|
|
4268
|
-
code = normalizeAnyCoreSpecToBridge(code);
|
|
4269
|
-
if (code !== __before) {
|
|
4270
|
-
code = `// [hmr-sanitize] core-literal->bridge\n` + code;
|
|
4271
|
-
}
|
|
4272
|
-
}
|
|
4273
|
-
catch { }
|
|
4274
|
-
// Final pass: deduplicate/resolve any bare-specifier imports that slipped
|
|
4275
|
-
// through the pipeline (e.g., extracted from JSDoc comments by import-splitting
|
|
4276
|
-
// regexes, or injected by the Angular linker on already-resolved code).
|
|
4277
|
-
try {
|
|
4278
|
-
code = deduplicateLinkerImports(code);
|
|
4279
|
-
}
|
|
4280
|
-
catch { }
|
|
4281
|
-
// CJS/UMD wrapping: if a module uses module.exports but has no ESM export default,
|
|
4282
|
-
// wrap it with CJS shims so the device HTTP ESM loader can consume it.
|
|
4283
|
-
// This handles npm packages that use CommonJS but aren't pre-bundled by Vite.
|
|
4284
|
-
//
|
|
4285
|
-
// Key constraints this must handle:
|
|
4286
|
-
// - CJS modules often declare local vars with the same names as their exports
|
|
4287
|
-
// (e.g. `function createLTTB() {...}; exports.createLTTB = createLTTB;`)
|
|
4288
|
-
// so `export var { createLTTB }` would cause a duplicate declaration.
|
|
4289
|
-
// - UMD modules reference `this` at top level (undefined in ESM) but
|
|
4290
|
-
// typically fall back to `self` or `globalThis`.
|
|
4291
|
-
// - `module`, `exports` must be shims since they don't exist in ESM.
|
|
4292
|
-
try {
|
|
4293
|
-
code = wrapCommonJsModuleForDevice(code);
|
|
4294
|
-
}
|
|
4295
|
-
catch { }
|
|
3058
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3059
|
+
const serverOrigin = getServerOrigin(server);
|
|
3060
|
+
let code;
|
|
4296
3061
|
try {
|
|
4297
|
-
|
|
3062
|
+
code = await finalizeNsMServedModule({
|
|
3063
|
+
code: transformed.code,
|
|
3064
|
+
spec,
|
|
3065
|
+
resolvedCandidate,
|
|
3066
|
+
forcedVer,
|
|
3067
|
+
bootTaggedRequest,
|
|
3068
|
+
graphVersion: Number(graphVersion || 0),
|
|
3069
|
+
serverOrigin,
|
|
3070
|
+
strategy: ACTIVE_STRATEGY,
|
|
3071
|
+
helpers: {
|
|
3072
|
+
requireGuardSnippet: REQUIRE_GUARD_SNIPPET,
|
|
3073
|
+
cleanCode,
|
|
3074
|
+
processCodeForDevice: (value, sourceId) => processCodeForDevice(value, false, true, /(?:^|\/)node_modules\//.test(sourceId || spec || ''), sourceId || spec),
|
|
3075
|
+
rewriteImports: (value, importerPath) => rewriteImports(value, importerPath, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true),
|
|
3076
|
+
rewriteAngularEntry: (value, importerPath) => prepareAngularEntryForDevice(value, importerPath, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true),
|
|
3077
|
+
expandStarExports: async (value) => expandStarExports(value, server, projectRoot, verbose),
|
|
3078
|
+
dedupeRtNamedImportsAgainstDestructures,
|
|
3079
|
+
ensureVariableDynamicImportHelper,
|
|
3080
|
+
ensureGuardPlainDynamicImports,
|
|
3081
|
+
deduplicateLinkerImports,
|
|
3082
|
+
wrapCommonJsModuleForDevice,
|
|
3083
|
+
assertNoOptimizedArtifacts,
|
|
3084
|
+
ensureVersionedRtImports,
|
|
3085
|
+
ensureDestructureCoreImports,
|
|
3086
|
+
buildBootProgressSnippet,
|
|
3087
|
+
hoistTopLevelStaticImports,
|
|
3088
|
+
warn: (message, error) => {
|
|
3089
|
+
if (verbose)
|
|
3090
|
+
console.warn(`${message}:`, error?.message || error);
|
|
3091
|
+
},
|
|
3092
|
+
},
|
|
3093
|
+
});
|
|
4298
3094
|
}
|
|
4299
3095
|
catch (e) {
|
|
4300
3096
|
res.statusCode = 500;
|
|
4301
3097
|
return void res.end(`throw new Error(${JSON.stringify(e?.message || String(e))});\nexport {};`);
|
|
4302
3098
|
}
|
|
4303
|
-
// Defensive export normalization: if a module defines `routes` and only exports it named,
|
|
4304
|
-
// add a default export alias so both `import { routes }` and `import routes` work.
|
|
4305
|
-
try {
|
|
4306
|
-
if (!/\bexport\s+default\b/.test(code)) {
|
|
4307
|
-
const hasNamedRoutes = /\bexport\s*\{\s*routes\s*\}/.test(code);
|
|
4308
|
-
const hasConstRoutes = /\bconst\s+routes\s*=/.test(code) || /\bvar\s+routes\s*=/.test(code) || /\blet\s+routes\s*=/.test(code);
|
|
4309
|
-
if (hasNamedRoutes && hasConstRoutes) {
|
|
4310
|
-
code += `\nexport default routes;\n`;
|
|
4311
|
-
}
|
|
4312
|
-
}
|
|
4313
|
-
}
|
|
4314
|
-
catch { }
|
|
4315
|
-
try {
|
|
4316
|
-
const verNum = Number(forcedVer || graphVersion || 0);
|
|
4317
|
-
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
4318
|
-
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
4319
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
4320
|
-
}
|
|
4321
|
-
catch { }
|
|
4322
|
-
// Finalize: stamp all internal /ns/m imports with PATH-based cache busting.
|
|
4323
|
-
// IMPORTANT: use path prefix (not ?v= query) because the iOS HTTP ESM loader
|
|
4324
|
-
// strips query params when computing module cache keys, so ?v= doesn't bust the V8 cache.
|
|
4325
|
-
try {
|
|
4326
|
-
const ver = String(forcedVer || graphVersion || 0);
|
|
4327
|
-
const origin = getServerOrigin(server);
|
|
4328
|
-
const hmrPrefix = `/ns/m/__ns_hmr__/v${ver}`;
|
|
4329
|
-
const bootHmrPrefix = `/ns/m/__ns_boot__/b1/__ns_hmr__/v${ver}`;
|
|
4330
|
-
const rewritePath = (p) => {
|
|
4331
|
-
if (!p || !p.startsWith('/ns/m/'))
|
|
4332
|
-
return p;
|
|
4333
|
-
if (p.startsWith('/ns/m/__ns_boot__/'))
|
|
4334
|
-
return p;
|
|
4335
|
-
if (p.startsWith('/ns/m/__ns_hmr__/')) {
|
|
4336
|
-
return bootTaggedRequest ? `/ns/m/__ns_boot__/b1${p.slice('/ns/m'.length)}` : p;
|
|
4337
|
-
}
|
|
4338
|
-
return (bootTaggedRequest ? bootHmrPrefix : hmrPrefix) + p.slice('/ns/m'.length);
|
|
4339
|
-
};
|
|
4340
|
-
// 1) Static imports: import ... from "/ns/m/..."
|
|
4341
|
-
code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4342
|
-
// 2) Side-effect imports: import "/ns/m/..."
|
|
4343
|
-
code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4344
|
-
// 3) Dynamic imports: import("/ns/m/...")
|
|
4345
|
-
code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4346
|
-
// 4) new URL("/ns/m/...", import.meta.url)
|
|
4347
|
-
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4348
|
-
// 5) __ns_import(new URL('/ns/m/...', import.meta.url).href)
|
|
4349
|
-
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\)\.href)/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
4350
|
-
// 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → "${origin}/ns/m/__ns_hmr__/..."
|
|
4351
|
-
try {
|
|
4352
|
-
code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${rewritePath(p1)}`)}`);
|
|
4353
|
-
}
|
|
4354
|
-
catch { }
|
|
4355
|
-
// 7) Also fix SFC new URL('/ns/sfc/...', import.meta.url).href → "${origin}/ns/sfc/<ver>/..."
|
|
4356
|
-
try {
|
|
4357
|
-
code = code.replace(/new\s+URL\(\s*["']\/ns\/sfc(\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}/ns/sfc/${ver}${p1}`)}`);
|
|
4358
|
-
}
|
|
4359
|
-
catch { }
|
|
4360
|
-
}
|
|
4361
|
-
catch { }
|
|
4362
|
-
// Final guard: eliminate any lingering named imports from /ns/core to avoid
|
|
4363
|
-
// evaluation-time "does not provide an export named ..." in the device runtime.
|
|
4364
|
-
try {
|
|
4365
|
-
code = ensureDestructureCoreImports(code);
|
|
4366
|
-
}
|
|
4367
|
-
catch { }
|
|
4368
|
-
// Boot-time module graph progress: while the app is still replacing the
|
|
4369
|
-
// placeholder, emit lightweight progress updates as /ns/m modules begin
|
|
4370
|
-
// evaluating. This keeps the overlay moving during large initial graphs.
|
|
4371
|
-
try {
|
|
4372
|
-
if (bootTaggedRequest) {
|
|
4373
|
-
const bootModuleLabel = String(spec || '').replace(/\\/g, '/');
|
|
4374
|
-
const bootProgressSnippet = buildBootProgressSnippet(bootModuleLabel);
|
|
4375
|
-
code = bootProgressSnippet + code;
|
|
4376
|
-
code = hoistTopLevelStaticImports(code);
|
|
4377
|
-
}
|
|
4378
|
-
}
|
|
4379
|
-
catch { }
|
|
4380
3099
|
// Dev-only: link-check static imports to surface missing bindings early
|
|
4381
3100
|
try {
|
|
4382
3101
|
const devCheck = process.env.NODE_ENV !== 'production';
|
|
@@ -4410,1828 +3129,242 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4410
3129
|
const target = await server.transformRequest(imp.src.replace(/^https?:\/\/[^/]+/, ''));
|
|
4411
3130
|
targetCode = target?.code || '';
|
|
4412
3131
|
}
|
|
4413
|
-
else if (u.pathname.startsWith('/ns/sfc')) {
|
|
4414
|
-
// Delegator re-exports default from /ns/asm — skip; assembler will be checked when imported by upstream
|
|
4415
|
-
continue;
|
|
4416
|
-
}
|
|
4417
|
-
else if (u.pathname.startsWith('/ns/m')) {
|
|
4418
|
-
// Resolve to local project path and transform with same candidate logic as /ns/m handler
|
|
4419
|
-
let local = u.pathname.replace(/^\/ns\/m/, '');
|
|
4420
|
-
try {
|
|
4421
|
-
// Normalize project-relative path
|
|
4422
|
-
if (local.startsWith('@/'))
|
|
4423
|
-
local = APP_VIRTUAL_WITH_SLASH + local.slice(2);
|
|
4424
|
-
if (local.startsWith('./'))
|
|
4425
|
-
local = local.slice(1);
|
|
4426
|
-
if (!local.startsWith('/'))
|
|
4427
|
-
local = '/' + local;
|
|
4428
|
-
const hasExt = /(\.ts|\.tsx|\.js|\.jsx|\.mjs|\.mts|\.cts|\.vue)$/i.test(local);
|
|
4429
|
-
const baseNoExt = hasExt ? local.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : local;
|
|
4430
|
-
const cands = [...(hasExt ? [local] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
|
|
4431
|
-
let t = null;
|
|
4432
|
-
for (const cand of cands) {
|
|
4433
|
-
try {
|
|
4434
|
-
const r = await server.transformRequest(cand);
|
|
4435
|
-
if (r?.code) {
|
|
4436
|
-
t = r;
|
|
4437
|
-
break;
|
|
4438
|
-
}
|
|
4439
|
-
}
|
|
4440
|
-
catch { }
|
|
4441
|
-
if (t?.code)
|
|
4442
|
-
break;
|
|
4443
|
-
}
|
|
4444
|
-
if (!t?.code) {
|
|
4445
|
-
try {
|
|
4446
|
-
const rid = await server.pluginContainer?.resolveId?.(local, undefined);
|
|
4447
|
-
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
4448
|
-
if (ridStr) {
|
|
4449
|
-
const r2 = await server.transformRequest(ridStr);
|
|
4450
|
-
if (r2?.code)
|
|
4451
|
-
t = r2;
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
catch { }
|
|
4455
|
-
}
|
|
4456
|
-
targetCode = t?.code || '';
|
|
4457
|
-
}
|
|
4458
|
-
catch { }
|
|
4459
|
-
}
|
|
4460
|
-
else if (u.pathname.startsWith('/ns/rt') || u.pathname.startsWith('/ns/core')) {
|
|
4461
|
-
// Bridges export named/default as needed; skip default check
|
|
4462
|
-
continue;
|
|
4463
|
-
}
|
|
4464
|
-
}
|
|
4465
|
-
catch { }
|
|
4466
|
-
if (!targetCode)
|
|
4467
|
-
continue;
|
|
4468
|
-
const hasDefault = /\bexport\s+default\b/.test(targetCode) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(targetCode);
|
|
4469
|
-
if (!hasDefault) {
|
|
4470
|
-
// CJS/UMD modules won't have `export default` — they get CJS-wrapped
|
|
4471
|
-
// by the serving pipeline. Only warn, don't fatally block the importer.
|
|
4472
|
-
const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
|
|
4473
|
-
if (hasCjsPattern) {
|
|
4474
|
-
try {
|
|
4475
|
-
console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
|
|
4476
|
-
}
|
|
4477
|
-
catch { }
|
|
4478
|
-
continue;
|
|
4479
|
-
}
|
|
4480
|
-
const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
|
|
4481
|
-
// Emit a module that throws to surface the exact offender
|
|
4482
|
-
res.statusCode = 200;
|
|
4483
|
-
res.end(`throw new Error(${JSON.stringify(msg)});\nexport {};`);
|
|
4484
|
-
return;
|
|
4485
|
-
}
|
|
4486
|
-
}
|
|
4487
|
-
}
|
|
4488
|
-
}
|
|
4489
|
-
}
|
|
4490
|
-
catch (eLC) {
|
|
4491
|
-
try {
|
|
4492
|
-
console.warn('[ns:m][link-check] failed', eLC?.message || eLC);
|
|
4493
|
-
}
|
|
4494
|
-
catch { }
|
|
4495
|
-
}
|
|
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
|
-
res.statusCode = 200;
|
|
4507
|
-
res.end(code);
|
|
4508
|
-
}
|
|
4509
|
-
catch (e) {
|
|
4510
|
-
try {
|
|
4511
|
-
console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
|
|
4512
|
-
}
|
|
4513
|
-
catch { }
|
|
4514
|
-
res.statusCode = 500;
|
|
4515
|
-
res.end('export {}\n');
|
|
4516
|
-
}
|
|
4517
|
-
});
|
|
4518
|
-
// 2.5) ESM runtime bridge for NativeScript-Vue: GET /ns/rt
|
|
4519
|
-
// Provides a single authoritative source of Vue helpers bound to the NativeScript renderer.
|
|
4520
|
-
// V2.1: Lazy ensure bridge — does not statically import vue. It lazily resolves helpers from
|
|
4521
|
-
// globalThis or vendor registry/require on first evaluation, then exports references so SFCs
|
|
4522
|
-
// can immediately call them during module evaluation.
|
|
4523
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4524
|
-
try {
|
|
4525
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4526
|
-
// Accept only /ns/rt and /ns/rt/<ver> for cache-busting semantics
|
|
4527
|
-
if (!(urlObj.pathname === '/ns/rt' || /^\/ns\/rt\/[\d]+$/.test(urlObj.pathname)))
|
|
4528
|
-
return next();
|
|
4529
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4530
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4531
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4532
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4533
|
-
res.setHeader('Expires', '0');
|
|
4534
|
-
const rtVerSeg = urlObj.pathname.replace(/^\/ns\/rt\/?/, '');
|
|
4535
|
-
const rtVer = /^[0-9]+$/.test(rtVerSeg) ? rtVerSeg : String(graphVersion || 0);
|
|
4536
|
-
const origin = getServerOrigin(server);
|
|
4537
|
-
let code = `// [ns-rt][v2.3] NativeScript-Vue runtime bridge (module-scoped cache, no globals)\n` +
|
|
4538
|
-
`const __origin = ((typeof globalThis !== 'undefined' && globalThis && globalThis.__NS_HTTP_ORIGIN__) || (new URL(import.meta.url)).origin);\n` +
|
|
4539
|
-
`let __ns_core_bridge = null; try { import(__origin + "/ns/core/${rtVer}").then(m => { __ns_core_bridge = m; }).catch(() => {}); } catch {}\n` +
|
|
4540
|
-
`const g = globalThis;\n` +
|
|
4541
|
-
`const reg = (g.__nsVendorRegistry ||= new Map());\n` +
|
|
4542
|
-
`const req = reg && reg.get ? (g.__nsVendorRequire || g.__nsRequire || g.require) : (g.__nsRequire || g.require);\n` +
|
|
4543
|
-
`let __cached_rt = null;\n` +
|
|
4544
|
-
`let __cached_vm = null;\n` +
|
|
4545
|
-
`const __RT_REALM_TAG = (globalThis.__NS_RT_REALM__ ||= Math.random().toString(36).slice(2));\n` +
|
|
4546
|
-
// Unconditional one-shot evaluation marker to confirm bridge is executed on device
|
|
4547
|
-
`try { if (!(globalThis.__NS_RT_ONCE__ && globalThis.__NS_RT_ONCE__.eval)) { (globalThis.__NS_RT_ONCE__ ||= {}).eval = true; console.log('[ns-rt] evaluated', { rtRealm: __RT_REALM_TAG }); } } catch {}\n` +
|
|
4548
|
-
`function __ensure(){\n` +
|
|
4549
|
-
` if (__cached_rt) return __cached_rt;\n` +
|
|
4550
|
-
` let vm = null;\n` +
|
|
4551
|
-
` try { vm = reg && reg.has && reg.has('nativescript-vue') ? reg.get('nativescript-vue') : (typeof req==='function' ? req('nativescript-vue') : null); } catch {}\n` +
|
|
4552
|
-
` if (!vm) { try { vm = reg && reg.has && reg.has('vue') ? reg.get('vue') : (typeof req==='function' ? req('vue') : null); } catch {} }\n` +
|
|
4553
|
-
` const rt = (vm && (vm.default ?? vm)) || {};\n` +
|
|
4554
|
-
` __cached_vm = vm;\n` +
|
|
4555
|
-
` __cached_rt = rt;\n` +
|
|
4556
|
-
` return rt;\n` +
|
|
4557
|
-
`}\n` +
|
|
4558
|
-
`// Soft-globals for @nativescript/core when missing (dev-only safety)\n` +
|
|
4559
|
-
`try {\n` +
|
|
4560
|
-
` const dev = typeof __DEV__ !== 'undefined' ? __DEV__ : true;\n` +
|
|
4561
|
-
` if (dev) {\n` +
|
|
4562
|
-
` const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {};\n` +
|
|
4563
|
-
` if (ns) {\n` +
|
|
4564
|
-
` if (!g.Frame && ns.Frame) g.Frame = ns.Frame;\n` +
|
|
4565
|
-
` if (!g.Page && ns.Page) g.Page = ns.Page;\n` +
|
|
4566
|
-
` if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application);\n` +
|
|
4567
|
-
` }\n` +
|
|
4568
|
-
` }\n` +
|
|
4569
|
-
`} catch {}\n` +
|
|
4570
|
-
`const __get = (k) => { const rt = __ensure(); const v = rt && rt[k]; if (typeof v !== 'function' && v === undefined) { throw new Error('[ns-rt] missing export '+k); } return v; };\n` +
|
|
4571
|
-
`export const __realm = __RT_REALM_TAG;\n` +
|
|
4572
|
-
`export const defineComponent = (...a) => (__get('defineComponent'))(...a);\n` +
|
|
4573
|
-
`export const resolveComponent = (...a) => (__ensure().resolveComponent)(...a);\n` +
|
|
4574
|
-
`export const createVNode = (...a) => (__ensure().createVNode)(...a);\n` +
|
|
4575
|
-
`export const createTextVNode = (...a) => (__ensure().createTextVNode)(...a);\n` +
|
|
4576
|
-
`export const createCommentVNode = (...a) => (__ensure().createCommentVNode)(...a);\n` +
|
|
4577
|
-
`export const Fragment = (__ensure().Fragment);\n` +
|
|
4578
|
-
`export const Teleport = (__ensure().Teleport);\n` +
|
|
4579
|
-
`export const Transition = (__ensure().Transition);\n` +
|
|
4580
|
-
`export const TransitionGroup = (__ensure().TransitionGroup);\n` +
|
|
4581
|
-
`export const KeepAlive = (__ensure().KeepAlive);\n` +
|
|
4582
|
-
`export const Suspense = (__ensure().Suspense);\n` +
|
|
4583
|
-
`export const withCtx = (...a) => (__ensure().withCtx)(...a);\n` +
|
|
4584
|
-
`export const openBlock = (...a) => (__ensure().openBlock)(...a);\n` +
|
|
4585
|
-
`export const createBlock = (...a) => (__ensure().createBlock)(...a);\n` +
|
|
4586
|
-
`export const createElementVNode = (...a) => (__ensure().createElementVNode)(...a);\n` +
|
|
4587
|
-
`export const createElementBlock = (...a) => (__ensure().createElementBlock)(...a);\n` +
|
|
4588
|
-
`export const renderSlot = (...a) => (__ensure().renderSlot)(...a);\n` +
|
|
4589
|
-
`export const mergeProps = (...a) => (__ensure().mergeProps)(...a);\n` +
|
|
4590
|
-
`export const toHandlers = (...a) => (__ensure().toHandlers)(...a);\n` +
|
|
4591
|
-
`export const renderList = (...a) => (__ensure().renderList)(...a);\n` +
|
|
4592
|
-
`export const normalizeProps = (...a) => (__ensure().normalizeProps)(...a);\n` +
|
|
4593
|
-
`export const guardReactiveProps = (...a) => (__ensure().guardReactiveProps)(...a);\n` +
|
|
4594
|
-
`export const normalizeClass = (...a) => (__ensure().normalizeClass)(...a);\n` +
|
|
4595
|
-
`export const normalizeStyle = (...a) => (__ensure().normalizeStyle)(...a);\n` +
|
|
4596
|
-
`export const toDisplayString = (...a) => (__ensure().toDisplayString)(...a);\n` +
|
|
4597
|
-
`export const withDirectives = (...a) => (__ensure().withDirectives)(...a);\n` +
|
|
4598
|
-
`export const resolveDirective = (...a) => (__ensure().resolveDirective)(...a);\n` +
|
|
4599
|
-
`export const withModifiers = (...a) => (__ensure().withModifiers)(...a);\n` +
|
|
4600
|
-
`export const withKeys = (...a) => (__ensure().withKeys)(...a);\n` +
|
|
4601
|
-
`export const resolveDynamicComponent = (...a) => (__ensure().resolveDynamicComponent)(...a);\n` +
|
|
4602
|
-
`export const isVNode = (...a) => (__ensure().isVNode)(...a);\n` +
|
|
4603
|
-
`export const cloneVNode = (...a) => (__ensure().cloneVNode)(...a);\n` +
|
|
4604
|
-
`export const isRef = (...a) => (__ensure().isRef)(...a);\n` +
|
|
4605
|
-
`export const ref = (...a) => (__ensure().ref)(...a);\n` +
|
|
4606
|
-
`export const shallowRef = (...a) => (__ensure().shallowRef)(...a);\n` +
|
|
4607
|
-
`export const unref = (...a) => (__ensure().unref)(...a);\n` +
|
|
4608
|
-
`export const computed = (...a) => (__ensure().computed)(...a);\n` +
|
|
4609
|
-
`export const reactive = (...a) => (__ensure().reactive)(...a);\n` +
|
|
4610
|
-
`export const readonly = (...a) => (__ensure().readonly)(...a);\n` +
|
|
4611
|
-
`export const isReactive = (...a) => (__ensure().isReactive)(...a);\n` +
|
|
4612
|
-
`export const isReadonly = (...a) => (__ensure().isReadonly)(...a);\n` +
|
|
4613
|
-
`export const toRaw = (...a) => (__ensure().toRaw)(...a);\n` +
|
|
4614
|
-
`export const markRaw = (...a) => (__ensure().markRaw)(...a);\n` +
|
|
4615
|
-
`export const shallowReactive = (...a) => (__ensure().shallowReactive)(...a);\n` +
|
|
4616
|
-
`export const shallowReadonly = (...a) => (__ensure().shallowReadonly)(...a);\n` +
|
|
4617
|
-
`export const watch = (...a) => (__ensure().watch)(...a);\n` +
|
|
4618
|
-
`export const watchEffect = (...a) => (__ensure().watchEffect)(...a);\n` +
|
|
4619
|
-
`export const watchPostEffect = (...a) => (__ensure().watchPostEffect)(...a);\n` +
|
|
4620
|
-
`export const watchSyncEffect = (...a) => (__ensure().watchSyncEffect)(...a);\n` +
|
|
4621
|
-
`export const onBeforeMount = (...a) => (__ensure().onBeforeMount)(...a);\n` +
|
|
4622
|
-
`export const onMounted = (...a) => (__ensure().onMounted)(...a);\n` +
|
|
4623
|
-
`export const onBeforeUpdate = (...a) => (__ensure().onBeforeUpdate)(...a);\n` +
|
|
4624
|
-
`export const onUpdated = (...a) => (__ensure().onUpdated)(...a);\n` +
|
|
4625
|
-
`export const onBeforeUnmount = (...a) => (__ensure().onBeforeUnmount)(...a);\n` +
|
|
4626
|
-
`export const onUnmounted = (...a) => (__ensure().onUnmounted)(...a);\n` +
|
|
4627
|
-
`export const onActivated = (...a) => (__ensure().onActivated)(...a);\n` +
|
|
4628
|
-
`export const onDeactivated = (...a) => (__ensure().onDeactivated)(...a);\n` +
|
|
4629
|
-
`export const onErrorCaptured = (...a) => (__ensure().onErrorCaptured)(...a);\n` +
|
|
4630
|
-
`export const onRenderTracked = (...a) => (__ensure().onRenderTracked)(...a);\n` +
|
|
4631
|
-
`export const onRenderTriggered = (...a) => (__ensure().onRenderTriggered)(...a);\n` +
|
|
4632
|
-
`export const nextTick = (...a) => (__ensure().nextTick)(...a);\n` +
|
|
4633
|
-
`export const h = (...a) => (__ensure().h)(...a);\n` +
|
|
4634
|
-
`export const provide = (...a) => (__ensure().provide)(...a);\n` +
|
|
4635
|
-
`export const inject = (...a) => (__ensure().inject)(...a);\n` +
|
|
4636
|
-
`export const vShow = (__ensure().vShow);\n` +
|
|
4637
|
-
`export const createApp = (...a) => (__ensure().createApp)(...a);\n` +
|
|
4638
|
-
`export const registerElement = (...a) => (__ensure().registerElement)(...a);\n` +
|
|
4639
|
-
`export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) { try { console.error('[ns-rt] $navigateTo app navigator error', e); } catch {} throw e; } } try { console.error('[ns-rt] $navigateTo unavailable: app navigator missing'); } catch {} throw new Error('$navigateTo unavailable: app navigator missing'); } ;\n` +
|
|
4640
|
-
`export const $navigateBack = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$navigateBack || (vm.default && vm.default.$navigateBack))) || (rt && (rt.$navigateBack || (rt.runtimeHelpers && rt.runtimeHelpers.navigateBack))); let res; try { const via = (impl && (impl === (vm && vm.$navigateBack) || impl === (vm && vm.default && vm.default.$navigateBack))) ? 'vm' : (impl ? 'rt' : 'none'); } catch {} try { if (typeof impl === 'function') res = impl(...a); } catch {} try { const top = (g && g.Frame && g.Frame.topmost && g.Frame.topmost()); if (!res && top && top.canGoBack && top.canGoBack()) { res = top.goBack(); } } catch {} try { const hook = g && (g.__NS_HMR_ON_NAVIGATE_BACK || g.__NS_HMR_ON_BACK || g.__nsAttemptBackRemount); if (typeof hook === 'function') hook(); } catch {} return res; }\n` +
|
|
4641
|
-
`export const $showModal = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$showModal || (vm.default && vm.default.$showModal))) || (rt && (rt.$showModal || (rt.runtimeHelpers && rt.runtimeHelpers.showModal))); try { if (typeof impl === 'function') return impl(...a); } catch (e) { } return undefined; }\n` +
|
|
4642
|
-
`export default {\n` +
|
|
4643
|
-
` defineComponent, resolveComponent, createVNode, createTextVNode, createCommentVNode,\n` +
|
|
4644
|
-
` Fragment, Teleport, Transition, TransitionGroup, KeepAlive, Suspense, withCtx, openBlock,\n` +
|
|
4645
|
-
` createBlock, createElementVNode, createElementBlock, renderSlot, mergeProps, toHandlers,\n` +
|
|
4646
|
-
` renderList, normalizeProps, guardReactiveProps, normalizeClass, normalizeStyle, toDisplayString,\n` +
|
|
4647
|
-
` withDirectives, resolveDirective, withModifiers, withKeys, resolveDynamicComponent,\n` +
|
|
4648
|
-
` isVNode, cloneVNode, isRef, ref, shallowRef, unref, computed, reactive, readonly, isReactive, isReadonly, toRaw, markRaw, shallowReactive, shallowReadonly,\n` +
|
|
4649
|
-
` watch, watchEffect, watchPostEffect, watchSyncEffect, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,\n` +
|
|
4650
|
-
` onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured, onRenderTracked, onRenderTriggered, nextTick, h, provide, inject, vShow, createApp, registerElement,\n` +
|
|
4651
|
-
` $navigateTo, $navigateBack, $showModal\n` +
|
|
4652
|
-
`};\n`;
|
|
4653
|
-
// Prepend guard and ship (harmless, keeps diagnostics consistent)
|
|
4654
|
-
code = REQUIRE_GUARD_SNIPPET + code;
|
|
4655
|
-
res.statusCode = 200;
|
|
4656
|
-
res.end(code);
|
|
4657
|
-
}
|
|
4658
|
-
catch (e) {
|
|
4659
|
-
res.statusCode = 500;
|
|
4660
|
-
res.end('export {}\n');
|
|
4661
|
-
}
|
|
4662
|
-
});
|
|
4663
|
-
// 2.55) Dev-only vendor import unifier: rewrite 'vue'/'nativescript-vue' to /ns/rt/<ver>
|
|
4664
|
-
// This ensures plugins and app share a single Vue/NativeScript-Vue instance/realm.
|
|
4665
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4666
|
-
try {
|
|
4667
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4668
|
-
const p = urlObj.pathname || '';
|
|
4669
|
-
// Ignore our own core/rt bridge endpoints and non-JS assets, but DO allow /ns/m/* through
|
|
4670
|
-
if (/^\/ns\/(?:rt|core)(?:\/|$)/.test(p))
|
|
4671
|
-
return next();
|
|
4672
|
-
if (!/(\.m?js$|\.ts$|\/node_modules\/|\/\.vite\/deps\/|^\/@id\/|^\/@fs\/)/.test(p))
|
|
4673
|
-
return next();
|
|
4674
|
-
if (/\.css($|\?)/.test(p))
|
|
4675
|
-
return next();
|
|
4676
|
-
const reqUrl = req.url || '';
|
|
4677
|
-
const transformed = await server.transformRequest(reqUrl);
|
|
4678
|
-
if (!transformed?.code)
|
|
4679
|
-
return next();
|
|
4680
|
-
const origin = getServerOrigin(server);
|
|
4681
|
-
const ver = Number(graphVersion || 0);
|
|
4682
|
-
const rewrite = ACTIVE_STRATEGY.rewriteVendorSpec;
|
|
4683
|
-
if (!rewrite)
|
|
4684
|
-
return next();
|
|
4685
|
-
const before = transformed.code;
|
|
4686
|
-
const code = rewrite(before, origin, ver);
|
|
4687
|
-
if (code === before)
|
|
4688
|
-
return next();
|
|
4689
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4690
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4691
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4692
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4693
|
-
res.setHeader('Expires', '0');
|
|
4694
|
-
res.statusCode = 200;
|
|
4695
|
-
res.end(code);
|
|
4696
|
-
}
|
|
4697
|
-
catch {
|
|
4698
|
-
return next();
|
|
4699
|
-
}
|
|
4700
|
-
});
|
|
4701
|
-
// 2.6) ESM bridge for @nativescript/core: GET /ns/core[/<ver>][?p=sub/path]
|
|
4702
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4703
|
-
try {
|
|
4704
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4705
|
-
// Match /ns/core, /ns/core/<ver>, and /ns/core/<subpath> (path-based deep imports)
|
|
4706
|
-
if (!urlObj.pathname.startsWith('/ns/core'))
|
|
4707
|
-
return next();
|
|
4708
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4709
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4710
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4711
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4712
|
-
res.setHeader('Expires', '0');
|
|
4713
|
-
const afterCore = urlObj.pathname.replace(/^\/ns\/core\/?/, '');
|
|
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`;
|
|
4721
|
-
// Any @nativescript/core subpath import (including shallow ones like
|
|
4722
|
-
// `utils`) may expose exports that are not available from the root
|
|
4723
|
-
// vendor bundle namespace. Serve the actual transformed module content
|
|
4724
|
-
// instead of the lightweight proxy bridge.
|
|
4725
|
-
if (sub) {
|
|
4726
|
-
try {
|
|
4727
|
-
const normalizedSub = sub.replace(/^\/+/, '');
|
|
4728
|
-
if (!hasExplicitVersion) {
|
|
4729
|
-
res.statusCode = 200;
|
|
4730
|
-
res.end(buildVersionedCoreSubpathAliasModule(normalizedSub, ver));
|
|
4731
|
-
return;
|
|
4732
|
-
}
|
|
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
|
-
if (transformed?.code) {
|
|
4739
|
-
// Minimal pipeline: Vite already produces correct ESM.
|
|
4740
|
-
// ONLY rewrite specifier strings to device-fetchable URLs.
|
|
4741
|
-
// Do NOT run processCodeForDevice, rewriteImports, or any
|
|
4742
|
-
// other heavy transform — those mangle newlines, eat exports,
|
|
4743
|
-
// and cause cascading "does not provide an export" failures.
|
|
4744
|
-
const moduleCode = rewriteSpecifiersForDevice(transformed.code, getServerOrigin(server), Number(ver));
|
|
4745
|
-
res.statusCode = 200;
|
|
4746
|
-
res.end(moduleCode);
|
|
4747
|
-
return;
|
|
4748
|
-
}
|
|
4749
|
-
}
|
|
4750
|
-
catch (e) {
|
|
4751
|
-
try {
|
|
4752
|
-
console.warn('[ns-core-bridge] deep subpath serve failed:', sub, e?.message);
|
|
4753
|
-
}
|
|
4754
|
-
catch { }
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
// Main entry or shallow subpath: use proxy bridge
|
|
4758
|
-
// HTTP-only core bridge: do NOT use require/createRequire. Export a proxy that maps
|
|
4759
|
-
// property access to globalThis first, then to any available vendor registry module.
|
|
4760
|
-
let code = REQUIRE_GUARD_SNIPPET +
|
|
4761
|
-
`// [ns-core-bridge][v${ver}] HTTP-only ESM bridge (default proxy only)\n` +
|
|
4762
|
-
`const g = globalThis;\n` +
|
|
4763
|
-
`const reg = (g.__nsVendorRegistry ||= new Map());\n` +
|
|
4764
|
-
`const __getVendorCore = () => { try { const m = reg && reg.get ? (reg.get(${JSON.stringify(key)}) || reg.get('@nativescript/core')) : null; return (m && (m.__esModule && m.default ? m.default : (m.default || m))) || m || null; } catch { return null; } };\n` +
|
|
4765
|
-
`const __core = new Proxy({}, { get(_t, p){ if (p === 'default') return __core; if (p === Symbol.toStringTag) return 'Module'; try { const vc = __getVendorCore(); if (vc) { const vv = vc[p]; if (vv !== undefined) return vv; } } catch {} try { const v = g[p]; if (v !== undefined) return v; } catch {} return undefined; } });\n` +
|
|
4766
|
-
`// Default export: namespace-like proxy\n` +
|
|
4767
|
-
`export default __core;\n`;
|
|
4768
|
-
res.statusCode = 200;
|
|
4769
|
-
res.end(code);
|
|
4770
|
-
}
|
|
4771
|
-
catch (e) {
|
|
4772
|
-
next();
|
|
4773
|
-
}
|
|
4774
|
-
});
|
|
4775
|
-
// 2.6a) Serve compiled entry runtime module: GET /ns/entry-rt[?v=<ver>]
|
|
4776
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4777
|
-
try {
|
|
4778
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4779
|
-
if (!(urlObj.pathname === '/ns/entry-rt'))
|
|
4780
|
-
return next();
|
|
4781
|
-
try {
|
|
4782
|
-
if (verbose) {
|
|
4783
|
-
const ra = req.socket?.remoteAddress;
|
|
4784
|
-
const rp = req.socket?.remotePort;
|
|
4785
|
-
console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
|
|
4786
|
-
}
|
|
4787
|
-
}
|
|
4788
|
-
catch { }
|
|
4789
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4790
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4791
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4792
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4793
|
-
res.setHeader('Expires', '0');
|
|
4794
|
-
let content = '';
|
|
4795
|
-
try {
|
|
4796
|
-
const _req = createRequire(import.meta.url);
|
|
4797
|
-
const entryRtPath = _req.resolve('@nativescript/vite/hmr/entry-runtime.js');
|
|
4798
|
-
content = readFileSync(entryRtPath, 'utf-8');
|
|
4799
|
-
}
|
|
4800
|
-
catch (e) {
|
|
4801
|
-
// .js not found (source tree without build) — transform .ts on the fly
|
|
4802
|
-
try {
|
|
4803
|
-
const tsPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'entry-runtime.ts');
|
|
4804
|
-
if (existsSync(tsPath)) {
|
|
4805
|
-
const tsSource = readFileSync(tsPath, 'utf-8');
|
|
4806
|
-
const result = babelCore.transformSync(tsSource, {
|
|
4807
|
-
filename: tsPath,
|
|
4808
|
-
plugins: [[pluginTransformTypescript, { isTSX: false, allowDeclareFields: true }]],
|
|
4809
|
-
sourceType: 'module',
|
|
4810
|
-
});
|
|
4811
|
-
if (result?.code) {
|
|
4812
|
-
content = result.code;
|
|
4813
|
-
}
|
|
4814
|
-
}
|
|
4815
|
-
}
|
|
4816
|
-
catch (e2) {
|
|
4817
|
-
if (verbose)
|
|
4818
|
-
console.warn('[hmr-http] entry-runtime.ts transform failed', e2);
|
|
4819
|
-
}
|
|
4820
|
-
if (!content) {
|
|
4821
|
-
content = 'export default async function start(){ console.error("[/ns/entry-rt] not found"); }\n';
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
|
|
4825
|
-
res.statusCode = 200;
|
|
4826
|
-
res.end(content);
|
|
4827
|
-
}
|
|
4828
|
-
catch (e) {
|
|
4829
|
-
console.warn('[hmr-http] /ns/entry-rt error', e);
|
|
4830
|
-
next();
|
|
4831
|
-
}
|
|
4832
|
-
});
|
|
4833
|
-
// 2.6b) HTTP-only app entry endpoint: GET /ns/entry[/<ver>]
|
|
4834
|
-
// Thin wrapper that imports the compiled entry runtime and starts it with parameters.
|
|
4835
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4836
|
-
try {
|
|
4837
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4838
|
-
if (!(urlObj.pathname === '/ns/entry' || /^\/ns\/entry\/[\d]+$/.test(urlObj.pathname)))
|
|
4839
|
-
return next();
|
|
4840
|
-
try {
|
|
4841
|
-
if (verbose) {
|
|
4842
|
-
const ra = req.socket?.remoteAddress;
|
|
4843
|
-
const rp = req.socket?.remotePort;
|
|
4844
|
-
console.log('[hmr-http] GET /ns/entry from', ra + (rp ? ':' + rp : ''));
|
|
4845
|
-
}
|
|
4846
|
-
}
|
|
4847
|
-
catch { }
|
|
4848
|
-
const verSeg = urlObj.pathname.replace(/^\/ns\/entry\/?/, '');
|
|
4849
|
-
// Resolve app main entry to an absolute path-like key used by /ns/m
|
|
4850
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4851
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4852
|
-
res.setHeader('Expires', '0');
|
|
4853
|
-
const ver = /^[0-9]+$/.test(verSeg) ? verSeg : String(graphVersion || 0);
|
|
4854
|
-
const origin = getServerOrigin(server) || `${urlObj.protocol}//${urlObj.host}`;
|
|
4855
|
-
// Resolve app main entry to an absolute path-like key used by /ns/m
|
|
4856
|
-
let mainEntry = '/';
|
|
4857
|
-
try {
|
|
4858
|
-
const pkg = getPackageJson();
|
|
4859
|
-
const main = pkg?.main || DEFAULT_MAIN_ENTRY;
|
|
4860
|
-
const abs = getProjectFilePath(main).replace(/\\/g, '/');
|
|
4861
|
-
// Normalize to '/app/...'
|
|
4862
|
-
const marker = `/${APP_ROOT_DIR}/`;
|
|
4863
|
-
const idx = abs.indexOf(marker);
|
|
4864
|
-
mainEntry = idx >= 0 ? abs.substring(idx) : DEFAULT_MAIN_ENTRY_VIRTUAL;
|
|
4865
|
-
}
|
|
4866
|
-
catch { }
|
|
4867
|
-
// Build a tiny wrapper that imports the compiled entry runtime from the dev server
|
|
4868
|
-
let code = REQUIRE_GUARD_SNIPPET +
|
|
4869
|
-
`// [ns-entry][v${ver}] wrapper (script-safe) bytes will follow\n` +
|
|
4870
|
-
`(async function(){\n` +
|
|
4871
|
-
` let origin = ${JSON.stringify(origin)}; const main = ${JSON.stringify(mainEntry)}; const __ns_graph_ver = ${JSON.stringify(ver)};\n` +
|
|
4872
|
-
` try { const __b = (globalThis && globalThis.__NS_ENTRY_BASE__) ? String(globalThis.__NS_ENTRY_BASE__) : ''; if (__b) { try { const __o = new URL(__b).origin; if (__o) origin = __o; } catch {} } } catch {}\n` +
|
|
4873
|
-
` const __VERBOSE__ = (typeof __NS_ENV_VERBOSE__ !== 'undefined' && __NS_ENV_VERBOSE__) || (globalThis && globalThis.process && globalThis.process.env && globalThis.process.env.verbose) || (globalThis && globalThis.__NS_ENV_VERBOSE__) || ${JSON.stringify(!!verbose)};\n` +
|
|
4874
|
-
` if (__VERBOSE__) console.info('[ns-entry][wrapper] start', { origin, main, ver: __ns_graph_ver });\n` +
|
|
4875
|
-
` async function __ns_import_entry_rt(u){\n` +
|
|
4876
|
-
` // Prefer fetch+eval script transformation to avoid module import limitations on device\n` +
|
|
4877
|
-
` try { const r = await fetch(u); const t = await r.text(); if (__VERBOSE__) console.info('[ns-entry][wrapper] entry-rt fetched bytes', (t&&t.length)||0);\n` +
|
|
4878
|
-
` // Transform 'export default function' or 'export default async function' into global assignment\n` +
|
|
4879
|
-
` let s = t.replace(/export\\s+default\\s+async\\s+function\\s+([A-Za-z0-9_$]+)?/,'globalThis.__NS_START_ENTRY__=async function $1')\n` +
|
|
4880
|
-
` .replace(/export\\s+default\\s+function\\s+([A-Za-z0-9_$]+)?/,'globalThis.__NS_START_ENTRY__=function $1');\n` +
|
|
4881
|
-
` // Fallback: if function-form replacements didn't run, handle expression default export too\n` +
|
|
4882
|
-
` if (String(s).indexOf('__NS_START_ENTRY__') === -1) { s = 'globalThis.__NS_START_ENTRY__=' + s.replace(/export\\s+default\\s*/,''); }\n` +
|
|
4883
|
-
` try { (0,eval)(s); } catch (ee) { console.error('[ns-entry][wrapper] eval entry-rt failed', ee && (ee.message||ee)); throw ee; }\n` +
|
|
4884
|
-
` const fn = globalThis.__NS_START_ENTRY__; if (!fn) { throw new Error('entry-rt missing __NS_START_ENTRY__'); }\n` +
|
|
4885
|
-
` return { default: fn };\n` +
|
|
4886
|
-
` } catch(e) { console.error('[ns-entry][wrapper] entry-rt fetch/eval failed', e && (e.message||e)); throw e; }\n` +
|
|
4887
|
-
` }\n` +
|
|
4888
|
-
` const __entryRtUrl = '/ns/entry-rt?v=' + String(__ns_graph_ver);\n` +
|
|
4889
|
-
` let __mod; try { __mod = await __ns_import_entry_rt(__entryRtUrl); if (__VERBOSE__) console.info('[ns-entry][wrapper] entry-rt ready'); } catch (e) { console.error('[ns-entry][wrapper] failed to prepare entry-rt', e && (e.message||e)); throw e; }\n` +
|
|
4890
|
-
` const startEntry = (__mod && (__mod.default || __mod));\n` +
|
|
4891
|
-
` try { await startEntry({ origin, main, ver: __ns_graph_ver, verbose: !!__VERBOSE__ }); if (__VERBOSE__) console.info('[ns-entry][wrapper] startEntry() resolved'); } catch (e) { console.error('[ns-entry][wrapper] startEntry() failed', e && (e.message||e)); throw e; }\n` +
|
|
4892
|
-
`})();\n`;
|
|
4893
|
-
code = code + `\n//# sourceURL=${origin}/ns/entry`;
|
|
4894
|
-
res.statusCode = 200;
|
|
4895
|
-
res.end(code);
|
|
4896
|
-
}
|
|
4897
|
-
catch (e) {
|
|
4898
|
-
next();
|
|
4899
|
-
}
|
|
4900
|
-
});
|
|
4901
|
-
// 2.6) Transactional HMR endpoint: GET /ns/txn/<ver>
|
|
4902
|
-
// Returns a single ESM that sequentially imports all changed modules for the given graphVersion.
|
|
4903
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4904
|
-
try {
|
|
4905
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4906
|
-
const p = urlObj.pathname || '';
|
|
4907
|
-
if (!p.startsWith('/ns/txn'))
|
|
4908
|
-
return next();
|
|
4909
|
-
let verStr = p.replace('/ns/txn', '').replace(/^\//, '');
|
|
4910
|
-
const ver = Number(verStr || urlObj.searchParams.get('v') || 0);
|
|
4911
|
-
let ids = txnBatches.get(ver) || [];
|
|
4912
|
-
if (!ids.length) {
|
|
4913
|
-
// Attempt to rebuild from any changed modules at this version if present in graph history is unavailable.
|
|
4914
|
-
// Fallback heuristic: use all modules with latest hash change equal to this version (we don't store per-module version, so use any changedIds from query 'ids' if provided)
|
|
4915
|
-
try {
|
|
4916
|
-
const q = (urlObj.searchParams.get('ids') || '')
|
|
4917
|
-
.split(',')
|
|
4918
|
-
.map((s) => s.trim())
|
|
4919
|
-
.filter(Boolean);
|
|
4920
|
-
if (q.length)
|
|
4921
|
-
ids = computeTxnOrderForChanged(q);
|
|
4922
|
-
}
|
|
4923
|
-
catch { }
|
|
4924
|
-
}
|
|
4925
|
-
const origin = getServerOrigin(server) || `${urlObj.protocol}//${urlObj.host}`;
|
|
4926
|
-
const lines = [];
|
|
4927
|
-
lines.push(`// [txn] version=${ver} count=${ids.length}`);
|
|
4928
|
-
if (!ids.length) {
|
|
4929
|
-
lines.push(`export default true;`);
|
|
4930
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4931
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4932
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4933
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4934
|
-
res.setHeader('Expires', '0');
|
|
4935
|
-
res.statusCode = 200;
|
|
4936
|
-
res.end(lines.join('\n'));
|
|
4937
|
-
return;
|
|
4938
|
-
}
|
|
4939
|
-
for (const id of ids) {
|
|
4940
|
-
const isVue = /\.vue$/i.test(id);
|
|
4941
|
-
const safe = id.startsWith('/') ? id : '/' + id;
|
|
4942
|
-
const abs = isVue ? `/ns/asm/${ver}?path=${encodeURIComponent(safe)}` : `/ns/m${safe}`;
|
|
4943
|
-
lines.push(`await import(${JSON.stringify(abs)});`);
|
|
4944
|
-
}
|
|
4945
|
-
lines.push(`export default true;`);
|
|
4946
|
-
const code = lines.join('\n');
|
|
4947
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4948
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4949
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4950
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4951
|
-
res.setHeader('Expires', '0');
|
|
4952
|
-
res.statusCode = 200;
|
|
4953
|
-
res.end(code);
|
|
4954
|
-
return;
|
|
4955
|
-
}
|
|
4956
|
-
catch (e) {
|
|
4957
|
-
/* fallthrough */
|
|
4958
|
-
}
|
|
4959
|
-
return next();
|
|
4960
|
-
});
|
|
4961
|
-
// 3) ESM endpoint for SFC modules: GET /ns/sfc?path=/src/Comp.vue[?vue&type=*] OR /ns/sfc/src/Comp.vue[?vue&type=*]
|
|
4962
|
-
// Also accept alias /ns/sfc
|
|
4963
|
-
// Preserves variant queries (?vue&type=script|template|style) and adds a diagnostic signature comment.
|
|
4964
|
-
server.middlewares.use(async (req, res, next) => {
|
|
4965
|
-
try {
|
|
4966
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4967
|
-
const p = urlObj.pathname;
|
|
4968
|
-
// Only match exactly "/ns/sfc" or paths under it.
|
|
4969
|
-
const isNs = p === '/ns/sfc' || p.startsWith('/ns/sfc/');
|
|
4970
|
-
if (!isNs)
|
|
4971
|
-
return next();
|
|
4972
|
-
if (p.startsWith('/ns/asm') || p.startsWith('/ns/sfc-meta'))
|
|
4973
|
-
return next();
|
|
4974
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4975
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4976
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4977
|
-
res.setHeader('Pragma', 'no-cache');
|
|
4978
|
-
res.setHeader('Expires', '0');
|
|
4979
|
-
const base = '/ns/sfc';
|
|
4980
|
-
// Determine request spec, preserving variant query when present and handling optional version in path
|
|
4981
|
-
let pathParam = urlObj.searchParams.get('path') || ''; // may include its own query
|
|
4982
|
-
const rawRemainder = urlObj.pathname.slice(base.length) || '';
|
|
4983
|
-
let verFromPath = null;
|
|
4984
|
-
let pathStyle = rawRemainder;
|
|
4985
|
-
if (rawRemainder && rawRemainder.startsWith('/')) {
|
|
4986
|
-
const parts = rawRemainder.split('/'); // ["", maybe "<ver>", ...]
|
|
4987
|
-
if (parts.length > 2 && /^[0-9]+$/.test(parts[1] || '')) {
|
|
4988
|
-
verFromPath = parts[1];
|
|
4989
|
-
pathStyle = '/' + parts.slice(2).join('/');
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
if (pathStyle && pathStyle !== '/' && !pathParam) {
|
|
4993
|
-
if (!pathStyle.startsWith('/'))
|
|
4994
|
-
pathStyle = '/' + pathStyle;
|
|
4995
|
-
// Include endpoint query for variant-style requests (e.g. /ns/sfc/Comp.vue?vue&type=template)
|
|
4996
|
-
pathParam = pathStyle + (urlObj.search || '');
|
|
4997
|
-
}
|
|
4998
|
-
let fullSpec = pathParam || '';
|
|
4999
|
-
if (!fullSpec) {
|
|
5000
|
-
res.statusCode = 200;
|
|
5001
|
-
res.end('export {}\n');
|
|
5002
|
-
return;
|
|
5003
|
-
}
|
|
5004
|
-
if (fullSpec.startsWith('@/'))
|
|
5005
|
-
fullSpec = APP_VIRTUAL_WITH_SLASH + fullSpec.slice(2);
|
|
5006
|
-
if (!fullSpec.startsWith('/'))
|
|
5007
|
-
fullSpec = '/' + fullSpec;
|
|
5008
|
-
const isVariant = /[?&]vue&type=/.test(fullSpec);
|
|
5009
|
-
const variantTypeMatch = /[?&]type=([^&]+)/.exec(fullSpec);
|
|
5010
|
-
const variantType = variantTypeMatch?.[1] || null;
|
|
5011
|
-
const isStyleVariant = /[?&]type=style\b/.test(fullSpec);
|
|
5012
|
-
// Determine candidate for transformRequest
|
|
5013
|
-
// For full SFCs we prefer a clean base path + '?vue'; if that fails, try base without query as fallback.
|
|
5014
|
-
let candidate = fullSpec;
|
|
5015
|
-
let transformed = null;
|
|
5016
|
-
if (!isVariant) {
|
|
5017
|
-
const basePath = fullSpec.replace(/[?#].*$/, '');
|
|
5018
|
-
const candidates = [basePath + (basePath.includes('?') ? '&' : '?') + 'vue', basePath];
|
|
5019
|
-
for (const c of candidates) {
|
|
5020
|
-
try {
|
|
5021
|
-
const r = await server.transformRequest(c);
|
|
5022
|
-
if (r?.code) {
|
|
5023
|
-
transformed = r;
|
|
5024
|
-
candidate = c;
|
|
5025
|
-
break;
|
|
5026
|
-
}
|
|
5027
|
-
}
|
|
5028
|
-
catch { }
|
|
5029
|
-
}
|
|
5030
|
-
if (!transformed?.code) {
|
|
5031
|
-
if (verbose) {
|
|
5032
|
-
try {
|
|
5033
|
-
console.warn(`[sfc][serve] transform miss for`, fullSpec);
|
|
5034
|
-
}
|
|
5035
|
-
catch { }
|
|
5036
|
-
}
|
|
5037
|
-
// Emit an erroring module to surface the failure at import site with helpful hints
|
|
5038
|
-
try {
|
|
5039
|
-
const tried = candidates.slice(0, 8);
|
|
5040
|
-
const out = `// [sfc] transform miss kind=full path=${fullSpec.replace(/\n/g, '')} tried=${tried.length}\n` + `throw new Error(${JSON.stringify('[ns/sfc] transform failed for full SFC: ' + fullSpec + ' (tried ' + tried.length + ')')});\nexport {}\n`;
|
|
5041
|
-
res.statusCode = 404;
|
|
5042
|
-
res.end(out);
|
|
5043
|
-
return;
|
|
5044
|
-
}
|
|
5045
|
-
catch {
|
|
5046
|
-
res.statusCode = 404;
|
|
5047
|
-
res.end('export {}\n');
|
|
5048
|
-
return;
|
|
5049
|
-
}
|
|
5050
|
-
}
|
|
5051
|
-
}
|
|
5052
|
-
else {
|
|
5053
|
-
try {
|
|
5054
|
-
transformed = await server.transformRequest(candidate);
|
|
5055
|
-
}
|
|
5056
|
-
catch { }
|
|
5057
|
-
if (!transformed?.code) {
|
|
5058
|
-
try {
|
|
5059
|
-
const out = `// [sfc] transform miss kind=variant path=${fullSpec.replace(/\n/g, '')}\n` + `throw new Error(${JSON.stringify('[ns/sfc] transform failed for variant: ' + fullSpec)});\nexport {}\n`;
|
|
5060
|
-
res.statusCode = 404;
|
|
5061
|
-
res.end(out);
|
|
5062
|
-
return;
|
|
5063
|
-
}
|
|
5064
|
-
catch {
|
|
5065
|
-
res.statusCode = 404;
|
|
5066
|
-
res.end('export {}\n');
|
|
5067
|
-
return;
|
|
5068
|
-
}
|
|
5069
|
-
}
|
|
5070
|
-
}
|
|
5071
|
-
// For style variants, return an empty module immediately
|
|
5072
|
-
if (isStyleVariant) {
|
|
5073
|
-
const sig = `// [sfc] kind=variant:style path=${fullSpec.replace(/\n/g, '')} len=0 default=false\n`;
|
|
5074
|
-
res.statusCode = 200;
|
|
5075
|
-
res.end(`${sig}export {}\n`);
|
|
5076
|
-
return;
|
|
5077
|
-
}
|
|
5078
|
-
let code = transformed.code;
|
|
5079
|
-
// Prepend guard to capture any URL-based require attempts
|
|
5080
|
-
code = REQUIRE_GUARD_SNIPPET + code;
|
|
5081
|
-
const projectRoot = server.config?.root || process.cwd();
|
|
5082
|
-
// IMPORTANT: Do not run cleanCode() on template variant; it can strip required pieces.
|
|
5083
|
-
// We'll handle script/full SFC below, and treat template minimally right away.
|
|
5084
|
-
// Full SFCs delegate to deterministic assembler module; variants (script/template) still go through processing
|
|
5085
|
-
if (!isVariant) {
|
|
5086
|
-
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
5087
|
-
const origin = getServerOrigin(server);
|
|
5088
|
-
const ver = verFromPath || '0';
|
|
5089
|
-
const asmPath = `/ns/asm/${ver}?path=${encodeURIComponent(importerPath)}`;
|
|
5090
|
-
const delegated = `// [sfc] kind=full (delegated to assembler) path=${importerPath}\nexport * from ${JSON.stringify(asmPath)};\nexport { default } from ${JSON.stringify(asmPath)};\n`;
|
|
5091
|
-
res.statusCode = 200;
|
|
5092
|
-
res.end(delegated);
|
|
5093
|
-
return;
|
|
5094
|
-
}
|
|
5095
|
-
else {
|
|
5096
|
-
// Variants
|
|
5097
|
-
if (variantType === 'template') {
|
|
5098
|
-
const preferSelfCompile = !!process.env.NS_HMR_SELF_COMPILE_TEMPLATE;
|
|
5099
|
-
// Compile the template ourselves to guarantee no Vite HMR code and stable output
|
|
5100
|
-
if (preferSelfCompile)
|
|
5101
|
-
try {
|
|
5102
|
-
const projectRootT = server.config?.root || process.cwd();
|
|
5103
|
-
const basePath = fullSpec.replace(/[?#].*$/, '');
|
|
5104
|
-
const abs = path.join(projectRootT, basePath.replace(/^\//, ''));
|
|
5105
|
-
let sfcSrc = '';
|
|
5106
|
-
try {
|
|
5107
|
-
sfcSrc = readFileSync(abs, 'utf-8');
|
|
5108
|
-
}
|
|
5109
|
-
catch { }
|
|
5110
|
-
if (sfcSrc) {
|
|
5111
|
-
const { descriptor } = parse(sfcSrc, { filename: abs });
|
|
5112
|
-
const id = createHash('md5').update(abs).digest('hex').slice(0, 8);
|
|
5113
|
-
let bindingMetadata = undefined;
|
|
5114
|
-
try {
|
|
5115
|
-
const s = compileScript(descriptor, {
|
|
5116
|
-
id,
|
|
5117
|
-
inlineTemplate: false,
|
|
5118
|
-
reactivityTransform: false,
|
|
5119
|
-
});
|
|
5120
|
-
bindingMetadata = s?.bindings;
|
|
5121
|
-
}
|
|
5122
|
-
catch { }
|
|
5123
|
-
const tpl = descriptor.template?.content || '';
|
|
5124
|
-
const ct = compileTemplate({
|
|
5125
|
-
source: tpl,
|
|
5126
|
-
id,
|
|
5127
|
-
filename: abs,
|
|
5128
|
-
isProd: false,
|
|
5129
|
-
ssr: false,
|
|
5130
|
-
compilerOptions: {
|
|
5131
|
-
bindingMetadata,
|
|
5132
|
-
isCustomElement: (tag) => NS_NATIVE_TAGS.has(tag),
|
|
5133
|
-
},
|
|
5134
|
-
});
|
|
5135
|
-
let out = (ct && (ct.code || '')) || '';
|
|
5136
|
-
// Map Vue helper imports to runtime bridge
|
|
5137
|
-
try {
|
|
5138
|
-
out = out.replace(/from\s+["'](?:nativescript-vue|vue)[^"']*["']/g, 'from "/ns/rt"');
|
|
5139
|
-
}
|
|
5140
|
-
catch { }
|
|
5141
|
-
// No import.meta.hot present when compiling ourselves, but keep minimal sanitizer just in case
|
|
5142
|
-
out = processTemplateVariantMinimal(out);
|
|
5143
|
-
code = out;
|
|
5144
|
-
}
|
|
5145
|
-
else {
|
|
5146
|
-
code = 'export {}\n';
|
|
5147
|
-
}
|
|
5148
|
-
}
|
|
5149
|
-
catch (eTplSelf) {
|
|
5150
|
-
if (verbose) {
|
|
5151
|
-
try {
|
|
5152
|
-
console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
|
|
5153
|
-
}
|
|
5154
|
-
catch { }
|
|
5155
|
-
}
|
|
5156
|
-
code = transformed.code || 'export {}\n';
|
|
5157
|
-
code = processTemplateVariantMinimal(code);
|
|
5158
|
-
}
|
|
5159
|
-
else {
|
|
5160
|
-
// Prefer using Vite's template transform and apply minimal sanitization; avoids compiler mismatches and warnings
|
|
5161
|
-
code = transformed.code || 'export {}\n';
|
|
5162
|
-
code = processTemplateVariantMinimal(code);
|
|
5163
|
-
}
|
|
5164
|
-
// fall through to shared post-processing (versioning, signature, etc.)
|
|
5165
|
-
}
|
|
5166
|
-
// Script variants still need vendor mappings and general device processing (no SFC assembly)
|
|
5167
|
-
// IMPORTANT: Use a Babel AST transform to remove imports of the template variant and
|
|
5168
|
-
// neutralize their usage without brittle regex.
|
|
5169
|
-
try {
|
|
5170
|
-
const ast = babelParse(code, {
|
|
5171
|
-
sourceType: 'module',
|
|
5172
|
-
plugins: ['typescript'],
|
|
5173
|
-
});
|
|
5174
|
-
const templateBindings = new Set();
|
|
5175
|
-
const navToLocals = [];
|
|
5176
|
-
const navBackLocals = [];
|
|
5177
|
-
babelTraverse(ast, {
|
|
5178
|
-
ImportDeclaration(path) {
|
|
5179
|
-
const spec = path.node.source.value || '';
|
|
5180
|
-
// Remove template variant imports and collect their local identifiers for neutralization
|
|
5181
|
-
if (typeof spec === 'string' && /\.vue\?[^\n]*type=template/.test(spec)) {
|
|
5182
|
-
const ids = [];
|
|
5183
|
-
for (const s of path.node.specifiers) {
|
|
5184
|
-
if (t.isImportSpecifier(s)) {
|
|
5185
|
-
const imported = t.isIdentifier(s.imported) ? s.imported.name : undefined;
|
|
5186
|
-
const local = t.isIdentifier(s.local) ? s.local.name : undefined;
|
|
5187
|
-
if ((imported === 'render' || imported === undefined) && local)
|
|
5188
|
-
ids.push(local);
|
|
5189
|
-
}
|
|
5190
|
-
else if (t.isImportDefaultSpecifier(s) || t.isImportNamespaceSpecifier(s)) {
|
|
5191
|
-
if (t.isIdentifier(s.local))
|
|
5192
|
-
ids.push(s.local.name);
|
|
5193
|
-
}
|
|
5194
|
-
}
|
|
5195
|
-
ids.forEach((n) => templateBindings.add(n));
|
|
5196
|
-
path.remove();
|
|
5197
|
-
return;
|
|
5198
|
-
}
|
|
5199
|
-
// Rewrite $navigateTo/$navigateBack imports from nativescript-vue (or prebundle) to use globals
|
|
5200
|
-
const isNsVue = typeof spec === 'string' && (/nativescript-vue/.test(spec) || /vendor\.mjs$/.test(spec) || /\/node_modules\/\.vite\/deps\/nativescript-vue\.js/.test(spec));
|
|
5201
|
-
if (isNsVue) {
|
|
5202
|
-
const remain = [];
|
|
5203
|
-
for (const s of path.node.specifiers) {
|
|
5204
|
-
if (t.isImportSpecifier(s)) {
|
|
5205
|
-
const imported = t.isIdentifier(s.imported) ? s.imported.name : undefined;
|
|
5206
|
-
const local = t.isIdentifier(s.local) ? s.local.name : undefined;
|
|
5207
|
-
if (local && (imported === '$navigateTo' || imported === 'navigateTo')) {
|
|
5208
|
-
navToLocals.push(local);
|
|
5209
|
-
continue;
|
|
5210
|
-
}
|
|
5211
|
-
if (local && (imported === '$navigateBack' || imported === 'navigateBack')) {
|
|
5212
|
-
navBackLocals.push(local);
|
|
5213
|
-
continue;
|
|
5214
|
-
}
|
|
5215
|
-
}
|
|
5216
|
-
remain.push(s);
|
|
5217
|
-
}
|
|
5218
|
-
if (remain.length) {
|
|
5219
|
-
path.node.specifiers = remain;
|
|
5220
|
-
}
|
|
5221
|
-
else {
|
|
5222
|
-
path.remove();
|
|
5223
|
-
}
|
|
5224
|
-
}
|
|
5225
|
-
},
|
|
5226
|
-
});
|
|
5227
|
-
if (templateBindings.size) {
|
|
5228
|
-
babelTraverse(ast, {
|
|
5229
|
-
Identifier(path) {
|
|
5230
|
-
if (templateBindings.has(path.node.name)) {
|
|
5231
|
-
path.replaceWith(t.identifier('undefined'));
|
|
5232
|
-
}
|
|
5233
|
-
},
|
|
5234
|
-
AssignmentExpression(path) {
|
|
5235
|
-
// Guard component.render = <alias> to avoid TDZ when alias is undefined
|
|
5236
|
-
if (t.isMemberExpression(path.node.left) &&
|
|
5237
|
-
t.isIdentifier(path.node.left.property, {
|
|
5238
|
-
name: 'render',
|
|
5239
|
-
})) {
|
|
5240
|
-
const e = t.identifier('__e');
|
|
5241
|
-
const guarded = t.tryStatement(t.blockStatement([t.variableDeclaration('const', [t.variableDeclarator(e, path.node.right)]), t.ifStatement(t.logicalExpression('&&', t.binaryExpression('!==', t.unaryExpression('typeof', path.node.left.object, true), t.stringLiteral('undefined')), t.binaryExpression('!==', t.unaryExpression('typeof', e, true), t.stringLiteral('undefined'))), t.blockStatement([t.expressionStatement(t.assignmentExpression('=', path.node.left, e))]))]), t.catchClause(t.identifier('_e'), t.blockStatement([])));
|
|
5242
|
-
path.replaceWithMultiple([guarded]);
|
|
5243
|
-
}
|
|
5244
|
-
},
|
|
5245
|
-
});
|
|
5246
|
-
}
|
|
5247
|
-
let outCode = genCode(ast).code;
|
|
5248
|
-
if (navToLocals.length || navBackLocals.length) {
|
|
5249
|
-
const shimLines = [];
|
|
5250
|
-
for (const n of navToLocals)
|
|
5251
|
-
shimLines.push(`import __ns_rt_nav_to_mod from "/ns/rt";\nconst ${n} = (...args) => __ns_rt_nav_to_mod.$navigateTo(...args);`);
|
|
5252
|
-
for (const n of navBackLocals)
|
|
5253
|
-
shimLines.push(`import __ns_rt_nav_back_mod from "/ns/rt";\nconst ${n} = (...args) => __ns_rt_nav_back_mod.$navigateBack(...args);`);
|
|
5254
|
-
outCode = shimLines.join('\n') + '\n' + outCode;
|
|
5255
|
-
}
|
|
5256
|
-
code = outCode;
|
|
5257
|
-
}
|
|
5258
|
-
catch { }
|
|
5259
|
-
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(fullSpec), fullSpec);
|
|
5260
|
-
// Transform static .vue imports into static imports from the assembler (no TLA) via AST
|
|
5261
|
-
try {
|
|
5262
|
-
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
5263
|
-
const origin = getServerOrigin(server);
|
|
5264
|
-
const ver = verFromPath || '0';
|
|
5265
|
-
const ast2 = babelParse(code, {
|
|
5266
|
-
sourceType: 'module',
|
|
5267
|
-
plugins: ['typescript'],
|
|
5268
|
-
});
|
|
5269
|
-
babelTraverse(ast2, {
|
|
5270
|
-
ImportDeclaration(p) {
|
|
5271
|
-
const src = p.node.source.value || '';
|
|
5272
|
-
if (typeof src !== 'string')
|
|
5273
|
-
return;
|
|
5274
|
-
if (/^https?:\/\//.test(src))
|
|
5275
|
-
return; // leave absolute URLs
|
|
5276
|
-
if (/\.vue(?:$|\?)/.test(src)) {
|
|
5277
|
-
let spec = src;
|
|
5278
|
-
// Resolve to absolute project path
|
|
5279
|
-
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
5280
|
-
spec = path.posix.normalize(path.posix.join(path.posix.dirname(importerPath), spec));
|
|
5281
|
-
if (!spec.startsWith('/'))
|
|
5282
|
-
spec = '/' + spec;
|
|
5283
|
-
}
|
|
5284
|
-
else if (!spec.startsWith('/')) {
|
|
5285
|
-
// Handle '@/'
|
|
5286
|
-
if (spec.startsWith('@@/'))
|
|
5287
|
-
spec = '/' + spec.slice(2);
|
|
5288
|
-
if (spec.startsWith('@/'))
|
|
5289
|
-
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
5290
|
-
}
|
|
5291
|
-
// Strip query for plain .vue (keep variant imports intact)
|
|
5292
|
-
if (!/\bvue&type=/.test(src)) {
|
|
5293
|
-
spec = spec.replace(/[?#].*$/, '');
|
|
5294
|
-
const asmUrl = `/ns/asm/${ver}?path=${encodeURIComponent(spec)}&mode=inline`;
|
|
5295
|
-
p.node.source = t.stringLiteral(asmUrl);
|
|
5296
|
-
}
|
|
5297
|
-
}
|
|
5298
|
-
},
|
|
5299
|
-
});
|
|
5300
|
-
code = genCode(ast2).code;
|
|
5301
|
-
}
|
|
5302
|
-
catch { }
|
|
5303
|
-
// After rewrites, strip any TypeScript syntax from the script variant to avoid device-side parse errors
|
|
5304
|
-
try {
|
|
5305
|
-
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
5306
|
-
const tsRes = await babelCore.transformAsync(code, {
|
|
5307
|
-
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5308
|
-
sourceType: 'module',
|
|
5309
|
-
// Help Babel infer TS parsing even if the virtual filename isn't .ts
|
|
5310
|
-
filename: importerPath.endsWith('.vue') ? importerPath.replace(/\.vue$/, '.ts') : importerPath + '.ts',
|
|
5311
|
-
comments: true,
|
|
5312
|
-
configFile: false,
|
|
5313
|
-
babelrc: false,
|
|
5314
|
-
});
|
|
5315
|
-
if (tsRes?.code) {
|
|
5316
|
-
code = tsRes.code;
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
catch (eTsVar) {
|
|
5320
|
-
if (verbose) {
|
|
5321
|
-
try {
|
|
5322
|
-
console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
|
|
5323
|
-
}
|
|
5324
|
-
catch { }
|
|
5325
|
-
}
|
|
5326
|
-
}
|
|
5327
|
-
}
|
|
5328
|
-
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
5329
|
-
// Only run cleanCode for non-template cases (script/full). Template code must remain intact.
|
|
5330
|
-
if (!isVariant || variantType !== 'template') {
|
|
5331
|
-
code = cleanCode(code);
|
|
5332
|
-
}
|
|
5333
|
-
code = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
5334
|
-
code = ensureVariableDynamicImportHelper(code);
|
|
5335
|
-
try {
|
|
5336
|
-
// For variant requests under /ns/sfc, prefer the version from the path segment when present
|
|
5337
|
-
// so that any internal '/ns/rt', '/ns/core', or '/ns/sfc' imports are aligned with the same version.
|
|
5338
|
-
const verNum = Number(verFromPath || '0');
|
|
5339
|
-
if (Number.isFinite(verNum) && verNum > 0) {
|
|
5340
|
-
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
5341
|
-
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
5342
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
5343
|
-
}
|
|
5344
|
-
else {
|
|
5345
|
-
code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
|
|
5346
|
-
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), graphVersion);
|
|
5347
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), graphVersion);
|
|
5348
|
-
}
|
|
5349
|
-
}
|
|
5350
|
-
catch { }
|
|
5351
|
-
// Final guard for SFC variant output as well
|
|
5352
|
-
try {
|
|
5353
|
-
code = ensureDestructureCoreImports(code);
|
|
5354
|
-
}
|
|
5355
|
-
catch { }
|
|
5356
|
-
// CRITICAL: As a last step for script/template variants, re-run AST normalization and strip
|
|
5357
|
-
// any sentinel destructures that could cause duplicate locals, then re-apply core versioning.
|
|
5358
|
-
try {
|
|
5359
|
-
code = astNormalizeModuleImportsAndHelpers(code);
|
|
5360
|
-
}
|
|
5361
|
-
catch { }
|
|
5362
|
-
try {
|
|
5363
|
-
// Remove any rt->core sentinel destructures that slipped in late
|
|
5364
|
-
code = stripRtCoreSentinel(code);
|
|
5365
|
-
}
|
|
5366
|
-
catch { }
|
|
5367
|
-
try {
|
|
5368
|
-
const verNum = Number(verFromPath || '0');
|
|
5369
|
-
if (Number.isFinite(verNum) && verNum > 0) {
|
|
5370
|
-
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
5371
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
5372
|
-
}
|
|
5373
|
-
else {
|
|
5374
|
-
code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
|
|
5375
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), graphVersion);
|
|
5376
|
-
}
|
|
5377
|
-
}
|
|
5378
|
-
catch { }
|
|
5379
|
-
// Last-chance sanitizer for dangling Vite CJS import helper usages that may surface after late transforms
|
|
5380
|
-
try {
|
|
5381
|
-
code = stripDanglingViteCjsImports(code);
|
|
5382
|
-
}
|
|
5383
|
-
catch { }
|
|
5384
|
-
const hasDefault = /\bexport\s+default\b/.test(code);
|
|
5385
|
-
const kind = isVariant ? `variant:${variantType || 'unknown'}` : 'full';
|
|
5386
|
-
const sig = `// [sfc] kind=${kind} path=${importerPath} len=${code.length} default=${hasDefault} wrapped=${false}\n`;
|
|
5387
|
-
if (verbose) {
|
|
5388
|
-
try {
|
|
5389
|
-
console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
|
|
5390
|
-
}
|
|
5391
|
-
catch { }
|
|
5392
|
-
}
|
|
5393
|
-
// Ensure script variants always provide a default export if they declare a component
|
|
5394
|
-
if (!hasDefault) {
|
|
5395
|
-
// Prefer an explicit identifier if present
|
|
5396
|
-
const m = code.match(/\b(?:const|let|var)\s+(__ns_sfc__|_sfc_main)\b/);
|
|
5397
|
-
if (m && m[1]) {
|
|
5398
|
-
code += `\nexport default ${m[1]};`;
|
|
5399
|
-
}
|
|
5400
|
-
else if (/\b_defineComponent\s*\(|\bdefineComponent\s*\(/.test(code)) {
|
|
5401
|
-
// Fallback: export whichever is defined at runtime without throwing on missing identifiers
|
|
5402
|
-
code += `\nexport default (typeof __ns_sfc__ !== "undefined" ? __ns_sfc__ : (typeof _sfc_main !== "undefined" ? _sfc_main : undefined));`;
|
|
5403
|
-
}
|
|
5404
|
-
}
|
|
5405
|
-
res.statusCode = 200;
|
|
5406
|
-
res.end(sig + code);
|
|
5407
|
-
}
|
|
5408
|
-
catch (e) {
|
|
5409
|
-
res.statusCode = 500;
|
|
5410
|
-
res.end('export {}\n');
|
|
5411
|
-
}
|
|
5412
|
-
});
|
|
5413
|
-
// 4) JSON metadata endpoint for SFCs: GET /ns/sfc-meta?path=/src/Comp.vue OR /ns/sfc-meta/<ver>?path=/src/Comp.vue
|
|
5414
|
-
server.middlewares.use(async (req, res, next) => {
|
|
5415
|
-
try {
|
|
5416
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
5417
|
-
if (!urlObj.pathname.startsWith('/ns/sfc-meta'))
|
|
5418
|
-
return next();
|
|
5419
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
5420
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
5421
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
5422
|
-
res.setHeader('Pragma', 'no-cache');
|
|
5423
|
-
res.setHeader('Expires', '0');
|
|
5424
|
-
// Accept optional version segment similar to /ns/sfc
|
|
5425
|
-
{
|
|
5426
|
-
const metaBase = '/ns/sfc-meta';
|
|
5427
|
-
if (urlObj.pathname.startsWith(metaBase + '/')) {
|
|
5428
|
-
const rawRemainder = urlObj.pathname.slice(metaBase.length);
|
|
5429
|
-
const parts = rawRemainder.split('/');
|
|
5430
|
-
if (parts.length > 2 && /^[0-9]+$/.test(parts[1] || '')) {
|
|
5431
|
-
// consume version but we don't need it server-side
|
|
5432
|
-
}
|
|
5433
|
-
}
|
|
5434
|
-
}
|
|
5435
|
-
let spec = urlObj.searchParams.get('path') || '';
|
|
5436
|
-
if (!spec) {
|
|
5437
|
-
res.statusCode = 400;
|
|
5438
|
-
res.end(JSON.stringify({ error: 'missing path' }));
|
|
5439
|
-
return;
|
|
5440
|
-
}
|
|
5441
|
-
if (spec.startsWith('@/'))
|
|
5442
|
-
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
5443
|
-
if (!spec.startsWith('/'))
|
|
5444
|
-
spec = '/' + spec;
|
|
5445
|
-
const base = spec.replace(/[?#].*$/, '');
|
|
5446
|
-
// Transform variants to inspect exports
|
|
5447
|
-
const [scriptR, templateR] = await Promise.all([server.transformRequest(base + '?vue&type=script'), server.transformRequest(base + '?vue&type=template')]);
|
|
5448
|
-
const scriptCode = scriptR?.code || '';
|
|
5449
|
-
const templateCode = templateR?.code || '';
|
|
5450
|
-
const scriptMeta = extractExportMetadata(scriptCode);
|
|
5451
|
-
// Robust render detection: Vue compiler may emit several shapes:
|
|
5452
|
-
// 1) export function render(_ctx, _cache) { ... }
|
|
5453
|
-
// 2) function render(_ctx,_cache) { ... } (later exported)
|
|
5454
|
-
// 3) export const render = (_ctx,_cache) => { ... }
|
|
5455
|
-
// 4) const render = (...) => { ... } (later exported)
|
|
5456
|
-
// 5) export { render } or export { render as render }
|
|
5457
|
-
// 6) Object property forms (rare in template output) render: (...) => {}
|
|
5458
|
-
const hasRender = /export\s+function\s+render\s*\(/.test(templateCode) || /(?:^|\n)\s*function\s+render\s*\(/.test(templateCode) || /export\s+(?:const|let|var)\s+render\s*=/.test(templateCode) || /(?:^|\n)\s*(?:const|let|var)\s+render\s*=/.test(templateCode) || /\brender\s*[:=]\s*/.test(templateCode) || /export\s*\{\s*render\s*(?:as\s*render)?\s*\}/.test(templateCode);
|
|
5459
|
-
if (hasRender && verbose) {
|
|
5460
|
-
try {
|
|
5461
|
-
console.log('[sfc-meta] detected render for', base);
|
|
5462
|
-
}
|
|
5463
|
-
catch { }
|
|
5464
|
-
}
|
|
5465
|
-
else if (!hasRender && verbose) {
|
|
5466
|
-
try {
|
|
5467
|
-
console.warn('[sfc-meta] render NOT detected for', base);
|
|
5468
|
-
}
|
|
5469
|
-
catch { }
|
|
5470
|
-
}
|
|
5471
|
-
const hash = createHash('md5').update(base).digest('hex').slice(0, 8);
|
|
5472
|
-
const payload = {
|
|
5473
|
-
path: base,
|
|
5474
|
-
hasScript: !!scriptCode,
|
|
5475
|
-
hasTemplate: !!templateCode,
|
|
5476
|
-
hasStyle: false,
|
|
5477
|
-
scriptExports: scriptMeta.named,
|
|
5478
|
-
scriptHasDefault: scriptMeta.hasDefault,
|
|
5479
|
-
templateHasRender: hasRender,
|
|
5480
|
-
hmrId: hash,
|
|
5481
|
-
};
|
|
5482
|
-
res.statusCode = 200;
|
|
5483
|
-
res.end(JSON.stringify(payload));
|
|
5484
|
-
}
|
|
5485
|
-
catch (e) {
|
|
5486
|
-
res.statusCode = 500;
|
|
5487
|
-
res.end(JSON.stringify({ error: e?.message || String(e) }));
|
|
5488
|
-
}
|
|
5489
|
-
});
|
|
5490
|
-
// 5) Deterministic SFC assembler: GET /ns/asm?path=/src/Comp.vue
|
|
5491
|
-
// Place BEFORE any broader /ns/sfc* handlers that might accidentally match and delegate.
|
|
5492
|
-
server.middlewares.use(async (req, res, next) => {
|
|
5493
|
-
try {
|
|
5494
|
-
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
5495
|
-
if (!urlObj.pathname.startsWith('/ns/asm'))
|
|
5496
|
-
return next();
|
|
5497
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
5498
|
-
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
5499
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
5500
|
-
res.setHeader('Pragma', 'no-cache');
|
|
5501
|
-
res.setHeader('Expires', '0');
|
|
5502
|
-
// Optional version segment as first path component after /ns/asm
|
|
5503
|
-
const asmBase = '/ns/asm';
|
|
5504
|
-
const asmRemainder = urlObj.pathname.slice(asmBase.length) || '';
|
|
5505
|
-
let verFromPath = null;
|
|
5506
|
-
if (asmRemainder && asmRemainder.startsWith('/')) {
|
|
5507
|
-
const p = asmRemainder.split('/');
|
|
5508
|
-
if (p.length > 1 && /^[0-9]+$/.test(p[1] || '')) {
|
|
5509
|
-
verFromPath = p[1];
|
|
5510
|
-
}
|
|
5511
|
-
}
|
|
5512
|
-
let spec = urlObj.searchParams.get('path') || '';
|
|
5513
|
-
const diag = urlObj.searchParams.get('diag') === '1';
|
|
5514
|
-
if (!spec) {
|
|
5515
|
-
res.statusCode = 400;
|
|
5516
|
-
res.end('export {}\n');
|
|
5517
|
-
return;
|
|
5518
|
-
}
|
|
5519
|
-
if (spec.startsWith('@/'))
|
|
5520
|
-
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
5521
|
-
if (!spec.startsWith('/'))
|
|
5522
|
-
spec = '/' + spec;
|
|
5523
|
-
const base = spec.replace(/[?#].*$/, '');
|
|
5524
|
-
if (diag) {
|
|
5525
|
-
const code = `// [sfc-asm] ${base} (diag)\n` + `// vue shim for diag-only instantiation\n` + `var _createElementVNode = globalThis.createElementVNode || globalThis._createElementVNode;\n` + `const __ns_sfc__ = { name: ${JSON.stringify(base.split('/').pop() || 'DiagComp')}, render(){ return _createElementVNode ? _createElementVNode('StackLayout') : (globalThis.createElementVNode ? globalThis.createElementVNode('StackLayout') : {}); } };\nexport default __ns_sfc__;\n`;
|
|
5526
|
-
res.statusCode = 200;
|
|
5527
|
-
res.end(code);
|
|
5528
|
-
return;
|
|
5529
|
-
}
|
|
5530
|
-
const projectRoot = server.config?.root || process.cwd();
|
|
5531
|
-
// Ensure variant transforms exist so imports resolve (avoid Promise.all short-circuit on single failure)
|
|
5532
|
-
const safeTransform = async (cand) => {
|
|
5533
|
-
try {
|
|
5534
|
-
return await server.transformRequest(cand);
|
|
5535
|
-
}
|
|
5536
|
-
catch {
|
|
5537
|
-
return null;
|
|
5538
|
-
}
|
|
5539
|
-
};
|
|
5540
|
-
const scriptR = await safeTransform(base + '?vue&type=script');
|
|
5541
|
-
const templateR = await safeTransform(base + '?vue&type=template');
|
|
5542
|
-
const fullR = await safeTransform(base + '?vue');
|
|
5543
|
-
const hasScript = !!scriptR?.code;
|
|
5544
|
-
const hasTemplate = !!templateR?.code;
|
|
5545
|
-
const origin = getServerOrigin(server);
|
|
5546
|
-
const ver = String(verFromPath || graphVersion || Date.now());
|
|
5547
|
-
const scriptUrl = `${origin}/ns/sfc/${ver}${base}?vue&type=script`;
|
|
5548
|
-
const templateCode = templateR?.code || '';
|
|
5549
|
-
// INLINE-FIRST assembler: compile SFC source into a self-contained ESM module (enhanced diagnostics)
|
|
5550
|
-
try {
|
|
5551
|
-
const root = server.config?.root || process.cwd();
|
|
5552
|
-
const abs = path.join(root, base.replace(/^\//, ''));
|
|
5553
|
-
let sfcSrc = '';
|
|
5554
|
-
try {
|
|
5555
|
-
sfcSrc = readFileSync(abs, 'utf-8');
|
|
5556
|
-
}
|
|
5557
|
-
catch { }
|
|
5558
|
-
if (sfcSrc) {
|
|
5559
|
-
const { descriptor } = parse(sfcSrc, { filename: abs });
|
|
5560
|
-
const id = createHash('md5').update(abs).digest('hex').slice(0, 8);
|
|
5561
|
-
// 1) Compile script (prefer inlineTemplate for a complete module)
|
|
5562
|
-
let compiledScript = '';
|
|
5563
|
-
let bindingMetadata = undefined;
|
|
5564
|
-
let triedInlineTemplate = false;
|
|
5565
|
-
let hadScriptDefaultPre = false;
|
|
5566
|
-
let usedInlineScript = false;
|
|
5567
|
-
try {
|
|
5568
|
-
// First try inlineTemplate for a holistic, self-contained module with render + hoists
|
|
5569
|
-
// Use a strict NativeScript native element detector for inlineTemplate that does NOT treat generic PascalCase as native.
|
|
5570
|
-
// This ensures imported components like PageWrapper remain true components and get referenced via bindings.
|
|
5571
|
-
const isNSNative = (tag) => NS_NATIVE_TAGS.has(tag);
|
|
5572
|
-
const sInline = compileScript(descriptor, {
|
|
5573
|
-
id,
|
|
5574
|
-
inlineTemplate: true,
|
|
5575
|
-
reactivityTransform: false,
|
|
5576
|
-
// Pass only strict NS native element predicate; avoid broad PascalCase heuristic here.
|
|
5577
|
-
templateOptions: {
|
|
5578
|
-
compilerOptions: { isCustomElement: isNSNative },
|
|
5579
|
-
},
|
|
5580
|
-
});
|
|
5581
|
-
triedInlineTemplate = true;
|
|
5582
|
-
if (/export\s+default/.test(sInline?.content || '')) {
|
|
5583
|
-
compiledScript = sInline.content;
|
|
5584
|
-
bindingMetadata = sInline?.bindings;
|
|
5585
|
-
hadScriptDefaultPre = true;
|
|
5586
|
-
usedInlineScript = true;
|
|
5587
|
-
}
|
|
5588
|
-
else {
|
|
5589
|
-
// Fallback to standard script (no inline) and attempt separate template compile
|
|
5590
|
-
const s = compileScript(descriptor, {
|
|
5591
|
-
id,
|
|
5592
|
-
inlineTemplate: false,
|
|
5593
|
-
reactivityTransform: false,
|
|
5594
|
-
});
|
|
5595
|
-
compiledScript = s?.content || '';
|
|
5596
|
-
bindingMetadata = s?.bindings;
|
|
5597
|
-
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
5598
|
-
usedInlineScript = false;
|
|
5599
|
-
}
|
|
5600
|
-
}
|
|
5601
|
-
catch (eScript) {
|
|
5602
|
-
if (verbose) {
|
|
5603
|
-
try {
|
|
5604
|
-
console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
|
|
5605
|
-
}
|
|
5606
|
-
catch { }
|
|
5607
|
-
}
|
|
5608
|
-
// Retry without inlineTemplate
|
|
5609
|
-
try {
|
|
5610
|
-
const s = compileScript(descriptor, {
|
|
5611
|
-
id,
|
|
5612
|
-
inlineTemplate: false,
|
|
5613
|
-
reactivityTransform: false,
|
|
5614
|
-
});
|
|
5615
|
-
compiledScript = s?.content || '';
|
|
5616
|
-
bindingMetadata = s?.bindings;
|
|
5617
|
-
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
5618
|
-
usedInlineScript = false;
|
|
5619
|
-
}
|
|
5620
|
-
catch (eNoInline) {
|
|
5621
|
-
if (verbose) {
|
|
5622
|
-
try {
|
|
5623
|
-
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5624
|
-
}
|
|
5625
|
-
catch { }
|
|
5626
|
-
}
|
|
5627
|
-
}
|
|
5628
|
-
}
|
|
5629
|
-
// Final fallback: if script compile yielded nothing, use the variant-transformed script
|
|
5630
|
-
if (!compiledScript && scriptR?.code) {
|
|
5631
|
-
try {
|
|
5632
|
-
compiledScript = scriptR.code;
|
|
5633
|
-
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
5634
|
-
}
|
|
5635
|
-
catch { }
|
|
5636
|
-
}
|
|
5637
|
-
// If inlineTemplate produced a default export AND visibly contains a render, allow early-return.
|
|
5638
|
-
// Visible render forms we accept:
|
|
5639
|
-
// - export function render(...) { ... }
|
|
5640
|
-
// - setup(...) { ... return (_ctx, _cache) => { ... } }
|
|
5641
|
-
const hasInlineRender = /(^|\n)\s*export\s+function\s+render\s*\(/.test(compiledScript || '') || /\breturn\s*\(\s*_ctx\s*,\s*_cache\s*\)\s*=>\s*\{/.test(compiledScript || '');
|
|
5642
|
-
// Always use canonical assembler path; avoid inlineTemplate early-return which can miss render attachment
|
|
5643
|
-
// If we reached here, we are going to assemble canonically. Ensure the script we use does NOT include inlineTemplate render.
|
|
5644
|
-
if (usedInlineScript) {
|
|
5645
|
-
try {
|
|
5646
|
-
const sNoInline = compileScript(descriptor, {
|
|
5647
|
-
id,
|
|
5648
|
-
inlineTemplate: false,
|
|
5649
|
-
reactivityTransform: false,
|
|
5650
|
-
});
|
|
5651
|
-
compiledScript = sNoInline?.content || compiledScript;
|
|
5652
|
-
bindingMetadata = sNoInline?.bindings || bindingMetadata;
|
|
5653
|
-
}
|
|
5654
|
-
catch (eNoInline) {
|
|
5655
|
-
if (verbose) {
|
|
5656
|
-
try {
|
|
5657
|
-
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
5658
|
-
}
|
|
5659
|
-
catch { }
|
|
5660
|
-
}
|
|
5661
|
-
}
|
|
5662
|
-
}
|
|
5663
|
-
// 2) Compile template
|
|
5664
|
-
let compiledTplCode = '';
|
|
5665
|
-
let templateErr = null;
|
|
5666
|
-
try {
|
|
5667
|
-
const tplSrc = descriptor.template?.content || '';
|
|
5668
|
-
if (tplSrc) {
|
|
5669
|
-
const ct = compileTemplate({
|
|
5670
|
-
source: tplSrc,
|
|
5671
|
-
id,
|
|
5672
|
-
filename: abs,
|
|
5673
|
-
isProd: false,
|
|
5674
|
-
ssr: false,
|
|
5675
|
-
compilerOptions: {
|
|
5676
|
-
bindingMetadata,
|
|
5677
|
-
isCustomElement: (tag) => NS_NATIVE_TAGS.has(tag),
|
|
5678
|
-
},
|
|
5679
|
-
});
|
|
5680
|
-
compiledTplCode = (ct && (ct.code || '')) || '';
|
|
5681
|
-
if (ct?.errors?.length && verbose) {
|
|
5682
|
-
try {
|
|
5683
|
-
console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
|
|
5684
|
-
}
|
|
5685
|
-
catch { }
|
|
5686
|
-
}
|
|
5687
|
-
}
|
|
5688
|
-
}
|
|
5689
|
-
catch (eTpl) {
|
|
5690
|
-
templateErr = eTpl;
|
|
5691
|
-
if (verbose) {
|
|
5692
|
-
try {
|
|
5693
|
-
console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
|
|
5694
|
-
}
|
|
5695
|
-
catch { }
|
|
5696
|
-
}
|
|
5697
|
-
// Fallback: use the variant-transformed template code if available
|
|
5698
|
-
try {
|
|
5699
|
-
if (templateR?.code)
|
|
5700
|
-
compiledTplCode = templateR.code;
|
|
5701
|
-
}
|
|
5702
|
-
catch { }
|
|
5703
|
-
}
|
|
5704
|
-
// If still no template code, synthesize a minimal render stub so the module is valid
|
|
5705
|
-
if (!compiledTplCode) {
|
|
5706
|
-
try {
|
|
5707
|
-
compiledTplCode = "export function render(){ const _ = (globalThis.createElementVNode||globalThis._createElementVNode); return _? _('StackLayout') : {}; }\n";
|
|
5708
|
-
}
|
|
5709
|
-
catch { }
|
|
5710
|
-
}
|
|
5711
|
-
// 3) Sanitize script and rewrite .vue imports to inline assembler
|
|
5712
|
-
let scriptBody = compiledScript || '';
|
|
5713
|
-
if (scriptBody) {
|
|
5714
|
-
// Do NOT strip Vue/nativescript-vue imports; retarget them to the runtime bridge so helpers (e.g., onMounted) are bound.
|
|
5715
|
-
// Preserve the import clause and only rewrite the source to '/ns/rt'.
|
|
5716
|
-
scriptBody = scriptBody.replace(/(^|\n)\s*import\s+([^;\n]+)\s+from\s+["'](?:vue|nativescript-vue|~\/vendor\.mjs)(?:\/[^"]*)?["'];?/g, (_m, pfx, clause) => `${pfx}import ${clause} from "/ns/rt";`);
|
|
5717
|
-
try {
|
|
5718
|
-
const importerDir = path.posix.dirname(base);
|
|
5719
|
-
scriptBody = scriptBody.replace(/(^|\n)\s*import\s+([^;\n]+)\s+from\s+["']([^"'\n]+\.vue)(?:\?[^"'\n]*)?["'];?/g, (_m, pfx, clause, spec) => {
|
|
5720
|
-
let absImp = spec;
|
|
5721
|
-
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
5722
|
-
absImp = path.posix.normalize(path.posix.join(importerDir, spec));
|
|
5723
|
-
if (!absImp.startsWith('/'))
|
|
5724
|
-
absImp = '/' + absImp;
|
|
5725
|
-
}
|
|
5726
|
-
else if (!spec.startsWith('/')) {
|
|
5727
|
-
if (absImp.startsWith('@/'))
|
|
5728
|
-
absImp = APP_VIRTUAL_WITH_SLASH + absImp.slice(2);
|
|
5729
|
-
}
|
|
5730
|
-
const asmUrl = `/ns/asm/${ver}?path=${encodeURIComponent(absImp)}&mode=inline`;
|
|
5731
|
-
return `${pfx}import ${clause} from ${JSON.stringify(asmUrl)};`;
|
|
5732
|
-
});
|
|
5733
|
-
}
|
|
5734
|
-
catch { }
|
|
5735
|
-
}
|
|
5736
|
-
// 4) Extract render from compiled template and prepare a full inline template block
|
|
5737
|
-
let helperBindings = '';
|
|
5738
|
-
let renderDecl = '';
|
|
5739
|
-
let inlineBlock = undefined;
|
|
5740
|
-
let renderOk = false;
|
|
5741
|
-
if (compiledTplCode) {
|
|
5742
|
-
try {
|
|
5743
|
-
// Build a full inline template block to preserve hoists where possible
|
|
5744
|
-
inlineBlock = buildInlineTemplateBlock(compiledTplCode) || undefined;
|
|
5745
|
-
if (!inlineBlock) {
|
|
5746
|
-
const extracted = extractTemplateRender(compiledTplCode);
|
|
5747
|
-
helperBindings = extracted.helperBindings;
|
|
5748
|
-
renderDecl = extracted.renderDecl;
|
|
5749
|
-
inlineBlock = extracted.inlineBlock;
|
|
5750
|
-
renderOk = extracted.ok;
|
|
5751
|
-
}
|
|
5752
|
-
else {
|
|
5753
|
-
renderOk = true;
|
|
5754
|
-
}
|
|
5755
|
-
}
|
|
5756
|
-
catch (eExtract) {
|
|
5757
|
-
if (verbose) {
|
|
5758
|
-
try {
|
|
5759
|
-
console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
|
|
5760
|
-
}
|
|
5761
|
-
catch { }
|
|
5762
|
-
}
|
|
5763
|
-
}
|
|
5764
|
-
}
|
|
5765
|
-
// Final guard: if no inline render extracted, attempt to import template variant or synthesize a no-op render
|
|
5766
|
-
if (!renderOk && !inlineBlock) {
|
|
5767
|
-
try {
|
|
5768
|
-
const templateUrl = `${origin}/ns/sfc/${ver}${base}?vue&type=template`;
|
|
5769
|
-
const importLine = `import * as __template from ${JSON.stringify(templateUrl)};`;
|
|
5770
|
-
// Attach only if scriptTransformed produces __ns_sfc__ later
|
|
5771
|
-
helperBindings += `\n${importLine}`;
|
|
5772
|
-
renderDecl += `\nfunction __ns_getRender(){\n try {\n if (__template && __template.render) return __template.render;\n } catch (_e) {}\n try {\n const _ = globalThis.createElementVNode || globalThis._createElementVNode;\n return _ ? function(){ return _('StackLayout'); } : function(){ return {}; };\n } catch (_e) { return function(){ return {}; }; }\n}\n`;
|
|
5773
|
-
renderOk = true;
|
|
5774
|
-
}
|
|
5775
|
-
catch { }
|
|
5776
|
-
}
|
|
5777
|
-
// 5) Convert default export to const __ns_sfc__
|
|
5778
|
-
let scriptTransformed = scriptBody;
|
|
5779
|
-
if (scriptTransformed) {
|
|
5780
|
-
scriptTransformed = scriptTransformed.replace(/(^|\n)\s*export\s+default\s+/g, '$1const __ns_sfc__ = ').replace(/(^|\n)\s*export\s*\{[^}]*\}\s*;?\s*/g, '\n/* removed named exports for inline asm */\n');
|
|
5781
|
-
// Normalize any prior declaration of __ns_sfc__ to a plain assignment to avoid redeclare
|
|
5782
|
-
// Accept a semicolon before the declaration too
|
|
5783
|
-
scriptTransformed = scriptTransformed.replace(/(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\s*=\s*/g, '$1__ns_sfc__ = ');
|
|
5784
|
-
// Ensure a single declaration appears once before first assignment
|
|
5785
|
-
if (!/(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\b/.test(scriptTransformed)) {
|
|
5786
|
-
scriptTransformed = `let __ns_sfc__;\n` + scriptTransformed;
|
|
5787
|
-
}
|
|
5788
|
-
// Remove stray leading braces (artifact defense)
|
|
5789
|
-
scriptTransformed = scriptTransformed.replace(/^\s*\}+(?=\s*[^}])/, (m) => `/* [asm-fix] removed ${m.length} stray leading braces */\n`);
|
|
5790
|
-
}
|
|
5791
|
-
else {
|
|
5792
|
-
try {
|
|
5793
|
-
const compName = (base.split('/').pop() || 'Component').replace(/\.vue$/i, '') || 'Component';
|
|
5794
|
-
scriptTransformed = `import { defineComponent as _defineComponent } from "/ns/rt";\nlet __ns_sfc__;\n__ns_sfc__ = /*@__PURE__*/_defineComponent({ __name: ${JSON.stringify(compName)} });`;
|
|
5795
|
-
}
|
|
5796
|
-
catch {
|
|
5797
|
-
scriptTransformed = `import { defineComponent as _defineComponent } from "/ns/rt";\nlet __ns_sfc__;\n__ns_sfc__ = /*@__PURE__*/_defineComponent({});`;
|
|
5798
|
-
}
|
|
5799
|
-
}
|
|
5800
|
-
// 6) Emit final inline module with diagnostics comment
|
|
5801
|
-
const parts = [];
|
|
5802
|
-
parts.push(`// [sfc-asm] ${base} (inline-compiled)`);
|
|
5803
|
-
// Deterministic path: always use extracted helperBindings + renderDecl + scriptTransformed (ignore inlineBlock)
|
|
5804
|
-
// Emit hoisted template bindings first
|
|
5805
|
-
if (helperBindings)
|
|
5806
|
-
parts.push(helperBindings);
|
|
5807
|
-
// IMPORTANT: place script (with its imports) BEFORE renderDecl so imports never appear inside the render function.
|
|
5808
|
-
parts.push(scriptTransformed);
|
|
5809
|
-
parts.push(renderDecl);
|
|
5810
|
-
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
|
-
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
|
-
parts.push(`export default __ns_sfc__`);
|
|
5814
|
-
let inlineCode = parts.filter(Boolean).join('\n');
|
|
5815
|
-
inlineCode = processCodeForDevice(inlineCode, false, true);
|
|
5816
|
-
try {
|
|
5817
|
-
inlineCode = ensureVersionedCoreImports(inlineCode, getServerOrigin(server), Number(ver));
|
|
5818
|
-
}
|
|
5819
|
-
catch { }
|
|
5820
|
-
try {
|
|
5821
|
-
inlineCode = ensureDestructureCoreImports(inlineCode);
|
|
5822
|
-
}
|
|
5823
|
-
catch { }
|
|
5824
|
-
// Replace legacy mutation pipeline with canonical assembler for reliability
|
|
5825
|
-
{
|
|
5826
|
-
// First: strip TypeScript robustly using Babel transform
|
|
5827
|
-
try {
|
|
5828
|
-
const tsRes = await babelCore.transformAsync(scriptTransformed, {
|
|
5829
|
-
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5830
|
-
ast: false,
|
|
5831
|
-
sourceType: 'module',
|
|
5832
|
-
configFile: false,
|
|
5833
|
-
babelrc: false,
|
|
5834
|
-
});
|
|
5835
|
-
if (tsRes?.code)
|
|
5836
|
-
scriptTransformed = tsRes.code;
|
|
5837
|
-
}
|
|
5838
|
-
catch (eTs) {
|
|
5839
|
-
if (verbose) {
|
|
5840
|
-
try {
|
|
5841
|
-
console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
|
|
5842
|
-
}
|
|
5843
|
-
catch { }
|
|
5844
|
-
}
|
|
5845
|
-
}
|
|
5846
|
-
// Hoist imports + strip residual TS via AST
|
|
5847
|
-
let importLines = [];
|
|
5848
|
-
try {
|
|
5849
|
-
const astRes = astExtractImportsAndStripTypes(scriptTransformed);
|
|
5850
|
-
importLines = astRes.imports;
|
|
5851
|
-
scriptTransformed = astRes.body;
|
|
5852
|
-
if (astRes.diagnostics.length && verbose) {
|
|
5853
|
-
try {
|
|
5854
|
-
console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
|
|
5855
|
-
}
|
|
5856
|
-
catch { }
|
|
5857
|
-
}
|
|
5858
|
-
}
|
|
5859
|
-
catch (eAst) {
|
|
5860
|
-
if (verbose) {
|
|
5861
|
-
try {
|
|
5862
|
-
console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
|
|
3132
|
+
else if (u.pathname.startsWith('/ns/sfc')) {
|
|
3133
|
+
// Delegator re-exports default from /ns/asm — skip; assembler will be checked when imported by upstream
|
|
3134
|
+
continue;
|
|
5863
3135
|
}
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
outParts.push(`// diagnostic: hadScriptDefaultPre=${hadScriptDefaultPre} triedInlineTemplate=${triedInlineTemplate} renderOk=${renderOk} tplBytes=${compiledTplCode.length} scriptBytes=${(compiledScript || '').length} templateErr=${templateErr ? templateErr?.message : ''}`);
|
|
5891
|
-
// Export named render as a function that resolves lazily
|
|
5892
|
-
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
|
-
outParts.push('export default __ns_sfc__');
|
|
5894
|
-
let inlineCode2 = outParts.filter(Boolean).join('\n');
|
|
5895
|
-
inlineCode2 = processCodeForDevice(inlineCode2, false, true);
|
|
5896
|
-
try {
|
|
5897
|
-
inlineCode2 = ensureVersionedCoreImports(inlineCode2, getServerOrigin(server), Number(ver));
|
|
5898
|
-
}
|
|
5899
|
-
catch { }
|
|
5900
|
-
try {
|
|
5901
|
-
inlineCode2 = ensureDestructureCoreImports(inlineCode2);
|
|
5902
|
-
}
|
|
5903
|
-
catch { }
|
|
5904
|
-
// Hoist any late imports that accidentally landed after render or script assembly
|
|
5905
|
-
try {
|
|
5906
|
-
const lateImportRe = /^(?!\/\/).*^\s*import\s+[^;]+;?$/gm;
|
|
5907
|
-
const allImports = [];
|
|
5908
|
-
inlineCode2 = inlineCode2.replace(lateImportRe, (imp) => {
|
|
5909
|
-
allImports.push(imp);
|
|
5910
|
-
return '';
|
|
5911
|
-
});
|
|
5912
|
-
if (allImports.length) {
|
|
5913
|
-
// Place after helperBindings sentinel
|
|
5914
|
-
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\]\n)/, `$1${Array.from(new Set(allImports)).join('\n')}\n/* [asm-fix] re-hoisted ${allImports.length} imports */\n`);
|
|
5915
|
-
}
|
|
5916
|
-
}
|
|
5917
|
-
catch { }
|
|
5918
|
-
// After hoisting, re-run AST normalization and duplicate-binding verification.
|
|
5919
|
-
// This guards against freshly hoisted imports reintroducing identifiers that collide
|
|
5920
|
-
// with earlier destructures (e.g., __ns_core_ns_1), which would otherwise surface at device runtime.
|
|
5921
|
-
try {
|
|
5922
|
-
inlineCode2 = astNormalizeModuleImportsAndHelpers(inlineCode2);
|
|
5923
|
-
}
|
|
5924
|
-
catch { }
|
|
5925
|
-
try {
|
|
5926
|
-
inlineCode2 = astVerifyAndAnnotateDuplicates(inlineCode2);
|
|
5927
|
-
if (/^\s*\/\/ \[ast-verify\]\[duplicate-bindings\]/m.test(inlineCode2)) {
|
|
5928
|
-
const diagnosticLine = (inlineCode2.match(/^\s*\/\/ \[ast-verify\]\[duplicate-bindings\][^\n]*/m) || [])[0] || '// [ast-verify][duplicate-bindings]';
|
|
5929
|
-
const brief = diagnosticLine.replace(/^[^:]*:?\s?/, '');
|
|
5930
|
-
const escaped = brief.replace(/["\\]/g, '\\$&');
|
|
5931
|
-
const thrower = `throw new Error("[nsv-hmr] Duplicate top-level bindings detected post-hoist: ${escaped}");`;
|
|
5932
|
-
inlineCode2 = `${thrower}\n` + inlineCode2;
|
|
5933
|
-
}
|
|
5934
|
-
}
|
|
5935
|
-
catch { }
|
|
5936
|
-
// Minimal cleanup only (avoid destructive type stripping breaking object literal property defaults)
|
|
5937
|
-
try {
|
|
5938
|
-
// Heal cases where a TS type strip earlier removed initializer: plain 'default' inside props objects
|
|
5939
|
-
// becomes 'default: undefined'. We only match when followed by ',' or '}' or newline to avoid 'export default'.
|
|
5940
|
-
inlineCode2 = inlineCode2.replace(/\bdefault\b\s*(?=\}|,|\n)/g, 'default: undefined');
|
|
5941
|
-
// Remove obvious leftover angle generic markers
|
|
5942
|
-
inlineCode2 = inlineCode2.replace(/<unknown>/g, '');
|
|
5943
|
-
// Fix accidental '}=> {' sequences
|
|
5944
|
-
inlineCode2 = inlineCode2.replace(/}\s*=>\s*\{/g, '');
|
|
5945
|
-
// No-op: removed prior broken normalization. Handlers are fixed in the dedicated passes below.
|
|
5946
|
-
}
|
|
5947
|
-
catch { }
|
|
5948
|
-
// Removed redundant render closure heal that could inject an extra '}' before component script.
|
|
5949
|
-
// Rewrite any remaining imports (e.g., relative app paths) to HTTP ESM endpoints
|
|
5950
|
-
try {
|
|
5951
|
-
inlineCode2 = rewriteImports(inlineCode2, base, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
5952
|
-
}
|
|
5953
|
-
catch { }
|
|
5954
|
-
// Final TS strip on the whole assembled module (safety net)
|
|
5955
|
-
try {
|
|
5956
|
-
const tsFinal = await babelCore.transformAsync(inlineCode2, {
|
|
5957
|
-
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5958
|
-
ast: false,
|
|
5959
|
-
sourceType: 'module',
|
|
5960
|
-
configFile: false,
|
|
5961
|
-
babelrc: false,
|
|
5962
|
-
});
|
|
5963
|
-
if (tsFinal?.code)
|
|
5964
|
-
inlineCode2 = tsFinal.code;
|
|
5965
|
-
}
|
|
5966
|
-
catch { }
|
|
5967
|
-
// Heal Vue v-model update handlers that lost the ": else" branch during transforms:
|
|
5968
|
-
// "onUpdate:modelValue": _cache[N] || (_cache[N] = $event => _isRef(name) ? name.value = $event)
|
|
5969
|
-
// → add else branch to keep syntax valid: : (name = $event)
|
|
5970
|
-
try {
|
|
5971
|
-
// Fix missing else branch on v-model handlers: support dotted expressions (e.g., $setup.acceptTerms)
|
|
5972
|
-
const reMissingElse = /\"onUpdate:modelValue\"\s*:\s*_cache\[(\d+)\]\s*\|\|\s*\(_cache\[\1\]\s*=\s*\$event\s*=>\s*_isRef\(\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\)\s*\?\s*\2\.value\s*=\s*\$event\s*\)/g;
|
|
5973
|
-
inlineCode2 = inlineCode2.replace(reMissingElse, (_m, idx, expr) => {
|
|
5974
|
-
return `\"onUpdate:modelValue\": _cache[${idx}] || (_cache[${idx}] = $event => (_isRef(${expr}) ? (${expr}.value = $event) : (${expr} = $event)))`;
|
|
5975
|
-
});
|
|
5976
|
-
// Repair malformed handlers without an arrow (introduced by previous transforms):
|
|
5977
|
-
// Convert pattern assigning to $event without an arrow into a proper arrow using the same target expression.
|
|
5978
|
-
const reMalformed = /\"onUpdate:modelValue\"\s*:\s*_cache\[(\d+)\]\s*\|\|\s*\(_cache\[\1\]\s*=\s*[^=]*\(\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\)[^=]*=\s*\$event\s*\)\s*\)/g;
|
|
5979
|
-
inlineCode2 = inlineCode2.replace(reMalformed, (_m, idx, expr) => {
|
|
5980
|
-
return `\"onUpdate:modelValue\": _cache[${idx}] || (_cache[${idx}] = $event => (_isRef(${expr}) ? (${expr}.value = $event) : (${expr} = $event)))`;
|
|
5981
|
-
});
|
|
5982
|
-
}
|
|
5983
|
-
catch { }
|
|
5984
|
-
// Structural heal: ensure balanced braces before the first import statement
|
|
5985
|
-
try {
|
|
5986
|
-
const idx = inlineCode2.search(/^[\t ]*import\b/m);
|
|
5987
|
-
if (idx > 0) {
|
|
5988
|
-
const prefix = inlineCode2.slice(0, idx);
|
|
5989
|
-
let open = 0, close = 0;
|
|
5990
|
-
let inS = false, inD = false, inT = false, inLC = false, inBC = false;
|
|
5991
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
5992
|
-
const ch = prefix[i], nx = prefix[i + 1];
|
|
5993
|
-
if (inLC) {
|
|
5994
|
-
if (ch === '\n')
|
|
5995
|
-
inLC = false;
|
|
5996
|
-
continue;
|
|
5997
|
-
}
|
|
5998
|
-
if (inBC) {
|
|
5999
|
-
if (ch === '*' && nx === '/') {
|
|
6000
|
-
inBC = false;
|
|
6001
|
-
i++;
|
|
6002
|
-
}
|
|
6003
|
-
continue;
|
|
6004
|
-
}
|
|
6005
|
-
if (inS) {
|
|
6006
|
-
if (ch === '\\') {
|
|
6007
|
-
i++;
|
|
6008
|
-
continue;
|
|
6009
|
-
}
|
|
6010
|
-
if (ch === "'")
|
|
6011
|
-
inS = false;
|
|
6012
|
-
continue;
|
|
6013
|
-
}
|
|
6014
|
-
if (inD) {
|
|
6015
|
-
if (ch === '\\') {
|
|
6016
|
-
i++;
|
|
6017
|
-
continue;
|
|
3136
|
+
else if (u.pathname.startsWith('/ns/m')) {
|
|
3137
|
+
// Resolve to local project path and transform with same candidate logic as /ns/m handler
|
|
3138
|
+
let local = u.pathname.replace(/^\/ns\/m/, '');
|
|
3139
|
+
try {
|
|
3140
|
+
// Normalize project-relative path
|
|
3141
|
+
if (local.startsWith('@/'))
|
|
3142
|
+
local = APP_VIRTUAL_WITH_SLASH + local.slice(2);
|
|
3143
|
+
if (local.startsWith('./'))
|
|
3144
|
+
local = local.slice(1);
|
|
3145
|
+
if (!local.startsWith('/'))
|
|
3146
|
+
local = '/' + local;
|
|
3147
|
+
const hasExt = /(\.ts|\.tsx|\.js|\.jsx|\.mjs|\.mts|\.cts|\.vue)$/i.test(local);
|
|
3148
|
+
const baseNoExt = hasExt ? local.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : local;
|
|
3149
|
+
const cands = [...(hasExt ? [local] : []), baseNoExt + '.ts', baseNoExt + '.js', baseNoExt + '.tsx', baseNoExt + '.jsx', baseNoExt + '.mjs', baseNoExt + '.mts', baseNoExt + '.cts', baseNoExt + '.vue', baseNoExt + '/index.ts', baseNoExt + '/index.js', baseNoExt + '/index.tsx', baseNoExt + '/index.jsx', baseNoExt + '/index.mjs'];
|
|
3150
|
+
let t = null;
|
|
3151
|
+
for (const cand of cands) {
|
|
3152
|
+
try {
|
|
3153
|
+
const r = await server.transformRequest(cand);
|
|
3154
|
+
if (r?.code) {
|
|
3155
|
+
t = r;
|
|
3156
|
+
break;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
catch { }
|
|
3160
|
+
if (t?.code)
|
|
3161
|
+
break;
|
|
6018
3162
|
}
|
|
6019
|
-
if (
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
3163
|
+
if (!t?.code) {
|
|
3164
|
+
try {
|
|
3165
|
+
const rid = await server.pluginContainer?.resolveId?.(local, undefined);
|
|
3166
|
+
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3167
|
+
if (ridStr) {
|
|
3168
|
+
const r2 = await server.transformRequest(ridStr);
|
|
3169
|
+
if (r2?.code)
|
|
3170
|
+
t = r2;
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
catch { }
|
|
6027
3174
|
}
|
|
6028
|
-
|
|
6029
|
-
inT = false;
|
|
6030
|
-
continue;
|
|
6031
|
-
}
|
|
6032
|
-
if (ch === '/' && nx === '/') {
|
|
6033
|
-
inLC = true;
|
|
6034
|
-
i++;
|
|
6035
|
-
continue;
|
|
6036
|
-
}
|
|
6037
|
-
if (ch === '/' && nx === '*') {
|
|
6038
|
-
inBC = true;
|
|
6039
|
-
i++;
|
|
6040
|
-
continue;
|
|
6041
|
-
}
|
|
6042
|
-
if (ch === "'") {
|
|
6043
|
-
inS = true;
|
|
6044
|
-
continue;
|
|
6045
|
-
}
|
|
6046
|
-
if (ch === '"') {
|
|
6047
|
-
inD = true;
|
|
6048
|
-
continue;
|
|
6049
|
-
}
|
|
6050
|
-
if (ch === '`') {
|
|
6051
|
-
inT = true;
|
|
6052
|
-
continue;
|
|
3175
|
+
targetCode = t?.code || '';
|
|
6053
3176
|
}
|
|
6054
|
-
|
|
6055
|
-
open++;
|
|
6056
|
-
else if (ch === '}')
|
|
6057
|
-
close++;
|
|
3177
|
+
catch { }
|
|
6058
3178
|
}
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
3179
|
+
else if (u.pathname.startsWith('/ns/rt') || u.pathname.startsWith('/ns/core')) {
|
|
3180
|
+
// Bridges export named/default as needed; skip default check
|
|
3181
|
+
continue;
|
|
6062
3182
|
}
|
|
6063
3183
|
}
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
inlineCode2 = REQUIRE_GUARD_SNIPPET + inlineCode2;
|
|
6082
|
-
// If no render materialized, return a clear error module for deterministic failure
|
|
6083
|
-
try {
|
|
6084
|
-
const lacksRender = !/__ns_render\b/.test(inlineCode2) && !/__ns_sfc__\.render\s*=/.test(inlineCode2);
|
|
6085
|
-
if (lacksRender) {
|
|
6086
|
-
const err = `throw new Error(\"[sfc-asm] ${base}: no render generated by assembler\");\nexport default {};`;
|
|
3184
|
+
catch { }
|
|
3185
|
+
if (!targetCode)
|
|
3186
|
+
continue;
|
|
3187
|
+
const hasDefault = /\bexport\s+default\b/.test(targetCode) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(targetCode);
|
|
3188
|
+
if (!hasDefault) {
|
|
3189
|
+
// CJS/UMD modules won't have `export default` — they get CJS-wrapped
|
|
3190
|
+
// by the serving pipeline. Only warn, don't fatally block the importer.
|
|
3191
|
+
const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
|
|
3192
|
+
if (hasCjsPattern) {
|
|
3193
|
+
try {
|
|
3194
|
+
console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
|
|
3195
|
+
}
|
|
3196
|
+
catch { }
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
|
|
3200
|
+
// Emit a module that throws to surface the exact offender
|
|
6087
3201
|
res.statusCode = 200;
|
|
6088
|
-
res.end(
|
|
3202
|
+
res.end(`throw new Error(${JSON.stringify(msg)});\nexport {};`);
|
|
6089
3203
|
return;
|
|
6090
3204
|
}
|
|
6091
3205
|
}
|
|
6092
|
-
catch { }
|
|
6093
|
-
// Cosmetic and parser-friendly: ensure a newline after the canonical banner
|
|
6094
|
-
try {
|
|
6095
|
-
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\])(?!\n)/, '$1\n');
|
|
6096
|
-
}
|
|
6097
|
-
catch { }
|
|
6098
|
-
// Bust device cache for runtime bridge so helpers are always current for this graph version
|
|
6099
|
-
try {
|
|
6100
|
-
const origin = getServerOrigin(server);
|
|
6101
|
-
inlineCode2 = ensureVersionedRtImports(inlineCode2, origin, Number(ver));
|
|
6102
|
-
inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports(inlineCode2, origin, Number(ver));
|
|
6103
|
-
inlineCode2 = ensureVersionedCoreImports(inlineCode2, origin, Number(ver));
|
|
6104
|
-
}
|
|
6105
|
-
catch { }
|
|
6106
|
-
// Normalize imports/helpers via AST to ensure _defineComponent and other helpers are bound once
|
|
6107
|
-
try {
|
|
6108
|
-
inlineCode2 = astNormalizeModuleImportsAndHelpers(inlineCode2);
|
|
6109
|
-
}
|
|
6110
|
-
catch { }
|
|
6111
|
-
// Guarantee a concrete component object exists before exporting default.
|
|
6112
|
-
try {
|
|
6113
|
-
// Detect an existing declaration of __ns_sfc__ even if it's appended after a semicolon on the same line
|
|
6114
|
-
// e.g., "import ...;let __ns_sfc__;" (no newline). Accept start-of-string, newline, or semicolon as anchors.
|
|
6115
|
-
const hasDecl = /(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\b/.test(inlineCode2);
|
|
6116
|
-
if (!hasDecl) {
|
|
6117
|
-
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\]\n)/, `$1let __ns_sfc__ = {};\n`);
|
|
6118
|
-
}
|
|
6119
|
-
// Heal empty declarations (e.g., "let __ns_sfc__;" → initialize to {}), also when preceded by a semicolon
|
|
6120
|
-
inlineCode2 = inlineCode2.replace(/(^|[\n;])\s*let\s+__ns_sfc__\s*;?/g, '$1let __ns_sfc__ = {};');
|
|
6121
|
-
inlineCode2 = inlineCode2.replace(/(^|[\n;])\s*var\s+__ns_sfc__\s*;?/g, '$1var __ns_sfc__ = {};');
|
|
6122
|
-
}
|
|
6123
|
-
catch { }
|
|
6124
|
-
if (!/export\s+default\s+__ns_sfc__/.test(inlineCode2) && /__ns_sfc__/.test(inlineCode2))
|
|
6125
|
-
inlineCode2 += '\nexport default __ns_sfc__';
|
|
6126
|
-
res.statusCode = 200;
|
|
6127
|
-
res.end(inlineCode2);
|
|
6128
|
-
return;
|
|
6129
3206
|
}
|
|
6130
3207
|
}
|
|
6131
3208
|
}
|
|
6132
|
-
catch {
|
|
6133
|
-
// Do not use compiled ?vue or variant fallbacks; assembler must succeed or emit an error
|
|
6134
|
-
// Prefer compiling template from source via compiler-sfc; fallback to variant extraction
|
|
6135
|
-
let inlineOk = false;
|
|
6136
|
-
let helperBindings = '';
|
|
6137
|
-
let renderDecl = '';
|
|
6138
|
-
let inlineBlock = undefined;
|
|
6139
|
-
try {
|
|
6140
|
-
const root = server.config?.root || process.cwd();
|
|
6141
|
-
const abs = path.join(root, base.replace(/^\//, ''));
|
|
6142
|
-
let sfcSrc = '';
|
|
3209
|
+
catch (eLC) {
|
|
6143
3210
|
try {
|
|
6144
|
-
|
|
3211
|
+
console.warn('[ns:m][link-check] failed', eLC?.message || eLC);
|
|
6145
3212
|
}
|
|
6146
3213
|
catch { }
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
3214
|
+
}
|
|
3215
|
+
res.statusCode = 200;
|
|
3216
|
+
res.end(code);
|
|
3217
|
+
}
|
|
3218
|
+
catch (e) {
|
|
3219
|
+
try {
|
|
3220
|
+
console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
|
|
3221
|
+
}
|
|
3222
|
+
catch { }
|
|
3223
|
+
res.statusCode = 500;
|
|
3224
|
+
res.end('export {}\n');
|
|
3225
|
+
}
|
|
3226
|
+
});
|
|
3227
|
+
registerRuntimeCompatHandlers(server, {
|
|
3228
|
+
verbose,
|
|
3229
|
+
requireGuardSnippet: REQUIRE_GUARD_SNIPPET,
|
|
3230
|
+
appRootDir: APP_ROOT_DIR,
|
|
3231
|
+
defaultMainEntry: DEFAULT_MAIN_ENTRY,
|
|
3232
|
+
defaultMainEntryVirtual: DEFAULT_MAIN_ENTRY_VIRTUAL,
|
|
3233
|
+
getGraphVersion: () => Number(graphVersion || 0),
|
|
3234
|
+
getServerOrigin,
|
|
3235
|
+
});
|
|
3236
|
+
// 2.55) Dev-only vendor import unifier: rewrite 'vue'/'nativescript-vue' to /ns/rt/<ver>
|
|
3237
|
+
// This ensures plugins and app share a single Vue/NativeScript-Vue instance/realm.
|
|
3238
|
+
registerVendorUnifierHandler(server, {
|
|
3239
|
+
getGraphVersion: () => Number(graphVersion || 0),
|
|
3240
|
+
getServerOrigin,
|
|
3241
|
+
getStrategy: () => ACTIVE_STRATEGY,
|
|
3242
|
+
});
|
|
3243
|
+
// 2.6) ESM bridge for @nativescript/core: GET /ns/core[/<ver>][?p=sub/path]
|
|
3244
|
+
server.middlewares.use(async (req, res, next) => {
|
|
3245
|
+
try {
|
|
3246
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
3247
|
+
const coreRequest = parseCoreBridgeRequest(urlObj.pathname, urlObj.searchParams, Number(graphVersion || 0));
|
|
3248
|
+
if (!coreRequest)
|
|
3249
|
+
return next();
|
|
3250
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
3251
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
3252
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3253
|
+
res.setHeader('Pragma', 'no-cache');
|
|
3254
|
+
res.setHeader('Expires', '0');
|
|
3255
|
+
const { hasExplicitVersion, key, normalizedSub, sub, ver } = coreRequest;
|
|
3256
|
+
// Any @nativescript/core subpath import (including shallow ones like
|
|
3257
|
+
// `utils`) may expose exports that are not available from the root
|
|
3258
|
+
// vendor bundle namespace. Serve the actual transformed module content
|
|
3259
|
+
// instead of the lightweight proxy bridge.
|
|
3260
|
+
if (sub) {
|
|
3261
|
+
try {
|
|
3262
|
+
const resolvedSubpath = normalizedSub || sub;
|
|
3263
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3264
|
+
const resolveModuleId = async (moduleId) => {
|
|
3265
|
+
const resolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3266
|
+
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3267
|
+
};
|
|
3268
|
+
const resolvedId = await resolveRuntimeCoreModulePath(resolvedSubpath, resolveModuleId);
|
|
3269
|
+
const modulePath = resolvedId || `/node_modules/@nativescript/core/${resolvedSubpath}`;
|
|
3270
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3271
|
+
if (!hasExplicitVersion) {
|
|
3272
|
+
if (transformed?.code) {
|
|
3273
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3274
|
+
res.statusCode = 200;
|
|
3275
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver, extractExportedNames(expandedModuleCode), hasModuleDefaultExport(expandedModuleCode)));
|
|
3276
|
+
return;
|
|
6176
3277
|
}
|
|
3278
|
+
res.statusCode = 200;
|
|
3279
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver));
|
|
3280
|
+
return;
|
|
3281
|
+
}
|
|
3282
|
+
if (transformed?.code) {
|
|
3283
|
+
// Minimal pipeline: Vite already produces correct ESM.
|
|
3284
|
+
// ONLY rewrite specifier strings to device-fetchable URLs.
|
|
3285
|
+
// Do NOT run processCodeForDevice, rewriteImports, or any
|
|
3286
|
+
// other heavy transform — those mangle newlines, eat exports,
|
|
3287
|
+
// and cause cascading "does not provide an export" failures.
|
|
3288
|
+
const moduleCode = rewriteSpecifiersForDevice(transformed.code, getServerOrigin(server), Number(ver));
|
|
3289
|
+
res.statusCode = 200;
|
|
3290
|
+
res.end(moduleCode);
|
|
3291
|
+
return;
|
|
6177
3292
|
}
|
|
6178
3293
|
}
|
|
3294
|
+
catch (e) {
|
|
3295
|
+
try {
|
|
3296
|
+
console.warn('[ns-core-bridge] deep subpath serve failed:', sub, e?.message);
|
|
3297
|
+
}
|
|
3298
|
+
catch { }
|
|
3299
|
+
}
|
|
6179
3300
|
}
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
if (!
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
3301
|
+
// Main entry or shallow subpath: use proxy bridge
|
|
3302
|
+
let code = buildVersionedCoreMainBridgeModule(key, ver);
|
|
3303
|
+
if (!sub) {
|
|
3304
|
+
try {
|
|
3305
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3306
|
+
const coreSpecifier = '@nativescript/core';
|
|
3307
|
+
const resolved = await server.pluginContainer?.resolveId?.(coreSpecifier, undefined);
|
|
3308
|
+
const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3309
|
+
const modulePath = resolvedId || '/node_modules/@nativescript/core/index.js';
|
|
3310
|
+
const staticExportNames = collectStaticExportNamesFromFile(modulePath);
|
|
3311
|
+
const staticExportOrigins = await normalizeCoreExportOriginsForRuntime(collectStaticExportOriginsFromFile(modulePath), async (moduleId) => {
|
|
3312
|
+
const nextResolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3313
|
+
return typeof nextResolved === 'string' ? nextResolved : nextResolved?.id || null;
|
|
3314
|
+
}, modulePath);
|
|
3315
|
+
if (staticExportNames.length) {
|
|
3316
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, staticExportNames, staticExportOrigins);
|
|
3317
|
+
}
|
|
3318
|
+
else {
|
|
3319
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3320
|
+
if (transformed?.code) {
|
|
3321
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3322
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, extractExportedNames(expandedModuleCode));
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
6194
3325
|
}
|
|
6195
|
-
|
|
6196
|
-
|
|
3326
|
+
catch (e) {
|
|
3327
|
+
try {
|
|
3328
|
+
console.warn('[ns-core-bridge] main bridge export discovery failed:', e?.message);
|
|
3329
|
+
}
|
|
3330
|
+
catch { }
|
|
6197
3331
|
}
|
|
6198
3332
|
}
|
|
6199
|
-
else {
|
|
6200
|
-
// Deterministic error path when template extraction failed
|
|
6201
|
-
res.statusCode = 500;
|
|
6202
|
-
res.end(`throw new Error('[sfc-asm] ${base}: template extraction failed');\nexport default {};`);
|
|
6203
|
-
return;
|
|
6204
|
-
}
|
|
6205
|
-
// Run full device processing so helper aliasing and globals are consistent in this path too
|
|
6206
|
-
let code = REQUIRE_GUARD_SNIPPET + asm;
|
|
6207
|
-
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(base), base);
|
|
6208
|
-
try {
|
|
6209
|
-
code = ensureVersionedCoreImports(code, getServerOrigin(server), Number(ver));
|
|
6210
|
-
}
|
|
6211
|
-
catch { }
|
|
6212
|
-
code = rewriteImports(code, base, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
6213
|
-
try {
|
|
6214
|
-
code = ensureDestructureCoreImports(code);
|
|
6215
|
-
}
|
|
6216
|
-
catch { }
|
|
6217
|
-
code = ensureVariableDynamicImportHelper(code);
|
|
6218
|
-
code = ensureGuardPlainDynamicImports(code, origin);
|
|
6219
|
-
try {
|
|
6220
|
-
const origin = getServerOrigin(server);
|
|
6221
|
-
code = ensureVersionedRtImports(code, origin, Number(ver));
|
|
6222
|
-
code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, Number(ver));
|
|
6223
|
-
code = ensureVersionedCoreImports(code, origin, Number(ver));
|
|
6224
|
-
}
|
|
6225
|
-
catch { }
|
|
6226
|
-
// Inline-template body path already runs processCodeForDevice (AST + sanitizers); no additional _defineComponent fix needed
|
|
6227
3333
|
res.statusCode = 200;
|
|
6228
3334
|
res.end(code);
|
|
6229
3335
|
}
|
|
6230
3336
|
catch (e) {
|
|
6231
|
-
|
|
6232
|
-
res.end('export {}\n');
|
|
3337
|
+
next();
|
|
6233
3338
|
}
|
|
6234
3339
|
});
|
|
3340
|
+
registerTxnHandler(server, {
|
|
3341
|
+
resolveTxnIds: (version, fallbackChangedIds) => {
|
|
3342
|
+
const ids = txnBatches.get(version) || [];
|
|
3343
|
+
if (ids.length) {
|
|
3344
|
+
return ids;
|
|
3345
|
+
}
|
|
3346
|
+
return fallbackChangedIds.length ? computeTxnOrderForChanged(fallbackChangedIds) : [];
|
|
3347
|
+
},
|
|
3348
|
+
});
|
|
3349
|
+
registerVueSfcHandlers(server, {
|
|
3350
|
+
verbose,
|
|
3351
|
+
requireGuardSnippet: REQUIRE_GUARD_SNIPPET,
|
|
3352
|
+
appVirtualWithSlash: APP_VIRTUAL_WITH_SLASH,
|
|
3353
|
+
sfcFileMap,
|
|
3354
|
+
depFileMap,
|
|
3355
|
+
getGraphVersion: () => Number(graphVersion || 0),
|
|
3356
|
+
getStrategy: () => ACTIVE_STRATEGY,
|
|
3357
|
+
getServerOrigin,
|
|
3358
|
+
cleanCode,
|
|
3359
|
+
processCodeForDevice,
|
|
3360
|
+
rewriteImports,
|
|
3361
|
+
ensureVariableDynamicImportHelper,
|
|
3362
|
+
ensureGuardPlainDynamicImports,
|
|
3363
|
+
ensureVersionedRtImports,
|
|
3364
|
+
ensureVersionedCoreImports,
|
|
3365
|
+
ensureDestructureCoreImports,
|
|
3366
|
+
extractExportMetadata,
|
|
3367
|
+
});
|
|
6235
3368
|
wss.on('connection', async (ws) => {
|
|
6236
3369
|
if (verbose)
|
|
6237
3370
|
console.log('[hmr-ws] Client connected (dynamic fetch mode)');
|
|
@@ -6465,8 +3598,8 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6465
3598
|
ts: Date.now(),
|
|
6466
3599
|
delta: true,
|
|
6467
3600
|
};
|
|
6468
|
-
wss
|
|
6469
|
-
if (c
|
|
3601
|
+
wss?.clients.forEach((c) => {
|
|
3602
|
+
if (isSocketClientOpen(c)) {
|
|
6470
3603
|
try {
|
|
6471
3604
|
c.send(JSON.stringify(single));
|
|
6472
3605
|
}
|
|
@@ -6554,6 +3687,9 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6554
3687
|
if (!wss) {
|
|
6555
3688
|
return;
|
|
6556
3689
|
}
|
|
3690
|
+
if (isRuntimeGraphExcludedPath(file)) {
|
|
3691
|
+
return;
|
|
3692
|
+
}
|
|
6557
3693
|
// Graph update for this file change (wrapped to avoid aborting rest of handler)
|
|
6558
3694
|
try {
|
|
6559
3695
|
const skipAngularHtmlGraphUpdate = ACTIVE_STRATEGY.flavor === 'angular' && /\.(html|htm)$/i.test(file);
|
|
@@ -6573,7 +3709,10 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6573
3709
|
.filter(Boolean);
|
|
6574
3710
|
const transformed = await server.transformRequest(mod.id);
|
|
6575
3711
|
const code = transformed?.code || '';
|
|
6576
|
-
upsertGraphModule((mod.id || '').replace(/\?.*$/, ''), code, deps
|
|
3712
|
+
upsertGraphModule((mod.id || '').replace(/\?.*$/, ''), code, deps, {
|
|
3713
|
+
emitDeltaOnInsert: true,
|
|
3714
|
+
broadcastDelta: ACTIVE_STRATEGY.flavor !== 'angular',
|
|
3715
|
+
});
|
|
6577
3716
|
}
|
|
6578
3717
|
catch (error) {
|
|
6579
3718
|
if (verbose)
|
|
@@ -6616,7 +3755,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6616
3755
|
],
|
|
6617
3756
|
};
|
|
6618
3757
|
wss.clients.forEach((client) => {
|
|
6619
|
-
if (client
|
|
3758
|
+
if (isSocketClientOpen(client)) {
|
|
6620
3759
|
client.send(JSON.stringify(msg));
|
|
6621
3760
|
}
|
|
6622
3761
|
});
|
|
@@ -6682,27 +3821,20 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6682
3821
|
}
|
|
6683
3822
|
}
|
|
6684
3823
|
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({
|
|
3824
|
+
const transitiveImporters = shouldInvalidateAngularTransitiveImporters({ flavor: ACTIVE_STRATEGY.flavor, file })
|
|
3825
|
+
? collectAngularTransitiveImportersForInvalidation({
|
|
6696
3826
|
modules: angularTransitiveInvalidationRoots,
|
|
6697
3827
|
isExcluded: (id) => id.includes('/node_modules/'),
|
|
6698
3828
|
maxDepth: 16,
|
|
6699
|
-
})
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
3829
|
+
})
|
|
3830
|
+
: [];
|
|
3831
|
+
const transformCacheInvalidationUrls = new Set(collectAngularTransformCacheInvalidationUrls({
|
|
3832
|
+
file,
|
|
3833
|
+
isTs,
|
|
3834
|
+
hotUpdateRoots: angularHotUpdateRoots,
|
|
3835
|
+
transitiveImporters,
|
|
3836
|
+
projectRoot: server.config.root || process.cwd(),
|
|
3837
|
+
}));
|
|
6706
3838
|
if (transformCacheInvalidationUrls.size) {
|
|
6707
3839
|
sharedTransformRequest.invalidateMany(transformCacheInvalidationUrls);
|
|
6708
3840
|
if (verbose) {
|
|
@@ -6723,10 +3855,18 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6723
3855
|
type: 'ns:angular-update',
|
|
6724
3856
|
origin,
|
|
6725
3857
|
path: rel,
|
|
3858
|
+
version: graphVersion,
|
|
6726
3859
|
timestamp: Date.now(),
|
|
6727
3860
|
};
|
|
3861
|
+
if (verbose) {
|
|
3862
|
+
console.log('[hmr-ws][angular] broadcasting update', Array.from(wss.clients || []).map((client) => ({
|
|
3863
|
+
role: getHmrSocketRole(client),
|
|
3864
|
+
readyState: client.readyState,
|
|
3865
|
+
openState: client.OPEN,
|
|
3866
|
+
})));
|
|
3867
|
+
}
|
|
6728
3868
|
wss.clients.forEach((client) => {
|
|
6729
|
-
if (client
|
|
3869
|
+
if (isSocketClientOpen(client)) {
|
|
6730
3870
|
client.send(JSON.stringify(msg));
|
|
6731
3871
|
}
|
|
6732
3872
|
});
|
|
@@ -6748,10 +3888,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6748
3888
|
// Treat the changed file itself as a graph module with no deps. We only
|
|
6749
3889
|
// care that its hash/identity changes so the client sees a delta and can
|
|
6750
3890
|
// 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], []);
|
|
3891
|
+
upsertGraphModule(rel, '', [], { emitDeltaOnInsert: true });
|
|
6755
3892
|
}
|
|
6756
3893
|
catch (e) {
|
|
6757
3894
|
if (verbose)
|
|
@@ -6782,7 +3919,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
6782
3919
|
if (!existing) {
|
|
6783
3920
|
// Module not in graph yet — force upsert with timestamp-based
|
|
6784
3921
|
// hash so the client sees a change.
|
|
6785
|
-
upsertGraphModule(rel, `/* solid-hmr ${Date.now()} */`, []);
|
|
3922
|
+
upsertGraphModule(rel, `/* solid-hmr ${Date.now()} */`, [], { emitDeltaOnInsert: true });
|
|
6786
3923
|
}
|
|
6787
3924
|
// Log what we're sending so devs can trace the flow on the server side.
|
|
6788
3925
|
if (verbose) {
|
|
@@ -6979,7 +4116,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
|
|
|
6979
4116
|
version: graphVersion,
|
|
6980
4117
|
};
|
|
6981
4118
|
wss.clients.forEach((client) => {
|
|
6982
|
-
if (client
|
|
4119
|
+
if (isSocketClientOpen(client)) {
|
|
6983
4120
|
client.send(JSON.stringify(registryUpdateMsg));
|
|
6984
4121
|
}
|
|
6985
4122
|
});
|
|
@@ -7150,4 +4287,6 @@ function getServerOrigin(server) {
|
|
|
7150
4287
|
// Test-only export: allow unit tests to run the sanitizer on snippets without booting a server
|
|
7151
4288
|
// Safe in production builds; this is a named export that tests can import explicitly.
|
|
7152
4289
|
export const __test_processCodeForDevice = processCodeForDevice;
|
|
4290
|
+
export const __test_resolveVendorRouting = resolveVendorRouting;
|
|
4291
|
+
export const __test_getBlockedDeviceNodeModulesReason = getBlockedDeviceNodeModulesReason;
|
|
7153
4292
|
//# sourceMappingURL=websocket.js.map
|