@nativescript/vite 8.0.0-alpha.1 → 8.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configuration/angular.d.ts +1 -1
- package/configuration/angular.js +323 -119
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +41 -24
- package/configuration/base.js.map +1 -1
- package/configuration/javascript.js +3 -3
- package/configuration/javascript.js.map +1 -1
- package/configuration/solid.js +7 -0
- package/configuration/solid.js.map +1 -1
- package/configuration/typescript.js +3 -3
- package/configuration/typescript.js.map +1 -1
- package/helpers/angular/angular-linker.js +39 -34
- package/helpers/angular/angular-linker.js.map +1 -1
- package/helpers/angular/inline-decorator-component-templates.d.ts +3 -0
- package/helpers/angular/inline-decorator-component-templates.js +400 -0
- package/helpers/angular/inline-decorator-component-templates.js.map +1 -0
- package/helpers/angular/shared-linker.d.ts +7 -0
- package/helpers/angular/shared-linker.js +37 -1
- package/helpers/angular/shared-linker.js.map +1 -1
- package/helpers/angular/synthesize-decorator-ctor-parameters.d.ts +1 -0
- package/helpers/angular/synthesize-decorator-ctor-parameters.js +256 -0
- package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -0
- package/helpers/angular/synthesize-injectable-factories.d.ts +3 -0
- package/helpers/angular/synthesize-injectable-factories.js +414 -0
- package/helpers/angular/synthesize-injectable-factories.js.map +1 -0
- 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/esbuild-platform-resolver.js +5 -5
- package/helpers/esbuild-platform-resolver.js.map +1 -1
- package/helpers/external-configs.d.ts +9 -1
- package/helpers/external-configs.js +31 -6
- package/helpers/external-configs.js.map +1 -1
- package/helpers/import-meta-path.d.ts +4 -0
- package/helpers/import-meta-path.js +5 -0
- package/helpers/import-meta-path.js.map +1 -0
- package/helpers/import-specifier.d.ts +1 -0
- package/helpers/import-specifier.js +18 -0
- package/helpers/import-specifier.js.map +1 -0
- package/helpers/main-entry.d.ts +5 -2
- package/helpers/main-entry.js +112 -95
- package/helpers/main-entry.js.map +1 -1
- package/helpers/nativeclass-transform.js +8 -127
- package/helpers/nativeclass-transform.js.map +1 -1
- package/helpers/nativeclass-transformer-plugin.d.ts +12 -1
- package/helpers/nativeclass-transformer-plugin.js +175 -36
- package/helpers/nativeclass-transformer-plugin.js.map +1 -1
- package/hmr/client/css-handler.js +60 -20
- package/hmr/client/css-handler.js.map +1 -1
- package/hmr/client/index.js +524 -23
- 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.d.ts +10 -0
- package/hmr/entry-runtime.js +263 -21
- 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 +72 -19
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/angular/server/linker.js +36 -2
- package/hmr/frameworks/angular/server/linker.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/helpers/ast-normalizer.js +22 -10
- package/hmr/helpers/ast-normalizer.js.map +1 -1
- package/hmr/server/constants.d.ts +1 -0
- package/hmr/server/constants.js +2 -0
- package/hmr/server/constants.js.map +1 -1
- package/hmr/server/core-sanitize.d.ts +43 -0
- package/hmr/server/core-sanitize.js +219 -13
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/import-map.d.ts +65 -0
- package/hmr/server/import-map.js +219 -0
- package/hmr/server/import-map.js.map +1 -0
- package/hmr/server/index.d.ts +2 -1
- package/hmr/server/index.js.map +1 -1
- package/hmr/server/runtime-graph-filter.d.ts +5 -0
- package/hmr/server/runtime-graph-filter.js +21 -0
- package/hmr/server/runtime-graph-filter.js.map +1 -0
- package/hmr/server/shared-transform-request.d.ts +12 -0
- package/hmr/server/shared-transform-request.js +137 -0
- package/hmr/server/shared-transform-request.js.map +1 -0
- package/hmr/server/vite-plugin.d.ts +21 -1
- package/hmr/server/vite-plugin.js +443 -22
- package/hmr/server/vite-plugin.js.map +1 -1
- package/hmr/server/websocket-angular-entry.d.ts +2 -0
- package/hmr/server/websocket-angular-entry.js +68 -0
- package/hmr/server/websocket-angular-entry.js.map +1 -0
- package/hmr/server/websocket-angular-hot-update.d.ts +61 -0
- package/hmr/server/websocket-angular-hot-update.js +239 -0
- package/hmr/server/websocket-angular-hot-update.js.map +1 -0
- package/hmr/server/websocket-core-bridge.d.ts +23 -0
- package/hmr/server/websocket-core-bridge.js +360 -0
- package/hmr/server/websocket-core-bridge.js.map +1 -0
- package/hmr/server/websocket-graph-upsert.d.ts +6 -0
- package/hmr/server/websocket-graph-upsert.js +13 -0
- package/hmr/server/websocket-graph-upsert.js.map +1 -0
- package/hmr/server/websocket-module-bindings.d.ts +6 -0
- package/hmr/server/websocket-module-bindings.js +471 -0
- package/hmr/server/websocket-module-bindings.js.map +1 -0
- package/hmr/server/websocket-module-specifiers.d.ts +37 -0
- package/hmr/server/websocket-module-specifiers.js +637 -0
- package/hmr/server/websocket-module-specifiers.js.map +1 -0
- package/hmr/server/websocket.d.ts +26 -3
- package/hmr/server/websocket.js +1402 -678
- 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 +38 -0
- package/hmr/shared/runtime/dev-overlay.js +675 -0
- package/hmr/shared/runtime/dev-overlay.js.map +1 -0
- package/hmr/shared/runtime/http-only-boot.d.ts +1 -0
- package/hmr/shared/runtime/http-only-boot.js +53 -6
- package/hmr/shared/runtime/http-only-boot.js.map +1 -1
- package/hmr/shared/runtime/module-provenance.d.ts +1 -0
- package/hmr/shared/runtime/module-provenance.js +66 -0
- package/hmr/shared/runtime/module-provenance.js.map +1 -0
- package/hmr/shared/runtime/platform-polyfills.d.ts +26 -0
- package/hmr/shared/runtime/platform-polyfills.js +122 -0
- package/hmr/shared/runtime/platform-polyfills.js.map +1 -0
- package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
- package/hmr/shared/runtime/root-placeholder.js +576 -76
- 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/runtime/vendor-bootstrap.js +51 -6
- package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
- package/hmr/shared/vendor/manifest.d.ts +7 -0
- package/hmr/shared/vendor/manifest.js +363 -23
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/hmr/shared/vendor/registry.js +104 -7
- package/hmr/shared/vendor/registry.js.map +1 -1
- package/package.json +12 -2
- package/runtime/core-aliases-early.js +83 -32
- package/runtime/core-aliases-early.js.map +1 -1
- package/shims/solid-jsx-runtime.d.ts +7 -0
- package/shims/solid-jsx-runtime.js +17 -0
- package/shims/solid-jsx-runtime.js.map +1 -0
package/hmr/server/websocket.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCoreSpecToBridge } from './core-sanitize.js';
|
|
2
|
+
import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCoreSpecToBridge, isDeepCoreSubpath, rewriteSpecifiersForDevice } from './core-sanitize.js';
|
|
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
6
|
import babelCore from '@babel/core';
|
|
7
|
-
import pluginTransformTypescript from '@babel/plugin-transform-typescript';
|
|
8
7
|
import traverse from '@babel/traverse';
|
|
9
8
|
// Ensure traverse callable across CJS/ESM builds
|
|
10
9
|
const babelTraverse = traverse?.default || traverse;
|
|
11
10
|
import * as t from '@babel/types';
|
|
12
|
-
import { existsSync, readFileSync } from 'fs';
|
|
11
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
13
12
|
import { astNormalizeModuleImportsAndHelpers, astVerifyAndAnnotateDuplicates } from '../helpers/ast-normalizer.js';
|
|
14
13
|
import { stripRtCoreSentinel, stripDanglingViteCjsImports } from '../helpers/sanitize.js';
|
|
15
14
|
import { WebSocketServer } from 'ws';
|
|
@@ -17,7 +16,7 @@ import * as path from 'path';
|
|
|
17
16
|
import { createHash } from 'crypto';
|
|
18
17
|
import * as PAT from './constants.js';
|
|
19
18
|
import { getVendorManifest, resolveVendorSpecifier } from '../shared/vendor/registry.js';
|
|
20
|
-
import { getPackageJson, getProjectFilePath } from '../../helpers/project.js';
|
|
19
|
+
import { getPackageJson, getProjectFilePath, getProjectRootPath } from '../../helpers/project.js';
|
|
21
20
|
import { loadPrebuiltVendorManifest } from '../shared/vendor/manifest-loader.js';
|
|
22
21
|
import '../vendor-bootstrap.js';
|
|
23
22
|
import { NS_NATIVE_TAGS } from './compiler.js';
|
|
@@ -30,6 +29,41 @@ import { typescriptServerStrategy } from '../frameworks/typescript/server/strate
|
|
|
30
29
|
import { buildInlineTemplateBlock, createProcessSfcCode, extractTemplateRender, processTemplateVariantMinimal } from '../frameworks/vue/server/sfc-transforms.js';
|
|
31
30
|
import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
|
|
32
31
|
import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
|
|
32
|
+
import { buildRuntimeConfig, generateImportMap } from './import-map.js';
|
|
33
|
+
import { getCliFlags } from '../../helpers/cli-flags.js';
|
|
34
|
+
import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
|
|
35
|
+
import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
36
|
+
import { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
|
|
37
|
+
import { classifyGraphUpsert, shouldBroadcastGraphUpsertDelta } from './websocket-graph-upsert.js';
|
|
38
|
+
import { extractVitePrebundleId, filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, getFlattenedManifestMap, isCoreGlobalsReference, isEsmFrameworkPackageSpecifier, isLikelyNativeScriptPluginSpecifier, isLikelyNativeScriptRuntimePluginSpecifier, isNativeScriptCoreModule, isNativeScriptPluginModule, normalizeNativeScriptCoreSpecifier, normalizeNodeModulesSpecifier, resolveCandidateFilePath, resolveInternalRuntimePluginBareSpecifier, resolveNodeModulesPackageBoundary, resolveVendorFromCandidate, resolveVendorRouting, shouldPreserveBareRuntimePluginSubpathImport, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule, viteDepsPathToBareSpecifier, } from './websocket-module-specifiers.js';
|
|
39
|
+
import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
40
|
+
import { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, extractDirectExportedNames, hasModuleDefaultExport, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
|
|
41
|
+
import { createSharedTransformRequestRunner } from './shared-transform-request.js';
|
|
42
|
+
export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
43
|
+
export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
|
|
44
|
+
export { buildVersionedCoreMainBridgeModule, buildVersionedCoreSubpathAliasModule, collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
|
|
45
|
+
export { rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
46
|
+
export { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, createSharedTransformRequestRunner, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload, classifyGraphUpsert, shouldBroadcastGraphUpsertDelta };
|
|
47
|
+
const pluginTransformTypescript = (() => {
|
|
48
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
49
|
+
const loaded = requireFromHere('@babel/plugin-transform-typescript');
|
|
50
|
+
return loaded?.default || loaded;
|
|
51
|
+
})();
|
|
52
|
+
// Build a serialized process.env object from CLI --env.* flags.
|
|
53
|
+
// This is injected into every HTTP-served module so app code referencing
|
|
54
|
+
// process.env.TEST_ENV (etc.) works on device in HMR dev mode.
|
|
55
|
+
const __processEnvEntries = { NODE_ENV: 'development' };
|
|
56
|
+
try {
|
|
57
|
+
const flags = getCliFlags();
|
|
58
|
+
for (const [k, v] of Object.entries(flags || {})) {
|
|
59
|
+
// Skip internal NativeScript build flags
|
|
60
|
+
if (['ios', 'android', 'visionos', 'platform', 'hmr', 'verbose'].includes(k))
|
|
61
|
+
continue;
|
|
62
|
+
__processEnvEntries[k] = String(v);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch { }
|
|
66
|
+
const __processEnvJson = JSON.stringify(__processEnvEntries);
|
|
33
67
|
const { parse, compileTemplate, compileScript } = vueSfcCompiler;
|
|
34
68
|
const APP_ROOT_DIR = getProjectAppPath();
|
|
35
69
|
const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
|
|
@@ -43,255 +77,177 @@ const STRATEGY_REGISTRY = new Map([
|
|
|
43
77
|
['typescript', typescriptServerStrategy],
|
|
44
78
|
]);
|
|
45
79
|
function resolveFrameworkStrategy(flavor) {
|
|
46
|
-
|
|
80
|
+
const strategy = STRATEGY_REGISTRY.get(flavor);
|
|
81
|
+
if (!strategy) {
|
|
82
|
+
throw new Error(`[ns-hmr] Unsupported framework strategy: ${flavor}`);
|
|
83
|
+
}
|
|
84
|
+
return strategy;
|
|
47
85
|
}
|
|
48
86
|
let ACTIVE_STRATEGY;
|
|
87
|
+
function isSocketClientOpen(client) {
|
|
88
|
+
if (!client) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const openState = typeof client.OPEN === 'number' ? client.OPEN : 1;
|
|
92
|
+
return client.readyState === openState;
|
|
93
|
+
}
|
|
94
|
+
function getHmrSocketRoleFromRequestUrl(requestUrl) {
|
|
95
|
+
try {
|
|
96
|
+
const url = new URL(requestUrl || '/ns-hmr', 'http://localhost');
|
|
97
|
+
return url.searchParams.get('ns_hmr_role') || 'unknown';
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return 'unknown';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getHmrSocketRole(client) {
|
|
104
|
+
if (!client) {
|
|
105
|
+
return 'unknown';
|
|
106
|
+
}
|
|
107
|
+
return typeof client.__nsHmrClientRole === 'string' && client.__nsHmrClientRole ? client.__nsHmrClientRole : 'unknown';
|
|
108
|
+
}
|
|
109
|
+
function shouldAllowLocalCoreSanitizerPaths(contextLabel) {
|
|
110
|
+
return /\bnode_modules\/@nativescript\/vite\/hmr\/(?:client|frameworks)\//.test(contextLabel);
|
|
111
|
+
}
|
|
112
|
+
export function prepareAngularEntryForDevice(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
113
|
+
const rewrittenCode = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp);
|
|
114
|
+
return rewriteAngularEntryRegisterOnly(rewrittenCode, resolveAngularCoreHmrImportSource(rewrittenCode, httpOrigin));
|
|
115
|
+
}
|
|
49
116
|
const processSfcCode = createProcessSfcCode(processCodeForDevice);
|
|
50
117
|
// Bare specifiers and special skip patterns (virtual, data:, etc.)
|
|
51
118
|
const VENDOR_PACKAGES = /^[A-Za-z@][^:\/\s]*$/;
|
|
52
119
|
const SKIP_PATTERNS = /^(?:data:|blob:|node:|virtual:|vite:|\0|\/@@?id|\/__vite|__vite|__x00__)/;
|
|
53
|
-
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
for (const canonical of mods) {
|
|
67
|
-
const flat = canonical.replace(/\./g, '__').replace(/\//g, '_');
|
|
68
|
-
map.set(flat, canonical);
|
|
69
|
-
const alias = manifest.aliases?.[canonical];
|
|
70
|
-
if (alias) {
|
|
71
|
-
const aliasFlat = String(alias).replace(/\./g, '__').replace(/\//g, '_');
|
|
72
|
-
map.set(aliasFlat, canonical);
|
|
120
|
+
const MODULE_IMPORT_ANALYSIS_PLUGINS = ['typescript', 'jsx', 'importMeta', 'topLevelAwait', 'classProperties', 'classPrivateProperties', 'classPrivateMethods', 'decorators-legacy'];
|
|
121
|
+
function collectTopLevelImportRecords(code) {
|
|
122
|
+
if (!code || typeof code !== 'string' || !/\bimport\b/.test(code)) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const ast = babelParse(code, {
|
|
127
|
+
sourceType: 'module',
|
|
128
|
+
plugins: MODULE_IMPORT_ANALYSIS_PLUGINS,
|
|
129
|
+
});
|
|
130
|
+
const body = ast?.program?.body;
|
|
131
|
+
if (!Array.isArray(body)) {
|
|
132
|
+
return [];
|
|
73
133
|
}
|
|
134
|
+
return body
|
|
135
|
+
.filter((node) => t.isImportDeclaration(node) && typeof node.start === 'number' && typeof node.end === 'number' && typeof node.source?.value === 'string')
|
|
136
|
+
.map((node) => ({
|
|
137
|
+
start: node.start,
|
|
138
|
+
end: node.end,
|
|
139
|
+
text: code.slice(node.start, node.end),
|
|
140
|
+
source: node.source.value,
|
|
141
|
+
hasOnlyNamedSpecifiers: Array.isArray(node.specifiers) && node.specifiers.length > 0 && node.specifiers.every((spec) => t.isImportSpecifier(spec)),
|
|
142
|
+
namedBindings: Array.isArray(node.specifiers)
|
|
143
|
+
? node.specifiers
|
|
144
|
+
.filter((spec) => t.isImportSpecifier(spec) && typeof spec.start === 'number' && typeof spec.end === 'number')
|
|
145
|
+
.map((spec) => ({
|
|
146
|
+
importedName: t.isIdentifier(spec.imported) ? spec.imported.name : String(spec.imported?.value || ''),
|
|
147
|
+
text: code.slice(spec.start, spec.end),
|
|
148
|
+
}))
|
|
149
|
+
: [],
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return [];
|
|
74
154
|
}
|
|
75
|
-
return map;
|
|
76
|
-
}
|
|
77
|
-
// NativeScript module detectors
|
|
78
|
-
function isCoreGlobalsReference(spec) {
|
|
79
|
-
return /@nativescript(?:[\/_-])core(?:[\/_-])globals/.test(spec || '');
|
|
80
|
-
}
|
|
81
|
-
function isNativeScriptCoreModule(spec) {
|
|
82
|
-
return /^(?:@nativescript[\/_-]core|@nativescript\/core)(?:\b|\/)/i.test(spec || '');
|
|
83
155
|
}
|
|
84
|
-
function
|
|
85
|
-
|
|
156
|
+
function hoistTopLevelStaticImports(code) {
|
|
157
|
+
const imports = collectTopLevelImportRecords(code);
|
|
158
|
+
if (!imports.length) {
|
|
159
|
+
return code;
|
|
160
|
+
}
|
|
161
|
+
let stripped = code;
|
|
162
|
+
for (const imp of [...imports].sort((left, right) => right.start - left.start)) {
|
|
163
|
+
stripped = stripped.slice(0, imp.start) + stripped.slice(imp.end);
|
|
164
|
+
}
|
|
165
|
+
const hoisted = [];
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
for (const imp of imports) {
|
|
168
|
+
const text = imp.text.trim();
|
|
169
|
+
if (!text || seen.has(text)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
seen.add(text);
|
|
173
|
+
hoisted.push(text);
|
|
174
|
+
}
|
|
175
|
+
if (!hoisted.length) {
|
|
176
|
+
return stripped;
|
|
177
|
+
}
|
|
178
|
+
return `${hoisted.join('\n')}\n${stripped.replace(/^\s*\n+/, '')}`;
|
|
86
179
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (/^(?:\.|\/|https?:\/\/)/i.test(s))
|
|
96
|
-
return false;
|
|
97
|
-
// App alias paths like '@/...' are not vendor packages
|
|
98
|
-
if (s.startsWith('@@/'))
|
|
99
|
-
return false; // extremely rare double '@' alias
|
|
100
|
-
if (s.startsWith('~/'))
|
|
101
|
-
return false; // NativeScript tilde alias (app root)
|
|
102
|
-
if (s.startsWith('@/'))
|
|
103
|
-
return false; // Common Vite alias for src
|
|
104
|
-
// .vue SFCs are not vendor packages
|
|
105
|
-
if (/\.vue(?:\?|$)/i.test(s))
|
|
106
|
-
return false;
|
|
107
|
-
// Exclude core and vue runtime which are handled by dedicated bridges
|
|
108
|
-
if (/^@nativescript\/core(\b|\/)/i.test(s))
|
|
109
|
-
return false;
|
|
110
|
-
if (/^(?:vue|nativescript-vue)(?:\b|\/)/i.test(s))
|
|
111
|
-
return false;
|
|
112
|
-
// Treat any other bare package id as device-resolved (require) during HMR
|
|
113
|
-
return true;
|
|
180
|
+
export function buildBootProgressSnippet(bootModuleLabel) {
|
|
181
|
+
const normalizedLabel = JSON.stringify(String(bootModuleLabel || '').replace(/\\/g, '/'));
|
|
182
|
+
return [
|
|
183
|
+
`const __nsBootGlobal=globalThis;`,
|
|
184
|
+
`try{if(!__nsBootGlobal.__NS_HMR_BOOT_COMPLETE__){const __nsBootApi=__nsBootGlobal.__NS_HMR_DEV_OVERLAY__;if(__nsBootApi&&typeof __nsBootApi.setBootStage==='function'){const __nsBootCount=(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__=Number(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__||0)+1);__nsBootGlobal.__NS_HMR_BOOT_LAST_MODULE__=${normalizedLabel};const __nsBootNow=Date.now();const __nsBootLast=Number(__nsBootGlobal.__NS_HMR_BOOT_LAST_PROGRESS_AT__||0);if(__nsBootCount<=8||__nsBootCount%6===0||__nsBootNow-__nsBootLast>90){__nsBootGlobal.__NS_HMR_BOOT_LAST_PROGRESS_AT__=__nsBootNow;const __nsBootProgress=Math.min(94,82+Math.min(10,Math.round((Math.log(__nsBootCount+1)/Math.LN2)*2)));__nsBootApi.setBootStage('importing-main',{detail:'Evaluated '+__nsBootCount+' modules\\n'+__nsBootGlobal.__NS_HMR_BOOT_LAST_MODULE__,attempt:Number(__nsBootGlobal.__NS_HMR_BOOT_MAIN_ATTEMPT__||1),attempts:Number(__nsBootGlobal.__NS_HMR_BOOT_MAIN_ATTEMPTS__||6),progress:__nsBootProgress});}}}}catch(__nsBootErr){}`,
|
|
185
|
+
`if(!__nsBootGlobal.__NS_HMR_BOOT_COMPLETE__){const __nsBootCount=Number(__nsBootGlobal.__NS_HMR_BOOT_MODULE_COUNT__||0);if(__nsBootCount<=24||__nsBootCount%8===0){await new Promise((resolve)=>setTimeout(resolve,0));}}`,
|
|
186
|
+
'',
|
|
187
|
+
].join('\n');
|
|
114
188
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
let entry = modules.get(canonical);
|
|
128
|
-
if (!entry) {
|
|
129
|
-
entry = {
|
|
130
|
-
default: new Set(),
|
|
131
|
-
namespace: new Set(),
|
|
132
|
-
named: [],
|
|
133
|
-
sideEffectOnly: false,
|
|
134
|
-
};
|
|
135
|
-
modules.set(canonical, entry);
|
|
189
|
+
function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
|
|
190
|
+
const imports = collectTopLevelImportRecords(code);
|
|
191
|
+
if (!imports.length) {
|
|
192
|
+
return code;
|
|
193
|
+
}
|
|
194
|
+
const edits = [];
|
|
195
|
+
for (const imp of imports) {
|
|
196
|
+
const source = imp.source;
|
|
197
|
+
const depMatch = source.match(/(?:^|\/)node_modules\/\.vite\/deps\/(.+)$/);
|
|
198
|
+
const depPath = depMatch?.[1] || (source.startsWith('.vite/deps/') ? source.slice('.vite/deps/'.length) : null);
|
|
199
|
+
if (!depPath) {
|
|
200
|
+
continue;
|
|
136
201
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
.map((segment) => segment.trim())
|
|
144
|
-
.filter(Boolean)
|
|
145
|
-
.forEach((segment) => {
|
|
146
|
-
const [imported, local] = segment.split(/\s+as\s+/i).map((s) => s.trim());
|
|
147
|
-
const resolvedImported = imported;
|
|
148
|
-
const resolvedLocal = local || imported;
|
|
149
|
-
if (resolvedImported) {
|
|
150
|
-
binding.named.push({
|
|
151
|
-
imported: resolvedImported,
|
|
152
|
-
local: resolvedLocal,
|
|
153
|
-
});
|
|
202
|
+
let replacement = '';
|
|
203
|
+
if (preserveVendorImports) {
|
|
204
|
+
const canonical = resolveVendorFromCandidate(`.vite/deps/${depPath}`);
|
|
205
|
+
const bareSpecifier = canonical || viteDepsPathToBareSpecifier(depPath);
|
|
206
|
+
if (bareSpecifier) {
|
|
207
|
+
replacement = imp.text.replace(source, bareSpecifier);
|
|
154
208
|
}
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
|
-
// Handle "import ... from 'x'" forms
|
|
158
|
-
code = code.replace(importRegex, (full, pfx, clause, rawSpec) => {
|
|
159
|
-
// Capture original for potential preservation (strip the leading newline to avoid double spacing when hoisted)
|
|
160
|
-
const original = full.replace(/^\n/, '');
|
|
161
|
-
// Do not touch type-only imports other than hoisting
|
|
162
|
-
if (full.trimStart().startsWith('import type')) {
|
|
163
|
-
preservedImports.push(original);
|
|
164
|
-
return pfx || '';
|
|
165
|
-
}
|
|
166
|
-
const specifier = rawSpec.replace(PAT.QUERY_PATTERN, '');
|
|
167
|
-
let canonical = resolveVendorFromCandidate(specifier);
|
|
168
|
-
// If not found in vendor manifest, treat well-known NativeScript plugin-style packages
|
|
169
|
-
// as require() based modules so the device can resolve them from the app bundle or vendor.
|
|
170
|
-
if (!canonical && isLikelyNativeScriptPluginSpecifier(specifier)) {
|
|
171
|
-
canonical = specifier;
|
|
172
|
-
}
|
|
173
|
-
// CRITICAL: never vendor-inject @nativescript/core here — preserve for the later core-bridge pass.
|
|
174
|
-
if (canonical && /^@nativescript\/core(\b|\/)/i.test(canonical)) {
|
|
175
|
-
preservedImports.push(original);
|
|
176
|
-
return pfx || '';
|
|
177
|
-
}
|
|
178
|
-
if (!canonical) {
|
|
179
|
-
preservedImports.push(original);
|
|
180
|
-
return pfx || '';
|
|
181
|
-
}
|
|
182
|
-
const binding = getModuleBinding(canonical);
|
|
183
|
-
const trimmed = String(clause).trim();
|
|
184
|
-
if (!trimmed) {
|
|
185
|
-
binding.sideEffectOnly = true;
|
|
186
|
-
return pfx || ''; // erase the import line
|
|
187
|
-
}
|
|
188
|
-
// namespace: import * as ns from 'x'
|
|
189
|
-
if (trimmed.startsWith('*')) {
|
|
190
|
-
const m = trimmed.match(/\*\s+as\s+(\w+)/i);
|
|
191
|
-
if (m?.[1])
|
|
192
|
-
binding.namespace.add(m[1]);
|
|
193
|
-
return pfx || '';
|
|
194
|
-
}
|
|
195
|
-
// named: import { a, b as c } from 'x'
|
|
196
|
-
if (trimmed.startsWith('{')) {
|
|
197
|
-
parseNamedImports(trimmed, binding);
|
|
198
|
-
return pfx || '';
|
|
199
|
-
}
|
|
200
|
-
// default + named: import Default, { a as A } from 'x'
|
|
201
|
-
if (trimmed.includes(',') && trimmed.includes('{')) {
|
|
202
|
-
const [defaultPart, namedPart] = trimmed.split(/,(.+)/, 2);
|
|
203
|
-
const def = defaultPart.trim();
|
|
204
|
-
if (def)
|
|
205
|
-
binding.default.add(def);
|
|
206
|
-
if (namedPart)
|
|
207
|
-
parseNamedImports(namedPart.trim(), binding);
|
|
208
|
-
return pfx || '';
|
|
209
|
-
}
|
|
210
|
-
// default only
|
|
211
|
-
binding.default.add(trimmed);
|
|
212
|
-
return pfx || '';
|
|
213
|
-
});
|
|
214
|
-
// Handle side-effect only imports: import 'x'
|
|
215
|
-
code = code.replace(sideEffectRegex, (full, _pfx, rawSpec) => {
|
|
216
|
-
const original = full.replace(/^\n/, '');
|
|
217
|
-
const specifier = rawSpec.replace(PAT.QUERY_PATTERN, '');
|
|
218
|
-
let canonical = resolveVendorFromCandidate(specifier);
|
|
219
|
-
if (!canonical && isLikelyNativeScriptPluginSpecifier(specifier)) {
|
|
220
|
-
canonical = specifier;
|
|
221
|
-
}
|
|
222
|
-
if (canonical && /^@nativescript\/core(\b|\/)/i.test(canonical)) {
|
|
223
|
-
preservedImports.push(original);
|
|
224
|
-
return _pfx || '';
|
|
225
|
-
}
|
|
226
|
-
if (!canonical) {
|
|
227
|
-
preservedImports.push(original);
|
|
228
|
-
return _pfx || '';
|
|
229
|
-
}
|
|
230
|
-
const binding = getModuleBinding(canonical);
|
|
231
|
-
binding.sideEffectOnly = true;
|
|
232
|
-
return _pfx || '';
|
|
233
|
-
});
|
|
234
|
-
// If there are no vendor modules to bind, still hoist preserved imports if any were collected.
|
|
235
|
-
if (!modules.size) {
|
|
236
|
-
if (preservedImports.length) {
|
|
237
|
-
const preserved = preservedImports.join('') + '\n';
|
|
238
|
-
return preserved + code;
|
|
239
209
|
}
|
|
210
|
+
edits.push({
|
|
211
|
+
start: imp.start,
|
|
212
|
+
end: imp.end,
|
|
213
|
+
text: replacement,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (!edits.length) {
|
|
240
217
|
return code;
|
|
241
218
|
}
|
|
242
|
-
let
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
for (const [canonical, binding] of modules) {
|
|
259
|
-
const cacheKey = JSON.stringify(canonical);
|
|
260
|
-
const moduleVar = `__nsVendorModule_${index++}`;
|
|
261
|
-
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`;
|
|
262
|
-
binding.namespace.forEach((alias) => {
|
|
263
|
-
// For namespace imports, expose both the raw module and a default fallback for interop consumers
|
|
264
|
-
injection += `const ${alias} = ${moduleVar};\n`;
|
|
265
|
-
injection += `(${alias} && typeof ${alias} === 'object' && !('default' in ${alias})) && (${alias}.default = ${alias});\n`;
|
|
266
|
-
});
|
|
267
|
-
if (binding.named.length) {
|
|
268
|
-
// Bind each named import robustly from either default or namespace using helper.
|
|
269
|
-
for (const { imported, local } of binding.named) {
|
|
270
|
-
const localName = local;
|
|
271
|
-
const importedName = imported;
|
|
272
|
-
injection += `const ${localName} = __nsPick(${moduleVar}, ${JSON.stringify(importedName)});\n`;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (binding.default.size) {
|
|
276
|
-
// Create one stable default candidate per module and reuse for all default locals
|
|
277
|
-
const defVar = `${moduleVar}__def`;
|
|
278
|
-
injection += `const ${defVar} = __nsDefault(${moduleVar});\n`;
|
|
279
|
-
binding.default.forEach((localName) => {
|
|
280
|
-
injection += `const ${localName} = (__nsHasInstall(${defVar})
|
|
281
|
-
? ${defVar}
|
|
282
|
-
: (__nsHasInstall(${moduleVar})
|
|
283
|
-
? ${moduleVar}
|
|
284
|
-
: (function(){ const _n = __nsNestedDefault(${moduleVar}); return _n !== undefined ? _n : ${defVar}; })()));\n`;
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
if (binding.sideEffectOnly && !binding.namespace.size && !binding.named.length && !binding.default.size) {
|
|
288
|
-
injection += `void ${moduleVar};\n`;
|
|
219
|
+
let next = code;
|
|
220
|
+
for (const edit of edits.sort((left, right) => right.start - left.start)) {
|
|
221
|
+
next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
|
|
222
|
+
}
|
|
223
|
+
return next;
|
|
224
|
+
}
|
|
225
|
+
function buildNodeModuleProvenancePrelude(sourceId) {
|
|
226
|
+
if (!sourceId) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
const cleaned = sourceId.replace(PAT.QUERY_PATTERN, '');
|
|
230
|
+
let normalized = normalizeNodeModulesSpecifier(cleaned);
|
|
231
|
+
if (!normalized) {
|
|
232
|
+
const viteDepsMatch = cleaned.match(/(?:^|\/)node_modules\/\.vite\/deps\/([^?#]+)/);
|
|
233
|
+
if (viteDepsMatch?.[1]) {
|
|
234
|
+
normalized = `.vite/deps/${viteDepsMatch[1]}`;
|
|
289
235
|
}
|
|
290
236
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
237
|
+
if (!normalized) {
|
|
238
|
+
return '';
|
|
239
|
+
}
|
|
240
|
+
let packageSpecifier = normalized;
|
|
241
|
+
let via = 'node_modules';
|
|
242
|
+
if (normalized.startsWith('.vite/deps/')) {
|
|
243
|
+
via = 'vite-deps';
|
|
244
|
+
packageSpecifier = viteDepsPathToBareSpecifier(normalized.slice('.vite/deps/'.length)) || normalized;
|
|
245
|
+
}
|
|
246
|
+
const rootPackage = resolveNodeModulesPackageBoundary(packageSpecifier, getProjectRootPath()).packageName;
|
|
247
|
+
if (!rootPackage) {
|
|
248
|
+
return '';
|
|
249
|
+
}
|
|
250
|
+
return `try { const __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(${JSON.stringify(rootPackage)}, ${JSON.stringify({ kind: 'http-esm', specifier: packageSpecifier, url: sourceId, via })}); } } catch {}\n`;
|
|
295
251
|
}
|
|
296
252
|
// Guard any bare dynamic import(spec) occurring in assembled module code.
|
|
297
253
|
// We cannot override native dynamic import globally; for SFC assembler outputs we inline
|
|
@@ -318,113 +274,6 @@ function guardBareDynamicImports(code) {
|
|
|
318
274
|
return code;
|
|
319
275
|
}
|
|
320
276
|
}
|
|
321
|
-
function normalizeNativeScriptCoreSpecifier(spec) {
|
|
322
|
-
let normalized = spec.replace(/@nativescript[_-]core/gi, '@nativescript/core').replace(/@nativescript\/core\/index\.js$/i, '@nativescript/core/index.js');
|
|
323
|
-
if (normalized.startsWith('/node_modules/')) {
|
|
324
|
-
const idx = normalized.toLowerCase().indexOf('@nativescript/core');
|
|
325
|
-
if (idx !== -1) {
|
|
326
|
-
normalized = normalized.slice(idx);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (normalized.toLowerCase().startsWith('@nativescript/core')) {
|
|
330
|
-
normalized = normalized.replace(/\?[^"'`]*$/, '');
|
|
331
|
-
}
|
|
332
|
-
return normalized;
|
|
333
|
-
}
|
|
334
|
-
function normalizeNodeModulesSpecifier(spec) {
|
|
335
|
-
if (!spec) {
|
|
336
|
-
return null;
|
|
337
|
-
}
|
|
338
|
-
let normalized = spec.replace(/\\/g, '/');
|
|
339
|
-
const idx = normalized.lastIndexOf('/node_modules/');
|
|
340
|
-
if (idx === -1) {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
let subPath = normalized.slice(idx + '/node_modules/'.length);
|
|
344
|
-
if (!subPath) {
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
subPath = subPath.replace(PAT.QUERY_PATTERN, '');
|
|
348
|
-
if (!subPath) {
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
351
|
-
// Skip Vite pre-bundled deps that we already map to vendor
|
|
352
|
-
if (subPath.startsWith('.vite/')) {
|
|
353
|
-
return null;
|
|
354
|
-
}
|
|
355
|
-
return subPath.startsWith('/') ? subPath.slice(1) : subPath;
|
|
356
|
-
}
|
|
357
|
-
function resolveVendorFromCandidate(specifier) {
|
|
358
|
-
if (!specifier) {
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
const manifest = getVendorManifest();
|
|
362
|
-
if (!manifest) {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
const cleaned = specifier.replace(PAT.QUERY_PATTERN, '');
|
|
366
|
-
const direct = resolveVendorSpecifier(cleaned);
|
|
367
|
-
if (direct) {
|
|
368
|
-
return direct;
|
|
369
|
-
}
|
|
370
|
-
const flattenedId = extractVitePrebundleId(cleaned);
|
|
371
|
-
if (flattenedId) {
|
|
372
|
-
const flattenedMap = getFlattenedManifestMap(manifest);
|
|
373
|
-
const flatMatch = flattenedMap.get(flattenedId);
|
|
374
|
-
if (flatMatch) {
|
|
375
|
-
return flatMatch;
|
|
376
|
-
}
|
|
377
|
-
for (const [flatKey, canonical] of flattenedMap.entries()) {
|
|
378
|
-
if (flattenedId === flatKey || flattenedId.startsWith(`${flatKey}_`)) {
|
|
379
|
-
return canonical;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
const guessedId = flattenedId.replace(/__/g, '.').replace(/_/g, '/');
|
|
383
|
-
if (guessedId && guessedId !== flattenedId) {
|
|
384
|
-
const guessedCanonical = resolveVendorSpecifier(guessedId);
|
|
385
|
-
if (guessedCanonical) {
|
|
386
|
-
return guessedCanonical;
|
|
387
|
-
}
|
|
388
|
-
const prefix = findVendorPrefix(guessedId, manifest);
|
|
389
|
-
if (prefix) {
|
|
390
|
-
return prefix;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const normalizedCore = normalizeNativeScriptCoreSpecifier(cleaned);
|
|
395
|
-
if (normalizedCore !== cleaned) {
|
|
396
|
-
const nsCanonical = resolveVendorSpecifier(normalizedCore);
|
|
397
|
-
if (nsCanonical) {
|
|
398
|
-
return nsCanonical;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(cleaned);
|
|
402
|
-
if (nodeModulesSpecifier) {
|
|
403
|
-
const canonical = resolveVendorSpecifier(nodeModulesSpecifier);
|
|
404
|
-
if (canonical) {
|
|
405
|
-
return canonical;
|
|
406
|
-
}
|
|
407
|
-
const prefix = findVendorPrefix(nodeModulesSpecifier, manifest);
|
|
408
|
-
if (prefix) {
|
|
409
|
-
return prefix;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
const prefix = findVendorPrefix(cleaned, manifest);
|
|
413
|
-
if (prefix) {
|
|
414
|
-
return prefix;
|
|
415
|
-
}
|
|
416
|
-
return null;
|
|
417
|
-
}
|
|
418
|
-
function findVendorPrefix(specifier, manifest) {
|
|
419
|
-
const { modules } = manifest;
|
|
420
|
-
const keys = Object.keys(modules || {});
|
|
421
|
-
for (const key of keys) {
|
|
422
|
-
if (specifier === key || specifier.startsWith(`${key}/`)) {
|
|
423
|
-
return key;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
277
|
function stripCoreGlobalsImports(code) {
|
|
429
278
|
const pattern = /^\s*(?:import\s+(?:[^'"\n]*from\s+)?|export\s+\*\s+from\s+)["'][^"']*(?:@nativescript(?:[/_-])core(?:[\/_-])globals|@nativescript_core_globals)[^"']*["'];?\s*$/gm;
|
|
430
279
|
return code.replace(pattern, '');
|
|
@@ -452,18 +301,14 @@ function ensureVariableDynamicImportHelper(code) {
|
|
|
452
301
|
`};\n`;
|
|
453
302
|
return `${helper}${code}`;
|
|
454
303
|
}
|
|
455
|
-
// Final safety net for plain dynamic import(expressions) that might slip through
|
|
456
|
-
// Vite's helper transformation. We rewrite occurrences of `import(` to `__ns_import(`
|
|
457
|
-
// and inject a small wrapper that maps the anomalous request '@' to a harmless stub.
|
|
458
304
|
function ensureGuardPlainDynamicImports(code, origin) {
|
|
459
305
|
try {
|
|
460
306
|
if (!code || !/\bimport\s*\(/.test(code))
|
|
461
307
|
return code;
|
|
462
|
-
const
|
|
463
|
-
// Replace only when `import(` is not part of an identifier or property (no preceding "." or word char)
|
|
308
|
+
const wrapper = `const __ns_import = (s) => { try { if (s === '@') { return import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href); } } catch {} return import(s); }\n`;
|
|
464
309
|
const replaced = code.replace(/(^|[^\.\w$])import\s*\(/g, (_m, p1) => `${p1}__ns_import(`);
|
|
465
310
|
if (replaced !== code) {
|
|
466
|
-
return
|
|
311
|
+
return wrapper + replaced;
|
|
467
312
|
}
|
|
468
313
|
return code;
|
|
469
314
|
}
|
|
@@ -471,13 +316,84 @@ function ensureGuardPlainDynamicImports(code, origin) {
|
|
|
471
316
|
return code;
|
|
472
317
|
}
|
|
473
318
|
}
|
|
474
|
-
|
|
475
|
-
|
|
319
|
+
function ensureDynamicHmrImportHelper(code) {
|
|
320
|
+
try {
|
|
321
|
+
if (!code.includes('__nsDynamicHmrImport('))
|
|
322
|
+
return code;
|
|
323
|
+
if (code.includes('const __nsDynamicHmrImport ='))
|
|
324
|
+
return code;
|
|
325
|
+
const helper = 'const __nsDynamicHmrImport = (spec) => {\n' +
|
|
326
|
+
" const __nsm = '/ns' + '/m';\n" +
|
|
327
|
+
" const __nsBootPrefix = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' && import.meta.url.includes('/__ns_boot__/b1/') ? '/__ns_boot__/b1' : '';\n" +
|
|
328
|
+
" const __nsImporterTagMatch = typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string' ? import.meta.url.match(/\\/__ns_hmr__\\/([^/]+)\\//) : null;\n" +
|
|
329
|
+
" const __nsImporterTag = __nsImporterTagMatch && __nsImporterTagMatch[1] ? decodeURIComponent(__nsImporterTagMatch[1]) : '';\n" +
|
|
330
|
+
" try { if (!spec || spec === '@') { return import(new URL(__nsm + '/__invalid_at__.mjs', import.meta.url).href); } } catch {}\n" +
|
|
331
|
+
' try {\n' +
|
|
332
|
+
" if (typeof spec === 'string' && spec.startsWith(__nsm + '/')) {\n" +
|
|
333
|
+
' const g = globalThis;\n' +
|
|
334
|
+
" const graphVersion = typeof g.__NS_HMR_GRAPH_VERSION__ === 'number' ? g.__NS_HMR_GRAPH_VERSION__ : 0;\n" +
|
|
335
|
+
" const nonce = typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0;\n" +
|
|
336
|
+
" const __nsActiveBootPrefix = graphVersion || nonce ? '' : __nsBootPrefix;\n" +
|
|
337
|
+
" if (spec.includes('/__ns_hmr__/')) {\n" +
|
|
338
|
+
" const __preservedSpec = !nonce && __nsBootPrefix && spec.startsWith(__nsm + '/__ns_hmr__/') && !spec.includes('/node_modules/') ? __nsm + __nsBootPrefix + spec.slice(__nsm.length) : spec;\n" +
|
|
339
|
+
' return import(new URL(__preservedSpec, import.meta.url).href);\n' +
|
|
340
|
+
' }\n' +
|
|
341
|
+
" if (spec.startsWith(__nsm + '/node_modules/')) { return import(new URL(spec, import.meta.url).href); }\n" +
|
|
342
|
+
" const tag = nonce ? `n${nonce}` : (graphVersion ? `v${graphVersion}` : (__nsImporterTag || 'live'));\n" +
|
|
343
|
+
" const nextPath = __nsm + __nsActiveBootPrefix + '/__ns_hmr__/' + encodeURIComponent(tag) + spec.slice(__nsm.length);\n" +
|
|
344
|
+
" const origin = typeof g.__NS_HTTP_ORIGIN__ === 'string' && /^https?:\\/\\//.test(g.__NS_HTTP_ORIGIN__) ? g.__NS_HTTP_ORIGIN__ : '';\n" +
|
|
345
|
+
' return import(origin ? origin + nextPath : new URL(nextPath, import.meta.url).href);\n' +
|
|
346
|
+
' }\n' +
|
|
347
|
+
' } catch {}\n' +
|
|
348
|
+
' return import(spec);\n' +
|
|
349
|
+
'};\n';
|
|
350
|
+
return helper + code;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return code;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function expandStarExports(code, server, projectRoot, verbose) {
|
|
357
|
+
const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
|
|
358
|
+
let match;
|
|
359
|
+
const replacements = [];
|
|
360
|
+
while ((match = STAR_RE.exec(code)) !== null) {
|
|
361
|
+
const url = match[2];
|
|
362
|
+
if (!url.includes('/node_modules/'))
|
|
363
|
+
continue;
|
|
364
|
+
replacements.push({ full: match[0], url, prefix: match[1], suffix: match[3] });
|
|
365
|
+
}
|
|
366
|
+
if (!replacements.length)
|
|
367
|
+
return code;
|
|
368
|
+
for (const rep of replacements) {
|
|
369
|
+
try {
|
|
370
|
+
let vitePath = rep.url.replace(/^https?:\/\/[^/]+/, '');
|
|
371
|
+
vitePath = vitePath.replace(/^\/ns\/m\//, '/');
|
|
372
|
+
vitePath = vitePath.replace(/^\/__ns_boot__\/[^/]+/, '');
|
|
373
|
+
vitePath = vitePath.replace(/\/__ns_hmr__\/[^/]+/, '');
|
|
374
|
+
const result = await server.transformRequest(vitePath);
|
|
375
|
+
if (!result?.code)
|
|
376
|
+
continue;
|
|
377
|
+
const names = extractExportedNames(result.code);
|
|
378
|
+
if (!names.length)
|
|
379
|
+
continue;
|
|
380
|
+
const explicit = `export { ${names.join(', ')} } from ${JSON.stringify(rep.url)};`;
|
|
381
|
+
code = code.replace(rep.full, explicit);
|
|
382
|
+
if (verbose) {
|
|
383
|
+
console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch { }
|
|
387
|
+
}
|
|
388
|
+
return code;
|
|
389
|
+
}
|
|
390
|
+
function extractExportedNames(code) {
|
|
391
|
+
return extractDirectExportedNames(code);
|
|
392
|
+
}
|
|
476
393
|
function repairImportEqualsAssignments(code) {
|
|
477
394
|
try {
|
|
478
395
|
if (!code || typeof code !== 'string')
|
|
479
396
|
return code;
|
|
480
|
-
// import { a, b as c } = expr; -> const { a, b: c } = expr;
|
|
481
397
|
code = code.replace(/(^|\n)\s*import\s*\{([^}]+)\}\s*=\s*([^;]+);?/g, (_m, p1, specList, rhs) => {
|
|
482
398
|
const cleaned = String(specList)
|
|
483
399
|
.split(',')
|
|
@@ -487,56 +403,28 @@ function repairImportEqualsAssignments(code) {
|
|
|
487
403
|
.join(', ');
|
|
488
404
|
return `${p1}const { ${cleaned} } = ${rhs};`;
|
|
489
405
|
});
|
|
490
|
-
|
|
491
|
-
code = code.replace(/(^|\n)\s*import\s
|
|
492
|
-
return `${p1}const ${ns} = (${rhs});`;
|
|
493
|
-
});
|
|
494
|
-
// import name = expr; -> const name = expr;
|
|
495
|
-
code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => {
|
|
496
|
-
return `${p1}const ${id} = ${rhs};`;
|
|
497
|
-
});
|
|
406
|
+
code = code.replace(/(^|\n)\s*import\s*\*\s*as\s*([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, ns, rhs) => `${p1}const ${ns} = (${rhs});`);
|
|
407
|
+
code = code.replace(/(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);?/g, (_m, p1, id, rhs) => `${p1}const ${id} = ${rhs};`);
|
|
498
408
|
}
|
|
499
409
|
catch { }
|
|
500
410
|
return code;
|
|
501
411
|
}
|
|
502
|
-
// Ensure imports of the NativeScript-Vue runtime bridge '/ns/rt' are versioned to
|
|
503
|
-
// bust the device HTTP loader cache whenever the HMR graph version increments.
|
|
504
412
|
function ensureVersionedRtImports(code, origin, ver) {
|
|
505
413
|
if (!code || !origin || !Number.isFinite(ver))
|
|
506
414
|
return code;
|
|
507
|
-
// Static imports: import { ... } from ".../ns/rt" (plus optional version)
|
|
508
415
|
code = code.replace(/(from\s+["'])(?:https?:\/\/[^"']+)?\/(?:\ns|ns)\/rt(?:\/[\d]+)?(["'])/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
|
|
509
|
-
// Dynamic imports: import(".../ns/rt") (plus optional version)
|
|
510
416
|
code = code.replace(/(import\(\s*["'])(?:https?:\/\/[^"']+)?\/(?:\@ns|ns)\/rt(?:\/[\d]+)?(["']\s*\))/g, (_m, p1, p3) => `${p1}/ns/rt/${ver}${p3}`);
|
|
511
417
|
return code;
|
|
512
418
|
}
|
|
513
|
-
// Ensure imports of @nativescript/core resolve via the unified /ns/core bridge to keep a single realm
|
|
514
|
-
function ensureVersionedCoreImports(code, origin, ver) {
|
|
515
|
-
try {
|
|
516
|
-
// Static imports already handled in rewriteImports; just ensure absolute origin prefix and version
|
|
517
|
-
code = code.replace(/(["'])\/ns\/core(\?p=[^"']+)?\1/g, (_m, q, qp) => `${q}/ns/core/${ver}${qp || ''}${q}`);
|
|
518
|
-
// Dynamic imports already handled in rewriteImports; ensure origin and version
|
|
519
|
-
code = code.replace(/import\(\s*(["'])\/ns\/core(\?p=[^"']+)?\1\s*\)/g, (_m, q, qp) => `import(${q}/ns/core/${ver}${qp || ''}${q})`);
|
|
520
|
-
}
|
|
521
|
-
catch { }
|
|
522
|
-
return code;
|
|
523
|
-
}
|
|
524
|
-
// Hardened removal of Vite's virtual dynamic-import-helper. Some variants (side-effect only
|
|
525
|
-
// or minified forms) slipped past earlier regexes causing runtime attempts to resolve
|
|
526
|
-
// /@id/__x00__vite/dynamic-import-helper.js which does not exist in the device mirror.
|
|
527
|
-
// We aggressively strip any reference and inline a helper if necessary.
|
|
528
419
|
function stripViteDynamicImportVirtual(code) {
|
|
529
420
|
if (!/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
530
421
|
return code;
|
|
531
422
|
}
|
|
532
423
|
const original = code;
|
|
533
|
-
// Remove any import lines referencing the virtual helper (with or without bindings)
|
|
534
424
|
code = code.replace(/^[\t ]*import[^\n]*\/@id\/__x00__vite\/dynamic-import-helper[^\n]*$/gm, '');
|
|
535
|
-
// If any raw spec strings remain (e.g. concatenated), neutralize them
|
|
536
425
|
if (/\/@id\/__x00__vite\/dynamic-import-helper/.test(code)) {
|
|
537
426
|
code = code.replace(/\/@id\/__x00__vite\/dynamic-import-helper[^"'`)]*/g, '/__NS_UNUSED_DYNAMIC_IMPORT_HELPER__');
|
|
538
427
|
}
|
|
539
|
-
// Ensure helper present
|
|
540
428
|
if (!/__variableDynamicImportRuntimeHelper/.test(code)) {
|
|
541
429
|
const inline = `const __variableDynamicImportRuntimeHelper = (map, request, importMode) => {\n try { if (request === '@') { return import('/ns/m/__invalid_at__.mjs'); } } catch {}\n const loader = map && (map[request] || map[request?.replace(/\\\\/g, '/')]);\n if (!loader) { const e = new Error('Cannot dynamically import: ' + request); /*@ts-ignore*/ e.code = 'ERR_MODULE_NOT_FOUND'; return Promise.reject(e); }\n try { return loader(importMode); } catch (e) { return Promise.reject(e); }\n};\n`;
|
|
542
430
|
code = inline + code;
|
|
@@ -546,17 +434,7 @@ function stripViteDynamicImportVirtual(code) {
|
|
|
546
434
|
}
|
|
547
435
|
return code;
|
|
548
436
|
}
|
|
549
|
-
// Small snippet injected into device-delivered modules to capture any require('http(s)://') calls
|
|
550
437
|
const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);try{console.error(e.message+'\\n'+(e.stack||''));}catch(e2){}try{g.__NS_REQUIRE_GUARD_LAST__={spec:s,stack:e.stack,label:l,ts:Date.now()};}catch(e3){}}}catch(e1){}return o.apply(this, arguments);};};if(typeof g.require==='function'&&!g.require.__NS_REQ_GUARDED__){var o1=g.require;g.require=mk(o1,'require');g.require.__NS_REQ_GUARDED__=true;}if(typeof g.__nsRequire==='function'&&!g.__nsRequire.__NS_REQ_GUARDED__){var o2=g.__nsRequire;g.__nsRequire=mk(o2,'__nsRequire');g.__nsRequire.__NS_REQ_GUARDED__=true;}g.__NS_REQUIRE_GUARD_INSTALLED__=true;}}catch(e){}})();\n`;
|
|
551
|
-
// ============================================================================
|
|
552
|
-
// HELPER FUNCTIONS
|
|
553
|
-
// ============================================================================
|
|
554
|
-
// Origin invariant: we own both client and server. All URLs must use an explicit
|
|
555
|
-
// http(s)://host:port origin with no trailing slash. Build it deterministically
|
|
556
|
-
// where needed; do not post-sanitize.
|
|
557
|
-
/**
|
|
558
|
-
* Check if an import spec should be remapped to dep-*.mjs
|
|
559
|
-
*/
|
|
560
438
|
function shouldRemapImport(spec) {
|
|
561
439
|
if (!spec || typeof spec !== 'string')
|
|
562
440
|
return false;
|
|
@@ -577,12 +455,9 @@ function shouldRemapImport(spec) {
|
|
|
577
455
|
}
|
|
578
456
|
return true;
|
|
579
457
|
}
|
|
580
|
-
// (legacy wrapSfcWithStableDefault removed; full SFCs now delegate to /ns/asm)
|
|
581
458
|
function removeNamedImports(code, names) {
|
|
582
459
|
const regex = /^(\s*import\s*\{)([^}]*)(\}\s*from\s*['"][^'"]+['"];?)/gm;
|
|
583
460
|
return code.replace(regex, (_m, p1, specList, p3) => {
|
|
584
|
-
// Only strip for known globalized framework sources (Vue/Nativescript-Vue).
|
|
585
|
-
// Keep imports from all other packages (Pinia, third-party libs, app modules) intact.
|
|
586
461
|
const srcMatch = /from\s*['"]\s*([^'"\s]+)\s*['"]/i.exec(_m);
|
|
587
462
|
const src = (srcMatch?.[1] || '').toLowerCase();
|
|
588
463
|
const isVueSource = /^(?:vue|nativescript-vue)(?:\b|\/)/i.test(src);
|
|
@@ -681,7 +556,7 @@ function normalizeImportPath(spec, importerDir) {
|
|
|
681
556
|
else if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
682
557
|
key = path.posix.normalize(path.posix.join(importerDir, spec));
|
|
683
558
|
if (!key.startsWith('/')) {
|
|
684
|
-
key =
|
|
559
|
+
key = `/${key}`;
|
|
685
560
|
}
|
|
686
561
|
}
|
|
687
562
|
else {
|
|
@@ -748,6 +623,70 @@ function findDependencyFileName(depFileMap, key) {
|
|
|
748
623
|
}
|
|
749
624
|
return undefined;
|
|
750
625
|
}
|
|
626
|
+
function isRuntimePluginRootEntrySpecifier(specifier, projectRoot) {
|
|
627
|
+
if (!specifier) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
const cleaned = specifier.replace(PAT.QUERY_PATTERN, '');
|
|
631
|
+
const normalized = normalizeNodeModulesSpecifier(cleaned) || cleaned.replace(/^\/+/, '');
|
|
632
|
+
if (!normalized) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
const { packageName, subpath } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
636
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (!subpath) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (subpath.includes('/')) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const pkgBaseName = packageName.split('/').pop() || '';
|
|
646
|
+
const withoutExt = /(?:\.(?:ios|android|visionos))?\.(?:ts|tsx|js|jsx|mjs|mts|cts)$/i.test(subpath) ? subpath.replace(/\.[^.]+$/, '') : subpath;
|
|
647
|
+
const withoutPlatform = withoutExt.replace(/\.(ios|android|visionos)$/i, '');
|
|
648
|
+
return withoutPlatform === 'index' || withoutPlatform === pkgBaseName;
|
|
649
|
+
}
|
|
650
|
+
function collectMixedRuntimePluginHttpRootPackages(code, projectRoot) {
|
|
651
|
+
const nonRootSubpathPackages = new Set();
|
|
652
|
+
const rootEntryPackages = new Set();
|
|
653
|
+
const visitSpecifier = (rawSpecifier) => {
|
|
654
|
+
if (!rawSpecifier) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const specifier = normalizeNativeScriptCoreSpecifier(rawSpecifier).replace(PAT.QUERY_PATTERN, '');
|
|
658
|
+
if (!specifier) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (/^https?:\/\//.test(specifier) || specifier.startsWith('/ns/')) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (/^(?:\.|\/)/.test(specifier) && !specifier.includes('/node_modules/')) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const normalized = normalizeNodeModulesSpecifier(specifier) || specifier.replace(/^\/+/, '');
|
|
668
|
+
if (!normalized) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalized, projectRoot);
|
|
672
|
+
if (!packageName || !isLikelyNativeScriptRuntimePluginSpecifier(packageName, projectRoot)) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (isRuntimePluginRootEntrySpecifier(normalized, projectRoot)) {
|
|
676
|
+
rootEntryPackages.add(packageName);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
nonRootSubpathPackages.add(packageName);
|
|
680
|
+
};
|
|
681
|
+
for (const pattern of [PAT.IMPORT_PATTERN_1, PAT.IMPORT_PATTERN_2, PAT.IMPORT_PATTERN_3, PAT.IMPORT_PATTERN_SIDE_EFFECT]) {
|
|
682
|
+
pattern.lastIndex = 0;
|
|
683
|
+
let match;
|
|
684
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
685
|
+
visitSpecifier(match[2]);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return new Set(Array.from(nonRootSubpathPackages).filter((packageName) => rootEntryPackages.has(packageName)));
|
|
689
|
+
}
|
|
751
690
|
function collectImportDependencies(code, importerPath) {
|
|
752
691
|
const importerDir = path.posix.dirname(importerPath);
|
|
753
692
|
const deps = new Set();
|
|
@@ -809,68 +748,15 @@ function cleanCode(code) {
|
|
|
809
748
|
result = ACTIVE_STRATEGY.preClean(result);
|
|
810
749
|
result = ACTIVE_STRATEGY.rewriteFrameworkImports(result);
|
|
811
750
|
// Vendor manifest-driven import rewrites
|
|
751
|
+
// NOTE: Static and side-effect vendor imports are intentionally NOT rewritten here.
|
|
752
|
+
// They are left as import statements so that ensureNativeScriptModuleBindings()
|
|
753
|
+
// (called later in processCodeForDevice) can transform them using the robust
|
|
754
|
+
// __nsVendorRequire + __nsPick pattern that works on device.
|
|
755
|
+
// Only dynamic imports are handled here since ensureNativeScriptModuleBindings
|
|
756
|
+
// does not process dynamic import() calls.
|
|
812
757
|
try {
|
|
813
758
|
const manifest = getVendorManifest();
|
|
814
759
|
if (manifest) {
|
|
815
|
-
// Pattern: capture full import statement (static) with optional bindings
|
|
816
|
-
// import X from 'pkg'; | import {a,b as c} from "pkg"; | import * as ns from 'pkg';
|
|
817
|
-
const staticImportRE = /(import\s+([^;]*?)\s+from\s*["'])([^"']+)(["'];?)/g;
|
|
818
|
-
result = result.replace(staticImportRE, (full, pre, bindings, spec, post) => {
|
|
819
|
-
// Do not vendor-rewrite @nativescript/core — handled by the unified HTTP bridge later
|
|
820
|
-
if (isNativeScriptCoreModule(spec))
|
|
821
|
-
return full;
|
|
822
|
-
const resolved = resolveVendorSpecifier(spec);
|
|
823
|
-
if (!resolved || /^@nativescript\/core(\b|\/)/i.test(resolved))
|
|
824
|
-
return full; // not vendor or is core
|
|
825
|
-
// Determine binding style
|
|
826
|
-
const trimmed = (bindings || '').trim();
|
|
827
|
-
let injected = '';
|
|
828
|
-
if (!trimmed || trimmed === '') {
|
|
829
|
-
// Side-effect import: import 'pkg'; -> we drop it (vendor already evaluated)
|
|
830
|
-
return `/* vendor side-effect dropped: ${spec} */`;
|
|
831
|
-
}
|
|
832
|
-
// Default + named or default only
|
|
833
|
-
// Examples of trimmed:
|
|
834
|
-
// defaultExport
|
|
835
|
-
// { a, b as c }
|
|
836
|
-
// * as ns
|
|
837
|
-
// defaultExport, { a, b }
|
|
838
|
-
const globalAccessor = `globalThis.__nsVendor && globalThis.__nsVendor(${JSON.stringify(resolved)})`;
|
|
839
|
-
const ensureHelper = `globalThis.__nsVendor=require? (globalThis.__nsVendor|| (globalThis.__nsVendor=(id)=>{const m=(globalThis.__NS_VENDOR_MANIFEST__?globalThis.__NS_VENDOR_MANIFEST__.modules[id]:null);return (globalThis.__nsModules && globalThis.__nsModules.get? (globalThis.__nsModules.get(id)||globalThis.__nsModules.get(m?.id||id)):undefined);})):globalThis.__nsVendor`;
|
|
840
|
-
if (trimmed.startsWith('{')) {
|
|
841
|
-
// Named only
|
|
842
|
-
injected = `${ensureHelper}; const ${trimmed} = ${globalAccessor} || {};`;
|
|
843
|
-
}
|
|
844
|
-
else if (trimmed.startsWith('*')) {
|
|
845
|
-
// Namespace import: * as ns
|
|
846
|
-
const m = /\*\s+as\s+(\w+)/.exec(trimmed);
|
|
847
|
-
if (m) {
|
|
848
|
-
injected = `${ensureHelper}; const ${m[1]} = ${globalAccessor} || {};`;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
else if (trimmed.includes(',')) {
|
|
852
|
-
// default plus named
|
|
853
|
-
const parts = trimmed.split(',');
|
|
854
|
-
const def = parts[0].trim();
|
|
855
|
-
const named = parts.slice(1).join(',').trim();
|
|
856
|
-
injected = `${ensureHelper}; const __vmod = ${globalAccessor} || {}; const ${def} = __vmod.default || __vmod; const ${named} = __vmod;`;
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
// default only
|
|
860
|
-
injected = `${ensureHelper}; const ${trimmed} = (${globalAccessor}||{}).default || ${globalAccessor};`;
|
|
861
|
-
}
|
|
862
|
-
return injected;
|
|
863
|
-
});
|
|
864
|
-
// Bare side-effect imports: import 'pkg';
|
|
865
|
-
const sideEffectRE = /(import\s*["'])([^"']+)(["'];?)/g;
|
|
866
|
-
result = result.replace(sideEffectRE, (full, pre, spec, post) => {
|
|
867
|
-
if (isNativeScriptCoreModule(spec))
|
|
868
|
-
return full;
|
|
869
|
-
const resolved = resolveVendorSpecifier(spec);
|
|
870
|
-
if (!resolved || /^@nativescript\/core(\b|\/)/i.test(resolved))
|
|
871
|
-
return full;
|
|
872
|
-
return `/* vendor side-effect skipped: ${spec} */`;
|
|
873
|
-
});
|
|
874
760
|
// Dynamic import rewrites: import('pkg') -> Promise.resolve(__nsVendor('id'))
|
|
875
761
|
const dynImportRE = /(import\(\s*["'])([^"']+)(["']\s*\))/g;
|
|
876
762
|
result = result.replace(dynImportRE, (full, pre, spec, post) => {
|
|
@@ -999,6 +885,59 @@ function toAppModuleBaseId(importPath, projectRoot) {
|
|
|
999
885
|
const base = projectRelative.replace(/\.mjs$/i, '');
|
|
1000
886
|
return `/${base}`;
|
|
1001
887
|
}
|
|
888
|
+
function toNodeModulesHttpModuleId(importPath) {
|
|
889
|
+
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(importPath);
|
|
890
|
+
if (!nodeModulesSpecifier) {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
return `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
894
|
+
}
|
|
895
|
+
export function rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest) {
|
|
896
|
+
const toHmrServeTag = (value) => {
|
|
897
|
+
const raw = String(value ?? '').trim();
|
|
898
|
+
if (!raw) {
|
|
899
|
+
return 'v0';
|
|
900
|
+
}
|
|
901
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
902
|
+
return raw;
|
|
903
|
+
}
|
|
904
|
+
if (/^\d+$/.test(raw)) {
|
|
905
|
+
return `v${raw}`;
|
|
906
|
+
}
|
|
907
|
+
return raw;
|
|
908
|
+
};
|
|
909
|
+
if (!p || !p.startsWith('/ns/m/')) {
|
|
910
|
+
return p;
|
|
911
|
+
}
|
|
912
|
+
const canonicalNodeModulesPath = p.replace(/^\/ns\/m\/__ns_boot__\/b1\/__ns_hmr__\/[^/]+\/node_modules\//, '/ns/m/node_modules/').replace(/^\/ns\/m\/__ns_hmr__\/[^/]+\/node_modules\//, '/ns/m/node_modules/');
|
|
913
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/node_modules/')) {
|
|
914
|
+
return canonicalNodeModulesPath;
|
|
915
|
+
}
|
|
916
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_boot__/')) {
|
|
917
|
+
return canonicalNodeModulesPath;
|
|
918
|
+
}
|
|
919
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_hmr__/')) {
|
|
920
|
+
return bootTaggedRequest ? `/ns/m/__ns_boot__/b1${canonicalNodeModulesPath.slice('/ns/m'.length)}` : canonicalNodeModulesPath;
|
|
921
|
+
}
|
|
922
|
+
const tag = toHmrServeTag(ver);
|
|
923
|
+
const hmrPrefix = `/ns/m/__ns_hmr__/${tag}`;
|
|
924
|
+
const bootHmrPrefix = `/ns/m/__ns_boot__/b1/__ns_hmr__/${tag}`;
|
|
925
|
+
return (bootTaggedRequest ? bootHmrPrefix : hmrPrefix) + canonicalNodeModulesPath.slice('/ns/m'.length);
|
|
926
|
+
}
|
|
927
|
+
function getNumericServeVersionTag(tag, fallback) {
|
|
928
|
+
const raw = String(tag || '').trim();
|
|
929
|
+
if (!raw) {
|
|
930
|
+
return fallback;
|
|
931
|
+
}
|
|
932
|
+
const versionMatch = raw.match(/^v(\d+)$/);
|
|
933
|
+
if (versionMatch?.[1]) {
|
|
934
|
+
return Number(versionMatch[1]);
|
|
935
|
+
}
|
|
936
|
+
if (/^\d+$/.test(raw)) {
|
|
937
|
+
return Number(raw);
|
|
938
|
+
}
|
|
939
|
+
return fallback;
|
|
940
|
+
}
|
|
1002
941
|
function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
1003
942
|
if (!spec || typeof spec !== 'string') {
|
|
1004
943
|
return null;
|
|
@@ -1025,13 +964,172 @@ function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
|
1025
964
|
}
|
|
1026
965
|
return absolute;
|
|
1027
966
|
}
|
|
967
|
+
/**
|
|
968
|
+
* After the Angular linker runs on code that Vite has already resolved (bare
|
|
969
|
+
* specifiers → full URLs), the linker injects NEW import statements with bare
|
|
970
|
+
* specifiers (e.g. `import {Component} from '@angular/core'`). These cause:
|
|
971
|
+
* 1. Duplicate-identifier SyntaxErrors (the name was already imported via URL)
|
|
972
|
+
* 2. Unresolvable bare specifiers at runtime on device
|
|
973
|
+
*
|
|
974
|
+
* This function:
|
|
975
|
+
* • builds a map packageName → resolvedURL from existing resolved imports
|
|
976
|
+
* • collects all binding names already imported per package
|
|
977
|
+
* • for each bare-specifier import, removes duplicate bindings
|
|
978
|
+
* • rewrites any genuinely-new bindings to use the resolved URL
|
|
979
|
+
*/
|
|
980
|
+
function deduplicateLinkerImports(code) {
|
|
981
|
+
if (!code)
|
|
982
|
+
return code;
|
|
983
|
+
try {
|
|
984
|
+
const imports = collectTopLevelImportRecords(code);
|
|
985
|
+
if (!imports.length) {
|
|
986
|
+
return code;
|
|
987
|
+
}
|
|
988
|
+
// ── Step 1: collect resolved imports already in the file ──────────
|
|
989
|
+
const pkgUrlMap = new Map();
|
|
990
|
+
const pkgBindings = new Map();
|
|
991
|
+
for (const imp of imports) {
|
|
992
|
+
const url = imp.source;
|
|
993
|
+
if (!/^https?:\/\//.test(url) && !url.startsWith('/')) {
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
const nmIdx = url.lastIndexOf('/node_modules/');
|
|
997
|
+
if (nmIdx === -1)
|
|
998
|
+
continue;
|
|
999
|
+
const afterNm = url.substring(nmIdx + '/node_modules/'.length);
|
|
1000
|
+
const parts = afterNm.split('/');
|
|
1001
|
+
const pkg = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
1002
|
+
if (!pkgUrlMap.has(pkg))
|
|
1003
|
+
pkgUrlMap.set(pkg, url);
|
|
1004
|
+
if (imp.namedBindings.length) {
|
|
1005
|
+
if (!pkgBindings.has(pkg))
|
|
1006
|
+
pkgBindings.set(pkg, new Set());
|
|
1007
|
+
for (const binding of imp.namedBindings) {
|
|
1008
|
+
if (binding.importedName)
|
|
1009
|
+
pkgBindings.get(pkg).add(binding.importedName);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (pkgUrlMap.size === 0)
|
|
1014
|
+
return code;
|
|
1015
|
+
// ── Step 2: rewrite bare-specifier imports ───────────────────────
|
|
1016
|
+
const edits = [];
|
|
1017
|
+
for (const imp of imports) {
|
|
1018
|
+
if (!imp.hasOnlyNamedSpecifiers) {
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
const specifier = imp.source;
|
|
1022
|
+
if (specifier.startsWith('/') || specifier.startsWith('.') || specifier.startsWith('http')) {
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
const parts = specifier.split('/');
|
|
1026
|
+
const pkg = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
1027
|
+
const url = pkgUrlMap.get(pkg);
|
|
1028
|
+
if (!url) {
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
const existing = pkgBindings.get(pkg) || new Set();
|
|
1032
|
+
const newBindings = imp.namedBindings.filter((binding) => !existing.has(binding.importedName));
|
|
1033
|
+
if (newBindings.length === 0) {
|
|
1034
|
+
edits.push({ start: imp.start, end: imp.end, text: '' });
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (newBindings.length === imp.namedBindings.length) {
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
for (const binding of newBindings) {
|
|
1041
|
+
existing.add(binding.importedName);
|
|
1042
|
+
}
|
|
1043
|
+
edits.push({
|
|
1044
|
+
start: imp.start,
|
|
1045
|
+
end: imp.end,
|
|
1046
|
+
text: `import { ${newBindings.map((binding) => binding.text).join(', ')} } from ${JSON.stringify(url)};`,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
if (!edits.length) {
|
|
1050
|
+
return code;
|
|
1051
|
+
}
|
|
1052
|
+
let next = code;
|
|
1053
|
+
for (const edit of edits.sort((left, right) => right.start - left.start)) {
|
|
1054
|
+
next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
|
|
1055
|
+
}
|
|
1056
|
+
return next;
|
|
1057
|
+
}
|
|
1058
|
+
catch {
|
|
1059
|
+
return code;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
export function wrapCommonJsModuleForDevice(code) {
|
|
1063
|
+
if (!code)
|
|
1064
|
+
return code;
|
|
1065
|
+
try {
|
|
1066
|
+
const hasExportDefault = /\bexport\s+default\b/.test(code) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(code);
|
|
1067
|
+
const hasNamedExports = /\bexport\s+(?:const|let|var|function|class|async)\b/.test(code) || /\bexport\s*\{/.test(code);
|
|
1068
|
+
const hasCjsExports = /\bmodule\s*\.\s*exports\b/.test(code) || /\bexports\s*\.\s*\w/.test(code);
|
|
1069
|
+
if (hasExportDefault || hasNamedExports || !hasCjsExports) {
|
|
1070
|
+
return code;
|
|
1071
|
+
}
|
|
1072
|
+
const namedExports = new Set();
|
|
1073
|
+
const exportsRe = /\bexports\s*\.\s*([A-Za-z_$][\w$]*)\s*=/g;
|
|
1074
|
+
let em;
|
|
1075
|
+
while ((em = exportsRe.exec(code)) !== null) {
|
|
1076
|
+
const name = em[1];
|
|
1077
|
+
if (name !== '__esModule' && name !== 'default') {
|
|
1078
|
+
namedExports.add(name);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
const defPropRe = /Object\s*\.\s*defineProperty\s*\(\s*exports\s*,\s*['"]([^'"]+)['"]/g;
|
|
1082
|
+
while ((em = defPropRe.exec(code)) !== null) {
|
|
1083
|
+
const name = em[1];
|
|
1084
|
+
if (name !== '__esModule' && name !== 'default') {
|
|
1085
|
+
namedExports.add(name);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
let suffix = `\nvar __cjs_mod = module.exports;\nexport default __cjs_mod;\n`;
|
|
1089
|
+
if (namedExports.size) {
|
|
1090
|
+
const entries = Array.from(namedExports);
|
|
1091
|
+
const temps = entries.map((name, i) => `var __cjs_e${i} = __cjs_mod[${JSON.stringify(name)}];`);
|
|
1092
|
+
const reExports = entries.map((name, i) => `__cjs_e${i} as ${name}`);
|
|
1093
|
+
suffix += `${temps.join(' ')}\nexport { ${reExports.join(', ')} };\n`;
|
|
1094
|
+
}
|
|
1095
|
+
const prelude = `var module = { exports: {} }; var exports = module.exports;\n` +
|
|
1096
|
+
`var __ns_cjs_require_base = (typeof globalThis.__nsBaseRequire === 'function' ? globalThis.__nsBaseRequire : (typeof globalThis.__nsRequire === 'function' ? globalThis.__nsRequire : (typeof globalThis.require === 'function' ? globalThis.require : undefined)));\n` +
|
|
1097
|
+
`var __ns_cjs_require_kind = (typeof globalThis.__nsBaseRequire === 'function' ? 'base-require' : (typeof globalThis.__nsRequire === 'function' ? 'vendor-require' : 'global-require'));\n` +
|
|
1098
|
+
`var require = function(spec) {\n` +
|
|
1099
|
+
` if (!__ns_cjs_require_base) { throw new Error('require is not defined'); }\n` +
|
|
1100
|
+
` try { var __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(String(spec), { kind: __ns_cjs_require_kind, specifier: String(spec), via: 'cjs-wrapper', parent: (typeof import.meta !== 'undefined' && import.meta && import.meta.url) ? import.meta.url : undefined }); } } catch (e) {}\n` +
|
|
1101
|
+
` var mod = __ns_cjs_require_base(spec);\n` +
|
|
1102
|
+
` try {\n` +
|
|
1103
|
+
` if (mod && (typeof mod === 'object' || typeof mod === 'function') && mod.default !== undefined) {\n` +
|
|
1104
|
+
` var keys = [];\n` +
|
|
1105
|
+
` try { keys = Object.keys(mod); } catch (e) {}\n` +
|
|
1106
|
+
` var defaultOnly = keys.length === 1 && keys[0] === 'default';\n` +
|
|
1107
|
+
` var esModuleOnly = keys.length === 2 && keys.indexOf('default') !== -1 && keys.indexOf('__esModule') !== -1;\n` +
|
|
1108
|
+
` if (mod.__esModule || defaultOnly || esModuleOnly) { return mod.default; }\n` +
|
|
1109
|
+
` }\n` +
|
|
1110
|
+
` } catch (e) {}\n` +
|
|
1111
|
+
` return mod;\n` +
|
|
1112
|
+
`};\n`;
|
|
1113
|
+
return `${prelude}${code}${suffix}`;
|
|
1114
|
+
}
|
|
1115
|
+
catch {
|
|
1116
|
+
return code;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1028
1119
|
/**
|
|
1029
1120
|
* Process code for device: inject globals, remove framework imports
|
|
1030
1121
|
*/
|
|
1031
|
-
function processCodeForDevice(code, isVitePreBundled) {
|
|
1122
|
+
function processCodeForDevice(code, isVitePreBundled, preserveVendorImports = false, isNodeModule = false, sourceId, options) {
|
|
1032
1123
|
let result = code;
|
|
1124
|
+
const resolvedSpecifierOverrides = options?.resolvedSpecifierOverrides || getProcessCodeResolvedSpecifierOverrides(sourceId, getProjectRootPath());
|
|
1125
|
+
const bindingOptions = {
|
|
1126
|
+
preserveNonPluginVendorImports: preserveVendorImports,
|
|
1127
|
+
resolvedSpecifierOverrides,
|
|
1128
|
+
};
|
|
1033
1129
|
// Ensure Angular partial declarations are linked before any sanitizers run so runtime never hits the JIT path.
|
|
1034
1130
|
result = linkAngularPartialsIfNeeded(result);
|
|
1131
|
+
// Post-linker: deduplicate/resolve imports the Angular linker injected with bare specifiers
|
|
1132
|
+
result = deduplicateLinkerImports(result);
|
|
1035
1133
|
// First: aggressively strip any lingering virtual dynamic-import-helper before anything else.
|
|
1036
1134
|
// Doing this up-front prevents downstream dependency collection from seeing the virtual id.
|
|
1037
1135
|
result = stripViteDynamicImportVirtual(result);
|
|
@@ -1042,13 +1140,17 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1042
1140
|
// Inject ALL NativeScript/build globals at the top (matching global-defines.ts)
|
|
1043
1141
|
// This ensures any code using __DEV__, __ANDROID__, __IOS__, etc. works correctly
|
|
1044
1142
|
const allGlobals = [
|
|
1143
|
+
// Minimal process shim — populated with CLI --env.* flags at module load time.
|
|
1144
|
+
// In production builds, Vite/Rollup replaces process.env.* statically.
|
|
1145
|
+
// In HMR dev mode the code runs as-is on device, so we need the shim.
|
|
1146
|
+
`if (typeof process === "undefined") { globalThis.process = { env: ${__processEnvJson} }; } else if (!process.env) { process.env = ${__processEnvJson}; }`,
|
|
1045
1147
|
'const __ANDROID__ = globalThis.__ANDROID__ !== undefined ? globalThis.__ANDROID__ : false;',
|
|
1046
1148
|
'const __IOS__ = globalThis.__IOS__ !== undefined ? globalThis.__IOS__ : false;',
|
|
1047
1149
|
'const __VISIONOS__ = globalThis.__VISIONOS__ !== undefined ? globalThis.__VISIONOS__ : false;',
|
|
1048
1150
|
'const __APPLE__ = globalThis.__APPLE__ !== undefined ? globalThis.__APPLE__ : (__IOS__ || __VISIONOS__);',
|
|
1049
1151
|
'const __DEV__ = globalThis.__DEV__ !== undefined ? globalThis.__DEV__ : false;',
|
|
1050
1152
|
'const __COMMONJS__ = globalThis.__COMMONJS__ !== undefined ? globalThis.__COMMONJS__ : false;',
|
|
1051
|
-
'const __NS_WEBPACK__ = globalThis.__NS_WEBPACK__ !== undefined ? globalThis.__NS_WEBPACK__ :
|
|
1153
|
+
'const __NS_WEBPACK__ = globalThis.__NS_WEBPACK__ !== undefined ? globalThis.__NS_WEBPACK__ : false;',
|
|
1052
1154
|
'const __NS_ENV_VERBOSE__ = globalThis.__NS_ENV_VERBOSE__ !== undefined ? !!globalThis.__NS_ENV_VERBOSE__ : false;',
|
|
1053
1155
|
"const __CSS_PARSER__ = globalThis.__CSS_PARSER__ !== undefined ? globalThis.__CSS_PARSER__ : 'css-tree';",
|
|
1054
1156
|
'const __UI_USE_XML_PARSER__ = globalThis.__UI_USE_XML_PARSER__ !== undefined ? globalThis.__UI_USE_XML_PARSER__ : true;',
|
|
@@ -1056,19 +1158,29 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1056
1158
|
'const __TEST__ = globalThis.__TEST__ !== undefined ? globalThis.__TEST__ : false;',
|
|
1057
1159
|
];
|
|
1058
1160
|
result = allGlobals.join('\n') + '\n' + result;
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
result =
|
|
1161
|
+
const nodeModuleProvenancePrelude = buildNodeModuleProvenancePrelude(sourceId);
|
|
1162
|
+
if (nodeModuleProvenancePrelude) {
|
|
1163
|
+
result = nodeModuleProvenancePrelude + result;
|
|
1062
1164
|
}
|
|
1063
|
-
|
|
1064
|
-
//
|
|
1065
|
-
|
|
1066
|
-
|
|
1165
|
+
// AST normalization: inject /ns/rt helper aliases for underscore-prefixed identifiers.
|
|
1166
|
+
// ONLY for app source files — library code in node_modules should be served as-is.
|
|
1167
|
+
// Running the normalizer on libraries like tslib injects harmful destructures
|
|
1168
|
+
// (e.g., `const { SuppressedError } = __ns_rt_ns_1`) that shadow globals.
|
|
1169
|
+
if (!isNodeModule) {
|
|
1170
|
+
try {
|
|
1171
|
+
result = astNormalizeModuleImportsAndHelpers(result);
|
|
1172
|
+
}
|
|
1173
|
+
catch { }
|
|
1174
|
+
// Verify there are no duplicate top-level const/let bindings after AST normalization
|
|
1175
|
+
try {
|
|
1176
|
+
result = astVerifyAndAnnotateDuplicates(result);
|
|
1177
|
+
}
|
|
1178
|
+
catch { }
|
|
1067
1179
|
}
|
|
1068
|
-
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
1071
|
-
if (!/^\s*(?:\/\/|\/\*) \[ast-normalized\]/m.test(result)) {
|
|
1180
|
+
// If AST marker present OR this is a node_modules file, skip regex-based helper
|
|
1181
|
+
// alias injection. Library code should NOT get /ns/rt destructures injected —
|
|
1182
|
+
// underscore-prefixed identifiers in libraries are internal variables, not NS helpers.
|
|
1183
|
+
if (!isNodeModule && !/^\s*(?:\/\/|\/\*) \[ast-normalized\]/m.test(result)) {
|
|
1072
1184
|
try {
|
|
1073
1185
|
const underscored = new Set();
|
|
1074
1186
|
const re = /(^|[^.\w$])_([A-Za-z]\w*)\b/g;
|
|
@@ -1149,7 +1261,11 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1149
1261
|
result = result.replace(/(^|\n)([\t ]*import\s+[^;]*?\s+from)\s*\n\s*("\/?node_modules\/\.vite\/deps\/[^"\n]+"\s*;?\s*)/gm, (_m, p1, p2, p3) => `${p1}${p2} ${p3}`);
|
|
1150
1262
|
}
|
|
1151
1263
|
catch { }
|
|
1152
|
-
|
|
1264
|
+
// When preserveVendorImports is true (HMR /ns/m/ endpoint), skip the
|
|
1265
|
+
// __nsVendorRequire + __nsPick rewrite. Vendor imports stay as bare
|
|
1266
|
+
// specifiers so the device-side import map resolves them via V8's native
|
|
1267
|
+
// module system, which correctly handles export * re-exports.
|
|
1268
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
1153
1269
|
// Repair any accidental "import ... = expr" assignments that may have slipped in.
|
|
1154
1270
|
try {
|
|
1155
1271
|
result = repairImportEqualsAssignments(result);
|
|
@@ -1158,10 +1274,7 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1158
1274
|
// Strip Vite prebundle deps imports (both named and side-effect) and any malformed const string artifacts
|
|
1159
1275
|
// Example problematic line observed: const "/node_modules/.vite/deps/@nativescript_firebase-messaging.js?v=...";
|
|
1160
1276
|
if (/node_modules\/\.vite\/deps\//.test(result)) {
|
|
1161
|
-
|
|
1162
|
-
result = result.replace(/^[\t ]*import\s+[^;]*from\s+["']\/?node_modules\/\.vite\/deps\/[^"']+["'];?\s*$/gm, '');
|
|
1163
|
-
// Side-effect only imports from prebundle deps
|
|
1164
|
-
result = result.replace(/^[\t ]*import\s+["']\/?node_modules\/\.vite\/deps\/[^"']+["'];?\s*$/gm, '');
|
|
1277
|
+
result = rewriteVitePrebundleImportsForDevice(result, preserveVendorImports);
|
|
1165
1278
|
// Malformed const string lines accidentally produced by upstream transforms
|
|
1166
1279
|
result = result.replace(/^[\t ]*const\s+["']\/?node_modules\/\.vite\/deps\/[^"']+["'];?\s*$/gm, '// [hmr-sanitize] stripped malformed const prebundle ref\n');
|
|
1167
1280
|
// Naked string-only lines pointing at prebundle deps
|
|
@@ -1248,7 +1361,7 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1248
1361
|
}
|
|
1249
1362
|
// Ensure vendor bindings also apply after potential wrapper injections above
|
|
1250
1363
|
// (idempotent: second pass will be a no-op if imports already consumed).
|
|
1251
|
-
result = ensureNativeScriptModuleBindings(result);
|
|
1364
|
+
result = ensureNativeScriptModuleBindings(result, bindingOptions);
|
|
1252
1365
|
try {
|
|
1253
1366
|
result = repairImportEqualsAssignments(result);
|
|
1254
1367
|
}
|
|
@@ -1289,17 +1402,17 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1289
1402
|
result = normalizeStrayCoreStringLiterals(result);
|
|
1290
1403
|
}
|
|
1291
1404
|
catch { }
|
|
1405
|
+
try {
|
|
1406
|
+
result = fixDanglingCoreFrom(result);
|
|
1407
|
+
}
|
|
1408
|
+
catch { }
|
|
1409
|
+
try {
|
|
1410
|
+
result = normalizeAnyCoreSpecToBridge(result);
|
|
1411
|
+
}
|
|
1412
|
+
catch { }
|
|
1292
1413
|
result = ensureVariableDynamicImportHelper(result);
|
|
1293
1414
|
// Normalize any lingering @nativescript/core imports to the /ns/core bridge (non-destructive best-effort)
|
|
1294
1415
|
try {
|
|
1295
|
-
result = result.replace(/from\s+["']@nativescript\/core([^"'\n]*)["']/g, (_m, sub) => {
|
|
1296
|
-
const qp = (sub || '').trim().replace(/^\//, '');
|
|
1297
|
-
return `from "/ns/core${qp ? `?p=${qp}` : ''}"`;
|
|
1298
|
-
});
|
|
1299
|
-
result = result.replace(/import\(\s*["']@nativescript\/core([^"'\n]*)["']\s*\)/g, (_m, sub) => {
|
|
1300
|
-
const qp = (sub || '').trim().replace(/^\//, '');
|
|
1301
|
-
return `import("/ns/core${qp ? `?p=${qp}` : ''}")`;
|
|
1302
|
-
});
|
|
1303
1416
|
// Rewrite named imports from the /ns/core bridge into default import + destructuring.
|
|
1304
1417
|
// This makes `import { Frame } from '@nativescript/core'` work even if the bridge provides only a default export.
|
|
1305
1418
|
{
|
|
@@ -1315,6 +1428,9 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1315
1428
|
.join(', ');
|
|
1316
1429
|
const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[\d]+)?(?:\?p=[^"']+)?)['"];?\s*/gm;
|
|
1317
1430
|
result = result.replace(reNamed, (_full, pfx, specList, src) => {
|
|
1431
|
+
// Deep subpath URLs serve actual ESM with real named exports — skip.
|
|
1432
|
+
if (isDeepCoreSubpath(src))
|
|
1433
|
+
return _full;
|
|
1318
1434
|
__core_ns_seq++;
|
|
1319
1435
|
const tmp = `__ns_core_ns${__core_ns_seq}`;
|
|
1320
1436
|
const decl = `const { ${toDestructureCore(specList)} } = ${tmp};`;
|
|
@@ -1322,6 +1438,8 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1322
1438
|
});
|
|
1323
1439
|
const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[\d]+)?(?:\?p=[^"']+)?)['"];?\s*/gm;
|
|
1324
1440
|
result = result.replace(reMixed, (_full, pfx, defName, specList, src) => {
|
|
1441
|
+
if (isDeepCoreSubpath(src))
|
|
1442
|
+
return _full;
|
|
1325
1443
|
const decl = `const { ${toDestructureCore(specList)} } = ${defName};`;
|
|
1326
1444
|
return `${pfx}import ${defName} from ${JSON.stringify(src)};\n${decl}\n`;
|
|
1327
1445
|
});
|
|
@@ -1335,8 +1453,11 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1335
1453
|
// Keep a single semicolon before the import to avoid generating ';;'
|
|
1336
1454
|
result = result.replace(/;\s*import\s+/g, ';\nimport ');
|
|
1337
1455
|
result = result.replace(/}\s*import\s+/g, '}\nimport ');
|
|
1338
|
-
// Fallback: ensure any static import that isn't at start of line gets a newline before it
|
|
1339
|
-
|
|
1456
|
+
// Fallback: ensure any static import that isn't at start of line gets a newline before it.
|
|
1457
|
+
// Only match after statement-ending characters (;, }, ), ], quotes) — NOT after `*` or
|
|
1458
|
+
// spaces inside JSDoc comment blocks, which would accidentally extract example imports
|
|
1459
|
+
// from documentation comments and hoist them as real code.
|
|
1460
|
+
result = result.replace(/([;}\)\]'"`])\s*(import\s+[^;\n]*\s+from\s*["'][^"']+["'])/g, '$1\n$2');
|
|
1340
1461
|
}
|
|
1341
1462
|
catch { }
|
|
1342
1463
|
// Collapse duplicate destructuring from the same temp namespace var (e.g., multiple const { x } = __ns_rt_ns1)
|
|
@@ -1370,22 +1491,13 @@ function processCodeForDevice(code, isVitePreBundled) {
|
|
|
1370
1491
|
// always come before any statements that might reference their bindings. This ordering avoids
|
|
1371
1492
|
// device runtimes that are stricter about imports-first semantics during module instantiation.
|
|
1372
1493
|
try {
|
|
1373
|
-
|
|
1374
|
-
const lines = [];
|
|
1375
|
-
result = result.replace(importLineRe, (imp) => {
|
|
1376
|
-
lines.push(imp.trim());
|
|
1377
|
-
return '';
|
|
1378
|
-
});
|
|
1379
|
-
if (lines.length) {
|
|
1380
|
-
const hoisted = Array.from(new Set(lines)).join('\n') + '\n';
|
|
1381
|
-
result = hoisted + result;
|
|
1382
|
-
}
|
|
1494
|
+
result = hoistTopLevelStaticImports(result);
|
|
1383
1495
|
}
|
|
1384
1496
|
catch { }
|
|
1385
1497
|
// Final safety: normalize any lingering named imports from /ns/rt into default+destructure
|
|
1386
|
-
// Skip
|
|
1498
|
+
// Skip for node_modules (no /ns/rt helpers needed) and when AST marker present
|
|
1387
1499
|
try {
|
|
1388
|
-
if (!/^\s*\/\* \[ast-normalized\] \*\//m.test(result)) {
|
|
1500
|
+
if (!isNodeModule && !/^\s*\/\* \[ast-normalized\] \*\//m.test(result)) {
|
|
1389
1501
|
result = ensureDestructureRtImports(result);
|
|
1390
1502
|
}
|
|
1391
1503
|
}
|
|
@@ -1531,6 +1643,16 @@ function assertNoOptimizedArtifacts(code, contextLabel) {
|
|
|
1531
1643
|
}
|
|
1532
1644
|
}
|
|
1533
1645
|
if (localCore.test(ln)) {
|
|
1646
|
+
// Comments can never cause split-realm risk at runtime — skip them.
|
|
1647
|
+
// Library authors commonly reference @nativescript/core in comments
|
|
1648
|
+
// (e.g. TSDoc /// <reference> directives, module resolution notes).
|
|
1649
|
+
const trimmed = ln.trimStart();
|
|
1650
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
if (shouldAllowLocalCoreSanitizerPaths(contextLabel)) {
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1534
1656
|
offenders.push(`${i + 1}: ${ln.substring(0, 200)} [local-core-path]`);
|
|
1535
1657
|
}
|
|
1536
1658
|
if (offenders.length >= 10)
|
|
@@ -1555,6 +1677,7 @@ function assertNoOptimizedArtifacts(code, contextLabel) {
|
|
|
1555
1677
|
function ensureDestructureCoreImports(code) {
|
|
1556
1678
|
try {
|
|
1557
1679
|
let result = code;
|
|
1680
|
+
let coreImportCounter = 0;
|
|
1558
1681
|
const toDestructure = (specList) => specList
|
|
1559
1682
|
.split(',')
|
|
1560
1683
|
.map((s) => s.trim())
|
|
@@ -1567,13 +1690,19 @@ function ensureDestructureCoreImports(code) {
|
|
|
1567
1690
|
// import { A, B } from '/ns/core[/ver][?p=...]'
|
|
1568
1691
|
const reNamed = /(^|\n)\s*import\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[\d]+)?(?:\?p=[^"']+)?)['"];?\s*/gm;
|
|
1569
1692
|
result = result.replace(reNamed, (_full, pfx, specList, src) => {
|
|
1570
|
-
|
|
1693
|
+
// Deep subpath URLs serve actual ESM with real named exports — skip.
|
|
1694
|
+
if (isDeepCoreSubpath(src))
|
|
1695
|
+
return _full;
|
|
1696
|
+
const tmp = `__ns_core_ns_re${coreImportCounter > 0 ? `_${coreImportCounter}` : ''}`;
|
|
1697
|
+
coreImportCounter++;
|
|
1571
1698
|
const decl = `const { ${toDestructure(specList)} } = ${tmp};`;
|
|
1572
1699
|
return `${pfx}import ${tmp} from ${JSON.stringify(src)};\n${decl}\n`;
|
|
1573
1700
|
});
|
|
1574
1701
|
// import Default, { A, B } from '/ns/core[...]'
|
|
1575
1702
|
const reMixed = /(^|\n)\s*import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]+)\}\s*from\s*["']((?:https?:\/\/[^"']+)?\/ns\/core(?:\/[\d]+)?(?:\?p=[^"']+)?)['"];?\s*/gm;
|
|
1576
1703
|
result = result.replace(reMixed, (_full, pfx, defName, specList, src) => {
|
|
1704
|
+
if (isDeepCoreSubpath(src))
|
|
1705
|
+
return _full;
|
|
1577
1706
|
const decl = `const { ${toDestructure(specList)} } = ${defName};`;
|
|
1578
1707
|
return `${pfx}import ${defName} from ${JSON.stringify(src)};\n${decl}\n`;
|
|
1579
1708
|
});
|
|
@@ -1685,14 +1814,17 @@ function dedupeRtNamedImportsAgainstDestructures(code) {
|
|
|
1685
1814
|
/**
|
|
1686
1815
|
* THE SINGLE REWRITE FUNCTION - used everywhere for consistency
|
|
1687
1816
|
*/
|
|
1688
|
-
function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin) {
|
|
1817
|
+
export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, verbose = false, outputDirOverrideRel, httpOrigin, resolveVendorAsHttp = false) {
|
|
1689
1818
|
let result = code;
|
|
1690
1819
|
const httpOriginSafe = httpOrigin;
|
|
1820
|
+
const mixedRuntimePluginHttpRootPackages = collectMixedRuntimePluginHttpRootPackages(result, projectRoot);
|
|
1821
|
+
const isDynamicImportPrefix = (prefix) => /import\(\s*["']?$/.test(prefix.trimStart());
|
|
1691
1822
|
const importerDir = path.posix.dirname(importerPath);
|
|
1692
1823
|
// Determine importer output relative path (project-relative .mjs) to compute relative imports consistently
|
|
1693
1824
|
const importerOutRel = outputDirOverrideRel || getProjectRelativeImportPath(importerPath, projectRoot) || stripToProjectRelative(importerPath, projectRoot).replace(/\.(ts|js|tsx|jsx|mjs|mts|cts)$/i, '.mjs');
|
|
1694
1825
|
const importerOutDir = importerOutRel ? path.posix.dirname(importerOutRel) : '';
|
|
1695
1826
|
const ensureRel = (p) => (p.startsWith('.') ? p : `./${p}`);
|
|
1827
|
+
const isNsSfcSpecifier = (spec) => /^(?:https?:\/\/[^/]+)?\/ns\/sfc(?:\/\d+)?(?:\/|$)/.test(spec.replace(PAT.QUERY_PATTERN, ''));
|
|
1696
1828
|
// Normalize all @nativescript/core imports to the unified HTTP ESM core bridge to guarantee a single realm on device
|
|
1697
1829
|
try {
|
|
1698
1830
|
let coreAliasIdx = 0;
|
|
@@ -1835,6 +1967,16 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1835
1967
|
return `${prefix}${stub}${suffix}`;
|
|
1836
1968
|
}
|
|
1837
1969
|
spec = normalizeNativeScriptCoreSpecifier(spec);
|
|
1970
|
+
// Route Vite virtual modules (/@solid-refresh, etc.) through /ns/m/ so their
|
|
1971
|
+
// internal imports (e.g. solid-js) get vendor-rewritten by our pipeline.
|
|
1972
|
+
// Skip known Vite internals (/@vite/, /@id/, /@fs/) which are handled elsewhere.
|
|
1973
|
+
if (spec.startsWith('/@') && !/^\/@(?:vite|id|fs)\//.test(spec)) {
|
|
1974
|
+
const out = `/ns/m${spec}`;
|
|
1975
|
+
if (httpOriginSafe) {
|
|
1976
|
+
return `${prefix}${httpOriginSafe}${out}${suffix}`;
|
|
1977
|
+
}
|
|
1978
|
+
return `${prefix}${out}${suffix}`;
|
|
1979
|
+
}
|
|
1838
1980
|
// Route internal NS endpoints to absolute HTTP origin for device
|
|
1839
1981
|
if (spec.startsWith('/ns/')) {
|
|
1840
1982
|
if (httpOriginSafe) {
|
|
@@ -1846,20 +1988,50 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1846
1988
|
return `${prefix}${spec}${suffix}`;
|
|
1847
1989
|
}
|
|
1848
1990
|
const nodeModulesSpecifier = normalizeNodeModulesSpecifier(spec);
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
if (
|
|
1853
|
-
|
|
1991
|
+
const normalizedRuntimePluginSpec = nodeModulesSpecifier || spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '');
|
|
1992
|
+
if (normalizedRuntimePluginSpec && mixedRuntimePluginHttpRootPackages.size > 0) {
|
|
1993
|
+
const { packageName } = resolveNodeModulesPackageBoundary(normalizedRuntimePluginSpec, projectRoot);
|
|
1994
|
+
if (packageName && mixedRuntimePluginHttpRootPackages.has(packageName)) {
|
|
1995
|
+
const httpNodeModulesSpecifier = nodeModulesSpecifier || normalizedRuntimePluginSpec;
|
|
1996
|
+
const httpSpec = `/ns/m/node_modules/${httpNodeModulesSpecifier}`;
|
|
1997
|
+
if (httpOriginSafe) {
|
|
1998
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
1999
|
+
}
|
|
2000
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
1854
2001
|
}
|
|
1855
|
-
return `${prefix}${spec.replace(PAT.QUERY_PATTERN, '')}${suffix}`;
|
|
1856
2002
|
}
|
|
1857
|
-
if (
|
|
1858
|
-
const
|
|
1859
|
-
|
|
2003
|
+
if (shouldPreserveBareRuntimePluginSubpathImport(spec, projectRoot)) {
|
|
2004
|
+
const httpSpec = `/ns/m/node_modules/${spec.replace(PAT.QUERY_PATTERN, '').replace(/^\/+/, '')}`;
|
|
2005
|
+
if (httpOriginSafe) {
|
|
2006
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
2007
|
+
}
|
|
2008
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
1860
2009
|
}
|
|
2010
|
+
const candidateNativeScriptSpec = nodeModulesSpecifier ?? spec;
|
|
2011
|
+
// ── Node modules routing ──────────────────────────────────────
|
|
2012
|
+
// Uses the package's own package.json exports field to determine
|
|
2013
|
+
// whether an import is the main entry (→ vendor bridge) or a
|
|
2014
|
+
// subpath entry (→ HTTP). This replaces the old heuristic-based
|
|
2015
|
+
// approach that tried to guess from file paths.
|
|
1861
2016
|
if (nodeModulesSpecifier) {
|
|
1862
|
-
|
|
2017
|
+
const vendorRouting = resolveVendorRouting(nodeModulesSpecifier, projectRoot);
|
|
2018
|
+
if (vendorRouting) {
|
|
2019
|
+
if (vendorRouting.route === 'vendor') {
|
|
2020
|
+
return `${prefix}${vendorRouting.bareSpec}${suffix}`;
|
|
2021
|
+
}
|
|
2022
|
+
// Vendor package but subpath/platform-specific → HTTP
|
|
2023
|
+
const httpSpec = `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
2024
|
+
if (httpOriginSafe) {
|
|
2025
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
2026
|
+
}
|
|
2027
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
2028
|
+
}
|
|
2029
|
+
// Not a vendor package → serve via HTTP from Vite dev server
|
|
2030
|
+
const httpSpec = `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
2031
|
+
if (httpOriginSafe) {
|
|
2032
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
2033
|
+
}
|
|
2034
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
1863
2035
|
}
|
|
1864
2036
|
// Handle .vue imports
|
|
1865
2037
|
if (PAT.VUE_FILE_PATTERN.test(spec)) {
|
|
@@ -1883,7 +2055,7 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1883
2055
|
return `${prefix}${out}${suffix}`;
|
|
1884
2056
|
}
|
|
1885
2057
|
// Case B: plain .vue module → rewrite to SFC endpoint or local artifact
|
|
1886
|
-
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, ''));
|
|
2058
|
+
const vueKey = resolveVueKey(spec.replace(PAT.QUERY_PATTERN, '')) || '';
|
|
1887
2059
|
if (vueKey) {
|
|
1888
2060
|
if (true) {
|
|
1889
2061
|
const absVue = vueKey.startsWith('/') ? vueKey : '/' + vueKey;
|
|
@@ -1913,9 +2085,25 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1913
2085
|
// Rewrite relative application imports to HTTP for served modules
|
|
1914
2086
|
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
1915
2087
|
const absMaybe = normalizeImportPath(spec, importerDir);
|
|
2088
|
+
const nodeModulesHttpSpec = absMaybe ? toNodeModulesHttpModuleId(absMaybe) : null;
|
|
2089
|
+
if (nodeModulesHttpSpec) {
|
|
2090
|
+
if (isDynamicImportPrefix(prefix)) {
|
|
2091
|
+
if (verbose)
|
|
2092
|
+
console.log(`[rewrite][http] dynamic relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2093
|
+
return `__nsDynamicHmrImport(${JSON.stringify(nodeModulesHttpSpec)})`;
|
|
2094
|
+
}
|
|
2095
|
+
if (verbose)
|
|
2096
|
+
console.log(`[rewrite][http] relative node_modules import → ${nodeModulesHttpSpec} (from ${spec})`);
|
|
2097
|
+
return `${prefix}${nodeModulesHttpSpec}${suffix}`;
|
|
2098
|
+
}
|
|
1916
2099
|
const baseId = absMaybe ? toAppModuleBaseId(absMaybe, projectRoot) : null; // e.g. /src/foo.mjs
|
|
1917
2100
|
if (baseId) {
|
|
1918
2101
|
const httpSpec = `/ns/m${baseId}`;
|
|
2102
|
+
if (isDynamicImportPrefix(prefix)) {
|
|
2103
|
+
if (verbose)
|
|
2104
|
+
console.log(`[rewrite][http] dynamic relative app import → ${httpSpec} (from ${spec})`);
|
|
2105
|
+
return `__nsDynamicHmrImport(${JSON.stringify(httpSpec)})`;
|
|
2106
|
+
}
|
|
1919
2107
|
if (verbose)
|
|
1920
2108
|
console.log(`[rewrite][http] relative app import → ${httpSpec} (from ${spec})`);
|
|
1921
2109
|
return `${prefix}${httpSpec}${suffix}`;
|
|
@@ -1928,6 +2116,11 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1928
2116
|
const baseId = toAppModuleBaseId(spec, projectRoot);
|
|
1929
2117
|
if (baseId) {
|
|
1930
2118
|
const httpSpec = `/ns/m${baseId}`;
|
|
2119
|
+
if (isDynamicImportPrefix(prefix)) {
|
|
2120
|
+
if (verbose)
|
|
2121
|
+
console.log(`[rewrite][http] dynamic app import → ${httpSpec} (from ${spec})`);
|
|
2122
|
+
return `__nsDynamicHmrImport(${JSON.stringify(httpSpec)})`;
|
|
2123
|
+
}
|
|
1931
2124
|
if (verbose)
|
|
1932
2125
|
console.log(`[rewrite][http] absolute app import → ${httpSpec} (from ${spec})`);
|
|
1933
2126
|
return `${prefix}${httpSpec}${suffix}`;
|
|
@@ -1959,6 +2152,10 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1959
2152
|
result = result.replace(PAT.IMPORT_PATTERN_2, replaceVueImport);
|
|
1960
2153
|
result = result.replace(PAT.EXPORT_PATTERN, replaceVueImport);
|
|
1961
2154
|
result = result.replace(PAT.IMPORT_PATTERN_3, replaceVueImport);
|
|
2155
|
+
// Side-effect imports (import "spec") — must run AFTER named-import patterns
|
|
2156
|
+
// since IMPORT_PATTERN_1 already handles `import ... from "spec"`.
|
|
2157
|
+
result = result.replace(PAT.IMPORT_PATTERN_SIDE_EFFECT, replaceVueImport);
|
|
2158
|
+
result = ensureDynamicHmrImportHelper(result);
|
|
1962
2159
|
// Extra guard: map any lingering dynamic import('@') to a safe stub module path
|
|
1963
2160
|
// to prevent device runtime normalization errors.
|
|
1964
2161
|
// Example matched: import('@') or import("@") with optional whitespace before closing paren
|
|
@@ -1999,6 +2196,11 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
1999
2196
|
// In HTTP mode, skip legacy local-path rewrite to avoid mixing module origins
|
|
2000
2197
|
result = result.replace(PAT.VUE_FILE_IMPORT, (_m, p1, spec, p3) => {
|
|
2001
2198
|
if (httpOrigin) {
|
|
2199
|
+
if (isNsSfcSpecifier(spec)) {
|
|
2200
|
+
if (verbose)
|
|
2201
|
+
console.log(`[rewrite] .vue already routed (VUE_FILE_IMPORT http): ${spec}`);
|
|
2202
|
+
return `${p1}${spec}${p3}`;
|
|
2203
|
+
}
|
|
2002
2204
|
// Route via /ns/sfc with full query preserved
|
|
2003
2205
|
try {
|
|
2004
2206
|
let base = spec;
|
|
@@ -2048,6 +2250,13 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
2048
2250
|
console.log(`[rewrite][http] internal ns import (dynamic) → ${spec} via import.meta.url`);
|
|
2049
2251
|
return expr;
|
|
2050
2252
|
}
|
|
2253
|
+
const nodeModulesHttpSpec = toNodeModulesHttpModuleId(spec);
|
|
2254
|
+
if (nodeModulesHttpSpec) {
|
|
2255
|
+
const expr = `import(new URL('${nodeModulesHttpSpec}', import.meta.url).href)`;
|
|
2256
|
+
if (verbose)
|
|
2257
|
+
console.log(`[rewrite][http] absolute dynamic node_modules import → ${nodeModulesHttpSpec} via import.meta.url (from ${spec})`);
|
|
2258
|
+
return expr;
|
|
2259
|
+
}
|
|
2051
2260
|
const baseId = toAppModuleBaseId(spec, projectRoot);
|
|
2052
2261
|
if (!baseId)
|
|
2053
2262
|
return match;
|
|
@@ -2064,6 +2273,8 @@ function rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot,
|
|
|
2064
2273
|
function createHmrWebSocketPlugin(opts) {
|
|
2065
2274
|
const verbose = !!opts.verbose;
|
|
2066
2275
|
let wss = null;
|
|
2276
|
+
let sharedTransformRequest;
|
|
2277
|
+
const pendingAngularReloadSuppressions = new Map();
|
|
2067
2278
|
const sfcFileMap = new Map();
|
|
2068
2279
|
const depFileMap = new Map();
|
|
2069
2280
|
// Generic module manifest (spec -> emitted relative .mjs path)
|
|
@@ -2078,10 +2289,27 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2078
2289
|
// Transactional HMR batches: map graphVersion -> ordered list of changed ids for that version
|
|
2079
2290
|
const txnBatches = new Map();
|
|
2080
2291
|
const graph = new Map();
|
|
2292
|
+
function rememberAngularReloadSuppression(root, file, ttlMs = 3000) {
|
|
2293
|
+
const absPath = normalizeHotReloadMatchPath(file);
|
|
2294
|
+
const relPath = normalizeHotReloadMatchPath(file, root);
|
|
2295
|
+
pendingAngularReloadSuppressions.set(absPath, {
|
|
2296
|
+
absPath,
|
|
2297
|
+
relPath,
|
|
2298
|
+
expiresAt: Date.now() + ttlMs,
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
function pruneAngularReloadSuppressions(now = Date.now()) {
|
|
2302
|
+
for (const [key, entry] of pendingAngularReloadSuppressions) {
|
|
2303
|
+
if (!entry || entry.expiresAt <= now) {
|
|
2304
|
+
pendingAngularReloadSuppressions.delete(key);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2081
2308
|
// Compute a dependency-closed, topologically sorted list of modules for a given set of changed ids.
|
|
2082
2309
|
// Only include application modules we can serve (e.g., under /src and known .vue/.ts/.js entries in the graph).
|
|
2083
2310
|
function computeTxnOrderForChanged(changedIds) {
|
|
2084
|
-
const
|
|
2311
|
+
const includeAppModule = (id) => matchesRuntimeGraphModuleId(id, APP_VIRTUAL_WITH_SLASH, /\.(ts|js|mjs|tsx|jsx)$/i);
|
|
2312
|
+
const includeExt = (id) => ACTIVE_STRATEGY.matchesFile(id) || includeAppModule(id);
|
|
2085
2313
|
const isApp = (id) => id.startsWith(APP_VIRTUAL_WITH_SLASH);
|
|
2086
2314
|
const roots = changedIds.map(normalizeGraphId).filter((id) => graph.has(id) && (isApp(id) || ACTIVE_STRATEGY.matchesFile(id)) && includeExt(id));
|
|
2087
2315
|
const toVisit = new Set();
|
|
@@ -2220,7 +2448,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2220
2448
|
catch { }
|
|
2221
2449
|
});
|
|
2222
2450
|
}
|
|
2223
|
-
function upsertGraphModule(rawId, code, deps) {
|
|
2451
|
+
function upsertGraphModule(rawId, code, deps, options) {
|
|
2224
2452
|
const id = normalizeGraphId(rawId);
|
|
2225
2453
|
const normDeps = deps
|
|
2226
2454
|
.map((d) => normalizeGraphId(d))
|
|
@@ -2229,19 +2457,23 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2229
2457
|
.sort();
|
|
2230
2458
|
const hash = computeHash(code);
|
|
2231
2459
|
const existing = graph.get(id);
|
|
2232
|
-
|
|
2233
|
-
|
|
2460
|
+
const classification = classifyGraphUpsert(existing, hash, normDeps);
|
|
2461
|
+
if (classification === 'unchanged')
|
|
2462
|
+
return existing;
|
|
2234
2463
|
graphVersion++;
|
|
2235
2464
|
const gm = { id, deps: normDeps, hash };
|
|
2236
2465
|
graph.set(id, gm);
|
|
2237
2466
|
if (verbose) {
|
|
2238
2467
|
try {
|
|
2239
|
-
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion });
|
|
2468
|
+
console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification });
|
|
2240
2469
|
console.log('[hmr-ws][graph] size', graph.size);
|
|
2241
2470
|
}
|
|
2242
2471
|
catch { }
|
|
2243
2472
|
}
|
|
2244
|
-
|
|
2473
|
+
if (shouldBroadcastGraphUpsertDelta(classification, options?.emitDeltaOnInsert === true, options?.broadcastDelta !== false)) {
|
|
2474
|
+
emitDelta([gm], []);
|
|
2475
|
+
}
|
|
2476
|
+
return gm;
|
|
2245
2477
|
}
|
|
2246
2478
|
function isTypescriptFlavor() {
|
|
2247
2479
|
try {
|
|
@@ -2278,15 +2510,15 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2278
2510
|
}
|
|
2279
2511
|
async function walk(dir) {
|
|
2280
2512
|
for (const name of fs.readdirSync(dir)) {
|
|
2281
|
-
|
|
2282
|
-
if (name === 'node_modules' || name.startsWith('.'))
|
|
2513
|
+
if (name === 'node_modules' || name.startsWith('.') || shouldSkipRuntimeGraphDirectoryName(name))
|
|
2283
2514
|
continue;
|
|
2515
|
+
const full = pathMod.join(dir, name);
|
|
2284
2516
|
try {
|
|
2285
2517
|
const stat = fs.statSync(full);
|
|
2286
2518
|
if (stat.isDirectory())
|
|
2287
2519
|
await walk(full);
|
|
2288
2520
|
else if (stat.isFile()) {
|
|
2289
|
-
if (/\.(vue|ts|js|mjs|tsx|jsx)
|
|
2521
|
+
if (shouldIncludeRuntimeGraphFile(full, /\.(vue|ts|js|mjs|tsx|jsx)$/i)) {
|
|
2290
2522
|
const rel = '/' + pathMod.relative(root, full).split(pathMod.sep).join('/');
|
|
2291
2523
|
// Transform via Vite to gather deps (ignore failures)
|
|
2292
2524
|
try {
|
|
@@ -2323,6 +2555,41 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2323
2555
|
const httpServer = server.httpServer;
|
|
2324
2556
|
if (!httpServer)
|
|
2325
2557
|
return;
|
|
2558
|
+
const wsAny = server.ws;
|
|
2559
|
+
if (!wsAny.__NS_ANGULAR_FULL_RELOAD_FILTER_INSTALLED__) {
|
|
2560
|
+
const originalSend = server.ws.send.bind(server.ws);
|
|
2561
|
+
wsAny.__NS_ANGULAR_FULL_RELOAD_FILTER_INSTALLED__ = true;
|
|
2562
|
+
server.ws.send = ((payload, ...rest) => {
|
|
2563
|
+
pruneAngularReloadSuppressions();
|
|
2564
|
+
if (shouldSuppressViteFullReloadPayload({
|
|
2565
|
+
payload,
|
|
2566
|
+
pendingEntries: pendingAngularReloadSuppressions.values(),
|
|
2567
|
+
root: pluginRoot,
|
|
2568
|
+
})) {
|
|
2569
|
+
if (verbose) {
|
|
2570
|
+
console.log('[hmr-ws][angular] suppressed vite full-reload payload', payload);
|
|
2571
|
+
}
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
return originalSend(payload, ...rest);
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2577
|
+
// Default to serialized transform execution for deterministic HTTP HMR startup.
|
|
2578
|
+
// Higher fan-out can be re-enabled explicitly via NS_VITE_HMR_TRANSFORM_CONCURRENCY.
|
|
2579
|
+
const configuredTransformConcurrency = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CONCURRENCY || '1', 10);
|
|
2580
|
+
const transformConcurrency = Number.isFinite(configuredTransformConcurrency) && configuredTransformConcurrency > 0 ? configuredTransformConcurrency : 1;
|
|
2581
|
+
const configuredTransformCacheMs = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CACHE_MS || '15000', 10);
|
|
2582
|
+
const transformCacheMs = Number.isFinite(configuredTransformCacheMs) && configuredTransformCacheMs >= 0 ? configuredTransformCacheMs : 15000;
|
|
2583
|
+
sharedTransformRequest = createSharedTransformRequestRunner((url) => server.transformRequest(url), (url, timeoutMs) => {
|
|
2584
|
+
try {
|
|
2585
|
+
console.warn('[ns:m] slow transformRequest for', url, '(>' + timeoutMs + 'ms)');
|
|
2586
|
+
}
|
|
2587
|
+
catch { }
|
|
2588
|
+
}, {
|
|
2589
|
+
maxConcurrent: transformConcurrency,
|
|
2590
|
+
resultCacheTtlMs: transformCacheMs,
|
|
2591
|
+
getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot || process.cwd()),
|
|
2592
|
+
});
|
|
2326
2593
|
// Attempt early vendor manifest bootstrap once per server.
|
|
2327
2594
|
if (!vendorBootstrapDone) {
|
|
2328
2595
|
vendorBootstrapDone = true;
|
|
@@ -2366,14 +2633,26 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2366
2633
|
});
|
|
2367
2634
|
// Additional connection diagnostics
|
|
2368
2635
|
wss.on('connection', (ws, req) => {
|
|
2636
|
+
const role = getHmrSocketRoleFromRequestUrl(req.url);
|
|
2637
|
+
ws.__nsHmrClientRole = role;
|
|
2369
2638
|
try {
|
|
2370
2639
|
if (verbose) {
|
|
2371
2640
|
const ra = req.socket?.remoteAddress;
|
|
2372
2641
|
const rp = req.socket?.remotePort;
|
|
2373
|
-
console.log('[hmr-ws] Client connected', ra + (rp ? ':' + rp : ''));
|
|
2642
|
+
console.log('[hmr-ws] Client connected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
2374
2643
|
}
|
|
2375
2644
|
}
|
|
2376
2645
|
catch { }
|
|
2646
|
+
ws.on('close', () => {
|
|
2647
|
+
try {
|
|
2648
|
+
if (verbose) {
|
|
2649
|
+
const ra = req.socket?.remoteAddress;
|
|
2650
|
+
const rp = req.socket?.remotePort;
|
|
2651
|
+
console.log('[hmr-ws] Client disconnected', { role, remote: ra + (rp ? ':' + rp : '') });
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
catch { }
|
|
2655
|
+
});
|
|
2377
2656
|
});
|
|
2378
2657
|
wss.on('error', (err) => {
|
|
2379
2658
|
try {
|
|
@@ -2381,6 +2660,40 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2381
2660
|
}
|
|
2382
2661
|
catch { }
|
|
2383
2662
|
});
|
|
2663
|
+
// Import map endpoint: GET /ns/import-map.json
|
|
2664
|
+
// Returns the import map + runtime config for __nsConfigureRuntime()
|
|
2665
|
+
server.middlewares.use(async (req, res, next) => {
|
|
2666
|
+
try {
|
|
2667
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
2668
|
+
if (urlObj.pathname !== '/ns/import-map.json')
|
|
2669
|
+
return next();
|
|
2670
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
2671
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
2672
|
+
if (req.method === 'OPTIONS') {
|
|
2673
|
+
res.statusCode = 204;
|
|
2674
|
+
res.end();
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2677
|
+
// Determine origin from request headers or server config
|
|
2678
|
+
const host = req.headers.host || 'localhost:5173';
|
|
2679
|
+
const protocol = 'http';
|
|
2680
|
+
const origin = `${protocol}://${host}`;
|
|
2681
|
+
const runtimeConfig = buildRuntimeConfig({
|
|
2682
|
+
origin,
|
|
2683
|
+
flavor: ACTIVE_STRATEGY?.flavor || 'typescript',
|
|
2684
|
+
});
|
|
2685
|
+
res.setHeader('Content-Type', 'application/json');
|
|
2686
|
+
res.end(JSON.stringify({
|
|
2687
|
+
importMap: JSON.parse(runtimeConfig.importMap),
|
|
2688
|
+
volatilePatterns: runtimeConfig.volatilePatterns,
|
|
2689
|
+
}, null, 2));
|
|
2690
|
+
}
|
|
2691
|
+
catch (err) {
|
|
2692
|
+
console.error('[import-map] error generating import map:', err?.message || err);
|
|
2693
|
+
res.statusCode = 500;
|
|
2694
|
+
res.end(JSON.stringify({ error: 'Failed to generate import map' }));
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2384
2697
|
// Dev-only HTTP ESM loader endpoint for device clients
|
|
2385
2698
|
// 1) Legacy JSON module endpoint (kept temporarily): GET /ns-module?path=/abs -> { path, code, additionalFiles }
|
|
2386
2699
|
server.middlewares.use(async (req, res, next) => {
|
|
@@ -2415,13 +2728,15 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2415
2728
|
// Transform via Vite with variant resolution (same as ws ns:fetch-module)
|
|
2416
2729
|
const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
|
|
2417
2730
|
const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
|
|
2731
|
+
const transformRoot = server.config?.root || process.cwd();
|
|
2418
2732
|
const candidates = [];
|
|
2419
2733
|
if (hasExt)
|
|
2420
2734
|
candidates.push(spec);
|
|
2421
2735
|
candidates.push(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');
|
|
2736
|
+
const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, transformRoot);
|
|
2422
2737
|
let transformed = null;
|
|
2423
2738
|
let resolvedCandidate = null;
|
|
2424
|
-
for (const cand of
|
|
2739
|
+
for (const cand of transformCandidates) {
|
|
2425
2740
|
try {
|
|
2426
2741
|
const r = await server.transformRequest(cand);
|
|
2427
2742
|
if (r?.code) {
|
|
@@ -2443,7 +2758,10 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2443
2758
|
code = REQUIRE_GUARD_SNIPPET + code;
|
|
2444
2759
|
// Apply same sanitation/rewrite pipeline used for WS path
|
|
2445
2760
|
code = cleanCode(code);
|
|
2446
|
-
|
|
2761
|
+
// preserveVendorImports=true: vendor imports stay as bare specifiers
|
|
2762
|
+
// for the device-side import map (ns-vendor://) instead of being
|
|
2763
|
+
// transformed to __nsVendorRequire calls with fragile __nsPick lookups.
|
|
2764
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(resolvedCandidate || spec), resolvedCandidate || spec);
|
|
2447
2765
|
code = rewriteImports(code, spec, sfcFileMap, depFileMap, server.config?.root || process.cwd(), !!verbose, undefined, getServerOrigin(server));
|
|
2448
2766
|
code = ensureVariableDynamicImportHelper(code);
|
|
2449
2767
|
// Enforce upstream guarantee: no optimized deps or virtual ids remain
|
|
@@ -2455,18 +2773,6 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2455
2773
|
res.setHeader('Content-Type', 'application/json');
|
|
2456
2774
|
return void res.end(JSON.stringify({ error: e?.message || String(e) }));
|
|
2457
2775
|
}
|
|
2458
|
-
// Optional diagnostics: when ?diag=1, inject simple entry/exit logs to help isolate
|
|
2459
|
-
// execution-time failures on device without changing semantics.
|
|
2460
|
-
try {
|
|
2461
|
-
const wantDiag = urlObj.searchParams.get('diag') === '1';
|
|
2462
|
-
if (wantDiag) {
|
|
2463
|
-
const importerPath = spec.replace(/[?#].*$/, '');
|
|
2464
|
-
const enter = `try { console.log('[sfc][enter]', ${JSON.stringify(importerPath)}, 'hasReq=', (typeof globalThis.__nsRequire==='function'||typeof globalThis.require==='function')); } catch {}`;
|
|
2465
|
-
const exit = `\n;try { console.log('[sfc][loaded]', ${JSON.stringify(importerPath)}); } catch {}`;
|
|
2466
|
-
code = `${enter}\n${code}${exit}`;
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
catch { }
|
|
2470
2776
|
try {
|
|
2471
2777
|
const origin = getServerOrigin(server);
|
|
2472
2778
|
code = ensureVersionedRtImports(code, origin, graphVersion);
|
|
@@ -2499,7 +2805,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2499
2805
|
if (seen.has(depBase))
|
|
2500
2806
|
continue;
|
|
2501
2807
|
seen.add(depBase);
|
|
2502
|
-
const depCandidates = [depBase + '.ts', depBase + '.js', depBase + '.tsx', depBase + '.jsx', depBase + '.mjs', depBase + '.mts', depBase + '.cts', depBase + '.vue', depBase + '/index.ts', depBase + '/index.js', depBase + '/index.tsx', depBase + '/index.jsx', depBase + '/index.mjs'];
|
|
2808
|
+
const depCandidates = filterExistingNodeModulesTransformCandidates(depBase, [depBase + '.ts', depBase + '.js', depBase + '.tsx', depBase + '.jsx', depBase + '.mjs', depBase + '.mts', depBase + '.cts', depBase + '.vue', depBase + '/index.ts', depBase + '/index.js', depBase + '/index.tsx', depBase + '/index.jsx', depBase + '/index.mjs'], transformRoot);
|
|
2503
2809
|
let depTrans = null;
|
|
2504
2810
|
let depResolved = null;
|
|
2505
2811
|
for (const c of depCandidates) {
|
|
@@ -2516,7 +2822,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2516
2822
|
if (depTrans?.code && depResolved) {
|
|
2517
2823
|
let depCode = depTrans.code;
|
|
2518
2824
|
depCode = cleanCode(depCode);
|
|
2519
|
-
depCode = processCodeForDevice(depCode, false);
|
|
2825
|
+
depCode = processCodeForDevice(depCode, false, true, /(?:^|\/)node_modules\//.test(depResolved), depResolved);
|
|
2520
2826
|
depCode = rewriteImports(depCode, depResolved, sfcFileMap, depFileMap, server.config?.root || process.cwd(), !!verbose, undefined, getServerOrigin(server));
|
|
2521
2827
|
depCode = ensureVariableDynamicImportHelper(depCode);
|
|
2522
2828
|
try {
|
|
@@ -2567,7 +2873,8 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2567
2873
|
// Support both query (?path=/abs) and path-style (/ns/m/abs)
|
|
2568
2874
|
let spec = urlObj.searchParams.get('path') || '';
|
|
2569
2875
|
// Optional graph version pin for deterministic boot
|
|
2570
|
-
|
|
2876
|
+
let forcedVer = urlObj.searchParams.get('v');
|
|
2877
|
+
let bootTaggedRequest = false;
|
|
2571
2878
|
if (!spec) {
|
|
2572
2879
|
const base = '/ns/m';
|
|
2573
2880
|
let rest = urlObj.pathname.slice(base.length);
|
|
@@ -2585,22 +2892,25 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2585
2892
|
res.end('export {}\n');
|
|
2586
2893
|
return;
|
|
2587
2894
|
}
|
|
2895
|
+
const serverRoot = (server.config?.root || process.cwd());
|
|
2588
2896
|
spec = spec.replace(/[?#].*$/, '');
|
|
2589
|
-
// Accept path-based HMR
|
|
2897
|
+
// Accept path-based boot/HMR prefixes:
|
|
2898
|
+
// /ns/m/__ns_boot__/b1/<real-spec>
|
|
2899
|
+
// /ns/m/__ns_hmr__/<tag>/<real-spec>
|
|
2900
|
+
// /ns/m/__ns_boot__/b1/__ns_hmr__/<tag>/<real-spec>
|
|
2590
2901
|
// The iOS HTTP ESM loader canonicalizes cache keys by stripping query params,
|
|
2591
2902
|
// so we must carry the cache-buster in the path.
|
|
2592
2903
|
try {
|
|
2593
|
-
const
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2904
|
+
const decorated = stripDecoratedServePrefixes(spec);
|
|
2905
|
+
spec = decorated.cleanedSpec;
|
|
2906
|
+
bootTaggedRequest = decorated.bootTaggedRequest;
|
|
2907
|
+
forcedVer || (forcedVer = decorated.forcedVer);
|
|
2597
2908
|
}
|
|
2598
2909
|
catch { }
|
|
2599
2910
|
// Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
|
|
2600
2911
|
try {
|
|
2601
|
-
const projectRoot = (server.config?.root || process.cwd());
|
|
2602
2912
|
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
2603
|
-
const rootPosix = toPosix(
|
|
2913
|
+
const rootPosix = toPosix(serverRoot);
|
|
2604
2914
|
const specPosix = toPosix(spec);
|
|
2605
2915
|
// If spec is an absolute path under the project root, convert to '/'+relative
|
|
2606
2916
|
const isAbsFs = /^\//.test(specPosix) || /^[A-Za-z]:\//.test(spec); // posix or win drive
|
|
@@ -2615,27 +2925,78 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2615
2925
|
}
|
|
2616
2926
|
}
|
|
2617
2927
|
catch { }
|
|
2928
|
+
// Serve Vite virtual modules (/@id/ prefix). These are internal
|
|
2929
|
+
// virtual modules (e.g., \0nsvite:nsconfig-json for ~/package.json)
|
|
2930
|
+
// that don't exist on disk. Decode the ID and load via plugin container.
|
|
2931
|
+
if (spec.startsWith('/@id/')) {
|
|
2932
|
+
try {
|
|
2933
|
+
// First try Vite's transform pipeline directly
|
|
2934
|
+
const vr = await sharedTransformRequest(spec);
|
|
2935
|
+
if (vr?.code) {
|
|
2936
|
+
res.statusCode = 200;
|
|
2937
|
+
res.end(vr.code);
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
catch { }
|
|
2942
|
+
try {
|
|
2943
|
+
// Fallback: decode the virtual module ID (__x00__ → \0) and
|
|
2944
|
+
// load through the plugin container directly
|
|
2945
|
+
const rawId = spec.slice('/@id/'.length).replace(/__x00__/g, '\0');
|
|
2946
|
+
const loadResult = await server.pluginContainer.load(rawId);
|
|
2947
|
+
if (loadResult) {
|
|
2948
|
+
const code = typeof loadResult === 'string' ? loadResult : loadResult.code;
|
|
2949
|
+
if (code) {
|
|
2950
|
+
res.statusCode = 200;
|
|
2951
|
+
res.end(code);
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
catch { }
|
|
2957
|
+
}
|
|
2618
2958
|
if (spec.startsWith('@/'))
|
|
2619
2959
|
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
2620
2960
|
if (spec.startsWith('./'))
|
|
2621
2961
|
spec = spec.slice(1);
|
|
2962
|
+
const blockedNodeModulesReason = getBlockedDeviceNodeModulesReason(spec);
|
|
2963
|
+
if (blockedNodeModulesReason) {
|
|
2964
|
+
res.statusCode = 404;
|
|
2965
|
+
res.end(`// [ns:m] blocked device import\nthrow new Error(${JSON.stringify(`[ns/m] ${blockedNodeModulesReason}`)});\nexport {};\n`);
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2622
2968
|
if (!spec.startsWith('/'))
|
|
2623
2969
|
spec = '/' + spec;
|
|
2624
2970
|
const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
|
|
2625
2971
|
const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
|
|
2626
2972
|
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'];
|
|
2973
|
+
const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, serverRoot);
|
|
2627
2974
|
let transformed = null;
|
|
2628
2975
|
let resolvedCandidate = null;
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2976
|
+
const rawExplicitModule = tryReadRawExplicitJavaScriptModule(spec, serverRoot);
|
|
2977
|
+
if (rawExplicitModule) {
|
|
2978
|
+
transformed = { code: rawExplicitModule.code };
|
|
2979
|
+
resolvedCandidate = rawExplicitModule.resolvedId;
|
|
2980
|
+
}
|
|
2981
|
+
// Queue and dedupe transformRequest calls so heavy app graphs do not
|
|
2982
|
+
// overwhelm Vite with concurrent work. Slow-transform warnings start only
|
|
2983
|
+
// when the transform actually begins executing, and requests stay pending
|
|
2984
|
+
// until Vite returns a real result.
|
|
2985
|
+
const transformWithTimeout = (url, timeoutMs = 120000) => {
|
|
2986
|
+
return sharedTransformRequest(url, timeoutMs);
|
|
2987
|
+
};
|
|
2988
|
+
if (!transformed?.code) {
|
|
2989
|
+
for (const cand of transformCandidates) {
|
|
2990
|
+
try {
|
|
2991
|
+
const r = await transformWithTimeout(cand);
|
|
2992
|
+
if (r?.code) {
|
|
2993
|
+
transformed = r;
|
|
2994
|
+
resolvedCandidate = cand;
|
|
2995
|
+
break;
|
|
2996
|
+
}
|
|
2636
2997
|
}
|
|
2998
|
+
catch { }
|
|
2637
2999
|
}
|
|
2638
|
-
catch { }
|
|
2639
3000
|
}
|
|
2640
3001
|
// Fallback 1: ask Vite to resolve the id, then transform the resolved id (handles aliases and virtual ids)
|
|
2641
3002
|
if (!transformed?.code) {
|
|
@@ -2643,7 +3004,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2643
3004
|
const rid = await server.pluginContainer?.resolveId?.(spec, undefined);
|
|
2644
3005
|
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
2645
3006
|
if (ridStr) {
|
|
2646
|
-
const r = await
|
|
3007
|
+
const r = await transformWithTimeout(ridStr);
|
|
2647
3008
|
if (r?.code) {
|
|
2648
3009
|
transformed = r;
|
|
2649
3010
|
resolvedCandidate = ridStr;
|
|
@@ -2652,27 +3013,49 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2652
3013
|
}
|
|
2653
3014
|
catch { }
|
|
2654
3015
|
}
|
|
3016
|
+
// Fallback 1b: if spec is a /node_modules/ path, extract bare specifier
|
|
3017
|
+
// and try resolveId with that. This handles package.json "exports" field
|
|
3018
|
+
// resolution (e.g., solid-js/jsx-runtime → solid-js/dist/solid.js).
|
|
3019
|
+
if (!transformed?.code && spec.includes('/node_modules/')) {
|
|
3020
|
+
try {
|
|
3021
|
+
const nmIdx = spec.lastIndexOf('/node_modules/');
|
|
3022
|
+
const bare = spec.slice(nmIdx + '/node_modules/'.length);
|
|
3023
|
+
if (bare && !bare.startsWith('.')) {
|
|
3024
|
+
const rid = await server.pluginContainer?.resolveId?.(bare, undefined);
|
|
3025
|
+
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3026
|
+
if (ridStr) {
|
|
3027
|
+
const r = await sharedTransformRequest(ridStr);
|
|
3028
|
+
if (r?.code) {
|
|
3029
|
+
transformed = r;
|
|
3030
|
+
resolvedCandidate = ridStr;
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
catch { }
|
|
3036
|
+
}
|
|
2655
3037
|
// Fallback 2: try /@fs absolute path under project root (Vite file system alias)
|
|
2656
3038
|
if (!transformed?.code) {
|
|
2657
3039
|
try {
|
|
2658
|
-
const projectRoot = (server.config?.root || process.cwd());
|
|
2659
3040
|
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
2660
|
-
const rootPosix = toPosix(
|
|
3041
|
+
const rootPosix = toPosix(serverRoot).replace(/\/$/, '');
|
|
2661
3042
|
const absPosix = `${rootPosix}${spec.startsWith('/') ? '' : '/'}${spec}`;
|
|
2662
3043
|
const fsId = `/@fs${absPosix}`;
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
3044
|
+
if (resolveCandidateFilePath(fsId, serverRoot)) {
|
|
3045
|
+
const r = await transformWithTimeout(fsId);
|
|
3046
|
+
if (r?.code) {
|
|
3047
|
+
transformed = r;
|
|
3048
|
+
resolvedCandidate = fsId;
|
|
3049
|
+
}
|
|
2667
3050
|
}
|
|
2668
3051
|
}
|
|
2669
3052
|
catch { }
|
|
2670
3053
|
}
|
|
2671
3054
|
// Fallback 3: try adding ?import to hint Vite's transform pipeline
|
|
2672
3055
|
if (!transformed?.code) {
|
|
2673
|
-
for (const cand of
|
|
3056
|
+
for (const cand of transformCandidates) {
|
|
2674
3057
|
try {
|
|
2675
|
-
const r = await
|
|
3058
|
+
const r = await transformWithTimeout(`${cand}${cand.includes('?') ? '&' : '?'}import`);
|
|
2676
3059
|
if (r?.code) {
|
|
2677
3060
|
transformed = r;
|
|
2678
3061
|
resolvedCandidate = `${cand}?import`;
|
|
@@ -2682,39 +3065,67 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2682
3065
|
catch { }
|
|
2683
3066
|
}
|
|
2684
3067
|
}
|
|
2685
|
-
//
|
|
2686
|
-
//
|
|
2687
|
-
//
|
|
3068
|
+
// Solid HMR: patch @@solid-refresh's $$refreshESM to do inline patching
|
|
3069
|
+
// during module re-evaluation instead of deferring to hot.accept() callback.
|
|
3070
|
+
// In NativeScript's HTTP ESM environment, accept callbacks are registered
|
|
3071
|
+
// but not invoked by the HMR client. By adding a direct patchRegistry()
|
|
3072
|
+
// call when hot.data already has a stored registry, component updates
|
|
3073
|
+
// apply immediately when the module re-evaluates.
|
|
2688
3074
|
try {
|
|
2689
|
-
if (transformed?.code) {
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
3075
|
+
if (transformed?.code && ACTIVE_STRATEGY?.flavor === 'solid' && (resolvedCandidate || spec || '').includes('@solid-refresh')) {
|
|
3076
|
+
const PATCH_SENTINEL = '/* __ns_solid_refresh_patched__ */';
|
|
3077
|
+
const alreadyPatched = transformed.code.includes(PATCH_SENTINEL);
|
|
3078
|
+
console.log('[hmr-ws][solid] @solid-refresh patch check:', { spec: resolvedCandidate || spec, alreadyPatched, codeLen: transformed.code.length });
|
|
3079
|
+
if (!alreadyPatched) {
|
|
3080
|
+
let patchedCode = transformed.code;
|
|
3081
|
+
// Patch 1: Bypass shouldWarnAndDecline() — the vendor-bundled solid-js
|
|
3082
|
+
// may not have the 'development' condition active, making DEV empty/undefined.
|
|
3083
|
+
// In NativeScript HMR mode we are always in dev, so force it to return false.
|
|
3084
|
+
const declineCheck = 'function shouldWarnAndDecline() {';
|
|
3085
|
+
if (patchedCode.includes(declineCheck)) {
|
|
3086
|
+
patchedCode = patchedCode.replace(declineCheck, `${PATCH_SENTINEL}\nfunction shouldWarnAndDecline() { return false; /* NS HMR: always allow refresh */ }\nfunction __original_shouldWarnAndDecline() {`);
|
|
3087
|
+
console.log('[hmr-ws][solid] bypassed shouldWarnAndDecline() for NativeScript HMR');
|
|
2702
3088
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
3089
|
+
// Patch 2: Force createMemo path in createProxy.
|
|
3090
|
+
// Without the 'development' condition, $DEVCOMP is not set on components,
|
|
3091
|
+
// so createProxy falls through to `return s(props)` — a direct call with
|
|
3092
|
+
// no reactive subscription. When patchComponent fires update() (the signal
|
|
3093
|
+
// setter), nobody is listening. By forcing the createMemo path, HMRComp
|
|
3094
|
+
// subscribes to the signal and re-renders when the component changes.
|
|
3095
|
+
const proxyCondition = 'if (!s || $DEVCOMP in s) {';
|
|
3096
|
+
if (patchedCode.includes(proxyCondition)) {
|
|
3097
|
+
patchedCode = patchedCode.replace(proxyCondition, 'if (true) { /* NS HMR: always use createMemo for reactive HMR updates */');
|
|
3098
|
+
console.log('[hmr-ws][solid] forced createMemo path in createProxy for NativeScript HMR');
|
|
2705
3099
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
3100
|
+
// Patch 3: Inline patchRegistry call so updates apply immediately
|
|
3101
|
+
// on module re-evaluation (accept callbacks are not invoked by the HMR client).
|
|
3102
|
+
const marker = 'hot.data[SOLID_REFRESH] = hot.data[SOLID_REFRESH] || registry;';
|
|
3103
|
+
if (patchedCode.includes(marker)) {
|
|
3104
|
+
const patchCode = [
|
|
3105
|
+
`console.log('[solid-refresh][$$refreshESM] hot.data keys=', hot.data ? Object.keys(hot.data) : 'no-data', 'has=', !!(hot.data && hot.data[SOLID_REFRESH]));`,
|
|
3106
|
+
`if (hot.data[SOLID_REFRESH]) {`,
|
|
3107
|
+
` console.log('[solid-refresh][$$refreshESM] patching: oldComponents=', hot.data[SOLID_REFRESH].components ? hot.data[SOLID_REFRESH].components.size : 0, 'newComponents=', registry.components ? registry.components.size : 0);`,
|
|
3108
|
+
` var _shouldInvalidate = patchRegistry(hot.data[SOLID_REFRESH], registry);`,
|
|
3109
|
+
` console.log('[solid-refresh][$$refreshESM] patchRegistry result: shouldInvalidate=', _shouldInvalidate);`,
|
|
3110
|
+
`} else {`,
|
|
3111
|
+
` console.log('[solid-refresh][$$refreshESM] first load — creating registry, components=', registry.components ? registry.components.size : 0);`,
|
|
3112
|
+
`}`,
|
|
3113
|
+
].join('\n ');
|
|
3114
|
+
patchedCode = patchedCode.replace(marker, `${patchCode}\n ${marker}`);
|
|
3115
|
+
console.log('[hmr-ws][solid] added inline patchRegistry for NativeScript HMR');
|
|
3116
|
+
}
|
|
3117
|
+
// Work on a copy to avoid mutating Vite's cached TransformResult
|
|
3118
|
+
transformed = { ...transformed, code: patchedCode };
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
catch { }
|
|
3123
|
+
// NOTE: Path-based cache busting for /ns/m/* imports is applied in the
|
|
3124
|
+
// finalize step below (after rewriteImports adds the /ns/m/ prefix).
|
|
3125
|
+
// The block here only handles TypeScript-specific graph population.
|
|
3126
|
+
try {
|
|
3127
|
+
if (transformed?.code) {
|
|
3128
|
+
const code = transformed.code;
|
|
2718
3129
|
// TypeScript-specific graph population: when TS flavor is active
|
|
2719
3130
|
// and this is an application module under the virtual app root,
|
|
2720
3131
|
// upsert it into the HMR graph so ns:hmr-full-graph is non-empty.
|
|
@@ -2723,7 +3134,7 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2723
3134
|
const id = (resolvedCandidate || spec).replace(/[?#].*$/, '');
|
|
2724
3135
|
// Only track app modules (under APP_VIRTUAL_WITH_SLASH) and ts/js/tsx/jsx/mjs.
|
|
2725
3136
|
const isApp = id.startsWith(APP_VIRTUAL_WITH_SLASH) || id.startsWith('/app/');
|
|
2726
|
-
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id)) {
|
|
3137
|
+
if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
|
|
2727
3138
|
const deps = Array.from(collectImportDependencies(code, id));
|
|
2728
3139
|
if (verbose) {
|
|
2729
3140
|
try {
|
|
@@ -2843,7 +3254,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
2843
3254
|
if (!transformed?.code) {
|
|
2844
3255
|
// Emit a module that throws with context for easier on-device debugging
|
|
2845
3256
|
try {
|
|
2846
|
-
const tried = Array.from(new Set(candidates)).slice(0, 12);
|
|
3257
|
+
const tried = Array.from(new Set(transformCandidates.length > 0 ? transformCandidates : candidates)).slice(0, 12);
|
|
2847
3258
|
const out = `// [ns:m] transform miss path=${spec} tried=${tried.length}\n` + `throw new Error(${JSON.stringify(`[ns/m] transform failed for ${spec} (tried ${tried.length} candidates).`)});\nexport {};\n`;
|
|
2848
3259
|
res.statusCode = 404;
|
|
2849
3260
|
res.end(out);
|
|
@@ -2860,8 +3271,33 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
2860
3271
|
// Prepend guard to capture any URL-based require attempts
|
|
2861
3272
|
code = REQUIRE_GUARD_SNIPPET + code;
|
|
2862
3273
|
code = cleanCode(code);
|
|
2863
|
-
|
|
2864
|
-
code =
|
|
3274
|
+
const isNodeMod = /(?:^|\/)node_modules\//.test(resolvedCandidate || spec || '');
|
|
3275
|
+
code = processCodeForDevice(code, false, true, isNodeMod, resolvedCandidate || spec);
|
|
3276
|
+
// Solid HMR: The NativeScript iOS/Android runtime provides import.meta.hot
|
|
3277
|
+
// natively (via InitializeImportMetaHot in HMRSupport.mm) with C++-backed
|
|
3278
|
+
// persistent hot.data that survives across module re-evaluations.
|
|
3279
|
+
// cleanCode() strips Vite's __vite__createHotContext assignment, which is
|
|
3280
|
+
// correct — the runtime's native hot context is better.
|
|
3281
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3282
|
+
const serverOrigin = getServerOrigin(server);
|
|
3283
|
+
if (ACTIVE_STRATEGY?.flavor === 'angular') {
|
|
3284
|
+
code = prepareAngularEntryForDevice(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
3285
|
+
}
|
|
3286
|
+
else {
|
|
3287
|
+
code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
3288
|
+
}
|
|
3289
|
+
// Expand `export * from "url"` into explicit named re-exports.
|
|
3290
|
+
// NativeScript's HTTP ESM loader may not propagate star-re-exports across
|
|
3291
|
+
// HTTP module boundaries (the namespace object gets direct exports but
|
|
3292
|
+
// misses re-exported names). By expanding to `export { a, b } from "url"`,
|
|
3293
|
+
// the engine sees explicit named exports and resolves them correctly.
|
|
3294
|
+
try {
|
|
3295
|
+
code = await expandStarExports(code, server, server.config?.root || process.cwd(), verbose);
|
|
3296
|
+
}
|
|
3297
|
+
catch (e) {
|
|
3298
|
+
if (verbose)
|
|
3299
|
+
console.warn('[ns/m] export* expansion failed:', e?.message);
|
|
3300
|
+
}
|
|
2865
3301
|
// Dedupe any /ns/rt named imports that duplicate destructured bindings off default /ns/rt
|
|
2866
3302
|
try {
|
|
2867
3303
|
code = dedupeRtNamedImportsAgainstDestructures(code);
|
|
@@ -2888,6 +3324,28 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
2888
3324
|
}
|
|
2889
3325
|
}
|
|
2890
3326
|
catch { }
|
|
3327
|
+
// Final pass: deduplicate/resolve any bare-specifier imports that slipped
|
|
3328
|
+
// through the pipeline (e.g., extracted from JSDoc comments by import-splitting
|
|
3329
|
+
// regexes, or injected by the Angular linker on already-resolved code).
|
|
3330
|
+
try {
|
|
3331
|
+
code = deduplicateLinkerImports(code);
|
|
3332
|
+
}
|
|
3333
|
+
catch { }
|
|
3334
|
+
// CJS/UMD wrapping: if a module uses module.exports but has no ESM export default,
|
|
3335
|
+
// wrap it with CJS shims so the device HTTP ESM loader can consume it.
|
|
3336
|
+
// This handles npm packages that use CommonJS but aren't pre-bundled by Vite.
|
|
3337
|
+
//
|
|
3338
|
+
// Key constraints this must handle:
|
|
3339
|
+
// - CJS modules often declare local vars with the same names as their exports
|
|
3340
|
+
// (e.g. `function createLTTB() {...}; exports.createLTTB = createLTTB;`)
|
|
3341
|
+
// so `export var { createLTTB }` would cause a duplicate declaration.
|
|
3342
|
+
// - UMD modules reference `this` at top level (undefined in ESM) but
|
|
3343
|
+
// typically fall back to `self` or `globalThis`.
|
|
3344
|
+
// - `module`, `exports` must be shims since they don't exist in ESM.
|
|
3345
|
+
try {
|
|
3346
|
+
code = wrapCommonJsModuleForDevice(code);
|
|
3347
|
+
}
|
|
3348
|
+
catch { }
|
|
2891
3349
|
try {
|
|
2892
3350
|
assertNoOptimizedArtifacts(code, `NS M ${resolvedCandidate || spec}`);
|
|
2893
3351
|
}
|
|
@@ -2908,29 +3366,43 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
2908
3366
|
}
|
|
2909
3367
|
catch { }
|
|
2910
3368
|
try {
|
|
2911
|
-
const verNum =
|
|
3369
|
+
const verNum = getNumericServeVersionTag(forcedVer, Number(graphVersion || 0));
|
|
2912
3370
|
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
2913
3371
|
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
2914
3372
|
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
2915
3373
|
}
|
|
2916
3374
|
catch { }
|
|
2917
|
-
// Finalize:
|
|
3375
|
+
// Finalize: stamp all internal /ns/m imports with PATH-based cache busting.
|
|
3376
|
+
// IMPORTANT: use path prefix (not ?v= query) because the iOS HTTP ESM loader
|
|
3377
|
+
// strips query params when computing module cache keys, so ?v= doesn't bust the V8 cache.
|
|
2918
3378
|
try {
|
|
2919
|
-
const ver =
|
|
3379
|
+
const ver = (() => {
|
|
3380
|
+
const raw = String(forcedVer || '').trim();
|
|
3381
|
+
if (raw) {
|
|
3382
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
3383
|
+
return raw;
|
|
3384
|
+
}
|
|
3385
|
+
if (/^\d+$/.test(raw)) {
|
|
3386
|
+
return `v${raw}`;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
return `v${String(graphVersion || 0)}`;
|
|
3390
|
+
})();
|
|
2920
3391
|
const origin = getServerOrigin(server);
|
|
3392
|
+
const rewritePath = (p) => rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest);
|
|
2921
3393
|
// 1) Static imports: import ... from "/ns/m/..."
|
|
2922
|
-
code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, `$
|
|
3394
|
+
code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
2923
3395
|
// 2) Side-effect imports: import "/ns/m/..."
|
|
2924
|
-
code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, `$
|
|
3396
|
+
code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
2925
3397
|
// 3) Dynamic imports: import("/ns/m/...")
|
|
2926
|
-
code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, `$
|
|
3398
|
+
code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
2927
3399
|
// 4) new URL("/ns/m/...", import.meta.url)
|
|
2928
|
-
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, `$
|
|
3400
|
+
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
2929
3401
|
// 5) __ns_import(new URL('/ns/m/...', import.meta.url).href)
|
|
2930
|
-
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\)\.href)/g, `$
|
|
2931
|
-
// 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → "${origin}/ns/m/..."
|
|
3402
|
+
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}`);
|
|
3403
|
+
// 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → "${origin}/ns/m/__ns_hmr__/..."
|
|
2932
3404
|
try {
|
|
2933
|
-
code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${p1}
|
|
3405
|
+
code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${rewritePath(p1)}`)}`);
|
|
2934
3406
|
}
|
|
2935
3407
|
catch { }
|
|
2936
3408
|
// 7) Also fix SFC new URL('/ns/sfc/...', import.meta.url).href → "${origin}/ns/sfc/<ver>/..."
|
|
@@ -2946,13 +3418,25 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
2946
3418
|
code = ensureDestructureCoreImports(code);
|
|
2947
3419
|
}
|
|
2948
3420
|
catch { }
|
|
3421
|
+
// Boot-time module graph progress: while the app is still replacing the
|
|
3422
|
+
// placeholder, emit lightweight progress updates as /ns/m modules begin
|
|
3423
|
+
// evaluating. This keeps the overlay moving during large initial graphs.
|
|
3424
|
+
try {
|
|
3425
|
+
if (bootTaggedRequest) {
|
|
3426
|
+
const bootModuleLabel = String(spec || '').replace(/\\/g, '/');
|
|
3427
|
+
const bootProgressSnippet = buildBootProgressSnippet(bootModuleLabel);
|
|
3428
|
+
code = bootProgressSnippet + code;
|
|
3429
|
+
code = hoistTopLevelStaticImports(code);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
catch { }
|
|
2949
3433
|
// Dev-only: link-check static imports to surface missing bindings early
|
|
2950
3434
|
try {
|
|
2951
3435
|
const devCheck = process.env.NODE_ENV !== 'production';
|
|
2952
3436
|
if (devCheck) {
|
|
2953
3437
|
const ast = babelParse(code, {
|
|
2954
3438
|
sourceType: 'module',
|
|
2955
|
-
plugins:
|
|
3439
|
+
plugins: MODULE_IMPORT_ANALYSIS_PLUGINS,
|
|
2956
3440
|
});
|
|
2957
3441
|
const imports = [];
|
|
2958
3442
|
babelTraverse(ast, {
|
|
@@ -3036,6 +3520,16 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3036
3520
|
continue;
|
|
3037
3521
|
const hasDefault = /\bexport\s+default\b/.test(targetCode) || /export\s*\{\s*default\s*(?:as\s*default)?\s*\}/.test(targetCode);
|
|
3038
3522
|
if (!hasDefault) {
|
|
3523
|
+
// CJS/UMD modules won't have `export default` — they get CJS-wrapped
|
|
3524
|
+
// by the serving pipeline. Only warn, don't fatally block the importer.
|
|
3525
|
+
const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
|
|
3526
|
+
if (hasCjsPattern) {
|
|
3527
|
+
try {
|
|
3528
|
+
console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
|
|
3529
|
+
}
|
|
3530
|
+
catch { }
|
|
3531
|
+
continue;
|
|
3532
|
+
}
|
|
3039
3533
|
const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
|
|
3040
3534
|
// Emit a module that throws to surface the exact offender
|
|
3041
3535
|
res.statusCode = 200;
|
|
@@ -3251,27 +3745,92 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3251
3745
|
server.middlewares.use(async (req, res, next) => {
|
|
3252
3746
|
try {
|
|
3253
3747
|
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
3254
|
-
|
|
3748
|
+
const coreRequest = parseCoreBridgeRequest(urlObj.pathname, urlObj.searchParams, Number(graphVersion || 0));
|
|
3749
|
+
if (!coreRequest)
|
|
3255
3750
|
return next();
|
|
3256
3751
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
3257
3752
|
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
3258
3753
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3259
3754
|
res.setHeader('Pragma', 'no-cache');
|
|
3260
3755
|
res.setHeader('Expires', '0');
|
|
3261
|
-
const
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
//
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3756
|
+
const { hasExplicitVersion, key, normalizedSub, sub, ver } = coreRequest;
|
|
3757
|
+
// Any @nativescript/core subpath import (including shallow ones like
|
|
3758
|
+
// `utils`) may expose exports that are not available from the root
|
|
3759
|
+
// vendor bundle namespace. Serve the actual transformed module content
|
|
3760
|
+
// instead of the lightweight proxy bridge.
|
|
3761
|
+
if (sub) {
|
|
3762
|
+
try {
|
|
3763
|
+
const resolvedSubpath = normalizedSub || sub;
|
|
3764
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3765
|
+
const resolveModuleId = async (moduleId) => {
|
|
3766
|
+
const resolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3767
|
+
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3768
|
+
};
|
|
3769
|
+
const resolvedId = await resolveRuntimeCoreModulePath(resolvedSubpath, resolveModuleId);
|
|
3770
|
+
const modulePath = resolvedId || `/node_modules/@nativescript/core/${resolvedSubpath}`;
|
|
3771
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3772
|
+
if (!hasExplicitVersion) {
|
|
3773
|
+
if (transformed?.code) {
|
|
3774
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3775
|
+
res.statusCode = 200;
|
|
3776
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver, extractExportedNames(expandedModuleCode), hasModuleDefaultExport(expandedModuleCode)));
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
res.statusCode = 200;
|
|
3780
|
+
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver));
|
|
3781
|
+
return;
|
|
3782
|
+
}
|
|
3783
|
+
if (transformed?.code) {
|
|
3784
|
+
// Minimal pipeline: Vite already produces correct ESM.
|
|
3785
|
+
// ONLY rewrite specifier strings to device-fetchable URLs.
|
|
3786
|
+
// Do NOT run processCodeForDevice, rewriteImports, or any
|
|
3787
|
+
// other heavy transform — those mangle newlines, eat exports,
|
|
3788
|
+
// and cause cascading "does not provide an export" failures.
|
|
3789
|
+
const moduleCode = rewriteSpecifiersForDevice(transformed.code, getServerOrigin(server), Number(ver));
|
|
3790
|
+
res.statusCode = 200;
|
|
3791
|
+
res.end(moduleCode);
|
|
3792
|
+
return;
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
catch (e) {
|
|
3796
|
+
try {
|
|
3797
|
+
console.warn('[ns-core-bridge] deep subpath serve failed:', sub, e?.message);
|
|
3798
|
+
}
|
|
3799
|
+
catch { }
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
// Main entry or shallow subpath: use proxy bridge
|
|
3803
|
+
let code = buildVersionedCoreMainBridgeModule(key, ver);
|
|
3804
|
+
if (!sub) {
|
|
3805
|
+
try {
|
|
3806
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
3807
|
+
const coreSpecifier = '@nativescript/core';
|
|
3808
|
+
const resolved = await server.pluginContainer?.resolveId?.(coreSpecifier, undefined);
|
|
3809
|
+
const resolvedId = typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3810
|
+
const modulePath = resolvedId || '/node_modules/@nativescript/core/index.js';
|
|
3811
|
+
const staticExportNames = collectStaticExportNamesFromFile(modulePath);
|
|
3812
|
+
const staticExportOrigins = await normalizeCoreExportOriginsForRuntime(collectStaticExportOriginsFromFile(modulePath), async (moduleId) => {
|
|
3813
|
+
const nextResolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3814
|
+
return typeof nextResolved === 'string' ? nextResolved : nextResolved?.id || null;
|
|
3815
|
+
}, modulePath);
|
|
3816
|
+
if (staticExportNames.length) {
|
|
3817
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, staticExportNames, staticExportOrigins);
|
|
3818
|
+
}
|
|
3819
|
+
else {
|
|
3820
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3821
|
+
if (transformed?.code) {
|
|
3822
|
+
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3823
|
+
code = buildVersionedCoreMainBridgeModule(key, ver, extractExportedNames(expandedModuleCode));
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
catch (e) {
|
|
3828
|
+
try {
|
|
3829
|
+
console.warn('[ns-core-bridge] main bridge export discovery failed:', e?.message);
|
|
3830
|
+
}
|
|
3831
|
+
catch { }
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3275
3834
|
res.statusCode = 200;
|
|
3276
3835
|
res.end(code);
|
|
3277
3836
|
}
|
|
@@ -3300,18 +3859,40 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3300
3859
|
res.setHeader('Expires', '0');
|
|
3301
3860
|
let content = '';
|
|
3302
3861
|
try {
|
|
3303
|
-
const
|
|
3304
|
-
const entryRtPath =
|
|
3305
|
-
|
|
3306
|
-
content = fs.readFileSync(entryRtPath, 'utf-8');
|
|
3862
|
+
const _req = createRequire(import.meta.url);
|
|
3863
|
+
const entryRtPath = _req.resolve('@nativescript/vite/hmr/entry-runtime.js');
|
|
3864
|
+
content = readFileSync(entryRtPath, 'utf-8');
|
|
3307
3865
|
}
|
|
3308
3866
|
catch (e) {
|
|
3309
|
-
|
|
3867
|
+
// .js not found (source tree without build) — transform .ts on the fly
|
|
3868
|
+
try {
|
|
3869
|
+
const tsPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'entry-runtime.ts');
|
|
3870
|
+
if (existsSync(tsPath)) {
|
|
3871
|
+
const tsSource = readFileSync(tsPath, 'utf-8');
|
|
3872
|
+
const result = babelCore.transformSync(tsSource, {
|
|
3873
|
+
filename: tsPath,
|
|
3874
|
+
plugins: [[pluginTransformTypescript, { isTSX: false, allowDeclareFields: true }]],
|
|
3875
|
+
sourceType: 'module',
|
|
3876
|
+
});
|
|
3877
|
+
if (result?.code) {
|
|
3878
|
+
content = result.code;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
catch (e2) {
|
|
3883
|
+
if (verbose)
|
|
3884
|
+
console.warn('[hmr-http] entry-runtime.ts transform failed', e2);
|
|
3885
|
+
}
|
|
3886
|
+
if (!content) {
|
|
3887
|
+
content = 'export default async function start(){ console.error("[/ns/entry-rt] not found"); }\n';
|
|
3888
|
+
}
|
|
3310
3889
|
}
|
|
3890
|
+
console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
|
|
3311
3891
|
res.statusCode = 200;
|
|
3312
3892
|
res.end(content);
|
|
3313
3893
|
}
|
|
3314
3894
|
catch (e) {
|
|
3895
|
+
console.warn('[hmr-http] /ns/entry-rt error', e);
|
|
3315
3896
|
next();
|
|
3316
3897
|
}
|
|
3317
3898
|
});
|
|
@@ -3741,7 +4322,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3741
4322
|
code = outCode;
|
|
3742
4323
|
}
|
|
3743
4324
|
catch { }
|
|
3744
|
-
code = processCodeForDevice(code, false);
|
|
4325
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(fullSpec), fullSpec);
|
|
3745
4326
|
// Transform static .vue imports into static imports from the assembler (no TLA) via AST
|
|
3746
4327
|
try {
|
|
3747
4328
|
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
@@ -4293,11 +4874,10 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4293
4874
|
parts.push(scriptTransformed);
|
|
4294
4875
|
parts.push(renderDecl);
|
|
4295
4876
|
parts.push(`try { if (!__ns_sfc__.render) Object.defineProperty(__ns_sfc__, 'render', { configurable: true, enumerable: true, get(){ const r = (typeof __ns_getRender==='function' ? __ns_getRender() : undefined); Object.defineProperty(__ns_sfc__, 'render', { value: r, writable: true, configurable: true, enumerable: true }); return r; }, set(v){ Object.defineProperty(__ns_sfc__, 'render', { value: v, writable: true, configurable: true, enumerable: true }); } }); } catch(_e){}`);
|
|
4296
|
-
parts.push(`// diagnostic: hadScriptDefaultPre=${hadScriptDefaultPre} triedInlineTemplate=${triedInlineTemplate} renderOk=${renderOk} tplBytes=${compiledTplCode.length} scriptBytes=${(compiledScript || '').length} templateErr=${templateErr ? templateErr?.message : ''}`);
|
|
4297
4877
|
parts.push(`export function render(){ const f = (typeof __ns_getRender==='function' ? __ns_getRender() : (__ns_sfc__ && __ns_sfc__.render)); return typeof f==='function' ? f.apply(this, arguments) : undefined; }`);
|
|
4298
4878
|
parts.push(`export default __ns_sfc__`);
|
|
4299
4879
|
let inlineCode = parts.filter(Boolean).join('\n');
|
|
4300
|
-
inlineCode = processCodeForDevice(inlineCode, false);
|
|
4880
|
+
inlineCode = processCodeForDevice(inlineCode, false, true);
|
|
4301
4881
|
try {
|
|
4302
4882
|
inlineCode = ensureVersionedCoreImports(inlineCode, getServerOrigin(server), Number(ver));
|
|
4303
4883
|
}
|
|
@@ -4372,12 +4952,11 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4372
4952
|
outParts.push(renderDecl);
|
|
4373
4953
|
}
|
|
4374
4954
|
outParts.push(`try { if (!__ns_sfc__.render) Object.defineProperty(__ns_sfc__, 'render', { configurable: true, enumerable: true, get(){ const r = (typeof __ns_getRender==='function' ? __ns_getRender() : (typeof __ns_render==='function' ? __ns_render : undefined)); Object.defineProperty(__ns_sfc__, 'render', { value: r, writable: true, configurable: true, enumerable: true }); return r; }, set(v){ Object.defineProperty(__ns_sfc__, 'render', { value: v, writable: true, configurable: true, enumerable: true }); } }); } catch(_e){}`);
|
|
4375
|
-
outParts.push(`// diagnostic: hadScriptDefaultPre=${hadScriptDefaultPre} triedInlineTemplate=${triedInlineTemplate} renderOk=${renderOk} tplBytes=${compiledTplCode.length} scriptBytes=${(compiledScript || '').length} templateErr=${templateErr ? templateErr?.message : ''}`);
|
|
4376
4955
|
// Export named render as a function that resolves lazily
|
|
4377
4956
|
outParts.push('export function render(){ const f = (typeof __ns_getRender==="function" ? __ns_getRender() : (typeof __ns_render==="function" ? __ns_render : (__ns_sfc__ && __ns_sfc__.render))); return typeof f === "function" ? f.apply(this, arguments) : undefined; }');
|
|
4378
4957
|
outParts.push('export default __ns_sfc__');
|
|
4379
4958
|
let inlineCode2 = outParts.filter(Boolean).join('\n');
|
|
4380
|
-
inlineCode2 = processCodeForDevice(inlineCode2, false);
|
|
4959
|
+
inlineCode2 = processCodeForDevice(inlineCode2, false, true);
|
|
4381
4960
|
try {
|
|
4382
4961
|
inlineCode2 = ensureVersionedCoreImports(inlineCode2, getServerOrigin(server), Number(ver));
|
|
4383
4962
|
}
|
|
@@ -4673,12 +5252,11 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4673
5252
|
}
|
|
4674
5253
|
let asm;
|
|
4675
5254
|
if (inlineOk) {
|
|
4676
|
-
const diagLine = `// diagnostic:inlineOk ver=${ver} inlineBlock=${!!(inlineBlock && inlineBlock.trim())} helperBindingsLen=${helperBindings.length} renderDeclLen=${renderDecl.length}`;
|
|
4677
5255
|
if (inlineBlock && inlineBlock.trim()) {
|
|
4678
|
-
asm = [`// [sfc-asm] ${base} (inlined template body)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, inlineBlock, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__
|
|
5256
|
+
asm = [`// [sfc-asm] ${base} (inlined template body)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, inlineBlock, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__;`].join('\n');
|
|
4679
5257
|
}
|
|
4680
5258
|
else {
|
|
4681
|
-
asm = [`// [sfc-asm] ${base} (inlined template)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, helperBindings, renderDecl, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__
|
|
5259
|
+
asm = [`// [sfc-asm] ${base} (inlined template)`, `export * from ${JSON.stringify(scriptUrl)};`, `import * as __script from ${JSON.stringify(scriptUrl)};`, helperBindings, renderDecl, `const __ns_sfc__ = (__script && __script.default) ? __script.default : {};`, `try { if (typeof __ns_render === 'function' && !__ns_sfc__.render) __ns_sfc__.render = __ns_render; } catch {}`, `export default __ns_sfc__;`].filter(Boolean).join('\n');
|
|
4682
5260
|
}
|
|
4683
5261
|
}
|
|
4684
5262
|
else {
|
|
@@ -4689,7 +5267,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4689
5267
|
}
|
|
4690
5268
|
// Run full device processing so helper aliasing and globals are consistent in this path too
|
|
4691
5269
|
let code = REQUIRE_GUARD_SNIPPET + asm;
|
|
4692
|
-
code = processCodeForDevice(code, false);
|
|
5270
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(base), base);
|
|
4693
5271
|
try {
|
|
4694
5272
|
code = ensureVersionedCoreImports(code, getServerOrigin(server), Number(ver));
|
|
4695
5273
|
}
|
|
@@ -4862,7 +5440,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4862
5440
|
let code = transformed.code;
|
|
4863
5441
|
// Reuse existing sanitation chain (lightweight)
|
|
4864
5442
|
code = cleanCode(code);
|
|
4865
|
-
code = processCodeForDevice(code, false);
|
|
5443
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(resolvedCandidate || spec), resolvedCandidate || spec);
|
|
4866
5444
|
try {
|
|
4867
5445
|
code = ensureVersionedCoreImports(code, getServerOrigin(server), graphVersion);
|
|
4868
5446
|
}
|
|
@@ -4917,7 +5495,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4917
5495
|
if (depTrans?.code && depResolved) {
|
|
4918
5496
|
let depCode = depTrans.code;
|
|
4919
5497
|
depCode = cleanCode(depCode);
|
|
4920
|
-
depCode = processCodeForDevice(depCode, false);
|
|
5498
|
+
depCode = processCodeForDevice(depCode, false, true, /(?:^|\/)node_modules\//.test(depResolved), depResolved);
|
|
4921
5499
|
try {
|
|
4922
5500
|
depCode = ensureVersionedCoreImports(depCode, getServerOrigin(server), graphVersion);
|
|
4923
5501
|
}
|
|
@@ -4950,8 +5528,8 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4950
5528
|
ts: Date.now(),
|
|
4951
5529
|
delta: true,
|
|
4952
5530
|
};
|
|
4953
|
-
wss
|
|
4954
|
-
if (c
|
|
5531
|
+
wss?.clients.forEach((c) => {
|
|
5532
|
+
if (isSocketClientOpen(c)) {
|
|
4955
5533
|
try {
|
|
4956
5534
|
c.send(JSON.stringify(single));
|
|
4957
5535
|
}
|
|
@@ -4990,32 +5568,33 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
4990
5568
|
if (verbose)
|
|
4991
5569
|
console.warn('[hmr-ws][graph] initial population failed', e);
|
|
4992
5570
|
}
|
|
4993
|
-
// Send SFC registry on
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
}
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5571
|
+
// Send SFC registry on every connection (not just the first).
|
|
5572
|
+
// When the NativeScript app restarts (e.g. CLI auto-reload), the new
|
|
5573
|
+
// JS context has an empty sfcArtifactMap. Without the registry the
|
|
5574
|
+
// rescue-mount cannot find the root .vue component.
|
|
5575
|
+
try {
|
|
5576
|
+
await ACTIVE_STRATEGY.buildRegistry({
|
|
5577
|
+
server,
|
|
5578
|
+
sfcFileMap,
|
|
5579
|
+
depFileMap,
|
|
5580
|
+
wss: wss,
|
|
5581
|
+
verbose,
|
|
5582
|
+
helpers: {
|
|
5583
|
+
cleanCode,
|
|
5584
|
+
collectImportDependencies,
|
|
5585
|
+
isCoreGlobalsReference,
|
|
5586
|
+
isNativeScriptCoreModule,
|
|
5587
|
+
isNativeScriptPluginModule,
|
|
5588
|
+
resolveVendorFromCandidate,
|
|
5589
|
+
createHash: (value) => createHash('md5').update(value).digest('hex'),
|
|
5590
|
+
rewriteImports,
|
|
5591
|
+
processSfcCode,
|
|
5592
|
+
},
|
|
5593
|
+
});
|
|
5594
|
+
registrySent = true;
|
|
5595
|
+
}
|
|
5596
|
+
catch (error) {
|
|
5597
|
+
console.warn('[hmr-ws] Failed to send registry:', error);
|
|
5019
5598
|
}
|
|
5020
5599
|
emitFullGraph(ws);
|
|
5021
5600
|
// After sending registry & graph also send current module manifest if any
|
|
@@ -5038,16 +5617,38 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5038
5617
|
if (!wss) {
|
|
5039
5618
|
return;
|
|
5040
5619
|
}
|
|
5620
|
+
if (isRuntimeGraphExcludedPath(file)) {
|
|
5621
|
+
return;
|
|
5622
|
+
}
|
|
5041
5623
|
// Graph update for this file change (wrapped to avoid aborting rest of handler)
|
|
5042
5624
|
try {
|
|
5043
|
-
const
|
|
5044
|
-
if (
|
|
5045
|
-
const
|
|
5046
|
-
|
|
5047
|
-
.
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5625
|
+
const skipAngularHtmlGraphUpdate = ACTIVE_STRATEGY.flavor === 'angular' && /\.(html|htm)$/i.test(file);
|
|
5626
|
+
if (!skipAngularHtmlGraphUpdate) {
|
|
5627
|
+
const graphTargets = collectGraphUpdateModulesForHotUpdate({
|
|
5628
|
+
file,
|
|
5629
|
+
flavor: ACTIVE_STRATEGY.flavor,
|
|
5630
|
+
modules: ctx.modules,
|
|
5631
|
+
getModuleById: (id) => server.moduleGraph.getModuleById(id),
|
|
5632
|
+
});
|
|
5633
|
+
for (const mod of graphTargets) {
|
|
5634
|
+
if (!mod?.id)
|
|
5635
|
+
continue;
|
|
5636
|
+
try {
|
|
5637
|
+
const deps = Array.from(mod.importedModules || [])
|
|
5638
|
+
.map((m) => (m.id || '').replace(/\?.*$/, ''))
|
|
5639
|
+
.filter(Boolean);
|
|
5640
|
+
const transformed = await server.transformRequest(mod.id);
|
|
5641
|
+
const code = transformed?.code || '';
|
|
5642
|
+
upsertGraphModule((mod.id || '').replace(/\?.*$/, ''), code, deps, {
|
|
5643
|
+
emitDeltaOnInsert: true,
|
|
5644
|
+
broadcastDelta: ACTIVE_STRATEGY.flavor !== 'angular',
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
catch (error) {
|
|
5648
|
+
if (verbose)
|
|
5649
|
+
console.warn('[hmr-ws][v2] failed graph update target', mod.id, error);
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5051
5652
|
}
|
|
5052
5653
|
}
|
|
5053
5654
|
catch (e) {
|
|
@@ -5084,7 +5685,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5084
5685
|
],
|
|
5085
5686
|
};
|
|
5086
5687
|
wss.clients.forEach((client) => {
|
|
5087
|
-
if (client
|
|
5688
|
+
if (isSocketClientOpen(client)) {
|
|
5088
5689
|
client.send(JSON.stringify(msg));
|
|
5089
5690
|
}
|
|
5090
5691
|
});
|
|
@@ -5099,20 +5700,103 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5099
5700
|
// For Angular, react to component TS or external template HTML changes under /src
|
|
5100
5701
|
const isHtml = file.endsWith('.html');
|
|
5101
5702
|
const isTs = file.endsWith('.ts');
|
|
5703
|
+
const angularHotUpdateRoots = collectAngularHotUpdateRoots({
|
|
5704
|
+
file,
|
|
5705
|
+
modules: ctx.modules,
|
|
5706
|
+
getModuleById: (id) => server.moduleGraph.getModuleById(id),
|
|
5707
|
+
getModulesByFile: (targetFile) => server.moduleGraph.getModulesByFile?.(targetFile),
|
|
5708
|
+
});
|
|
5102
5709
|
if (!(isHtml || isTs))
|
|
5103
5710
|
return;
|
|
5711
|
+
if (angularHotUpdateRoots.length) {
|
|
5712
|
+
for (const mod of angularHotUpdateRoots) {
|
|
5713
|
+
try {
|
|
5714
|
+
server.moduleGraph.invalidateModule(mod);
|
|
5715
|
+
}
|
|
5716
|
+
catch (invalidationError) {
|
|
5717
|
+
if (verbose) {
|
|
5718
|
+
console.warn('[hmr-ws][angular] hot-update root invalidation failed', mod?.id, invalidationError);
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
if (verbose) {
|
|
5723
|
+
console.log('[hmr-ws][angular] invalidated hot-update root modules:', angularHotUpdateRoots.length);
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
const angularTransitiveInvalidationRoots = (angularHotUpdateRoots.length ? angularHotUpdateRoots : ctx.modules);
|
|
5727
|
+
if (shouldInvalidateAngularTransitiveImporters({ flavor: ACTIVE_STRATEGY.flavor, file })) {
|
|
5728
|
+
try {
|
|
5729
|
+
const transitiveImporters = collectAngularTransitiveImportersForInvalidation({
|
|
5730
|
+
modules: angularTransitiveInvalidationRoots,
|
|
5731
|
+
isExcluded: (id) => id.includes('/node_modules/'),
|
|
5732
|
+
maxDepth: 16,
|
|
5733
|
+
});
|
|
5734
|
+
for (const mod of transitiveImporters) {
|
|
5735
|
+
try {
|
|
5736
|
+
server.moduleGraph.invalidateModule(mod);
|
|
5737
|
+
}
|
|
5738
|
+
catch (invalidationError) {
|
|
5739
|
+
if (verbose) {
|
|
5740
|
+
console.warn('[hmr-ws][angular] transitive importer invalidation failed', mod?.id, invalidationError);
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
5744
|
+
if (verbose && transitiveImporters.length) {
|
|
5745
|
+
console.log('[hmr-ws][angular] invalidated transitive importers:', transitiveImporters.length);
|
|
5746
|
+
}
|
|
5747
|
+
}
|
|
5748
|
+
catch (error) {
|
|
5749
|
+
if (verbose)
|
|
5750
|
+
console.warn('[hmr-ws][angular] transitive importer collection failed', error);
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
try {
|
|
5754
|
+
const transitiveImporters = shouldInvalidateAngularTransitiveImporters({ flavor: ACTIVE_STRATEGY.flavor, file })
|
|
5755
|
+
? collectAngularTransitiveImportersForInvalidation({
|
|
5756
|
+
modules: angularTransitiveInvalidationRoots,
|
|
5757
|
+
isExcluded: (id) => id.includes('/node_modules/'),
|
|
5758
|
+
maxDepth: 16,
|
|
5759
|
+
})
|
|
5760
|
+
: [];
|
|
5761
|
+
const transformCacheInvalidationUrls = new Set(collectAngularTransformCacheInvalidationUrls({
|
|
5762
|
+
file,
|
|
5763
|
+
isTs,
|
|
5764
|
+
hotUpdateRoots: angularHotUpdateRoots,
|
|
5765
|
+
transitiveImporters,
|
|
5766
|
+
projectRoot: server.config.root || process.cwd(),
|
|
5767
|
+
}));
|
|
5768
|
+
if (transformCacheInvalidationUrls.size) {
|
|
5769
|
+
sharedTransformRequest.invalidateMany(transformCacheInvalidationUrls);
|
|
5770
|
+
if (verbose) {
|
|
5771
|
+
console.log('[hmr-ws][angular] purged shared transform cache entries:', transformCacheInvalidationUrls.size);
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
catch (error) {
|
|
5776
|
+
if (verbose)
|
|
5777
|
+
console.warn('[hmr-ws][angular] shared transform cache purge failed', error);
|
|
5778
|
+
}
|
|
5104
5779
|
try {
|
|
5105
5780
|
const root = server.config.root || process.cwd();
|
|
5106
5781
|
const rel = '/' + path.posix.normalize(path.relative(root, file)).split(path.sep).join('/');
|
|
5782
|
+
rememberAngularReloadSuppression(root, file);
|
|
5107
5783
|
const origin = getServerOrigin(server);
|
|
5108
5784
|
const msg = {
|
|
5109
5785
|
type: 'ns:angular-update',
|
|
5110
5786
|
origin,
|
|
5111
5787
|
path: rel,
|
|
5788
|
+
version: graphVersion,
|
|
5112
5789
|
timestamp: Date.now(),
|
|
5113
5790
|
};
|
|
5791
|
+
if (verbose) {
|
|
5792
|
+
console.log('[hmr-ws][angular] broadcasting update', Array.from(wss.clients || []).map((client) => ({
|
|
5793
|
+
role: getHmrSocketRole(client),
|
|
5794
|
+
readyState: client.readyState,
|
|
5795
|
+
openState: client.OPEN,
|
|
5796
|
+
})));
|
|
5797
|
+
}
|
|
5114
5798
|
wss.clients.forEach((client) => {
|
|
5115
|
-
if (client
|
|
5799
|
+
if (isSocketClientOpen(client)) {
|
|
5116
5800
|
client.send(JSON.stringify(msg));
|
|
5117
5801
|
}
|
|
5118
5802
|
});
|
|
@@ -5120,6 +5804,9 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5120
5804
|
catch (error) {
|
|
5121
5805
|
console.warn('[hmr-ws][angular] update failed:', error);
|
|
5122
5806
|
}
|
|
5807
|
+
if (shouldSuppressDefaultViteHotUpdate({ flavor: ACTIVE_STRATEGY.flavor, file })) {
|
|
5808
|
+
return [];
|
|
5809
|
+
}
|
|
5123
5810
|
return;
|
|
5124
5811
|
}
|
|
5125
5812
|
// TypeScript flavor: emit generic graph delta for app XML/TS/style changes
|
|
@@ -5131,10 +5818,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5131
5818
|
// Treat the changed file itself as a graph module with no deps. We only
|
|
5132
5819
|
// care that its hash/identity changes so the client sees a delta and can
|
|
5133
5820
|
// perform a TS root reset. Code is not used for execution here.
|
|
5134
|
-
upsertGraphModule(rel, '', []);
|
|
5135
|
-
const gm = graph.get(normalizeGraphId(rel));
|
|
5136
|
-
if (gm)
|
|
5137
|
-
emitDelta([gm], []);
|
|
5821
|
+
upsertGraphModule(rel, '', [], { emitDeltaOnInsert: true });
|
|
5138
5822
|
}
|
|
5139
5823
|
catch (e) {
|
|
5140
5824
|
if (verbose)
|
|
@@ -5142,6 +5826,43 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5142
5826
|
}
|
|
5143
5827
|
return;
|
|
5144
5828
|
}
|
|
5829
|
+
// Solid flavor: emit graph delta for app TSX/TS/JSX file changes.
|
|
5830
|
+
// The common graph-update block above (moduleGraph lookup) may have
|
|
5831
|
+
// already emitted a delta if the file was in Vite's module graph.
|
|
5832
|
+
// This handler ensures a delta is emitted even if the module wasn't
|
|
5833
|
+
// found (e.g. new file, or moduleGraph mismatch), and provides
|
|
5834
|
+
// Solid-specific logging. The client-side processQueue handles
|
|
5835
|
+
// propagation from non-component .ts files to .tsx component boundaries.
|
|
5836
|
+
if (ACTIVE_STRATEGY.flavor === 'solid') {
|
|
5837
|
+
const isSolidFile = /\.(tsx?|jsx?)$/i.test(file);
|
|
5838
|
+
if (!isSolidFile)
|
|
5839
|
+
return;
|
|
5840
|
+
try {
|
|
5841
|
+
const rel = '/' + path.posix.normalize(path.relative(root, file)).split(path.sep).join('/');
|
|
5842
|
+
if (verbose)
|
|
5843
|
+
console.log('[hmr-ws][solid] app file hot update', { file, rel });
|
|
5844
|
+
// If the common block already upserted (hash changed), this will
|
|
5845
|
+
// detect unchanged hash and no-op. If the common block missed it
|
|
5846
|
+
// (module not in Vite's graph), this forces the delta emission.
|
|
5847
|
+
const normalizedId = normalizeGraphId(rel);
|
|
5848
|
+
const existing = graph.get(normalizedId);
|
|
5849
|
+
if (!existing) {
|
|
5850
|
+
// Module not in graph yet — force upsert with timestamp-based
|
|
5851
|
+
// hash so the client sees a change.
|
|
5852
|
+
upsertGraphModule(rel, `/* solid-hmr ${Date.now()} */`, [], { emitDeltaOnInsert: true });
|
|
5853
|
+
}
|
|
5854
|
+
// Log what we're sending so devs can trace the flow on the server side.
|
|
5855
|
+
if (verbose) {
|
|
5856
|
+
const gm = graph.get(normalizedId);
|
|
5857
|
+
console.log('[hmr-ws][solid] delta module', { id: gm?.id, hash: gm?.hash });
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
catch (e) {
|
|
5861
|
+
if (verbose)
|
|
5862
|
+
console.warn('[hmr-ws][solid] failed to handle hot update for', file, e);
|
|
5863
|
+
}
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5145
5866
|
// Handle .vue file updates
|
|
5146
5867
|
if (!file.endsWith('.vue')) {
|
|
5147
5868
|
if (verbose)
|
|
@@ -5249,6 +5970,7 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
5249
5970
|
// Rewrite ONLY .vue imports (everything else is now inlined)
|
|
5250
5971
|
const projectRoot = server.config.root || process.cwd();
|
|
5251
5972
|
code = rewriteImports(code, rel, sfcFileMap, depFileMap, projectRoot, opts.verbose, undefined);
|
|
5973
|
+
upsertGraphModule(rel, code, [...deps, ...vueDeps]);
|
|
5252
5974
|
// Add HMR runtime prelude (CRITICAL for runtime)
|
|
5253
5975
|
const hmrPrelude = `
|
|
5254
5976
|
// Embedded HMR Runtime for NativeScript runtime
|
|
@@ -5324,7 +6046,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
|
|
|
5324
6046
|
version: graphVersion,
|
|
5325
6047
|
};
|
|
5326
6048
|
wss.clients.forEach((client) => {
|
|
5327
|
-
if (client
|
|
6049
|
+
if (isSocketClientOpen(client)) {
|
|
5328
6050
|
client.send(JSON.stringify(registryUpdateMsg));
|
|
5329
6051
|
}
|
|
5330
6052
|
});
|
|
@@ -5495,4 +6217,6 @@ function getServerOrigin(server) {
|
|
|
5495
6217
|
// Test-only export: allow unit tests to run the sanitizer on snippets without booting a server
|
|
5496
6218
|
// Safe in production builds; this is a named export that tests can import explicitly.
|
|
5497
6219
|
export const __test_processCodeForDevice = processCodeForDevice;
|
|
6220
|
+
export const __test_resolveVendorRouting = resolveVendorRouting;
|
|
6221
|
+
export const __test_getBlockedDeviceNodeModulesReason = getBlockedDeviceNodeModulesReason;
|
|
5498
6222
|
//# sourceMappingURL=websocket.js.map
|