@nativescript/vite 8.0.0-alpha.4 → 8.0.0-alpha.5
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/base.js +57 -0
- package/configuration/base.js.map +1 -1
- package/helpers/config-as-json.js +10 -0
- package/helpers/config-as-json.js.map +1 -1
- package/helpers/main-entry.js +167 -13
- package/helpers/main-entry.js.map +1 -1
- package/helpers/ns-core-url.d.ts +84 -0
- package/helpers/ns-core-url.js +168 -0
- package/helpers/ns-core-url.js.map +1 -0
- package/hmr/server/core-sanitize.d.ts +8 -4
- package/hmr/server/core-sanitize.js +71 -41
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/import-map.js +7 -3
- package/hmr/server/import-map.js.map +1 -1
- package/hmr/server/ns-core-cjs-shape.d.ts +206 -0
- package/hmr/server/ns-core-cjs-shape.js +273 -0
- package/hmr/server/ns-core-cjs-shape.js.map +1 -0
- package/hmr/server/websocket-core-bridge.d.ts +0 -2
- package/hmr/server/websocket-core-bridge.js +60 -58
- package/hmr/server/websocket-core-bridge.js.map +1 -1
- package/hmr/server/websocket-module-specifiers.js +12 -0
- package/hmr/server/websocket-module-specifiers.js.map +1 -1
- package/hmr/server/websocket-ns-m-finalize.d.ts +0 -10
- package/hmr/server/websocket-ns-m-finalize.js +26 -11
- package/hmr/server/websocket-ns-m-finalize.js.map +1 -1
- package/hmr/server/websocket-served-module-helpers.d.ts +36 -0
- package/hmr/server/websocket-served-module-helpers.js +589 -0
- package/hmr/server/websocket-served-module-helpers.js.map +1 -0
- package/hmr/server/websocket-vue-sfc.d.ts +0 -8
- package/hmr/server/websocket-vue-sfc.js +15 -14
- package/hmr/server/websocket-vue-sfc.js.map +1 -1
- package/hmr/server/websocket.d.ts +2 -2
- package/hmr/server/websocket.js +2355 -177
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/vendor/manifest.js +114 -12
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/package.json +1 -1
package/hmr/server/websocket.js
CHANGED
|
@@ -1,53 +1,56 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { normalizeStrayCoreStringLiterals, fixDanglingCoreFrom, normalizeAnyCoreSpecToBridge, isDeepCoreSubpath, rewriteSpecifiersForDevice } from './core-sanitize.js';
|
|
3
|
+
import { buildDefaultExportFooter, buildShapeInstallHeader, hasNamespaceReExport, rewriteNamespaceReExportsForShape } from './ns-core-cjs-shape.js';
|
|
3
4
|
// AST tooling for robust transformations
|
|
4
5
|
import { parse as babelParse } from '@babel/parser';
|
|
5
6
|
import { genCode } from '../helpers/babel.js';
|
|
7
|
+
import babelCore from '@babel/core';
|
|
6
8
|
import traverse from '@babel/traverse';
|
|
7
9
|
// Ensure traverse callable across CJS/ESM builds
|
|
8
10
|
const babelTraverse = traverse?.default || traverse;
|
|
9
11
|
import * as t from '@babel/types';
|
|
10
12
|
import { existsSync, readFileSync, statSync } from 'fs';
|
|
11
13
|
import { astNormalizeModuleImportsAndHelpers, astVerifyAndAnnotateDuplicates } from '../helpers/ast-normalizer.js';
|
|
12
|
-
import { stripDanglingViteCjsImports } from '../helpers/sanitize.js';
|
|
14
|
+
import { stripRtCoreSentinel, stripDanglingViteCjsImports } from '../helpers/sanitize.js';
|
|
13
15
|
import { WebSocketServer } from 'ws';
|
|
14
16
|
import * as path from 'path';
|
|
15
17
|
import { createHash } from 'crypto';
|
|
16
18
|
import * as PAT from './constants.js';
|
|
17
19
|
import { getVendorManifest, resolveVendorSpecifier } from '../shared/vendor/registry.js';
|
|
18
|
-
import { getProjectRootPath } from '../../helpers/project.js';
|
|
20
|
+
import { getPackageJson, getProjectFilePath, getProjectRootPath } from '../../helpers/project.js';
|
|
19
21
|
import { loadPrebuiltVendorManifest } from '../shared/vendor/manifest-loader.js';
|
|
20
22
|
import '../vendor-bootstrap.js';
|
|
23
|
+
import { NS_NATIVE_TAGS } from './compiler.js';
|
|
24
|
+
import { vueSfcCompiler } from '../frameworks/vue/server/compiler.js';
|
|
21
25
|
import { linkAngularPartialsIfNeeded } from '../frameworks/angular/server/linker.js';
|
|
22
26
|
import { vueServerStrategy } from '../frameworks/vue/server/strategy.js';
|
|
23
27
|
import { angularServerStrategy } from '../frameworks/angular/server/strategy.js';
|
|
24
28
|
import { solidServerStrategy } from '../frameworks/solid/server/strategy.js';
|
|
25
29
|
import { typescriptServerStrategy } from '../frameworks/typescript/server/strategy.js';
|
|
26
|
-
import { createProcessSfcCode } from '../frameworks/vue/server/sfc-transforms.js';
|
|
30
|
+
import { buildInlineTemplateBlock, createProcessSfcCode, extractTemplateRender, processTemplateVariantMinimal } from '../frameworks/vue/server/sfc-transforms.js';
|
|
31
|
+
import { astExtractImportsAndStripTypes } from '../helpers/ast-extract.js';
|
|
27
32
|
import { getProjectAppPath, getProjectAppRelativePath, getProjectAppVirtualPath } from '../../helpers/utils.js';
|
|
28
33
|
import { buildRuntimeConfig, generateImportMap } from './import-map.js';
|
|
29
34
|
import { getCliFlags } from '../../helpers/cli-flags.js';
|
|
35
|
+
import { normalizeCoreSub as normalizeCoreSubCanonical } from '../../helpers/ns-core-url.js';
|
|
30
36
|
import { isRuntimeGraphExcludedPath, matchesRuntimeGraphModuleId, normalizeRuntimeGraphPath, shouldIncludeRuntimeGraphFile, shouldSkipRuntimeGraphDirectoryName } from './runtime-graph-filter.js';
|
|
31
37
|
import { resolveAngularCoreHmrImportSource, rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
32
38
|
import { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload } from './websocket-angular-hot-update.js';
|
|
33
39
|
import { classifyGraphUpsert, shouldBroadcastGraphUpsertDelta } from './websocket-graph-upsert.js';
|
|
34
40
|
import { extractVitePrebundleId, filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, getFlattenedManifestMap, isCoreGlobalsReference, isEsmFrameworkPackageSpecifier, isLikelyNativeScriptPluginSpecifier, isLikelyNativeScriptRuntimePluginSpecifier, isNativeScriptCoreModule, isNativeScriptPluginModule, normalizeNativeScriptCoreSpecifier, normalizeNodeModulesSpecifier, resolveCandidateFilePath, resolveInternalRuntimePluginBareSpecifier, resolveNodeModulesPackageBoundary, resolveVendorFromCandidate, resolveVendorRouting, shouldPreserveBareRuntimePluginSubpathImport, stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule, viteDepsPathToBareSpecifier, } from './websocket-module-specifiers.js';
|
|
35
41
|
import { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
36
|
-
import {
|
|
37
|
-
import { finalizeNsMServedModule } from './websocket-ns-m-finalize.js';
|
|
38
|
-
import { createNsMRequestContext, resolveNsMTransformedModule } from './websocket-ns-m-request.js';
|
|
39
|
-
import { getNumericServeVersionTag, rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
|
|
40
|
-
import { registerRuntimeCompatHandlers } from './websocket-runtime-compat.js';
|
|
41
|
-
import { registerTxnHandler } from './websocket-txn.js';
|
|
42
|
-
import { registerVendorUnifierHandler } from './websocket-vendor-unifier.js';
|
|
43
|
-
import { registerVueSfcHandlers } from './websocket-vue-sfc.js';
|
|
42
|
+
import { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, extractDirectExportedNames, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest, resolveRuntimeCoreModulePath } from './websocket-core-bridge.js';
|
|
44
43
|
import { createSharedTransformRequestRunner } from './shared-transform-request.js';
|
|
45
44
|
export { ensureNativeScriptModuleBindings, getProcessCodeResolvedSpecifierOverrides } from './websocket-module-bindings.js';
|
|
46
45
|
export { stripDecoratedServePrefixes, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
|
|
47
|
-
export {
|
|
48
|
-
export { rewriteNsMImportPathForHmr } from './websocket-ns-m-paths.js';
|
|
46
|
+
export { collectStaticExportNamesFromFile, collectStaticExportOriginsFromFile, ensureVersionedCoreImports, normalizeCoreExportOriginsForRuntime, parseCoreBridgeRequest } from './websocket-core-bridge.js';
|
|
49
47
|
export { rewriteAngularEntryRegisterOnly } from './websocket-angular-entry.js';
|
|
50
48
|
export { canonicalizeTransformRequestCacheKey, collectAngularHotUpdateRoots, collectAngularTransformCacheInvalidationUrls, collectAngularTransitiveImportersForInvalidation, collectGraphUpdateModulesForHotUpdate, createSharedTransformRequestRunner, normalizeHotReloadMatchPath, shouldInvalidateAngularTransitiveImporters, shouldSuppressDefaultViteHotUpdate, shouldSuppressViteFullReloadPayload, classifyGraphUpsert, shouldBroadcastGraphUpsertDelta };
|
|
49
|
+
const pluginTransformTypescript = (() => {
|
|
50
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
51
|
+
const loaded = requireFromHere('@babel/plugin-transform-typescript');
|
|
52
|
+
return loaded?.default || loaded;
|
|
53
|
+
})();
|
|
51
54
|
// Build a serialized process.env object from CLI --env.* flags.
|
|
52
55
|
// This is injected into every HTTP-served module so app code referencing
|
|
53
56
|
// process.env.TEST_ENV (etc.) works on device in HMR dev mode.
|
|
@@ -63,6 +66,7 @@ try {
|
|
|
63
66
|
}
|
|
64
67
|
catch { }
|
|
65
68
|
const __processEnvJson = JSON.stringify(__processEnvEntries);
|
|
69
|
+
const { parse, compileTemplate, compileScript } = vueSfcCompiler;
|
|
66
70
|
const APP_ROOT_DIR = getProjectAppPath();
|
|
67
71
|
const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
|
|
68
72
|
const APP_VIRTUAL_WITH_SLASH = `${APP_VIRTUAL_PREFIX}/`;
|
|
@@ -890,6 +894,52 @@ function toNodeModulesHttpModuleId(importPath) {
|
|
|
890
894
|
}
|
|
891
895
|
return `/ns/m/node_modules/${nodeModulesSpecifier}`;
|
|
892
896
|
}
|
|
897
|
+
export function rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest) {
|
|
898
|
+
const toHmrServeTag = (value) => {
|
|
899
|
+
const raw = String(value ?? '').trim();
|
|
900
|
+
if (!raw) {
|
|
901
|
+
return 'v0';
|
|
902
|
+
}
|
|
903
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
904
|
+
return raw;
|
|
905
|
+
}
|
|
906
|
+
if (/^\d+$/.test(raw)) {
|
|
907
|
+
return `v${raw}`;
|
|
908
|
+
}
|
|
909
|
+
return raw;
|
|
910
|
+
};
|
|
911
|
+
if (!p || !p.startsWith('/ns/m/')) {
|
|
912
|
+
return p;
|
|
913
|
+
}
|
|
914
|
+
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/');
|
|
915
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/node_modules/')) {
|
|
916
|
+
return canonicalNodeModulesPath;
|
|
917
|
+
}
|
|
918
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_boot__/')) {
|
|
919
|
+
return canonicalNodeModulesPath;
|
|
920
|
+
}
|
|
921
|
+
if (canonicalNodeModulesPath.startsWith('/ns/m/__ns_hmr__/')) {
|
|
922
|
+
return bootTaggedRequest ? `/ns/m/__ns_boot__/b1${canonicalNodeModulesPath.slice('/ns/m'.length)}` : canonicalNodeModulesPath;
|
|
923
|
+
}
|
|
924
|
+
const tag = toHmrServeTag(ver);
|
|
925
|
+
const hmrPrefix = `/ns/m/__ns_hmr__/${tag}`;
|
|
926
|
+
const bootHmrPrefix = `/ns/m/__ns_boot__/b1/__ns_hmr__/${tag}`;
|
|
927
|
+
return (bootTaggedRequest ? bootHmrPrefix : hmrPrefix) + canonicalNodeModulesPath.slice('/ns/m'.length);
|
|
928
|
+
}
|
|
929
|
+
function getNumericServeVersionTag(tag, fallback) {
|
|
930
|
+
const raw = String(tag || '').trim();
|
|
931
|
+
if (!raw) {
|
|
932
|
+
return fallback;
|
|
933
|
+
}
|
|
934
|
+
const versionMatch = raw.match(/^v(\d+)$/);
|
|
935
|
+
if (versionMatch?.[1]) {
|
|
936
|
+
return Number(versionMatch[1]);
|
|
937
|
+
}
|
|
938
|
+
if (/^\d+$/.test(raw)) {
|
|
939
|
+
return Number(raw);
|
|
940
|
+
}
|
|
941
|
+
return fallback;
|
|
942
|
+
}
|
|
893
943
|
function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
|
|
894
944
|
if (!spec || typeof spec !== 'string') {
|
|
895
945
|
return null;
|
|
@@ -1049,8 +1099,31 @@ export function wrapCommonJsModuleForDevice(code) {
|
|
|
1049
1099
|
`var __ns_cjs_require_kind = (typeof globalThis.__nsBaseRequire === 'function' ? 'base-require' : (typeof globalThis.__nsRequire === 'function' ? 'vendor-require' : 'global-require'));\n` +
|
|
1050
1100
|
`var require = function(spec) {\n` +
|
|
1051
1101
|
` if (!__ns_cjs_require_base) { throw new Error('require is not defined'); }\n` +
|
|
1052
|
-
|
|
1053
|
-
|
|
1102
|
+
// Resolve relative specifiers against the HTTP-served module's URL
|
|
1103
|
+
// before delegating to NS's runtime require. Without this step,
|
|
1104
|
+
// \`require('./base64-vlq')\` inside a CJS module served from
|
|
1105
|
+
// \`http://.../ns/m/node_modules/source-map-js/lib/source-map-generator.js\`
|
|
1106
|
+
// would pass a literal '"./base64-vlq"' to the native require, which
|
|
1107
|
+
// has no notion of the current HTTP-module's location and either
|
|
1108
|
+
// throws "Module not found" or fetches an arbitrary filesystem path
|
|
1109
|
+
// that happens to parse as code (producing misleading syntax errors
|
|
1110
|
+
// like "missing ) after argument list" from unrelated modules).
|
|
1111
|
+
` var __nsResolvedSpec = spec;\n` +
|
|
1112
|
+
` try {\n` +
|
|
1113
|
+
` if (typeof spec === 'string' && (spec.indexOf('./') === 0 || spec.indexOf('../') === 0)) {\n` +
|
|
1114
|
+
` var __nsParentUrl = (typeof import.meta !== 'undefined' && import.meta && typeof import.meta.url === 'string') ? import.meta.url : null;\n` +
|
|
1115
|
+
` if (__nsParentUrl) {\n` +
|
|
1116
|
+
` var __nsResolvedUrl = new URL(spec, __nsParentUrl);\n` +
|
|
1117
|
+
` // Common Node-style bare extensions: prefer .js if the resolved URL lacks an extension in its last path segment.\n` +
|
|
1118
|
+
` if (!/\\.[A-Za-z0-9]+$/.test(__nsResolvedUrl.pathname.split('/').pop() || '')) {\n` +
|
|
1119
|
+
` __nsResolvedUrl.pathname = __nsResolvedUrl.pathname.replace(/\\/+$/, '') + '.js';\n` +
|
|
1120
|
+
` }\n` +
|
|
1121
|
+
` __nsResolvedSpec = __nsResolvedUrl.href;\n` +
|
|
1122
|
+
` }\n` +
|
|
1123
|
+
` }\n` +
|
|
1124
|
+
` } catch (e) {}\n` +
|
|
1125
|
+
` try { var __nsRecord = globalThis.__NS_RECORD_MODULE_PROVENANCE__; if (typeof __nsRecord === 'function') { __nsRecord(String(__nsResolvedSpec), { kind: __ns_cjs_require_kind, specifier: String(spec), url: __nsResolvedSpec !== spec ? __nsResolvedSpec : undefined, via: 'cjs-wrapper', parent: (typeof import.meta !== 'undefined' && import.meta && import.meta.url) ? import.meta.url : undefined }); } } catch (e) {}\n` +
|
|
1126
|
+
` var mod = __ns_cjs_require_base(__nsResolvedSpec);\n` +
|
|
1054
1127
|
` try {\n` +
|
|
1055
1128
|
` if (mod && (typeof mod === 'object' || typeof mod === 'function') && mod.default !== undefined) {\n` +
|
|
1056
1129
|
` var keys = [];\n` +
|
|
@@ -2096,6 +2169,27 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
|
|
|
2096
2169
|
return `${prefix}./${depFile}${suffix}`;
|
|
2097
2170
|
}
|
|
2098
2171
|
}
|
|
2172
|
+
// Bare npm package specifier fallback — route to /ns/m/node_modules/.
|
|
2173
|
+
// This catches specifiers like `source-map-js/lib/source-map-generator.js`
|
|
2174
|
+
// emitted by helpers such as the CommonJS compat transform, which Vite
|
|
2175
|
+
// would normally resolve to an absolute path but which pass through the
|
|
2176
|
+
// rewriter as bare strings here. Under HMR (core external) bundle.mjs
|
|
2177
|
+
// depends on these resolving over HTTP rather than via a filesystem
|
|
2178
|
+
// bare-specifier lookup, which iOS can't satisfy and which crashes with
|
|
2179
|
+
// "Module not found".
|
|
2180
|
+
if (spec && !spec.startsWith('/') && !spec.startsWith('./') && !spec.startsWith('../') && !/^https?:\/\//i.test(spec) && !spec.startsWith('ns-vendor:') && !spec.startsWith('@nativescript/core')) {
|
|
2181
|
+
// Only treat as a package spec if it looks like one — disallow
|
|
2182
|
+
// plain identifiers like `moment` unresolved (those are left alone
|
|
2183
|
+
// for existing vendor-routing paths to handle).
|
|
2184
|
+
const bareNpmRe = /^(?:@[A-Za-z0-9][\w.-]*\/)?[A-Za-z0-9][\w.-]*(?:\/[\w.\-/]+)?$/;
|
|
2185
|
+
if (bareNpmRe.test(spec)) {
|
|
2186
|
+
const httpSpec = `/ns/m/node_modules/${spec}`;
|
|
2187
|
+
if (httpOriginSafe) {
|
|
2188
|
+
return `${prefix}${httpOriginSafe}${httpSpec}${suffix}`;
|
|
2189
|
+
}
|
|
2190
|
+
return `${prefix}${httpSpec}${suffix}`;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2099
2193
|
// Leave everything else unchanged (vendor imports, etc.)
|
|
2100
2194
|
return `${prefix}${spec}${suffix}`;
|
|
2101
2195
|
};
|
|
@@ -2816,27 +2910,139 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2816
2910
|
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
2817
2911
|
if (!urlObj.pathname.startsWith('/ns/m'))
|
|
2818
2912
|
return next();
|
|
2913
|
+
// Populate the initial graph on first /ns/m request so graphVersion is
|
|
2914
|
+
// non-zero and stable before we rewrite any child import paths. Without
|
|
2915
|
+
// this the first dyn-imports (e.g. main.ts → routed tab components) are
|
|
2916
|
+
// served with graphVersion=0, which makes the 'live' → v{N} substitution
|
|
2917
|
+
// below no-op; later dyn-imports (after the HMR websocket client connects
|
|
2918
|
+
// and populateInitialGraph runs inside 'connection') arrive when
|
|
2919
|
+
// graphVersion has jumped to some N>0, so their child URLs land at /v{N}/
|
|
2920
|
+
// while the early tree still references /live/ — two distinct iOS HTTP
|
|
2921
|
+
// ESM cache entries for the same file, two Angular class identities,
|
|
2922
|
+
// NG0912 selector collisions.
|
|
2923
|
+
if (graph.size === 0) {
|
|
2924
|
+
try {
|
|
2925
|
+
await populateInitialGraph(server);
|
|
2926
|
+
}
|
|
2927
|
+
catch (e) {
|
|
2928
|
+
if (verbose)
|
|
2929
|
+
console.warn('[hmr-ws][graph] lazy initial population failed', e);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2819
2932
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
2820
2933
|
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
2821
2934
|
// Disable caching for dev ESM endpoints to avoid device-side stale module reuse
|
|
2822
2935
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
2823
2936
|
res.setHeader('Pragma', 'no-cache');
|
|
2824
2937
|
res.setHeader('Expires', '0');
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2938
|
+
// Support both query (?path=/abs) and path-style (/ns/m/abs)
|
|
2939
|
+
let spec = urlObj.searchParams.get('path') || '';
|
|
2940
|
+
// Optional graph version pin for deterministic boot
|
|
2941
|
+
let forcedVer = urlObj.searchParams.get('v');
|
|
2942
|
+
let bootTaggedRequest = false;
|
|
2943
|
+
if (!spec) {
|
|
2944
|
+
const base = '/ns/m';
|
|
2945
|
+
let rest = urlObj.pathname.slice(base.length);
|
|
2946
|
+
if (rest && rest !== '/')
|
|
2947
|
+
spec = rest;
|
|
2948
|
+
}
|
|
2949
|
+
// Special-case stub for anomalous '@' imports emitted as '/__invalid_at__.mjs'
|
|
2950
|
+
if (spec === '/__invalid_at__.mjs' || spec === '__invalid_at__.mjs') {
|
|
2951
|
+
res.statusCode = 200;
|
|
2952
|
+
res.end("// invalid '@' import stub\nexport {}\n");
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
if (!spec) {
|
|
2956
|
+
res.statusCode = 200;
|
|
2957
|
+
res.end('export {}\n');
|
|
2832
2958
|
return;
|
|
2833
2959
|
}
|
|
2834
|
-
|
|
2960
|
+
const serverRoot = (server.config?.root || process.cwd());
|
|
2961
|
+
spec = spec.replace(/[?#].*$/, '');
|
|
2962
|
+
// Accept path-based boot/HMR prefixes:
|
|
2963
|
+
// /ns/m/__ns_boot__/b1/<real-spec>
|
|
2964
|
+
// /ns/m/__ns_hmr__/<tag>/<real-spec>
|
|
2965
|
+
// /ns/m/__ns_boot__/b1/__ns_hmr__/<tag>/<real-spec>
|
|
2966
|
+
// The iOS HTTP ESM loader canonicalizes cache keys by stripping query params,
|
|
2967
|
+
// so we must carry the cache-buster in the path.
|
|
2968
|
+
try {
|
|
2969
|
+
const decorated = stripDecoratedServePrefixes(spec);
|
|
2970
|
+
spec = decorated.cleanedSpec;
|
|
2971
|
+
bootTaggedRequest = decorated.bootTaggedRequest;
|
|
2972
|
+
forcedVer || (forcedVer = decorated.forcedVer);
|
|
2973
|
+
}
|
|
2974
|
+
catch { }
|
|
2975
|
+
// Normalize absolute filesystem paths back to project-relative ids (e.g. /src/app.ts)
|
|
2976
|
+
try {
|
|
2977
|
+
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
2978
|
+
const rootPosix = toPosix(serverRoot);
|
|
2979
|
+
const specPosix = toPosix(spec);
|
|
2980
|
+
// If spec is an absolute path under the project root, convert to '/'+relative
|
|
2981
|
+
const isAbsFs = /^\//.test(specPosix) || /^[A-Za-z]:\//.test(spec); // posix or win drive
|
|
2982
|
+
if (isAbsFs) {
|
|
2983
|
+
let rel = specPosix.startsWith(rootPosix) ? specPosix.slice(rootPosix.length) : require('path').posix.relative(rootPosix, specPosix);
|
|
2984
|
+
if (!rel.startsWith('..')) {
|
|
2985
|
+
if (!rel.startsWith('/'))
|
|
2986
|
+
rel = '/' + rel;
|
|
2987
|
+
// Ensure leading '/src' style when path maps into src
|
|
2988
|
+
spec = rel;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
catch { }
|
|
2835
2993
|
// Serve Vite virtual modules (/@id/ prefix). These are internal
|
|
2836
2994
|
// virtual modules (e.g., \0nsvite:nsconfig-json for ~/package.json)
|
|
2837
2995
|
// that don't exist on disk. Decode the ID and load via plugin container.
|
|
2996
|
+
if (spec.startsWith('/@id/')) {
|
|
2997
|
+
try {
|
|
2998
|
+
// First try Vite's transform pipeline directly
|
|
2999
|
+
const vr = await sharedTransformRequest(spec);
|
|
3000
|
+
if (vr?.code) {
|
|
3001
|
+
res.statusCode = 200;
|
|
3002
|
+
res.end(vr.code);
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
catch { }
|
|
3007
|
+
try {
|
|
3008
|
+
// Fallback: decode the virtual module ID (__x00__ → \0) and
|
|
3009
|
+
// load through the plugin container directly
|
|
3010
|
+
const rawId = spec.slice('/@id/'.length).replace(/__x00__/g, '\0');
|
|
3011
|
+
const loadResult = await server.pluginContainer.load(rawId);
|
|
3012
|
+
if (loadResult) {
|
|
3013
|
+
const code = typeof loadResult === 'string' ? loadResult : loadResult.code;
|
|
3014
|
+
if (code) {
|
|
3015
|
+
res.statusCode = 200;
|
|
3016
|
+
res.end(code);
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
catch { }
|
|
3022
|
+
}
|
|
3023
|
+
if (spec.startsWith('@/'))
|
|
3024
|
+
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
3025
|
+
if (spec.startsWith('./'))
|
|
3026
|
+
spec = spec.slice(1);
|
|
3027
|
+
const blockedNodeModulesReason = getBlockedDeviceNodeModulesReason(spec);
|
|
3028
|
+
if (blockedNodeModulesReason) {
|
|
3029
|
+
res.statusCode = 404;
|
|
3030
|
+
res.end(`// [ns:m] blocked device import\nthrow new Error(${JSON.stringify(`[ns/m] ${blockedNodeModulesReason}`)});\nexport {};\n`);
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
if (!spec.startsWith('/'))
|
|
3034
|
+
spec = '/' + spec;
|
|
3035
|
+
const hasExt = /\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(spec);
|
|
3036
|
+
const baseNoExt = hasExt ? spec.replace(/\.(ts|tsx|js|jsx|mjs|mts|cts)$/i, '') : spec;
|
|
3037
|
+
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'];
|
|
3038
|
+
const transformCandidates = filterExistingNodeModulesTransformCandidates(spec, candidates, serverRoot);
|
|
2838
3039
|
let transformed = null;
|
|
2839
3040
|
let resolvedCandidate = null;
|
|
3041
|
+
const rawExplicitModule = tryReadRawExplicitJavaScriptModule(spec, serverRoot);
|
|
3042
|
+
if (rawExplicitModule) {
|
|
3043
|
+
transformed = { code: rawExplicitModule.code };
|
|
3044
|
+
resolvedCandidate = rawExplicitModule.resolvedId;
|
|
3045
|
+
}
|
|
2840
3046
|
// Queue and dedupe transformRequest calls so heavy app graphs do not
|
|
2841
3047
|
// overwhelm Vite with concurrent work. Slow-transform warnings start only
|
|
2842
3048
|
// when the transform actually begins executing, and requests stay pending
|
|
@@ -2844,15 +3050,86 @@ function createHmrWebSocketPlugin(opts) {
|
|
|
2844
3050
|
const transformWithTimeout = (url, timeoutMs = 120000) => {
|
|
2845
3051
|
return sharedTransformRequest(url, timeoutMs);
|
|
2846
3052
|
};
|
|
2847
|
-
(
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
3053
|
+
if (!transformed?.code) {
|
|
3054
|
+
for (const cand of transformCandidates) {
|
|
3055
|
+
try {
|
|
3056
|
+
const r = await transformWithTimeout(cand);
|
|
3057
|
+
if (r?.code) {
|
|
3058
|
+
transformed = r;
|
|
3059
|
+
resolvedCandidate = cand;
|
|
3060
|
+
break;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
catch { }
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
// Fallback 1: ask Vite to resolve the id, then transform the resolved id (handles aliases and virtual ids)
|
|
3067
|
+
if (!transformed?.code) {
|
|
3068
|
+
try {
|
|
3069
|
+
const rid = await server.pluginContainer?.resolveId?.(spec, undefined);
|
|
3070
|
+
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3071
|
+
if (ridStr) {
|
|
3072
|
+
const r = await transformWithTimeout(ridStr);
|
|
3073
|
+
if (r?.code) {
|
|
3074
|
+
transformed = r;
|
|
3075
|
+
resolvedCandidate = ridStr;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
catch { }
|
|
3080
|
+
}
|
|
3081
|
+
// Fallback 1b: if spec is a /node_modules/ path, extract bare specifier
|
|
3082
|
+
// and try resolveId with that. This handles package.json "exports" field
|
|
3083
|
+
// resolution (e.g., solid-js/jsx-runtime → solid-js/dist/solid.js).
|
|
3084
|
+
if (!transformed?.code && spec.includes('/node_modules/')) {
|
|
3085
|
+
try {
|
|
3086
|
+
const nmIdx = spec.lastIndexOf('/node_modules/');
|
|
3087
|
+
const bare = spec.slice(nmIdx + '/node_modules/'.length);
|
|
3088
|
+
if (bare && !bare.startsWith('.')) {
|
|
3089
|
+
const rid = await server.pluginContainer?.resolveId?.(bare, undefined);
|
|
3090
|
+
const ridStr = typeof rid === 'string' ? rid : rid?.id || null;
|
|
3091
|
+
if (ridStr) {
|
|
3092
|
+
const r = await sharedTransformRequest(ridStr);
|
|
3093
|
+
if (r?.code) {
|
|
3094
|
+
transformed = r;
|
|
3095
|
+
resolvedCandidate = ridStr;
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
catch { }
|
|
3101
|
+
}
|
|
3102
|
+
// Fallback 2: try /@fs absolute path under project root (Vite file system alias)
|
|
3103
|
+
if (!transformed?.code) {
|
|
3104
|
+
try {
|
|
3105
|
+
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
3106
|
+
const rootPosix = toPosix(serverRoot).replace(/\/$/, '');
|
|
3107
|
+
const absPosix = `${rootPosix}${spec.startsWith('/') ? '' : '/'}${spec}`;
|
|
3108
|
+
const fsId = `/@fs${absPosix}`;
|
|
3109
|
+
if (resolveCandidateFilePath(fsId, serverRoot)) {
|
|
3110
|
+
const r = await transformWithTimeout(fsId);
|
|
3111
|
+
if (r?.code) {
|
|
3112
|
+
transformed = r;
|
|
3113
|
+
resolvedCandidate = fsId;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
catch { }
|
|
3118
|
+
}
|
|
3119
|
+
// Fallback 3: try adding ?import to hint Vite's transform pipeline
|
|
3120
|
+
if (!transformed?.code) {
|
|
3121
|
+
for (const cand of transformCandidates) {
|
|
3122
|
+
try {
|
|
3123
|
+
const r = await transformWithTimeout(`${cand}${cand.includes('?') ? '&' : '?'}import`);
|
|
3124
|
+
if (r?.code) {
|
|
3125
|
+
transformed = r;
|
|
3126
|
+
resolvedCandidate = `${cand}?import`;
|
|
3127
|
+
break;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
catch { }
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
2856
3133
|
// Solid HMR: patch @@solid-refresh's $$refreshESM to do inline patching
|
|
2857
3134
|
// during module re-evaluation instead of deferring to hot.accept() callback.
|
|
2858
3135
|
// In NativeScript's HTTP ESM environment, accept callbacks are registered
|
|
@@ -3055,47 +3332,183 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3055
3332
|
}
|
|
3056
3333
|
}
|
|
3057
3334
|
}
|
|
3335
|
+
let code = transformed.code;
|
|
3336
|
+
// Prepend guard to capture any URL-based require attempts
|
|
3337
|
+
code = REQUIRE_GUARD_SNIPPET + code;
|
|
3338
|
+
code = cleanCode(code);
|
|
3339
|
+
const isNodeMod = /(?:^|\/)node_modules\//.test(resolvedCandidate || spec || '');
|
|
3340
|
+
code = processCodeForDevice(code, false, true, isNodeMod, resolvedCandidate || spec);
|
|
3341
|
+
// Solid HMR: The NativeScript iOS/Android runtime provides import.meta.hot
|
|
3342
|
+
// natively (via InitializeImportMetaHot in HMRSupport.mm) with C++-backed
|
|
3343
|
+
// persistent hot.data that survives across module re-evaluations.
|
|
3344
|
+
// cleanCode() strips Vite's __vite__createHotContext assignment, which is
|
|
3345
|
+
// correct — the runtime's native hot context is better.
|
|
3058
3346
|
const projectRoot = server.config?.root || process.cwd();
|
|
3059
3347
|
const serverOrigin = getServerOrigin(server);
|
|
3060
|
-
|
|
3348
|
+
if (ACTIVE_STRATEGY?.flavor === 'angular') {
|
|
3349
|
+
code = prepareAngularEntryForDevice(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
3350
|
+
}
|
|
3351
|
+
else {
|
|
3352
|
+
code = rewriteImports(code, resolvedCandidate || spec, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, serverOrigin, true);
|
|
3353
|
+
}
|
|
3354
|
+
// Expand `export * from "url"` into explicit named re-exports.
|
|
3355
|
+
// NativeScript's HTTP ESM loader may not propagate star-re-exports across
|
|
3356
|
+
// HTTP module boundaries (the namespace object gets direct exports but
|
|
3357
|
+
// misses re-exported names). By expanding to `export { a, b } from "url"`,
|
|
3358
|
+
// the engine sees explicit named exports and resolves them correctly.
|
|
3061
3359
|
try {
|
|
3062
|
-
code = await
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3360
|
+
code = await expandStarExports(code, server, server.config?.root || process.cwd(), verbose);
|
|
3361
|
+
}
|
|
3362
|
+
catch (e) {
|
|
3363
|
+
if (verbose)
|
|
3364
|
+
console.warn('[ns/m] export* expansion failed:', e?.message);
|
|
3365
|
+
}
|
|
3366
|
+
// Dedupe any /ns/rt named imports that duplicate destructured bindings off default /ns/rt
|
|
3367
|
+
try {
|
|
3368
|
+
code = dedupeRtNamedImportsAgainstDestructures(code);
|
|
3369
|
+
}
|
|
3370
|
+
catch { }
|
|
3371
|
+
code = ensureVariableDynamicImportHelper(code);
|
|
3372
|
+
// Final safety: guard any plain dynamic import(...) occurrences to reroute anomalous '@' specs
|
|
3373
|
+
try {
|
|
3374
|
+
code = ensureGuardPlainDynamicImports(code, getServerOrigin(server));
|
|
3375
|
+
}
|
|
3376
|
+
catch { }
|
|
3377
|
+
// Extra hardening: normalize any remaining core references to the unified bridge
|
|
3378
|
+
// - Stray string-literals
|
|
3379
|
+
// - Dangling `from` merges
|
|
3380
|
+
// - Any spec (including /node_modules resolves) that still references '@nativescript/core'
|
|
3381
|
+
// Do this right before the final fast-fail assertion. If a rewrite occurred, add a small marker for diagnostics.
|
|
3382
|
+
try {
|
|
3383
|
+
const __before = code;
|
|
3384
|
+
code = normalizeStrayCoreStringLiterals(code);
|
|
3385
|
+
code = fixDanglingCoreFrom(code);
|
|
3386
|
+
code = normalizeAnyCoreSpecToBridge(code);
|
|
3387
|
+
if (code !== __before) {
|
|
3388
|
+
code = `// [hmr-sanitize] core-literal->bridge\n` + code;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
catch { }
|
|
3392
|
+
// Final pass: deduplicate/resolve any bare-specifier imports that slipped
|
|
3393
|
+
// through the pipeline (e.g., extracted from JSDoc comments by import-splitting
|
|
3394
|
+
// regexes, or injected by the Angular linker on already-resolved code).
|
|
3395
|
+
try {
|
|
3396
|
+
code = deduplicateLinkerImports(code);
|
|
3397
|
+
}
|
|
3398
|
+
catch { }
|
|
3399
|
+
// CJS/UMD wrapping: if a module uses module.exports but has no ESM export default,
|
|
3400
|
+
// wrap it with CJS shims so the device HTTP ESM loader can consume it.
|
|
3401
|
+
// This handles npm packages that use CommonJS but aren't pre-bundled by Vite.
|
|
3402
|
+
//
|
|
3403
|
+
// Key constraints this must handle:
|
|
3404
|
+
// - CJS modules often declare local vars with the same names as their exports
|
|
3405
|
+
// (e.g. `function createLTTB() {...}; exports.createLTTB = createLTTB;`)
|
|
3406
|
+
// so `export var { createLTTB }` would cause a duplicate declaration.
|
|
3407
|
+
// - UMD modules reference `this` at top level (undefined in ESM) but
|
|
3408
|
+
// typically fall back to `self` or `globalThis`.
|
|
3409
|
+
// - `module`, `exports` must be shims since they don't exist in ESM.
|
|
3410
|
+
try {
|
|
3411
|
+
code = wrapCommonJsModuleForDevice(code);
|
|
3412
|
+
}
|
|
3413
|
+
catch { }
|
|
3414
|
+
try {
|
|
3415
|
+
assertNoOptimizedArtifacts(code, `NS M ${resolvedCandidate || spec}`);
|
|
3094
3416
|
}
|
|
3095
3417
|
catch (e) {
|
|
3096
3418
|
res.statusCode = 500;
|
|
3097
3419
|
return void res.end(`throw new Error(${JSON.stringify(e?.message || String(e))});\nexport {};`);
|
|
3098
3420
|
}
|
|
3421
|
+
// Defensive export normalization: if a module defines `routes` and only exports it named,
|
|
3422
|
+
// add a default export alias so both `import { routes }` and `import routes` work.
|
|
3423
|
+
try {
|
|
3424
|
+
if (!/\bexport\s+default\b/.test(code)) {
|
|
3425
|
+
const hasNamedRoutes = /\bexport\s*\{\s*routes\s*\}/.test(code);
|
|
3426
|
+
const hasConstRoutes = /\bconst\s+routes\s*=/.test(code) || /\bvar\s+routes\s*=/.test(code) || /\blet\s+routes\s*=/.test(code);
|
|
3427
|
+
if (hasNamedRoutes && hasConstRoutes) {
|
|
3428
|
+
code += `\nexport default routes;\n`;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
catch { }
|
|
3433
|
+
try {
|
|
3434
|
+
const verNum = getNumericServeVersionTag(forcedVer, Number(graphVersion || 0));
|
|
3435
|
+
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
3436
|
+
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
3437
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
3438
|
+
}
|
|
3439
|
+
catch { }
|
|
3440
|
+
// Finalize: stamp all internal /ns/m imports with PATH-based cache busting.
|
|
3441
|
+
// IMPORTANT: use path prefix (not ?v= query) because the iOS HTTP ESM loader
|
|
3442
|
+
// strips query params when computing module cache keys, so ?v= doesn't bust the V8 cache.
|
|
3443
|
+
try {
|
|
3444
|
+
const ver = (() => {
|
|
3445
|
+
const raw = String(forcedVer || '').trim();
|
|
3446
|
+
const gv = Number(graphVersion || 0);
|
|
3447
|
+
// 'live' is the client dynamic-import helper's fallback when
|
|
3448
|
+
// globalThis.__NS_HMR_GRAPH_VERSION__ has not yet been set (i.e. before the
|
|
3449
|
+
// full HMR client takes over). If we propagate 'live' into child import
|
|
3450
|
+
// URLs, a file loaded through a 'live'-tagged parent ends up at
|
|
3451
|
+
// /ns/m/__ns_hmr__/live/... in the iOS V8 module cache while a later
|
|
3452
|
+
// dyn-import at v${N} loads the same file at /ns/m/__ns_hmr__/v${N}/...
|
|
3453
|
+
// — two distinct cache entries, two module realms, two Angular class
|
|
3454
|
+
// identities per @Component, and NG0912 selector collisions. Replace
|
|
3455
|
+
// 'live' with the current graph version so every child resolves to one
|
|
3456
|
+
// canonical URL regardless of the parent's request tag.
|
|
3457
|
+
if (raw === 'live' && gv > 0) {
|
|
3458
|
+
return `v${gv}`;
|
|
3459
|
+
}
|
|
3460
|
+
if (raw) {
|
|
3461
|
+
if (raw === 'live' || /^n\d+$/i.test(raw) || /^v[^/]+$/i.test(raw)) {
|
|
3462
|
+
return raw;
|
|
3463
|
+
}
|
|
3464
|
+
if (/^\d+$/.test(raw)) {
|
|
3465
|
+
return `v${raw}`;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
return `v${String(graphVersion || 0)}`;
|
|
3469
|
+
})();
|
|
3470
|
+
const origin = getServerOrigin(server);
|
|
3471
|
+
const rewritePath = (p) => rewriteNsMImportPathForHmr(p, ver, bootTaggedRequest);
|
|
3472
|
+
// 1) Static imports: import ... from "/ns/m/..."
|
|
3473
|
+
code = code.replace(/(from\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
3474
|
+
// 2) Side-effect imports: import "/ns/m/..."
|
|
3475
|
+
code = code.replace(/(import\s*(?!\()\s*["'])(\/ns\/m\/[^"'?]+)(["'])/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
3476
|
+
// 3) Dynamic imports: import("/ns/m/...")
|
|
3477
|
+
code = code.replace(/(import\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
3478
|
+
// 4) new URL("/ns/m/...", import.meta.url)
|
|
3479
|
+
code = code.replace(/(new\s+URL\(\s*["'])(\/ns\/m\/[^"'?]+)(["']\s*,\s*import\.meta\.url\s*\))/g, (_m, a, p, b) => `${a}${rewritePath(p)}${b}`);
|
|
3480
|
+
// 5) __ns_import(new URL('/ns/m/...', import.meta.url).href)
|
|
3481
|
+
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}`);
|
|
3482
|
+
// 6) Force absolute HTTP for new URL('/ns/m/...', import.meta.url).href → "${origin}/ns/m/__ns_hmr__/..."
|
|
3483
|
+
try {
|
|
3484
|
+
code = code.replace(/new\s+URL\(\s*["'](\/ns\/m\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}${rewritePath(p1)}`)}`);
|
|
3485
|
+
}
|
|
3486
|
+
catch { }
|
|
3487
|
+
// 7) Also fix SFC new URL('/ns/sfc/...', import.meta.url).href → "${origin}/ns/sfc/<ver>/..."
|
|
3488
|
+
try {
|
|
3489
|
+
code = code.replace(/new\s+URL\(\s*["']\/ns\/sfc(\/[^"'?]+)(?:\?[^"']*)?["']\s*,\s*import\.meta\.url\s*\)\.href/g, (_m, p1) => `${JSON.stringify(`${origin}/ns/sfc/${ver}${p1}`)}`);
|
|
3490
|
+
}
|
|
3491
|
+
catch { }
|
|
3492
|
+
}
|
|
3493
|
+
catch { }
|
|
3494
|
+
// Final guard: eliminate any lingering named imports from /ns/core to avoid
|
|
3495
|
+
// evaluation-time "does not provide an export named ..." in the device runtime.
|
|
3496
|
+
try {
|
|
3497
|
+
code = ensureDestructureCoreImports(code);
|
|
3498
|
+
}
|
|
3499
|
+
catch { }
|
|
3500
|
+
// Boot-time module graph progress: while the app is still replacing the
|
|
3501
|
+
// placeholder, emit lightweight progress updates as /ns/m modules begin
|
|
3502
|
+
// evaluating. This keeps the overlay moving during large initial graphs.
|
|
3503
|
+
try {
|
|
3504
|
+
if (bootTaggedRequest) {
|
|
3505
|
+
const bootModuleLabel = String(spec || '').replace(/\\/g, '/');
|
|
3506
|
+
const bootProgressSnippet = buildBootProgressSnippet(bootModuleLabel);
|
|
3507
|
+
code = bootProgressSnippet + code;
|
|
3508
|
+
code = hoistTopLevelStaticImports(code);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
catch { }
|
|
3099
3512
|
// Dev-only: link-check static imports to surface missing bindings early
|
|
3100
3513
|
try {
|
|
3101
3514
|
const devCheck = process.env.NODE_ENV !== 'production';
|
|
@@ -3224,23 +3637,231 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3224
3637
|
res.end('export {}\n');
|
|
3225
3638
|
}
|
|
3226
3639
|
});
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3640
|
+
// 2.5) ESM runtime bridge for NativeScript-Vue: GET /ns/rt
|
|
3641
|
+
// Provides a single authoritative source of Vue helpers bound to the NativeScript renderer.
|
|
3642
|
+
// V2.1: Lazy ensure bridge — does not statically import vue. It lazily resolves helpers from
|
|
3643
|
+
// globalThis or vendor registry/require on first evaluation, then exports references so SFCs
|
|
3644
|
+
// can immediately call them during module evaluation.
|
|
3645
|
+
server.middlewares.use(async (req, res, next) => {
|
|
3646
|
+
try {
|
|
3647
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
3648
|
+
// Accept only /ns/rt and /ns/rt/<ver> for cache-busting semantics
|
|
3649
|
+
if (!(urlObj.pathname === '/ns/rt' || /^\/ns\/rt\/[\d]+$/.test(urlObj.pathname)))
|
|
3650
|
+
return next();
|
|
3651
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
3652
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
3653
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3654
|
+
res.setHeader('Pragma', 'no-cache');
|
|
3655
|
+
res.setHeader('Expires', '0');
|
|
3656
|
+
const rtVerSeg = urlObj.pathname.replace(/^\/ns\/rt\/?/, '');
|
|
3657
|
+
const rtVer = /^[0-9]+$/.test(rtVerSeg) ? rtVerSeg : String(graphVersion || 0);
|
|
3658
|
+
const origin = getServerOrigin(server);
|
|
3659
|
+
let code = `// [ns-rt][v2.3] NativeScript-Vue runtime bridge (module-scoped cache, no globals)\n` +
|
|
3660
|
+
`const __origin = ((typeof globalThis !== 'undefined' && globalThis && globalThis.__NS_HTTP_ORIGIN__) || (new URL(import.meta.url)).origin);\n` +
|
|
3661
|
+
`let __ns_core_bridge = null; try { import(__origin + "/ns/core/${rtVer}").then(m => { __ns_core_bridge = m; }).catch(() => {}); } catch {}\n` +
|
|
3662
|
+
`const g = globalThis;\n` +
|
|
3663
|
+
`const reg = (g.__nsVendorRegistry ||= new Map());\n` +
|
|
3664
|
+
`const req = reg && reg.get ? (g.__nsVendorRequire || g.__nsRequire || g.require) : (g.__nsRequire || g.require);\n` +
|
|
3665
|
+
`let __cached_rt = null;\n` +
|
|
3666
|
+
`let __cached_vm = null;\n` +
|
|
3667
|
+
`const __RT_REALM_TAG = (globalThis.__NS_RT_REALM__ ||= Math.random().toString(36).slice(2));\n` +
|
|
3668
|
+
// Unconditional one-shot evaluation marker to confirm bridge is executed on device
|
|
3669
|
+
`try { if (!(globalThis.__NS_RT_ONCE__ && globalThis.__NS_RT_ONCE__.eval)) { (globalThis.__NS_RT_ONCE__ ||= {}).eval = true; console.log('[ns-rt] evaluated', { rtRealm: __RT_REALM_TAG }); } } catch {}\n` +
|
|
3670
|
+
`function __ensure(){\n` +
|
|
3671
|
+
` if (__cached_rt) return __cached_rt;\n` +
|
|
3672
|
+
` let vm = null;\n` +
|
|
3673
|
+
` try { vm = reg && reg.has && reg.has('nativescript-vue') ? reg.get('nativescript-vue') : (typeof req==='function' ? req('nativescript-vue') : null); } catch {}\n` +
|
|
3674
|
+
` if (!vm) { try { vm = reg && reg.has && reg.has('vue') ? reg.get('vue') : (typeof req==='function' ? req('vue') : null); } catch {} }\n` +
|
|
3675
|
+
` const rt = (vm && (vm.default ?? vm)) || {};\n` +
|
|
3676
|
+
` __cached_vm = vm;\n` +
|
|
3677
|
+
` __cached_rt = rt;\n` +
|
|
3678
|
+
` return rt;\n` +
|
|
3679
|
+
`}\n` +
|
|
3680
|
+
`// Soft-globals for @nativescript/core when missing (dev-only safety)\n` +
|
|
3681
|
+
`try {\n` +
|
|
3682
|
+
` const dev = typeof __DEV__ !== 'undefined' ? __DEV__ : true;\n` +
|
|
3683
|
+
` if (dev) {\n` +
|
|
3684
|
+
` const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {};\n` +
|
|
3685
|
+
` if (ns) {\n` +
|
|
3686
|
+
` if (!g.Frame && ns.Frame) g.Frame = ns.Frame;\n` +
|
|
3687
|
+
` if (!g.Page && ns.Page) g.Page = ns.Page;\n` +
|
|
3688
|
+
` if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application);\n` +
|
|
3689
|
+
` }\n` +
|
|
3690
|
+
` }\n` +
|
|
3691
|
+
`} catch {}\n` +
|
|
3692
|
+
`const __get = (k) => { const rt = __ensure(); const v = rt && rt[k]; if (typeof v !== 'function' && v === undefined) { throw new Error('[ns-rt] missing export '+k); } return v; };\n` +
|
|
3693
|
+
`export const __realm = __RT_REALM_TAG;\n` +
|
|
3694
|
+
`export const defineComponent = (...a) => (__get('defineComponent'))(...a);\n` +
|
|
3695
|
+
`export const resolveComponent = (...a) => (__ensure().resolveComponent)(...a);\n` +
|
|
3696
|
+
`export const createVNode = (...a) => (__ensure().createVNode)(...a);\n` +
|
|
3697
|
+
`export const createTextVNode = (...a) => (__ensure().createTextVNode)(...a);\n` +
|
|
3698
|
+
`export const createCommentVNode = (...a) => (__ensure().createCommentVNode)(...a);\n` +
|
|
3699
|
+
`export const Fragment = (__ensure().Fragment);\n` +
|
|
3700
|
+
`export const Teleport = (__ensure().Teleport);\n` +
|
|
3701
|
+
`export const Transition = (__ensure().Transition);\n` +
|
|
3702
|
+
`export const TransitionGroup = (__ensure().TransitionGroup);\n` +
|
|
3703
|
+
`export const KeepAlive = (__ensure().KeepAlive);\n` +
|
|
3704
|
+
`export const Suspense = (__ensure().Suspense);\n` +
|
|
3705
|
+
`export const withCtx = (...a) => (__ensure().withCtx)(...a);\n` +
|
|
3706
|
+
`export const openBlock = (...a) => (__ensure().openBlock)(...a);\n` +
|
|
3707
|
+
`export const createBlock = (...a) => (__ensure().createBlock)(...a);\n` +
|
|
3708
|
+
`export const createElementVNode = (...a) => (__ensure().createElementVNode)(...a);\n` +
|
|
3709
|
+
`export const createElementBlock = (...a) => (__ensure().createElementBlock)(...a);\n` +
|
|
3710
|
+
`export const renderSlot = (...a) => (__ensure().renderSlot)(...a);\n` +
|
|
3711
|
+
`export const mergeProps = (...a) => (__ensure().mergeProps)(...a);\n` +
|
|
3712
|
+
`export const toHandlers = (...a) => (__ensure().toHandlers)(...a);\n` +
|
|
3713
|
+
`export const renderList = (...a) => (__ensure().renderList)(...a);\n` +
|
|
3714
|
+
`export const normalizeProps = (...a) => (__ensure().normalizeProps)(...a);\n` +
|
|
3715
|
+
`export const guardReactiveProps = (...a) => (__ensure().guardReactiveProps)(...a);\n` +
|
|
3716
|
+
`export const normalizeClass = (...a) => (__ensure().normalizeClass)(...a);\n` +
|
|
3717
|
+
`export const normalizeStyle = (...a) => (__ensure().normalizeStyle)(...a);\n` +
|
|
3718
|
+
`export const toDisplayString = (...a) => (__ensure().toDisplayString)(...a);\n` +
|
|
3719
|
+
`export const withDirectives = (...a) => (__ensure().withDirectives)(...a);\n` +
|
|
3720
|
+
`export const resolveDirective = (...a) => (__ensure().resolveDirective)(...a);\n` +
|
|
3721
|
+
`export const withModifiers = (...a) => (__ensure().withModifiers)(...a);\n` +
|
|
3722
|
+
`export const withKeys = (...a) => (__ensure().withKeys)(...a);\n` +
|
|
3723
|
+
`export const resolveDynamicComponent = (...a) => (__ensure().resolveDynamicComponent)(...a);\n` +
|
|
3724
|
+
`export const isVNode = (...a) => (__ensure().isVNode)(...a);\n` +
|
|
3725
|
+
`export const cloneVNode = (...a) => (__ensure().cloneVNode)(...a);\n` +
|
|
3726
|
+
`export const isRef = (...a) => (__ensure().isRef)(...a);\n` +
|
|
3727
|
+
`export const ref = (...a) => (__ensure().ref)(...a);\n` +
|
|
3728
|
+
`export const shallowRef = (...a) => (__ensure().shallowRef)(...a);\n` +
|
|
3729
|
+
`export const unref = (...a) => (__ensure().unref)(...a);\n` +
|
|
3730
|
+
`export const computed = (...a) => (__ensure().computed)(...a);\n` +
|
|
3731
|
+
`export const reactive = (...a) => (__ensure().reactive)(...a);\n` +
|
|
3732
|
+
`export const readonly = (...a) => (__ensure().readonly)(...a);\n` +
|
|
3733
|
+
`export const isReactive = (...a) => (__ensure().isReactive)(...a);\n` +
|
|
3734
|
+
`export const isReadonly = (...a) => (__ensure().isReadonly)(...a);\n` +
|
|
3735
|
+
`export const toRaw = (...a) => (__ensure().toRaw)(...a);\n` +
|
|
3736
|
+
`export const markRaw = (...a) => (__ensure().markRaw)(...a);\n` +
|
|
3737
|
+
`export const shallowReactive = (...a) => (__ensure().shallowReactive)(...a);\n` +
|
|
3738
|
+
`export const shallowReadonly = (...a) => (__ensure().shallowReadonly)(...a);\n` +
|
|
3739
|
+
`export const watch = (...a) => (__ensure().watch)(...a);\n` +
|
|
3740
|
+
`export const watchEffect = (...a) => (__ensure().watchEffect)(...a);\n` +
|
|
3741
|
+
`export const watchPostEffect = (...a) => (__ensure().watchPostEffect)(...a);\n` +
|
|
3742
|
+
`export const watchSyncEffect = (...a) => (__ensure().watchSyncEffect)(...a);\n` +
|
|
3743
|
+
`export const onBeforeMount = (...a) => (__ensure().onBeforeMount)(...a);\n` +
|
|
3744
|
+
`export const onMounted = (...a) => (__ensure().onMounted)(...a);\n` +
|
|
3745
|
+
`export const onBeforeUpdate = (...a) => (__ensure().onBeforeUpdate)(...a);\n` +
|
|
3746
|
+
`export const onUpdated = (...a) => (__ensure().onUpdated)(...a);\n` +
|
|
3747
|
+
`export const onBeforeUnmount = (...a) => (__ensure().onBeforeUnmount)(...a);\n` +
|
|
3748
|
+
`export const onUnmounted = (...a) => (__ensure().onUnmounted)(...a);\n` +
|
|
3749
|
+
`export const onActivated = (...a) => (__ensure().onActivated)(...a);\n` +
|
|
3750
|
+
`export const onDeactivated = (...a) => (__ensure().onDeactivated)(...a);\n` +
|
|
3751
|
+
`export const onErrorCaptured = (...a) => (__ensure().onErrorCaptured)(...a);\n` +
|
|
3752
|
+
`export const onRenderTracked = (...a) => (__ensure().onRenderTracked)(...a);\n` +
|
|
3753
|
+
`export const onRenderTriggered = (...a) => (__ensure().onRenderTriggered)(...a);\n` +
|
|
3754
|
+
`export const nextTick = (...a) => (__ensure().nextTick)(...a);\n` +
|
|
3755
|
+
`export const h = (...a) => (__ensure().h)(...a);\n` +
|
|
3756
|
+
`export const provide = (...a) => (__ensure().provide)(...a);\n` +
|
|
3757
|
+
`export const inject = (...a) => (__ensure().inject)(...a);\n` +
|
|
3758
|
+
`export const vShow = (__ensure().vShow);\n` +
|
|
3759
|
+
`export const createApp = (...a) => (__ensure().createApp)(...a);\n` +
|
|
3760
|
+
`export const registerElement = (...a) => (__ensure().registerElement)(...a);\n` +
|
|
3761
|
+
`export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) { try { console.error('[ns-rt] $navigateTo app navigator error', e); } catch {} throw e; } } try { console.error('[ns-rt] $navigateTo unavailable: app navigator missing'); } catch {} throw new Error('$navigateTo unavailable: app navigator missing'); } ;\n` +
|
|
3762
|
+
`export const $navigateBack = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$navigateBack || (vm.default && vm.default.$navigateBack))) || (rt && (rt.$navigateBack || (rt.runtimeHelpers && rt.runtimeHelpers.navigateBack))); let res; try { const via = (impl && (impl === (vm && vm.$navigateBack) || impl === (vm && vm.default && vm.default.$navigateBack))) ? 'vm' : (impl ? 'rt' : 'none'); } catch {} try { if (typeof impl === 'function') res = impl(...a); } catch {} try { const top = (g && g.Frame && g.Frame.topmost && g.Frame.topmost()); if (!res && top && top.canGoBack && top.canGoBack()) { res = top.goBack(); } } catch {} try { const hook = g && (g.__NS_HMR_ON_NAVIGATE_BACK || g.__NS_HMR_ON_BACK || g.__nsAttemptBackRemount); if (typeof hook === 'function') hook(); } catch {} return res; }\n` +
|
|
3763
|
+
`export const $showModal = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$showModal || (vm.default && vm.default.$showModal))) || (rt && (rt.$showModal || (rt.runtimeHelpers && rt.runtimeHelpers.showModal))); try { if (typeof impl === 'function') return impl(...a); } catch (e) { } return undefined; }\n` +
|
|
3764
|
+
`export default {\n` +
|
|
3765
|
+
` defineComponent, resolveComponent, createVNode, createTextVNode, createCommentVNode,\n` +
|
|
3766
|
+
` Fragment, Teleport, Transition, TransitionGroup, KeepAlive, Suspense, withCtx, openBlock,\n` +
|
|
3767
|
+
` createBlock, createElementVNode, createElementBlock, renderSlot, mergeProps, toHandlers,\n` +
|
|
3768
|
+
` renderList, normalizeProps, guardReactiveProps, normalizeClass, normalizeStyle, toDisplayString,\n` +
|
|
3769
|
+
` withDirectives, resolveDirective, withModifiers, withKeys, resolveDynamicComponent,\n` +
|
|
3770
|
+
` isVNode, cloneVNode, isRef, ref, shallowRef, unref, computed, reactive, readonly, isReactive, isReadonly, toRaw, markRaw, shallowReactive, shallowReadonly,\n` +
|
|
3771
|
+
` watch, watchEffect, watchPostEffect, watchSyncEffect, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,\n` +
|
|
3772
|
+
` onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured, onRenderTracked, onRenderTriggered, nextTick, h, provide, inject, vShow, createApp, registerElement,\n` +
|
|
3773
|
+
` $navigateTo, $navigateBack, $showModal\n` +
|
|
3774
|
+
`};\n`;
|
|
3775
|
+
// Prepend guard and ship (harmless, keeps diagnostics consistent)
|
|
3776
|
+
code = REQUIRE_GUARD_SNIPPET + code;
|
|
3777
|
+
res.statusCode = 200;
|
|
3778
|
+
res.end(code);
|
|
3779
|
+
}
|
|
3780
|
+
catch (e) {
|
|
3781
|
+
res.statusCode = 500;
|
|
3782
|
+
res.end('export {}\n');
|
|
3783
|
+
}
|
|
3235
3784
|
});
|
|
3236
3785
|
// 2.55) Dev-only vendor import unifier: rewrite 'vue'/'nativescript-vue' to /ns/rt/<ver>
|
|
3237
3786
|
// This ensures plugins and app share a single Vue/NativeScript-Vue instance/realm.
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3787
|
+
server.middlewares.use(async (req, res, next) => {
|
|
3788
|
+
try {
|
|
3789
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
3790
|
+
const p = urlObj.pathname || '';
|
|
3791
|
+
// Ignore our own core/rt bridge endpoints and non-JS assets, but DO allow /ns/m/* through
|
|
3792
|
+
if (/^\/ns\/(?:rt|core)(?:\/|$)/.test(p))
|
|
3793
|
+
return next();
|
|
3794
|
+
if (!/(\.m?js$|\.ts$|\/node_modules\/|\/\.vite\/deps\/|^\/@id\/|^\/@fs\/)/.test(p))
|
|
3795
|
+
return next();
|
|
3796
|
+
if (/\.css($|\?)/.test(p))
|
|
3797
|
+
return next();
|
|
3798
|
+
const reqUrl = req.url || '';
|
|
3799
|
+
const transformed = await server.transformRequest(reqUrl);
|
|
3800
|
+
if (!transformed?.code)
|
|
3801
|
+
return next();
|
|
3802
|
+
const origin = getServerOrigin(server);
|
|
3803
|
+
const ver = Number(graphVersion || 0);
|
|
3804
|
+
const rewrite = ACTIVE_STRATEGY.rewriteVendorSpec;
|
|
3805
|
+
if (!rewrite)
|
|
3806
|
+
return next();
|
|
3807
|
+
const before = transformed.code;
|
|
3808
|
+
const code = rewrite(before, origin, ver);
|
|
3809
|
+
if (code === before)
|
|
3810
|
+
return next();
|
|
3811
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
3812
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
3813
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3814
|
+
res.setHeader('Pragma', 'no-cache');
|
|
3815
|
+
res.setHeader('Expires', '0');
|
|
3816
|
+
res.statusCode = 200;
|
|
3817
|
+
res.end(code);
|
|
3818
|
+
}
|
|
3819
|
+
catch {
|
|
3820
|
+
return next();
|
|
3821
|
+
}
|
|
3822
|
+
});
|
|
3823
|
+
// 2.5.1) Catch-all redirect for stray /node_modules/@nativescript/core/*
|
|
3824
|
+
// requests — route them to the /ns/core bridge so they get the same
|
|
3825
|
+
// __DEV__/__IOS__ preamble and specifier rewriting. Without this,
|
|
3826
|
+
// Vite's default /node_modules/ handler serves the raw file, which
|
|
3827
|
+
// references bare __DEV__ and crashes at module eval.
|
|
3828
|
+
server.middlewares.use((req, _res, next) => {
|
|
3829
|
+
try {
|
|
3830
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
3831
|
+
const coreNmPrefix = '/node_modules/@nativescript/core';
|
|
3832
|
+
if (!urlObj.pathname.startsWith(coreNmPrefix))
|
|
3833
|
+
return next();
|
|
3834
|
+
const sub = urlObj.pathname.slice(coreNmPrefix.length).replace(/^\/+/, '');
|
|
3835
|
+
if (sub === '' || sub === 'index.js' || sub === 'index') {
|
|
3836
|
+
req.url = `/ns/core`;
|
|
3837
|
+
}
|
|
3838
|
+
else {
|
|
3839
|
+
req.url = `/ns/core/${sub}`;
|
|
3840
|
+
}
|
|
3841
|
+
return next();
|
|
3842
|
+
}
|
|
3843
|
+
catch {
|
|
3844
|
+
return next();
|
|
3845
|
+
}
|
|
3242
3846
|
});
|
|
3243
3847
|
// 2.6) ESM bridge for @nativescript/core: GET /ns/core[/<ver>][?p=sub/path]
|
|
3848
|
+
//
|
|
3849
|
+
// Since bundle.mjs no longer bundles @nativescript/core (see
|
|
3850
|
+
// HMR_CORE_REALM_DETERMINISTIC_PLAN.md — external in the rolldown
|
|
3851
|
+
// config under HMR), this endpoint is the ONE place core is
|
|
3852
|
+
// evaluated. Every consumer — bundle.mjs's own `@nativescript/core*`
|
|
3853
|
+
// imports (resolved to full HTTP URLs in the entry virtual module),
|
|
3854
|
+
// externalized vendor packages, HTTP-served app modules — all end
|
|
3855
|
+
// up here. No more proxy bridge, no enumeration, no namespace
|
|
3856
|
+
// detection, no prototype-polluted maps. We just serve Vite's
|
|
3857
|
+
// authoritative transformed module content.
|
|
3858
|
+
//
|
|
3859
|
+
// iOS caches by URL path, so each unique URL is evaluated exactly
|
|
3860
|
+
// once per app lifetime. Every class identity is shared, every
|
|
3861
|
+
// `register()` side effect runs once, every `Application` reference
|
|
3862
|
+
// is the same iosApp singleton. The entire class of "does not
|
|
3863
|
+
// provide an export named X" and "Cannot redefine property" errors
|
|
3864
|
+
// is eliminated by construction.
|
|
3244
3865
|
server.middlewares.use(async (req, res, next) => {
|
|
3245
3866
|
try {
|
|
3246
3867
|
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
@@ -3252,84 +3873,338 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3252
3873
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
3253
3874
|
res.setHeader('Pragma', 'no-cache');
|
|
3254
3875
|
res.setHeader('Expires', '0');
|
|
3255
|
-
const {
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3876
|
+
const { normalizedSub, sub, ver } = coreRequest;
|
|
3877
|
+
const resolveModuleId = async (moduleId) => {
|
|
3878
|
+
const resolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3879
|
+
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3880
|
+
};
|
|
3881
|
+
let modulePath = null;
|
|
3260
3882
|
if (sub) {
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const resolved = await server.pluginContainer?.resolveId?.(moduleId, undefined);
|
|
3266
|
-
return typeof resolved === 'string' ? resolved : resolved?.id || null;
|
|
3267
|
-
};
|
|
3268
|
-
const resolvedId = await resolveRuntimeCoreModulePath(resolvedSubpath, resolveModuleId);
|
|
3269
|
-
const modulePath = resolvedId || `/node_modules/@nativescript/core/${resolvedSubpath}`;
|
|
3270
|
-
const transformed = await sharedTransformRequest(modulePath);
|
|
3271
|
-
if (!hasExplicitVersion) {
|
|
3272
|
-
if (transformed?.code) {
|
|
3273
|
-
const expandedModuleCode = await expandStarExports(transformed.code, server, projectRoot, verbose);
|
|
3274
|
-
res.statusCode = 200;
|
|
3275
|
-
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver, extractExportedNames(expandedModuleCode), hasModuleDefaultExport(expandedModuleCode)));
|
|
3276
|
-
return;
|
|
3277
|
-
}
|
|
3278
|
-
res.statusCode = 200;
|
|
3279
|
-
res.end(buildVersionedCoreSubpathAliasModule(resolvedSubpath, ver));
|
|
3280
|
-
return;
|
|
3281
|
-
}
|
|
3282
|
-
if (transformed?.code) {
|
|
3283
|
-
// Minimal pipeline: Vite already produces correct ESM.
|
|
3284
|
-
// ONLY rewrite specifier strings to device-fetchable URLs.
|
|
3285
|
-
// Do NOT run processCodeForDevice, rewriteImports, or any
|
|
3286
|
-
// other heavy transform — those mangle newlines, eat exports,
|
|
3287
|
-
// and cause cascading "does not provide an export" failures.
|
|
3288
|
-
const moduleCode = rewriteSpecifiersForDevice(transformed.code, getServerOrigin(server), Number(ver));
|
|
3289
|
-
res.statusCode = 200;
|
|
3290
|
-
res.end(moduleCode);
|
|
3291
|
-
return;
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
catch (e) {
|
|
3295
|
-
try {
|
|
3296
|
-
console.warn('[ns-core-bridge] deep subpath serve failed:', sub, e?.message);
|
|
3297
|
-
}
|
|
3298
|
-
catch { }
|
|
3883
|
+
const resolvedSubpath = normalizedSub || sub;
|
|
3884
|
+
modulePath = await resolveRuntimeCoreModulePath(resolvedSubpath, resolveModuleId);
|
|
3885
|
+
if (!modulePath) {
|
|
3886
|
+
modulePath = `/node_modules/@nativescript/core/${resolvedSubpath}`;
|
|
3299
3887
|
}
|
|
3300
3888
|
}
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3889
|
+
else {
|
|
3890
|
+
modulePath = (await resolveModuleId('@nativescript/core')) || '/node_modules/@nativescript/core/index.js';
|
|
3891
|
+
}
|
|
3892
|
+
const transformed = await sharedTransformRequest(modulePath);
|
|
3893
|
+
if (!transformed?.code) {
|
|
3894
|
+
res.statusCode = 500;
|
|
3895
|
+
res.setHeader('Content-Type', 'application/json');
|
|
3896
|
+
res.end(JSON.stringify({ error: 'core-transform-failed', modulePath, sub: sub || null }));
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
// Vite's transform output references module IDs with /@fs,
|
|
3900
|
+
// relative specifiers, or absolute project paths. Rewrite
|
|
3901
|
+
// those to URLs iOS can fetch over HTTP.
|
|
3902
|
+
let rewritten = rewriteSpecifiersForDevice(transformed.code, getServerOrigin(server), Number(ver));
|
|
3903
|
+
// Invariant D (CJS/ESM interop shape) — EXPORT-SIDE fix.
|
|
3904
|
+
//
|
|
3905
|
+
// `@nativescript/core/index.js` declares namespace
|
|
3906
|
+
// re-exports like:
|
|
3907
|
+
// export * as Utils from './utils';
|
|
3908
|
+
// The ES spec says these produce Module Namespace Objects
|
|
3909
|
+
// with [[Prototype]] = null. Consumers that reach them
|
|
3910
|
+
// via direct ESM import — `import { Utils } from
|
|
3911
|
+
// '@nativescript/core'` — get the raw null-proto value,
|
|
3912
|
+
// bypassing any CJS `require` shim we install. Most
|
|
3913
|
+
// consumers tolerate this, but CJS-style interop (most
|
|
3914
|
+
// notably zone.js's `patchMethod`) calls
|
|
3915
|
+
// `hasOwnProperty` on the target and crashes on
|
|
3916
|
+
// null-proto.
|
|
3917
|
+
//
|
|
3918
|
+
// We rewrite the re-export to a shape-wrapped const:
|
|
3919
|
+
// import * as __ns_re_Utils__ from './utils';
|
|
3920
|
+
// export const Utils = __NS_CJS_SHAPE__(__ns_re_Utils__);
|
|
3921
|
+
// so the EXPORT itself is a plain object — visible to
|
|
3922
|
+
// both ESM and CJS consumers consistently.
|
|
3923
|
+
//
|
|
3924
|
+
// We only pay the rewrite cost when the module actually
|
|
3925
|
+
// contains namespace re-exports (i.e., the main
|
|
3926
|
+
// `index.js`). Subpaths (`/utils`, `/http`, …) don't
|
|
3927
|
+
// re-export via `export * as`; they expose named
|
|
3928
|
+
// exports directly, so the rewrite is a no-op on them.
|
|
3929
|
+
if (hasNamespaceReExport(rewritten)) {
|
|
3930
|
+
rewritten = rewriteNamespaceReExportsForShape(rewritten);
|
|
3931
|
+
}
|
|
3932
|
+
// Prepend the build-time defines (__DEV__, __IOS__, __ANDROID__,
|
|
3933
|
+
// __APPLE__, …) that @nativescript/core source references directly.
|
|
3934
|
+
// Vite's `define` config substitutes these in user-code transforms but
|
|
3935
|
+
// skips node_modules by default; since core is now external and served
|
|
3936
|
+
// over HTTP from this endpoint, the served transformed code still has
|
|
3937
|
+
// bare identifiers like `if (__DEV__) …`. Without these consts, V8
|
|
3938
|
+
// hits `ReferenceError: __DEV__ is not defined` at module eval because
|
|
3939
|
+
// globalThis.__DEV__ is set by bundle.mjs's body AFTER all static
|
|
3940
|
+
// imports (including these core modules) have resolved.
|
|
3941
|
+
//
|
|
3942
|
+
// We inject LITERAL boolean values based on CLI flags + dev-server
|
|
3943
|
+
// mode rather than reading from globalThis, so the defines are
|
|
3944
|
+
// resolved even before bundle.mjs's body runs.
|
|
3945
|
+
const __cliFlags = getCliFlags() || {};
|
|
3946
|
+
const __platformIsAndroid = !!__cliFlags.android;
|
|
3947
|
+
const __platformIsVisionOS = !!__cliFlags.visionos;
|
|
3948
|
+
const __platformIsIOS = !__platformIsAndroid && !__platformIsVisionOS;
|
|
3949
|
+
const preamble = [
|
|
3950
|
+
`const __ANDROID__ = ${__platformIsAndroid ? 'true' : 'false'};`,
|
|
3951
|
+
`const __IOS__ = ${__platformIsIOS ? 'true' : 'false'};`,
|
|
3952
|
+
`const __VISIONOS__ = ${__platformIsVisionOS ? 'true' : 'false'};`,
|
|
3953
|
+
`const __APPLE__ = __IOS__ || __VISIONOS__;`,
|
|
3954
|
+
`const __DEV__ = ${server.config?.mode === 'development' ? 'true' : 'false'};`,
|
|
3955
|
+
`const __COMMONJS__ = false;`,
|
|
3956
|
+
`const __NS_WEBPACK__ = false;`,
|
|
3957
|
+
`const __NS_ENV_VERBOSE__ = globalThis.__NS_ENV_VERBOSE__ !== undefined ? !!globalThis.__NS_ENV_VERBOSE__ : false;`,
|
|
3958
|
+
`const __CSS_PARSER__ = 'css-tree';`,
|
|
3959
|
+
`const __UI_USE_XML_PARSER__ = true;`,
|
|
3960
|
+
`const __UI_USE_EXTERNAL_RENDERER__ = false;`,
|
|
3961
|
+
`const __TEST__ = false;`,
|
|
3962
|
+
].join('\n');
|
|
3963
|
+
// Boot-time instrumentation + module self-registration.
|
|
3964
|
+
// See HMR_CORE_REALM_DETERMINISTIC_PLAN.md:
|
|
3965
|
+
// - Invariant A (URL canonicalization): the same
|
|
3966
|
+
// logical module must always resolve to byte-
|
|
3967
|
+
// identical URLs across every emitter. The /ns/core
|
|
3968
|
+
// handler records the first URL seen for each
|
|
3969
|
+
// canonical sub (or '' for main) in
|
|
3970
|
+
// `globalThis.__NS_CORE_FIRST_URL__` and fails hard
|
|
3971
|
+
// on mismatch so drift in any emitter surfaces
|
|
3972
|
+
// immediately, before the realm splits.
|
|
3973
|
+
// - Invariant C (boot-order): CommonJS
|
|
3974
|
+
// `require('@nativescript/core/...')` calls from
|
|
3975
|
+
// vendor install() hooks must resolve to the SAME
|
|
3976
|
+
// ESM namespace that ran this side-effect preamble.
|
|
3977
|
+
// The registration below keys the namespace object
|
|
3978
|
+
// under BOTH the bare specifier and the canonical
|
|
3979
|
+
// subpath (and raw subpath for back-compat) so the
|
|
3980
|
+
// vendor shim's `createRequire` and the main-entry
|
|
3981
|
+
// `_nsReq` hit on any lookup form.
|
|
3982
|
+
const rawSub = normalizedSub || sub || '';
|
|
3983
|
+
const canonicalSub = normalizeCoreSubCanonical(rawSub);
|
|
3984
|
+
const registrationKeySet = new Set();
|
|
3985
|
+
registrationKeySet.add(canonicalSub ? `@nativescript/core/${canonicalSub}` : '@nativescript/core');
|
|
3986
|
+
registrationKeySet.add(canonicalSub);
|
|
3987
|
+
if (rawSub && rawSub !== canonicalSub) {
|
|
3988
|
+
registrationKeySet.add(`@nativescript/core/${rawSub}`);
|
|
3989
|
+
registrationKeySet.add(rawSub);
|
|
3332
3990
|
}
|
|
3991
|
+
const registrationKeys = Array.from(registrationKeySet).map((k) => JSON.stringify(k));
|
|
3992
|
+
const canonicalUrl = `${getServerOrigin(server)}` + (canonicalSub ? `/ns/core/${canonicalSub}` : '/ns/core');
|
|
3993
|
+
const instrumentationHeader = [
|
|
3994
|
+
`/* @nativescript/core bridge — canonical URL: ${canonicalUrl} */`,
|
|
3995
|
+
`try { if (typeof globalThis !== 'undefined') {`,
|
|
3996
|
+
` const __nsFirst = globalThis.__NS_CORE_FIRST_URL__ || (globalThis.__NS_CORE_FIRST_URL__ = Object.create(null));`,
|
|
3997
|
+
` const __nsSeen = globalThis.__NS_CORE_FETCHED_URLS__ || (globalThis.__NS_CORE_FETCHED_URLS__ = []);`,
|
|
3998
|
+
` const __nsKey = ${JSON.stringify(canonicalSub)};`,
|
|
3999
|
+
` const __nsUrl = ${JSON.stringify(canonicalUrl)};`,
|
|
4000
|
+
` __nsSeen.push(__nsUrl);`,
|
|
4001
|
+
` if (typeof __nsFirst[__nsKey] === 'string' && __nsFirst[__nsKey] !== __nsUrl) {`,
|
|
4002
|
+
` throw new Error('[ns-core] URL drift for sub=' + __nsKey + ': first=' + __nsFirst[__nsKey] + ' now=' + __nsUrl + ' (see HMR_CORE_REALM_DETERMINISTIC_PLAN.md Invariant A)');`,
|
|
4003
|
+
` }`,
|
|
4004
|
+
` if (!__nsFirst[__nsKey]) __nsFirst[__nsKey] = __nsUrl;`,
|
|
4005
|
+
` globalThis.__NS_CORE_EVAL_COUNT__ = (globalThis.__NS_CORE_EVAL_COUNT__ || 0) + 1;`,
|
|
4006
|
+
`} } catch (e) { try { console.warn('[ns-core] instrumentation failed:', (e && e.message) || e); } catch {} }`,
|
|
4007
|
+
].join('\n');
|
|
4008
|
+
// Invariant D (CJS/ESM interop shape) — REGISTRATION side.
|
|
4009
|
+
//
|
|
4010
|
+
// The actual shape installer runs earlier in the module
|
|
4011
|
+
// body (between preamble and selfImport; see
|
|
4012
|
+
// buildShapeInstallHeader). At this point we just read
|
|
4013
|
+
// globalThis.__NS_CJS_SHAPE__ and apply it to the self
|
|
4014
|
+
// namespace before registering under the CJS key space.
|
|
4015
|
+
//
|
|
4016
|
+
// Why shape self at registration: consumers that reach
|
|
4017
|
+
// `@nativescript/core` via `require()` (legacy vendors,
|
|
4018
|
+
// `globalThis.require` shim) look up the registry. They
|
|
4019
|
+
// expect a plain object (Object.prototype in chain) so
|
|
4020
|
+
// `.hasOwnProperty` / `.toString` work. Shaping once on
|
|
4021
|
+
// registration — the shape function is identity-preserving
|
|
4022
|
+
// via WeakMap — gives a stable, shared, CJS-compatible
|
|
4023
|
+
// view without copying on every require.
|
|
4024
|
+
//
|
|
4025
|
+
// See HMR_CORE_REALM_DETERMINISTIC_PLAN.md § "Invariant D"
|
|
4026
|
+
// for the full rationale.
|
|
4027
|
+
const registrationFooter = [
|
|
4028
|
+
`try { if (typeof globalThis !== 'undefined') {`,
|
|
4029
|
+
` const __nsReg = globalThis.__NS_CORE_MODULES__ || (globalThis.__NS_CORE_MODULES__ = Object.create(null));`,
|
|
4030
|
+
` const __nsShapeFn = typeof globalThis.__NS_CJS_SHAPE__ === 'function' ? globalThis.__NS_CJS_SHAPE__ : function (x) { return x; };`,
|
|
4031
|
+
` const __nsSelfRaw = (typeof __ns_core_self_ns__ !== 'undefined') ? __ns_core_self_ns__ : { default: undefined };`,
|
|
4032
|
+
` const __nsSelf = __nsShapeFn(__nsSelfRaw);`,
|
|
4033
|
+
...registrationKeys.map((k) => ` __nsReg[${k}] = __nsSelf;`),
|
|
4034
|
+
`} } catch (e) { try { console.warn('[ns-core] self-register failed:', (e && e.message) || e); } catch {} }`,
|
|
4035
|
+
].join('\n');
|
|
4036
|
+
// Bind `import * as __ns_core_self_ns__` to the module's
|
|
4037
|
+
// own export namespace so the footer can stash it into
|
|
4038
|
+
// the registry. Self-import is a no-op at eval time —
|
|
4039
|
+
// V8 resolves it to the module record we're already
|
|
4040
|
+
// evaluating and the final namespace is the same object
|
|
4041
|
+
// the registry receives. We use the CANONICAL URL here
|
|
4042
|
+
// so the self-import participates in Invariant A along
|
|
4043
|
+
// with every other @nativescript/core URL.
|
|
4044
|
+
const canonicalUrlForSelf = canonicalSub ? `/ns/core/${canonicalSub}` : '/ns/core';
|
|
4045
|
+
const selfImport = `import * as __ns_core_self_ns__ from ${JSON.stringify(canonicalUrlForSelf)};`;
|
|
4046
|
+
// Invariant D — SHAPE INSTALLER.
|
|
4047
|
+
//
|
|
4048
|
+
// Emits idempotent body-code that installs
|
|
4049
|
+
// globalThis.__NS_CJS_SHAPE__ BEFORE `rewritten`'s body
|
|
4050
|
+
// runs. This matters because the rewrite step above may
|
|
4051
|
+
// have produced statements like
|
|
4052
|
+
// `export const Utils = (typeof globalThis.__NS_CJS_SHAPE__ ...)(__ns_re_Utils__);`
|
|
4053
|
+
// that execute during module evaluation. Without the
|
|
4054
|
+
// installer running first, the ternary falls back to
|
|
4055
|
+
// identity — still safe, but the null-proto namespace
|
|
4056
|
+
// leaks through and consumers that expect a plain
|
|
4057
|
+
// object would still crash.
|
|
4058
|
+
//
|
|
4059
|
+
// Placement is important: BEFORE selfImport in the
|
|
4060
|
+
// concatenation. ESM imports are hoisted regardless of
|
|
4061
|
+
// textual position, but body code executes in source
|
|
4062
|
+
// order. Placing the installer first guarantees it
|
|
4063
|
+
// runs before any body statement in `rewritten`.
|
|
4064
|
+
//
|
|
4065
|
+
// Install is idempotent: `|| (globalThis.X = ...)` so
|
|
4066
|
+
// whichever /ns/core module evaluates first wins and
|
|
4067
|
+
// every subsequent module becomes a no-op.
|
|
4068
|
+
const shapeInstallHeader = buildShapeInstallHeader();
|
|
4069
|
+
// Invariant D — DEFAULT EXPORT BRIDGE.
|
|
4070
|
+
//
|
|
4071
|
+
// See `buildDefaultExportFooter` in ns-core-cjs-shape.ts
|
|
4072
|
+
// for the full rationale (consumer matrix, skip conditions,
|
|
4073
|
+
// why the default isn't shaped). The short version:
|
|
4074
|
+
// upstream rewrites turn `import { X } from '@nativescript/core'`
|
|
4075
|
+
// into a DEFAULT import, and the bridge has to provide one.
|
|
4076
|
+
const defaultExportFooter = buildDefaultExportFooter(rewritten);
|
|
4077
|
+
const moduleCode = [instrumentationHeader, preamble, shapeInstallHeader, selfImport, rewritten, defaultExportFooter, registrationFooter].join('\n');
|
|
4078
|
+
res.statusCode = 200;
|
|
4079
|
+
res.end(moduleCode);
|
|
4080
|
+
}
|
|
4081
|
+
catch (e) {
|
|
4082
|
+
try {
|
|
4083
|
+
console.warn('[ns-core-bridge] serve failed:', e?.message);
|
|
4084
|
+
}
|
|
4085
|
+
catch { }
|
|
4086
|
+
next();
|
|
4087
|
+
}
|
|
4088
|
+
});
|
|
4089
|
+
// 2.6a) Serve compiled entry runtime module: GET /ns/entry-rt[?v=<ver>]
|
|
4090
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4091
|
+
try {
|
|
4092
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4093
|
+
if (!(urlObj.pathname === '/ns/entry-rt'))
|
|
4094
|
+
return next();
|
|
4095
|
+
try {
|
|
4096
|
+
if (verbose) {
|
|
4097
|
+
const ra = req.socket?.remoteAddress;
|
|
4098
|
+
const rp = req.socket?.remotePort;
|
|
4099
|
+
console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
catch { }
|
|
4103
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4104
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4105
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4106
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4107
|
+
res.setHeader('Expires', '0');
|
|
4108
|
+
let content = '';
|
|
4109
|
+
try {
|
|
4110
|
+
const _req = createRequire(import.meta.url);
|
|
4111
|
+
const entryRtPath = _req.resolve('@nativescript/vite/hmr/entry-runtime.js');
|
|
4112
|
+
content = readFileSync(entryRtPath, 'utf-8');
|
|
4113
|
+
}
|
|
4114
|
+
catch (e) {
|
|
4115
|
+
// .js not found (source tree without build) — transform .ts on the fly
|
|
4116
|
+
try {
|
|
4117
|
+
const tsPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', 'entry-runtime.ts');
|
|
4118
|
+
if (existsSync(tsPath)) {
|
|
4119
|
+
const tsSource = readFileSync(tsPath, 'utf-8');
|
|
4120
|
+
const result = babelCore.transformSync(tsSource, {
|
|
4121
|
+
filename: tsPath,
|
|
4122
|
+
plugins: [[pluginTransformTypescript, { isTSX: false, allowDeclareFields: true }]],
|
|
4123
|
+
sourceType: 'module',
|
|
4124
|
+
});
|
|
4125
|
+
if (result?.code) {
|
|
4126
|
+
content = result.code;
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
catch (e2) {
|
|
4131
|
+
if (verbose)
|
|
4132
|
+
console.warn('[hmr-http] entry-runtime.ts transform failed', e2);
|
|
4133
|
+
}
|
|
4134
|
+
if (!content) {
|
|
4135
|
+
content = 'export default async function start(){ console.error("[/ns/entry-rt] not found"); }\n';
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
|
|
4139
|
+
res.statusCode = 200;
|
|
4140
|
+
res.end(content);
|
|
4141
|
+
}
|
|
4142
|
+
catch (e) {
|
|
4143
|
+
console.warn('[hmr-http] /ns/entry-rt error', e);
|
|
4144
|
+
next();
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
// 2.6b) HTTP-only app entry endpoint: GET /ns/entry[/<ver>]
|
|
4148
|
+
// Thin wrapper that imports the compiled entry runtime and starts it with parameters.
|
|
4149
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4150
|
+
try {
|
|
4151
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4152
|
+
if (!(urlObj.pathname === '/ns/entry' || /^\/ns\/entry\/[\d]+$/.test(urlObj.pathname)))
|
|
4153
|
+
return next();
|
|
4154
|
+
try {
|
|
4155
|
+
if (verbose) {
|
|
4156
|
+
const ra = req.socket?.remoteAddress;
|
|
4157
|
+
const rp = req.socket?.remotePort;
|
|
4158
|
+
console.log('[hmr-http] GET /ns/entry from', ra + (rp ? ':' + rp : ''));
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
catch { }
|
|
4162
|
+
const verSeg = urlObj.pathname.replace(/^\/ns\/entry\/?/, '');
|
|
4163
|
+
// Resolve app main entry to an absolute path-like key used by /ns/m
|
|
4164
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4165
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4166
|
+
res.setHeader('Expires', '0');
|
|
4167
|
+
const ver = /^[0-9]+$/.test(verSeg) ? verSeg : String(graphVersion || 0);
|
|
4168
|
+
const origin = getServerOrigin(server) || `${urlObj.protocol}//${urlObj.host}`;
|
|
4169
|
+
// Resolve app main entry to an absolute path-like key used by /ns/m
|
|
4170
|
+
let mainEntry = '/';
|
|
4171
|
+
try {
|
|
4172
|
+
const pkg = getPackageJson();
|
|
4173
|
+
const main = pkg?.main || DEFAULT_MAIN_ENTRY;
|
|
4174
|
+
const abs = getProjectFilePath(main).replace(/\\/g, '/');
|
|
4175
|
+
// Normalize to '/app/...'
|
|
4176
|
+
const marker = `/${APP_ROOT_DIR}/`;
|
|
4177
|
+
const idx = abs.indexOf(marker);
|
|
4178
|
+
mainEntry = idx >= 0 ? abs.substring(idx) : DEFAULT_MAIN_ENTRY_VIRTUAL;
|
|
4179
|
+
}
|
|
4180
|
+
catch { }
|
|
4181
|
+
// Build a tiny wrapper that imports the compiled entry runtime from the dev server
|
|
4182
|
+
let code = REQUIRE_GUARD_SNIPPET +
|
|
4183
|
+
`// [ns-entry][v${ver}] wrapper (script-safe) bytes will follow\n` +
|
|
4184
|
+
`(async function(){\n` +
|
|
4185
|
+
` let origin = ${JSON.stringify(origin)}; const main = ${JSON.stringify(mainEntry)}; const __ns_graph_ver = ${JSON.stringify(ver)};\n` +
|
|
4186
|
+
` try { const __b = (globalThis && globalThis.__NS_ENTRY_BASE__) ? String(globalThis.__NS_ENTRY_BASE__) : ''; if (__b) { try { const __o = new URL(__b).origin; if (__o) origin = __o; } catch {} } } catch {}\n` +
|
|
4187
|
+
` const __VERBOSE__ = (typeof __NS_ENV_VERBOSE__ !== 'undefined' && __NS_ENV_VERBOSE__) || (globalThis && globalThis.process && globalThis.process.env && globalThis.process.env.verbose) || (globalThis && globalThis.__NS_ENV_VERBOSE__) || ${JSON.stringify(!!verbose)};\n` +
|
|
4188
|
+
` if (__VERBOSE__) console.info('[ns-entry][wrapper] start', { origin, main, ver: __ns_graph_ver });\n` +
|
|
4189
|
+
` async function __ns_import_entry_rt(u){\n` +
|
|
4190
|
+
` // Prefer fetch+eval script transformation to avoid module import limitations on device\n` +
|
|
4191
|
+
` try { const r = await fetch(u); const t = await r.text(); if (__VERBOSE__) console.info('[ns-entry][wrapper] entry-rt fetched bytes', (t&&t.length)||0);\n` +
|
|
4192
|
+
` // Transform 'export default function' or 'export default async function' into global assignment\n` +
|
|
4193
|
+
` let s = t.replace(/export\\s+default\\s+async\\s+function\\s+([A-Za-z0-9_$]+)?/,'globalThis.__NS_START_ENTRY__=async function $1')\n` +
|
|
4194
|
+
` .replace(/export\\s+default\\s+function\\s+([A-Za-z0-9_$]+)?/,'globalThis.__NS_START_ENTRY__=function $1');\n` +
|
|
4195
|
+
` // Fallback: if function-form replacements didn't run, handle expression default export too\n` +
|
|
4196
|
+
` if (String(s).indexOf('__NS_START_ENTRY__') === -1) { s = 'globalThis.__NS_START_ENTRY__=' + s.replace(/export\\s+default\\s*/,''); }\n` +
|
|
4197
|
+
` try { (0,eval)(s); } catch (ee) { console.error('[ns-entry][wrapper] eval entry-rt failed', ee && (ee.message||ee)); throw ee; }\n` +
|
|
4198
|
+
` const fn = globalThis.__NS_START_ENTRY__; if (!fn) { throw new Error('entry-rt missing __NS_START_ENTRY__'); }\n` +
|
|
4199
|
+
` return { default: fn };\n` +
|
|
4200
|
+
` } catch(e) { console.error('[ns-entry][wrapper] entry-rt fetch/eval failed', e && (e.message||e)); throw e; }\n` +
|
|
4201
|
+
` }\n` +
|
|
4202
|
+
` const __entryRtUrl = '/ns/entry-rt?v=' + String(__ns_graph_ver);\n` +
|
|
4203
|
+
` let __mod; try { __mod = await __ns_import_entry_rt(__entryRtUrl); if (__VERBOSE__) console.info('[ns-entry][wrapper] entry-rt ready'); } catch (e) { console.error('[ns-entry][wrapper] failed to prepare entry-rt', e && (e.message||e)); throw e; }\n` +
|
|
4204
|
+
` const startEntry = (__mod && (__mod.default || __mod));\n` +
|
|
4205
|
+
` try { await startEntry({ origin, main, ver: __ns_graph_ver, verbose: !!__VERBOSE__ }); if (__VERBOSE__) console.info('[ns-entry][wrapper] startEntry() resolved'); } catch (e) { console.error('[ns-entry][wrapper] startEntry() failed', e && (e.message||e)); throw e; }\n` +
|
|
4206
|
+
`})();\n`;
|
|
4207
|
+
code = code + `\n//# sourceURL=${origin}/ns/entry`;
|
|
3333
4208
|
res.statusCode = 200;
|
|
3334
4209
|
res.end(code);
|
|
3335
4210
|
}
|
|
@@ -3337,33 +4212,1336 @@ export const piniaSymbol = p.piniaSymbol;
|
|
|
3337
4212
|
next();
|
|
3338
4213
|
}
|
|
3339
4214
|
});
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
4215
|
+
// 2.6) Transactional HMR endpoint: GET /ns/txn/<ver>
|
|
4216
|
+
// Returns a single ESM that sequentially imports all changed modules for the given graphVersion.
|
|
4217
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4218
|
+
try {
|
|
4219
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4220
|
+
const p = urlObj.pathname || '';
|
|
4221
|
+
if (!p.startsWith('/ns/txn'))
|
|
4222
|
+
return next();
|
|
4223
|
+
let verStr = p.replace('/ns/txn', '').replace(/^\//, '');
|
|
4224
|
+
const ver = Number(verStr || urlObj.searchParams.get('v') || 0);
|
|
4225
|
+
let ids = txnBatches.get(ver) || [];
|
|
4226
|
+
if (!ids.length) {
|
|
4227
|
+
// Attempt to rebuild from any changed modules at this version if present in graph history is unavailable.
|
|
4228
|
+
// Fallback heuristic: use all modules with latest hash change equal to this version (we don't store per-module version, so use any changedIds from query 'ids' if provided)
|
|
4229
|
+
try {
|
|
4230
|
+
const q = (urlObj.searchParams.get('ids') || '')
|
|
4231
|
+
.split(',')
|
|
4232
|
+
.map((s) => s.trim())
|
|
4233
|
+
.filter(Boolean);
|
|
4234
|
+
if (q.length)
|
|
4235
|
+
ids = computeTxnOrderForChanged(q);
|
|
4236
|
+
}
|
|
4237
|
+
catch { }
|
|
4238
|
+
}
|
|
4239
|
+
const origin = getServerOrigin(server) || `${urlObj.protocol}//${urlObj.host}`;
|
|
4240
|
+
const lines = [];
|
|
4241
|
+
lines.push(`// [txn] version=${ver} count=${ids.length}`);
|
|
4242
|
+
if (!ids.length) {
|
|
4243
|
+
lines.push(`export default true;`);
|
|
4244
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4245
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4246
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4247
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4248
|
+
res.setHeader('Expires', '0');
|
|
4249
|
+
res.statusCode = 200;
|
|
4250
|
+
res.end(lines.join('\n'));
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
for (const id of ids) {
|
|
4254
|
+
const isVue = /\.vue$/i.test(id);
|
|
4255
|
+
const safe = id.startsWith('/') ? id : '/' + id;
|
|
4256
|
+
const abs = isVue ? `/ns/asm/${ver}?path=${encodeURIComponent(safe)}` : `/ns/m${safe}`;
|
|
4257
|
+
lines.push(`await import(${JSON.stringify(abs)});`);
|
|
4258
|
+
}
|
|
4259
|
+
lines.push(`export default true;`);
|
|
4260
|
+
const code = lines.join('\n');
|
|
4261
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4262
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4263
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4264
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4265
|
+
res.setHeader('Expires', '0');
|
|
4266
|
+
res.statusCode = 200;
|
|
4267
|
+
res.end(code);
|
|
4268
|
+
return;
|
|
4269
|
+
}
|
|
4270
|
+
catch (e) {
|
|
4271
|
+
/* fallthrough */
|
|
4272
|
+
}
|
|
4273
|
+
return next();
|
|
3348
4274
|
});
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
4275
|
+
// 3) ESM endpoint for SFC modules: GET /ns/sfc?path=/src/Comp.vue[?vue&type=*] OR /ns/sfc/src/Comp.vue[?vue&type=*]
|
|
4276
|
+
// Also accept alias /ns/sfc
|
|
4277
|
+
// Preserves variant queries (?vue&type=script|template|style) and adds a diagnostic signature comment.
|
|
4278
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4279
|
+
try {
|
|
4280
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4281
|
+
const p = urlObj.pathname;
|
|
4282
|
+
// Only match exactly "/ns/sfc" or paths under it.
|
|
4283
|
+
const isNs = p === '/ns/sfc' || p.startsWith('/ns/sfc/');
|
|
4284
|
+
if (!isNs)
|
|
4285
|
+
return next();
|
|
4286
|
+
if (p.startsWith('/ns/asm') || p.startsWith('/ns/sfc-meta'))
|
|
4287
|
+
return next();
|
|
4288
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4289
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4290
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4291
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4292
|
+
res.setHeader('Expires', '0');
|
|
4293
|
+
const base = '/ns/sfc';
|
|
4294
|
+
// Determine request spec, preserving variant query when present and handling optional version in path
|
|
4295
|
+
let pathParam = urlObj.searchParams.get('path') || ''; // may include its own query
|
|
4296
|
+
const rawRemainder = urlObj.pathname.slice(base.length) || '';
|
|
4297
|
+
let verFromPath = null;
|
|
4298
|
+
let pathStyle = rawRemainder;
|
|
4299
|
+
if (rawRemainder && rawRemainder.startsWith('/')) {
|
|
4300
|
+
const parts = rawRemainder.split('/'); // ["", maybe "<ver>", ...]
|
|
4301
|
+
if (parts.length > 2 && /^[0-9]+$/.test(parts[1] || '')) {
|
|
4302
|
+
verFromPath = parts[1];
|
|
4303
|
+
pathStyle = '/' + parts.slice(2).join('/');
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
if (pathStyle && pathStyle !== '/' && !pathParam) {
|
|
4307
|
+
if (!pathStyle.startsWith('/'))
|
|
4308
|
+
pathStyle = '/' + pathStyle;
|
|
4309
|
+
// Include endpoint query for variant-style requests (e.g. /ns/sfc/Comp.vue?vue&type=template)
|
|
4310
|
+
pathParam = pathStyle + (urlObj.search || '');
|
|
4311
|
+
}
|
|
4312
|
+
let fullSpec = pathParam || '';
|
|
4313
|
+
if (!fullSpec) {
|
|
4314
|
+
res.statusCode = 200;
|
|
4315
|
+
res.end('export {}\n');
|
|
4316
|
+
return;
|
|
4317
|
+
}
|
|
4318
|
+
if (fullSpec.startsWith('@/'))
|
|
4319
|
+
fullSpec = APP_VIRTUAL_WITH_SLASH + fullSpec.slice(2);
|
|
4320
|
+
if (!fullSpec.startsWith('/'))
|
|
4321
|
+
fullSpec = '/' + fullSpec;
|
|
4322
|
+
const isVariant = /[?&]vue&type=/.test(fullSpec);
|
|
4323
|
+
const variantTypeMatch = /[?&]type=([^&]+)/.exec(fullSpec);
|
|
4324
|
+
const variantType = variantTypeMatch?.[1] || null;
|
|
4325
|
+
const isStyleVariant = /[?&]type=style\b/.test(fullSpec);
|
|
4326
|
+
// Determine candidate for transformRequest
|
|
4327
|
+
// For full SFCs we prefer a clean base path + '?vue'; if that fails, try base without query as fallback.
|
|
4328
|
+
let candidate = fullSpec;
|
|
4329
|
+
let transformed = null;
|
|
4330
|
+
if (!isVariant) {
|
|
4331
|
+
const basePath = fullSpec.replace(/[?#].*$/, '');
|
|
4332
|
+
const candidates = [basePath + (basePath.includes('?') ? '&' : '?') + 'vue', basePath];
|
|
4333
|
+
for (const c of candidates) {
|
|
4334
|
+
try {
|
|
4335
|
+
const r = await server.transformRequest(c);
|
|
4336
|
+
if (r?.code) {
|
|
4337
|
+
transformed = r;
|
|
4338
|
+
candidate = c;
|
|
4339
|
+
break;
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
catch { }
|
|
4343
|
+
}
|
|
4344
|
+
if (!transformed?.code) {
|
|
4345
|
+
if (verbose) {
|
|
4346
|
+
try {
|
|
4347
|
+
console.warn(`[sfc][serve] transform miss for`, fullSpec);
|
|
4348
|
+
}
|
|
4349
|
+
catch { }
|
|
4350
|
+
}
|
|
4351
|
+
// Emit an erroring module to surface the failure at import site with helpful hints
|
|
4352
|
+
try {
|
|
4353
|
+
const tried = candidates.slice(0, 8);
|
|
4354
|
+
const out = `// [sfc] transform miss kind=full path=${fullSpec.replace(/\n/g, '')} tried=${tried.length}\n` + `throw new Error(${JSON.stringify('[ns/sfc] transform failed for full SFC: ' + fullSpec + ' (tried ' + tried.length + ')')});\nexport {}\n`;
|
|
4355
|
+
res.statusCode = 404;
|
|
4356
|
+
res.end(out);
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
catch {
|
|
4360
|
+
res.statusCode = 404;
|
|
4361
|
+
res.end('export {}\n');
|
|
4362
|
+
return;
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
else {
|
|
4367
|
+
try {
|
|
4368
|
+
transformed = await server.transformRequest(candidate);
|
|
4369
|
+
}
|
|
4370
|
+
catch { }
|
|
4371
|
+
if (!transformed?.code) {
|
|
4372
|
+
try {
|
|
4373
|
+
const out = `// [sfc] transform miss kind=variant path=${fullSpec.replace(/\n/g, '')}\n` + `throw new Error(${JSON.stringify('[ns/sfc] transform failed for variant: ' + fullSpec)});\nexport {}\n`;
|
|
4374
|
+
res.statusCode = 404;
|
|
4375
|
+
res.end(out);
|
|
4376
|
+
return;
|
|
4377
|
+
}
|
|
4378
|
+
catch {
|
|
4379
|
+
res.statusCode = 404;
|
|
4380
|
+
res.end('export {}\n');
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
}
|
|
4385
|
+
// For style variants, return an empty module immediately
|
|
4386
|
+
if (isStyleVariant) {
|
|
4387
|
+
const sig = `// [sfc] kind=variant:style path=${fullSpec.replace(/\n/g, '')} len=0 default=false\n`;
|
|
4388
|
+
res.statusCode = 200;
|
|
4389
|
+
res.end(`${sig}export {}\n`);
|
|
4390
|
+
return;
|
|
4391
|
+
}
|
|
4392
|
+
let code = transformed.code;
|
|
4393
|
+
// Prepend guard to capture any URL-based require attempts
|
|
4394
|
+
code = REQUIRE_GUARD_SNIPPET + code;
|
|
4395
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
4396
|
+
// IMPORTANT: Do not run cleanCode() on template variant; it can strip required pieces.
|
|
4397
|
+
// We'll handle script/full SFC below, and treat template minimally right away.
|
|
4398
|
+
// Full SFCs delegate to deterministic assembler module; variants (script/template) still go through processing
|
|
4399
|
+
if (!isVariant) {
|
|
4400
|
+
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
4401
|
+
const origin = getServerOrigin(server);
|
|
4402
|
+
const ver = verFromPath || '0';
|
|
4403
|
+
const asmPath = `/ns/asm/${ver}?path=${encodeURIComponent(importerPath)}`;
|
|
4404
|
+
const delegated = `// [sfc] kind=full (delegated to assembler) path=${importerPath}\nexport * from ${JSON.stringify(asmPath)};\nexport { default } from ${JSON.stringify(asmPath)};\n`;
|
|
4405
|
+
res.statusCode = 200;
|
|
4406
|
+
res.end(delegated);
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
else {
|
|
4410
|
+
// Variants
|
|
4411
|
+
if (variantType === 'template') {
|
|
4412
|
+
const preferSelfCompile = !!process.env.NS_HMR_SELF_COMPILE_TEMPLATE;
|
|
4413
|
+
// Compile the template ourselves to guarantee no Vite HMR code and stable output
|
|
4414
|
+
if (preferSelfCompile)
|
|
4415
|
+
try {
|
|
4416
|
+
const projectRootT = server.config?.root || process.cwd();
|
|
4417
|
+
const basePath = fullSpec.replace(/[?#].*$/, '');
|
|
4418
|
+
const abs = path.join(projectRootT, basePath.replace(/^\//, ''));
|
|
4419
|
+
let sfcSrc = '';
|
|
4420
|
+
try {
|
|
4421
|
+
sfcSrc = readFileSync(abs, 'utf-8');
|
|
4422
|
+
}
|
|
4423
|
+
catch { }
|
|
4424
|
+
if (sfcSrc) {
|
|
4425
|
+
const { descriptor } = parse(sfcSrc, { filename: abs });
|
|
4426
|
+
const id = createHash('md5').update(abs).digest('hex').slice(0, 8);
|
|
4427
|
+
let bindingMetadata = undefined;
|
|
4428
|
+
try {
|
|
4429
|
+
const s = compileScript(descriptor, {
|
|
4430
|
+
id,
|
|
4431
|
+
inlineTemplate: false,
|
|
4432
|
+
reactivityTransform: false,
|
|
4433
|
+
});
|
|
4434
|
+
bindingMetadata = s?.bindings;
|
|
4435
|
+
}
|
|
4436
|
+
catch { }
|
|
4437
|
+
const tpl = descriptor.template?.content || '';
|
|
4438
|
+
const ct = compileTemplate({
|
|
4439
|
+
source: tpl,
|
|
4440
|
+
id,
|
|
4441
|
+
filename: abs,
|
|
4442
|
+
isProd: false,
|
|
4443
|
+
ssr: false,
|
|
4444
|
+
compilerOptions: {
|
|
4445
|
+
bindingMetadata,
|
|
4446
|
+
isCustomElement: (tag) => NS_NATIVE_TAGS.has(tag),
|
|
4447
|
+
},
|
|
4448
|
+
});
|
|
4449
|
+
let out = (ct && (ct.code || '')) || '';
|
|
4450
|
+
// Map Vue helper imports to runtime bridge
|
|
4451
|
+
try {
|
|
4452
|
+
out = out.replace(/from\s+["'](?:nativescript-vue|vue)[^"']*["']/g, 'from "/ns/rt"');
|
|
4453
|
+
}
|
|
4454
|
+
catch { }
|
|
4455
|
+
// No import.meta.hot present when compiling ourselves, but keep minimal sanitizer just in case
|
|
4456
|
+
out = processTemplateVariantMinimal(out);
|
|
4457
|
+
code = out;
|
|
4458
|
+
}
|
|
4459
|
+
else {
|
|
4460
|
+
code = 'export {}\n';
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
catch (eTplSelf) {
|
|
4464
|
+
if (verbose) {
|
|
4465
|
+
try {
|
|
4466
|
+
console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
|
|
4467
|
+
}
|
|
4468
|
+
catch { }
|
|
4469
|
+
}
|
|
4470
|
+
code = transformed.code || 'export {}\n';
|
|
4471
|
+
code = processTemplateVariantMinimal(code);
|
|
4472
|
+
}
|
|
4473
|
+
else {
|
|
4474
|
+
// Prefer using Vite's template transform and apply minimal sanitization; avoids compiler mismatches and warnings
|
|
4475
|
+
code = transformed.code || 'export {}\n';
|
|
4476
|
+
code = processTemplateVariantMinimal(code);
|
|
4477
|
+
}
|
|
4478
|
+
// fall through to shared post-processing (versioning, signature, etc.)
|
|
4479
|
+
}
|
|
4480
|
+
// Script variants still need vendor mappings and general device processing (no SFC assembly)
|
|
4481
|
+
// IMPORTANT: Use a Babel AST transform to remove imports of the template variant and
|
|
4482
|
+
// neutralize their usage without brittle regex.
|
|
4483
|
+
try {
|
|
4484
|
+
const ast = babelParse(code, {
|
|
4485
|
+
sourceType: 'module',
|
|
4486
|
+
plugins: ['typescript'],
|
|
4487
|
+
});
|
|
4488
|
+
const templateBindings = new Set();
|
|
4489
|
+
const navToLocals = [];
|
|
4490
|
+
const navBackLocals = [];
|
|
4491
|
+
babelTraverse(ast, {
|
|
4492
|
+
ImportDeclaration(path) {
|
|
4493
|
+
const spec = path.node.source.value || '';
|
|
4494
|
+
// Remove template variant imports and collect their local identifiers for neutralization
|
|
4495
|
+
if (typeof spec === 'string' && /\.vue\?[^\n]*type=template/.test(spec)) {
|
|
4496
|
+
const ids = [];
|
|
4497
|
+
for (const s of path.node.specifiers) {
|
|
4498
|
+
if (t.isImportSpecifier(s)) {
|
|
4499
|
+
const imported = t.isIdentifier(s.imported) ? s.imported.name : undefined;
|
|
4500
|
+
const local = t.isIdentifier(s.local) ? s.local.name : undefined;
|
|
4501
|
+
if ((imported === 'render' || imported === undefined) && local)
|
|
4502
|
+
ids.push(local);
|
|
4503
|
+
}
|
|
4504
|
+
else if (t.isImportDefaultSpecifier(s) || t.isImportNamespaceSpecifier(s)) {
|
|
4505
|
+
if (t.isIdentifier(s.local))
|
|
4506
|
+
ids.push(s.local.name);
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
ids.forEach((n) => templateBindings.add(n));
|
|
4510
|
+
path.remove();
|
|
4511
|
+
return;
|
|
4512
|
+
}
|
|
4513
|
+
// Rewrite $navigateTo/$navigateBack imports from nativescript-vue (or prebundle) to use globals
|
|
4514
|
+
const isNsVue = typeof spec === 'string' && (/nativescript-vue/.test(spec) || /vendor\.mjs$/.test(spec) || /\/node_modules\/\.vite\/deps\/nativescript-vue\.js/.test(spec));
|
|
4515
|
+
if (isNsVue) {
|
|
4516
|
+
const remain = [];
|
|
4517
|
+
for (const s of path.node.specifiers) {
|
|
4518
|
+
if (t.isImportSpecifier(s)) {
|
|
4519
|
+
const imported = t.isIdentifier(s.imported) ? s.imported.name : undefined;
|
|
4520
|
+
const local = t.isIdentifier(s.local) ? s.local.name : undefined;
|
|
4521
|
+
if (local && (imported === '$navigateTo' || imported === 'navigateTo')) {
|
|
4522
|
+
navToLocals.push(local);
|
|
4523
|
+
continue;
|
|
4524
|
+
}
|
|
4525
|
+
if (local && (imported === '$navigateBack' || imported === 'navigateBack')) {
|
|
4526
|
+
navBackLocals.push(local);
|
|
4527
|
+
continue;
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
remain.push(s);
|
|
4531
|
+
}
|
|
4532
|
+
if (remain.length) {
|
|
4533
|
+
path.node.specifiers = remain;
|
|
4534
|
+
}
|
|
4535
|
+
else {
|
|
4536
|
+
path.remove();
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
},
|
|
4540
|
+
});
|
|
4541
|
+
if (templateBindings.size) {
|
|
4542
|
+
babelTraverse(ast, {
|
|
4543
|
+
Identifier(path) {
|
|
4544
|
+
if (templateBindings.has(path.node.name)) {
|
|
4545
|
+
path.replaceWith(t.identifier('undefined'));
|
|
4546
|
+
}
|
|
4547
|
+
},
|
|
4548
|
+
AssignmentExpression(path) {
|
|
4549
|
+
// Guard component.render = <alias> to avoid TDZ when alias is undefined
|
|
4550
|
+
if (t.isMemberExpression(path.node.left) &&
|
|
4551
|
+
t.isIdentifier(path.node.left.property, {
|
|
4552
|
+
name: 'render',
|
|
4553
|
+
})) {
|
|
4554
|
+
const e = t.identifier('__e');
|
|
4555
|
+
const guarded = t.tryStatement(t.blockStatement([t.variableDeclaration('const', [t.variableDeclarator(e, path.node.right)]), t.ifStatement(t.logicalExpression('&&', t.binaryExpression('!==', t.unaryExpression('typeof', path.node.left.object, true), t.stringLiteral('undefined')), t.binaryExpression('!==', t.unaryExpression('typeof', e, true), t.stringLiteral('undefined'))), t.blockStatement([t.expressionStatement(t.assignmentExpression('=', path.node.left, e))]))]), t.catchClause(t.identifier('_e'), t.blockStatement([])));
|
|
4556
|
+
path.replaceWithMultiple([guarded]);
|
|
4557
|
+
}
|
|
4558
|
+
},
|
|
4559
|
+
});
|
|
4560
|
+
}
|
|
4561
|
+
let outCode = genCode(ast).code;
|
|
4562
|
+
if (navToLocals.length || navBackLocals.length) {
|
|
4563
|
+
const shimLines = [];
|
|
4564
|
+
for (const n of navToLocals)
|
|
4565
|
+
shimLines.push(`import __ns_rt_nav_to_mod from "/ns/rt";\nconst ${n} = (...args) => __ns_rt_nav_to_mod.$navigateTo(...args);`);
|
|
4566
|
+
for (const n of navBackLocals)
|
|
4567
|
+
shimLines.push(`import __ns_rt_nav_back_mod from "/ns/rt";\nconst ${n} = (...args) => __ns_rt_nav_back_mod.$navigateBack(...args);`);
|
|
4568
|
+
outCode = shimLines.join('\n') + '\n' + outCode;
|
|
4569
|
+
}
|
|
4570
|
+
code = outCode;
|
|
4571
|
+
}
|
|
4572
|
+
catch { }
|
|
4573
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(fullSpec), fullSpec);
|
|
4574
|
+
// Transform static .vue imports into static imports from the assembler (no TLA) via AST
|
|
4575
|
+
try {
|
|
4576
|
+
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
4577
|
+
const origin = getServerOrigin(server);
|
|
4578
|
+
const ver = verFromPath || '0';
|
|
4579
|
+
const ast2 = babelParse(code, {
|
|
4580
|
+
sourceType: 'module',
|
|
4581
|
+
plugins: ['typescript'],
|
|
4582
|
+
});
|
|
4583
|
+
babelTraverse(ast2, {
|
|
4584
|
+
ImportDeclaration(p) {
|
|
4585
|
+
const src = p.node.source.value || '';
|
|
4586
|
+
if (typeof src !== 'string')
|
|
4587
|
+
return;
|
|
4588
|
+
if (/^https?:\/\//.test(src))
|
|
4589
|
+
return; // leave absolute URLs
|
|
4590
|
+
if (/\.vue(?:$|\?)/.test(src)) {
|
|
4591
|
+
let spec = src;
|
|
4592
|
+
// Resolve to absolute project path
|
|
4593
|
+
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
4594
|
+
spec = path.posix.normalize(path.posix.join(path.posix.dirname(importerPath), spec));
|
|
4595
|
+
if (!spec.startsWith('/'))
|
|
4596
|
+
spec = '/' + spec;
|
|
4597
|
+
}
|
|
4598
|
+
else if (!spec.startsWith('/')) {
|
|
4599
|
+
// Handle '@/'
|
|
4600
|
+
if (spec.startsWith('@@/'))
|
|
4601
|
+
spec = '/' + spec.slice(2);
|
|
4602
|
+
if (spec.startsWith('@/'))
|
|
4603
|
+
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
4604
|
+
}
|
|
4605
|
+
// Strip query for plain .vue (keep variant imports intact)
|
|
4606
|
+
if (!/\bvue&type=/.test(src)) {
|
|
4607
|
+
spec = spec.replace(/[?#].*$/, '');
|
|
4608
|
+
const asmUrl = `/ns/asm/${ver}?path=${encodeURIComponent(spec)}&mode=inline`;
|
|
4609
|
+
p.node.source = t.stringLiteral(asmUrl);
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
},
|
|
4613
|
+
});
|
|
4614
|
+
code = genCode(ast2).code;
|
|
4615
|
+
}
|
|
4616
|
+
catch { }
|
|
4617
|
+
// After rewrites, strip any TypeScript syntax from the script variant to avoid device-side parse errors
|
|
4618
|
+
try {
|
|
4619
|
+
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
4620
|
+
const tsRes = await babelCore.transformAsync(code, {
|
|
4621
|
+
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
4622
|
+
sourceType: 'module',
|
|
4623
|
+
// Help Babel infer TS parsing even if the virtual filename isn't .ts
|
|
4624
|
+
filename: importerPath.endsWith('.vue') ? importerPath.replace(/\.vue$/, '.ts') : importerPath + '.ts',
|
|
4625
|
+
comments: true,
|
|
4626
|
+
configFile: false,
|
|
4627
|
+
babelrc: false,
|
|
4628
|
+
});
|
|
4629
|
+
if (tsRes?.code) {
|
|
4630
|
+
code = tsRes.code;
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
catch (eTsVar) {
|
|
4634
|
+
if (verbose) {
|
|
4635
|
+
try {
|
|
4636
|
+
console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
|
|
4637
|
+
}
|
|
4638
|
+
catch { }
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
const importerPath = fullSpec.replace(/[?#].*$/, '');
|
|
4643
|
+
// Only run cleanCode for non-template cases (script/full). Template code must remain intact.
|
|
4644
|
+
if (!isVariant || variantType !== 'template') {
|
|
4645
|
+
code = cleanCode(code);
|
|
4646
|
+
}
|
|
4647
|
+
code = rewriteImports(code, importerPath, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
4648
|
+
code = ensureVariableDynamicImportHelper(code);
|
|
4649
|
+
try {
|
|
4650
|
+
// For variant requests under /ns/sfc, prefer the version from the path segment when present
|
|
4651
|
+
// so that any internal '/ns/rt', '/ns/core', or '/ns/sfc' imports are aligned with the same version.
|
|
4652
|
+
const verNum = Number(verFromPath || '0');
|
|
4653
|
+
if (Number.isFinite(verNum) && verNum > 0) {
|
|
4654
|
+
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
4655
|
+
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), verNum);
|
|
4656
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
4657
|
+
}
|
|
4658
|
+
else {
|
|
4659
|
+
code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
|
|
4660
|
+
code = ACTIVE_STRATEGY.ensureVersionedImports(code, getServerOrigin(server), graphVersion);
|
|
4661
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), graphVersion);
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
catch { }
|
|
4665
|
+
// Final guard for SFC variant output as well
|
|
4666
|
+
try {
|
|
4667
|
+
code = ensureDestructureCoreImports(code);
|
|
4668
|
+
}
|
|
4669
|
+
catch { }
|
|
4670
|
+
// CRITICAL: As a last step for script/template variants, re-run AST normalization and strip
|
|
4671
|
+
// any sentinel destructures that could cause duplicate locals, then re-apply core versioning.
|
|
4672
|
+
try {
|
|
4673
|
+
code = astNormalizeModuleImportsAndHelpers(code);
|
|
4674
|
+
}
|
|
4675
|
+
catch { }
|
|
4676
|
+
try {
|
|
4677
|
+
// Remove any rt->core sentinel destructures that slipped in late
|
|
4678
|
+
code = stripRtCoreSentinel(code);
|
|
4679
|
+
}
|
|
4680
|
+
catch { }
|
|
4681
|
+
try {
|
|
4682
|
+
const verNum = Number(verFromPath || '0');
|
|
4683
|
+
if (Number.isFinite(verNum) && verNum > 0) {
|
|
4684
|
+
code = ensureVersionedRtImports(code, getServerOrigin(server), verNum);
|
|
4685
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
|
|
4686
|
+
}
|
|
4687
|
+
else {
|
|
4688
|
+
code = ensureVersionedRtImports(code, getServerOrigin(server), graphVersion);
|
|
4689
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), graphVersion);
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
catch { }
|
|
4693
|
+
// Last-chance sanitizer for dangling Vite CJS import helper usages that may surface after late transforms
|
|
4694
|
+
try {
|
|
4695
|
+
code = stripDanglingViteCjsImports(code);
|
|
4696
|
+
}
|
|
4697
|
+
catch { }
|
|
4698
|
+
const hasDefault = /\bexport\s+default\b/.test(code);
|
|
4699
|
+
const kind = isVariant ? `variant:${variantType || 'unknown'}` : 'full';
|
|
4700
|
+
const sig = `// [sfc] kind=${kind} path=${importerPath} len=${code.length} default=${hasDefault} wrapped=${false}\n`;
|
|
4701
|
+
if (verbose) {
|
|
4702
|
+
try {
|
|
4703
|
+
console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
|
|
4704
|
+
}
|
|
4705
|
+
catch { }
|
|
4706
|
+
}
|
|
4707
|
+
// Ensure script variants always provide a default export if they declare a component
|
|
4708
|
+
if (!hasDefault) {
|
|
4709
|
+
// Prefer an explicit identifier if present
|
|
4710
|
+
const m = code.match(/\b(?:const|let|var)\s+(__ns_sfc__|_sfc_main)\b/);
|
|
4711
|
+
if (m && m[1]) {
|
|
4712
|
+
code += `\nexport default ${m[1]};`;
|
|
4713
|
+
}
|
|
4714
|
+
else if (/\b_defineComponent\s*\(|\bdefineComponent\s*\(/.test(code)) {
|
|
4715
|
+
// Fallback: export whichever is defined at runtime without throwing on missing identifiers
|
|
4716
|
+
code += `\nexport default (typeof __ns_sfc__ !== "undefined" ? __ns_sfc__ : (typeof _sfc_main !== "undefined" ? _sfc_main : undefined));`;
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
res.statusCode = 200;
|
|
4720
|
+
res.end(sig + code);
|
|
4721
|
+
}
|
|
4722
|
+
catch (e) {
|
|
4723
|
+
res.statusCode = 500;
|
|
4724
|
+
res.end('export {}\n');
|
|
4725
|
+
}
|
|
4726
|
+
});
|
|
4727
|
+
// 4) JSON metadata endpoint for SFCs: GET /ns/sfc-meta?path=/src/Comp.vue OR /ns/sfc-meta/<ver>?path=/src/Comp.vue
|
|
4728
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4729
|
+
try {
|
|
4730
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4731
|
+
if (!urlObj.pathname.startsWith('/ns/sfc-meta'))
|
|
4732
|
+
return next();
|
|
4733
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4734
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
4735
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4736
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4737
|
+
res.setHeader('Expires', '0');
|
|
4738
|
+
// Accept optional version segment similar to /ns/sfc
|
|
4739
|
+
{
|
|
4740
|
+
const metaBase = '/ns/sfc-meta';
|
|
4741
|
+
if (urlObj.pathname.startsWith(metaBase + '/')) {
|
|
4742
|
+
const rawRemainder = urlObj.pathname.slice(metaBase.length);
|
|
4743
|
+
const parts = rawRemainder.split('/');
|
|
4744
|
+
if (parts.length > 2 && /^[0-9]+$/.test(parts[1] || '')) {
|
|
4745
|
+
// consume version but we don't need it server-side
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
let spec = urlObj.searchParams.get('path') || '';
|
|
4750
|
+
if (!spec) {
|
|
4751
|
+
res.statusCode = 400;
|
|
4752
|
+
res.end(JSON.stringify({ error: 'missing path' }));
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
if (spec.startsWith('@/'))
|
|
4756
|
+
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
4757
|
+
if (!spec.startsWith('/'))
|
|
4758
|
+
spec = '/' + spec;
|
|
4759
|
+
const base = spec.replace(/[?#].*$/, '');
|
|
4760
|
+
// Transform variants to inspect exports
|
|
4761
|
+
const [scriptR, templateR] = await Promise.all([server.transformRequest(base + '?vue&type=script'), server.transformRequest(base + '?vue&type=template')]);
|
|
4762
|
+
const scriptCode = scriptR?.code || '';
|
|
4763
|
+
const templateCode = templateR?.code || '';
|
|
4764
|
+
const scriptMeta = extractExportMetadata(scriptCode);
|
|
4765
|
+
// Robust render detection: Vue compiler may emit several shapes:
|
|
4766
|
+
// 1) export function render(_ctx, _cache) { ... }
|
|
4767
|
+
// 2) function render(_ctx,_cache) { ... } (later exported)
|
|
4768
|
+
// 3) export const render = (_ctx,_cache) => { ... }
|
|
4769
|
+
// 4) const render = (...) => { ... } (later exported)
|
|
4770
|
+
// 5) export { render } or export { render as render }
|
|
4771
|
+
// 6) Object property forms (rare in template output) render: (...) => {}
|
|
4772
|
+
const hasRender = /export\s+function\s+render\s*\(/.test(templateCode) || /(?:^|\n)\s*function\s+render\s*\(/.test(templateCode) || /export\s+(?:const|let|var)\s+render\s*=/.test(templateCode) || /(?:^|\n)\s*(?:const|let|var)\s+render\s*=/.test(templateCode) || /\brender\s*[:=]\s*/.test(templateCode) || /export\s*\{\s*render\s*(?:as\s*render)?\s*\}/.test(templateCode);
|
|
4773
|
+
if (hasRender && verbose) {
|
|
4774
|
+
try {
|
|
4775
|
+
console.log('[sfc-meta] detected render for', base);
|
|
4776
|
+
}
|
|
4777
|
+
catch { }
|
|
4778
|
+
}
|
|
4779
|
+
else if (!hasRender && verbose) {
|
|
4780
|
+
try {
|
|
4781
|
+
console.warn('[sfc-meta] render NOT detected for', base);
|
|
4782
|
+
}
|
|
4783
|
+
catch { }
|
|
4784
|
+
}
|
|
4785
|
+
const hash = createHash('md5').update(base).digest('hex').slice(0, 8);
|
|
4786
|
+
const payload = {
|
|
4787
|
+
path: base,
|
|
4788
|
+
hasScript: !!scriptCode,
|
|
4789
|
+
hasTemplate: !!templateCode,
|
|
4790
|
+
hasStyle: false,
|
|
4791
|
+
scriptExports: scriptMeta.named,
|
|
4792
|
+
scriptHasDefault: scriptMeta.hasDefault,
|
|
4793
|
+
templateHasRender: hasRender,
|
|
4794
|
+
hmrId: hash,
|
|
4795
|
+
};
|
|
4796
|
+
res.statusCode = 200;
|
|
4797
|
+
res.end(JSON.stringify(payload));
|
|
4798
|
+
}
|
|
4799
|
+
catch (e) {
|
|
4800
|
+
res.statusCode = 500;
|
|
4801
|
+
res.end(JSON.stringify({ error: e?.message || String(e) }));
|
|
4802
|
+
}
|
|
4803
|
+
});
|
|
4804
|
+
// 5) Deterministic SFC assembler: GET /ns/asm?path=/src/Comp.vue
|
|
4805
|
+
// Place BEFORE any broader /ns/sfc* handlers that might accidentally match and delegate.
|
|
4806
|
+
server.middlewares.use(async (req, res, next) => {
|
|
4807
|
+
try {
|
|
4808
|
+
const urlObj = new URL(req.url || '', 'http://localhost');
|
|
4809
|
+
if (!urlObj.pathname.startsWith('/ns/asm'))
|
|
4810
|
+
return next();
|
|
4811
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4812
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
4813
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
4814
|
+
res.setHeader('Pragma', 'no-cache');
|
|
4815
|
+
res.setHeader('Expires', '0');
|
|
4816
|
+
// Optional version segment as first path component after /ns/asm
|
|
4817
|
+
const asmBase = '/ns/asm';
|
|
4818
|
+
const asmRemainder = urlObj.pathname.slice(asmBase.length) || '';
|
|
4819
|
+
let verFromPath = null;
|
|
4820
|
+
if (asmRemainder && asmRemainder.startsWith('/')) {
|
|
4821
|
+
const p = asmRemainder.split('/');
|
|
4822
|
+
if (p.length > 1 && /^[0-9]+$/.test(p[1] || '')) {
|
|
4823
|
+
verFromPath = p[1];
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
let spec = urlObj.searchParams.get('path') || '';
|
|
4827
|
+
const diag = urlObj.searchParams.get('diag') === '1';
|
|
4828
|
+
if (!spec) {
|
|
4829
|
+
res.statusCode = 400;
|
|
4830
|
+
res.end('export {}\n');
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
if (spec.startsWith('@/'))
|
|
4834
|
+
spec = APP_VIRTUAL_WITH_SLASH + spec.slice(2);
|
|
4835
|
+
if (!spec.startsWith('/'))
|
|
4836
|
+
spec = '/' + spec;
|
|
4837
|
+
const base = spec.replace(/[?#].*$/, '');
|
|
4838
|
+
if (diag) {
|
|
4839
|
+
const code = `// [sfc-asm] ${base} (diag)\n` + `// vue shim for diag-only instantiation\n` + `var _createElementVNode = globalThis.createElementVNode || globalThis._createElementVNode;\n` + `const __ns_sfc__ = { name: ${JSON.stringify(base.split('/').pop() || 'DiagComp')}, render(){ return _createElementVNode ? _createElementVNode('StackLayout') : (globalThis.createElementVNode ? globalThis.createElementVNode('StackLayout') : {}); } };\nexport default __ns_sfc__;\n`;
|
|
4840
|
+
res.statusCode = 200;
|
|
4841
|
+
res.end(code);
|
|
4842
|
+
return;
|
|
4843
|
+
}
|
|
4844
|
+
const projectRoot = server.config?.root || process.cwd();
|
|
4845
|
+
// Ensure variant transforms exist so imports resolve (avoid Promise.all short-circuit on single failure)
|
|
4846
|
+
const safeTransform = async (cand) => {
|
|
4847
|
+
try {
|
|
4848
|
+
return await server.transformRequest(cand);
|
|
4849
|
+
}
|
|
4850
|
+
catch {
|
|
4851
|
+
return null;
|
|
4852
|
+
}
|
|
4853
|
+
};
|
|
4854
|
+
const scriptR = await safeTransform(base + '?vue&type=script');
|
|
4855
|
+
const templateR = await safeTransform(base + '?vue&type=template');
|
|
4856
|
+
const fullR = await safeTransform(base + '?vue');
|
|
4857
|
+
const hasScript = !!scriptR?.code;
|
|
4858
|
+
const hasTemplate = !!templateR?.code;
|
|
4859
|
+
const origin = getServerOrigin(server);
|
|
4860
|
+
const ver = String(verFromPath || graphVersion || Date.now());
|
|
4861
|
+
const scriptUrl = `${origin}/ns/sfc/${ver}${base}?vue&type=script`;
|
|
4862
|
+
const templateCode = templateR?.code || '';
|
|
4863
|
+
// INLINE-FIRST assembler: compile SFC source into a self-contained ESM module (enhanced diagnostics)
|
|
4864
|
+
try {
|
|
4865
|
+
const root = server.config?.root || process.cwd();
|
|
4866
|
+
const abs = path.join(root, base.replace(/^\//, ''));
|
|
4867
|
+
let sfcSrc = '';
|
|
4868
|
+
try {
|
|
4869
|
+
sfcSrc = readFileSync(abs, 'utf-8');
|
|
4870
|
+
}
|
|
4871
|
+
catch { }
|
|
4872
|
+
if (sfcSrc) {
|
|
4873
|
+
const { descriptor } = parse(sfcSrc, { filename: abs });
|
|
4874
|
+
const id = createHash('md5').update(abs).digest('hex').slice(0, 8);
|
|
4875
|
+
// 1) Compile script (prefer inlineTemplate for a complete module)
|
|
4876
|
+
let compiledScript = '';
|
|
4877
|
+
let bindingMetadata = undefined;
|
|
4878
|
+
let triedInlineTemplate = false;
|
|
4879
|
+
let hadScriptDefaultPre = false;
|
|
4880
|
+
let usedInlineScript = false;
|
|
4881
|
+
try {
|
|
4882
|
+
// First try inlineTemplate for a holistic, self-contained module with render + hoists
|
|
4883
|
+
// Use a strict NativeScript native element detector for inlineTemplate that does NOT treat generic PascalCase as native.
|
|
4884
|
+
// This ensures imported components like PageWrapper remain true components and get referenced via bindings.
|
|
4885
|
+
const isNSNative = (tag) => NS_NATIVE_TAGS.has(tag);
|
|
4886
|
+
const sInline = compileScript(descriptor, {
|
|
4887
|
+
id,
|
|
4888
|
+
inlineTemplate: true,
|
|
4889
|
+
reactivityTransform: false,
|
|
4890
|
+
// Pass only strict NS native element predicate; avoid broad PascalCase heuristic here.
|
|
4891
|
+
templateOptions: {
|
|
4892
|
+
compilerOptions: { isCustomElement: isNSNative },
|
|
4893
|
+
},
|
|
4894
|
+
});
|
|
4895
|
+
triedInlineTemplate = true;
|
|
4896
|
+
if (/export\s+default/.test(sInline?.content || '')) {
|
|
4897
|
+
compiledScript = sInline.content;
|
|
4898
|
+
bindingMetadata = sInline?.bindings;
|
|
4899
|
+
hadScriptDefaultPre = true;
|
|
4900
|
+
usedInlineScript = true;
|
|
4901
|
+
}
|
|
4902
|
+
else {
|
|
4903
|
+
// Fallback to standard script (no inline) and attempt separate template compile
|
|
4904
|
+
const s = compileScript(descriptor, {
|
|
4905
|
+
id,
|
|
4906
|
+
inlineTemplate: false,
|
|
4907
|
+
reactivityTransform: false,
|
|
4908
|
+
});
|
|
4909
|
+
compiledScript = s?.content || '';
|
|
4910
|
+
bindingMetadata = s?.bindings;
|
|
4911
|
+
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
4912
|
+
usedInlineScript = false;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
catch (eScript) {
|
|
4916
|
+
if (verbose) {
|
|
4917
|
+
try {
|
|
4918
|
+
console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
|
|
4919
|
+
}
|
|
4920
|
+
catch { }
|
|
4921
|
+
}
|
|
4922
|
+
// Retry without inlineTemplate
|
|
4923
|
+
try {
|
|
4924
|
+
const s = compileScript(descriptor, {
|
|
4925
|
+
id,
|
|
4926
|
+
inlineTemplate: false,
|
|
4927
|
+
reactivityTransform: false,
|
|
4928
|
+
});
|
|
4929
|
+
compiledScript = s?.content || '';
|
|
4930
|
+
bindingMetadata = s?.bindings;
|
|
4931
|
+
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
4932
|
+
usedInlineScript = false;
|
|
4933
|
+
}
|
|
4934
|
+
catch (eNoInline) {
|
|
4935
|
+
if (verbose) {
|
|
4936
|
+
try {
|
|
4937
|
+
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
4938
|
+
}
|
|
4939
|
+
catch { }
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
// Final fallback: if script compile yielded nothing, use the variant-transformed script
|
|
4944
|
+
if (!compiledScript && scriptR?.code) {
|
|
4945
|
+
try {
|
|
4946
|
+
compiledScript = scriptR.code;
|
|
4947
|
+
hadScriptDefaultPre = /export\s+default/.test(compiledScript);
|
|
4948
|
+
}
|
|
4949
|
+
catch { }
|
|
4950
|
+
}
|
|
4951
|
+
// If inlineTemplate produced a default export AND visibly contains a render, allow early-return.
|
|
4952
|
+
// Visible render forms we accept:
|
|
4953
|
+
// - export function render(...) { ... }
|
|
4954
|
+
// - setup(...) { ... return (_ctx, _cache) => { ... } }
|
|
4955
|
+
const hasInlineRender = /(^|\n)\s*export\s+function\s+render\s*\(/.test(compiledScript || '') || /\breturn\s*\(\s*_ctx\s*,\s*_cache\s*\)\s*=>\s*\{/.test(compiledScript || '');
|
|
4956
|
+
// Always use canonical assembler path; avoid inlineTemplate early-return which can miss render attachment
|
|
4957
|
+
// If we reached here, we are going to assemble canonically. Ensure the script we use does NOT include inlineTemplate render.
|
|
4958
|
+
if (usedInlineScript) {
|
|
4959
|
+
try {
|
|
4960
|
+
const sNoInline = compileScript(descriptor, {
|
|
4961
|
+
id,
|
|
4962
|
+
inlineTemplate: false,
|
|
4963
|
+
reactivityTransform: false,
|
|
4964
|
+
});
|
|
4965
|
+
compiledScript = sNoInline?.content || compiledScript;
|
|
4966
|
+
bindingMetadata = sNoInline?.bindings || bindingMetadata;
|
|
4967
|
+
}
|
|
4968
|
+
catch (eNoInline) {
|
|
4969
|
+
if (verbose) {
|
|
4970
|
+
try {
|
|
4971
|
+
console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
|
|
4972
|
+
}
|
|
4973
|
+
catch { }
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
// 2) Compile template
|
|
4978
|
+
let compiledTplCode = '';
|
|
4979
|
+
let templateErr = null;
|
|
4980
|
+
try {
|
|
4981
|
+
const tplSrc = descriptor.template?.content || '';
|
|
4982
|
+
if (tplSrc) {
|
|
4983
|
+
const ct = compileTemplate({
|
|
4984
|
+
source: tplSrc,
|
|
4985
|
+
id,
|
|
4986
|
+
filename: abs,
|
|
4987
|
+
isProd: false,
|
|
4988
|
+
ssr: false,
|
|
4989
|
+
compilerOptions: {
|
|
4990
|
+
bindingMetadata,
|
|
4991
|
+
isCustomElement: (tag) => NS_NATIVE_TAGS.has(tag),
|
|
4992
|
+
},
|
|
4993
|
+
});
|
|
4994
|
+
compiledTplCode = (ct && (ct.code || '')) || '';
|
|
4995
|
+
if (ct?.errors?.length && verbose) {
|
|
4996
|
+
try {
|
|
4997
|
+
console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
|
|
4998
|
+
}
|
|
4999
|
+
catch { }
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
catch (eTpl) {
|
|
5004
|
+
templateErr = eTpl;
|
|
5005
|
+
if (verbose) {
|
|
5006
|
+
try {
|
|
5007
|
+
console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
|
|
5008
|
+
}
|
|
5009
|
+
catch { }
|
|
5010
|
+
}
|
|
5011
|
+
// Fallback: use the variant-transformed template code if available
|
|
5012
|
+
try {
|
|
5013
|
+
if (templateR?.code)
|
|
5014
|
+
compiledTplCode = templateR.code;
|
|
5015
|
+
}
|
|
5016
|
+
catch { }
|
|
5017
|
+
}
|
|
5018
|
+
// If still no template code, synthesize a minimal render stub so the module is valid
|
|
5019
|
+
if (!compiledTplCode) {
|
|
5020
|
+
try {
|
|
5021
|
+
compiledTplCode = "export function render(){ const _ = (globalThis.createElementVNode||globalThis._createElementVNode); return _? _('StackLayout') : {}; }\n";
|
|
5022
|
+
}
|
|
5023
|
+
catch { }
|
|
5024
|
+
}
|
|
5025
|
+
// 3) Sanitize script and rewrite .vue imports to inline assembler
|
|
5026
|
+
let scriptBody = compiledScript || '';
|
|
5027
|
+
if (scriptBody) {
|
|
5028
|
+
// Do NOT strip Vue/nativescript-vue imports; retarget them to the runtime bridge so helpers (e.g., onMounted) are bound.
|
|
5029
|
+
// Preserve the import clause and only rewrite the source to '/ns/rt'.
|
|
5030
|
+
scriptBody = scriptBody.replace(/(^|\n)\s*import\s+([^;\n]+)\s+from\s+["'](?:vue|nativescript-vue|~\/vendor\.mjs)(?:\/[^"]*)?["'];?/g, (_m, pfx, clause) => `${pfx}import ${clause} from "/ns/rt";`);
|
|
5031
|
+
try {
|
|
5032
|
+
const importerDir = path.posix.dirname(base);
|
|
5033
|
+
scriptBody = scriptBody.replace(/(^|\n)\s*import\s+([^;\n]+)\s+from\s+["']([^"'\n]+\.vue)(?:\?[^"'\n]*)?["'];?/g, (_m, pfx, clause, spec) => {
|
|
5034
|
+
let absImp = spec;
|
|
5035
|
+
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
5036
|
+
absImp = path.posix.normalize(path.posix.join(importerDir, spec));
|
|
5037
|
+
if (!absImp.startsWith('/'))
|
|
5038
|
+
absImp = '/' + absImp;
|
|
5039
|
+
}
|
|
5040
|
+
else if (!spec.startsWith('/')) {
|
|
5041
|
+
if (absImp.startsWith('@/'))
|
|
5042
|
+
absImp = APP_VIRTUAL_WITH_SLASH + absImp.slice(2);
|
|
5043
|
+
}
|
|
5044
|
+
const asmUrl = `/ns/asm/${ver}?path=${encodeURIComponent(absImp)}&mode=inline`;
|
|
5045
|
+
return `${pfx}import ${clause} from ${JSON.stringify(asmUrl)};`;
|
|
5046
|
+
});
|
|
5047
|
+
}
|
|
5048
|
+
catch { }
|
|
5049
|
+
}
|
|
5050
|
+
// 4) Extract render from compiled template and prepare a full inline template block
|
|
5051
|
+
let helperBindings = '';
|
|
5052
|
+
let renderDecl = '';
|
|
5053
|
+
let inlineBlock = undefined;
|
|
5054
|
+
let renderOk = false;
|
|
5055
|
+
if (compiledTplCode) {
|
|
5056
|
+
try {
|
|
5057
|
+
// Build a full inline template block to preserve hoists where possible
|
|
5058
|
+
inlineBlock = buildInlineTemplateBlock(compiledTplCode) || undefined;
|
|
5059
|
+
if (!inlineBlock) {
|
|
5060
|
+
const extracted = extractTemplateRender(compiledTplCode);
|
|
5061
|
+
helperBindings = extracted.helperBindings;
|
|
5062
|
+
renderDecl = extracted.renderDecl;
|
|
5063
|
+
inlineBlock = extracted.inlineBlock;
|
|
5064
|
+
renderOk = extracted.ok;
|
|
5065
|
+
}
|
|
5066
|
+
else {
|
|
5067
|
+
renderOk = true;
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
catch (eExtract) {
|
|
5071
|
+
if (verbose) {
|
|
5072
|
+
try {
|
|
5073
|
+
console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
|
|
5074
|
+
}
|
|
5075
|
+
catch { }
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
// Final guard: if no inline render extracted, attempt to import template variant or synthesize a no-op render
|
|
5080
|
+
if (!renderOk && !inlineBlock) {
|
|
5081
|
+
try {
|
|
5082
|
+
const templateUrl = `${origin}/ns/sfc/${ver}${base}?vue&type=template`;
|
|
5083
|
+
const importLine = `import * as __template from ${JSON.stringify(templateUrl)};`;
|
|
5084
|
+
// Attach only if scriptTransformed produces __ns_sfc__ later
|
|
5085
|
+
helperBindings += `\n${importLine}`;
|
|
5086
|
+
renderDecl += `\nfunction __ns_getRender(){\n try {\n if (__template && __template.render) return __template.render;\n } catch (_e) {}\n try {\n const _ = globalThis.createElementVNode || globalThis._createElementVNode;\n return _ ? function(){ return _('StackLayout'); } : function(){ return {}; };\n } catch (_e) { return function(){ return {}; }; }\n}\n`;
|
|
5087
|
+
renderOk = true;
|
|
5088
|
+
}
|
|
5089
|
+
catch { }
|
|
5090
|
+
}
|
|
5091
|
+
// 5) Convert default export to const __ns_sfc__
|
|
5092
|
+
let scriptTransformed = scriptBody;
|
|
5093
|
+
if (scriptTransformed) {
|
|
5094
|
+
scriptTransformed = scriptTransformed.replace(/(^|\n)\s*export\s+default\s+/g, '$1const __ns_sfc__ = ').replace(/(^|\n)\s*export\s*\{[^}]*\}\s*;?\s*/g, '\n/* removed named exports for inline asm */\n');
|
|
5095
|
+
// Normalize any prior declaration of __ns_sfc__ to a plain assignment to avoid redeclare
|
|
5096
|
+
// Accept a semicolon before the declaration too
|
|
5097
|
+
scriptTransformed = scriptTransformed.replace(/(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\s*=\s*/g, '$1__ns_sfc__ = ');
|
|
5098
|
+
// Ensure a single declaration appears once before first assignment
|
|
5099
|
+
if (!/(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\b/.test(scriptTransformed)) {
|
|
5100
|
+
scriptTransformed = `let __ns_sfc__;\n` + scriptTransformed;
|
|
5101
|
+
}
|
|
5102
|
+
// Remove stray leading braces (artifact defense)
|
|
5103
|
+
scriptTransformed = scriptTransformed.replace(/^\s*\}+(?=\s*[^}])/, (m) => `/* [asm-fix] removed ${m.length} stray leading braces */\n`);
|
|
5104
|
+
}
|
|
5105
|
+
else {
|
|
5106
|
+
try {
|
|
5107
|
+
const compName = (base.split('/').pop() || 'Component').replace(/\.vue$/i, '') || 'Component';
|
|
5108
|
+
scriptTransformed = `import { defineComponent as _defineComponent } from "/ns/rt";\nlet __ns_sfc__;\n__ns_sfc__ = /*@__PURE__*/_defineComponent({ __name: ${JSON.stringify(compName)} });`;
|
|
5109
|
+
}
|
|
5110
|
+
catch {
|
|
5111
|
+
scriptTransformed = `import { defineComponent as _defineComponent } from "/ns/rt";\nlet __ns_sfc__;\n__ns_sfc__ = /*@__PURE__*/_defineComponent({});`;
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
// 6) Emit final inline module with diagnostics comment
|
|
5115
|
+
const parts = [];
|
|
5116
|
+
parts.push(`// [sfc-asm] ${base} (inline-compiled)`);
|
|
5117
|
+
// Deterministic path: always use extracted helperBindings + renderDecl + scriptTransformed (ignore inlineBlock)
|
|
5118
|
+
// Emit hoisted template bindings first
|
|
5119
|
+
if (helperBindings)
|
|
5120
|
+
parts.push(helperBindings);
|
|
5121
|
+
// IMPORTANT: place script (with its imports) BEFORE renderDecl so imports never appear inside the render function.
|
|
5122
|
+
parts.push(scriptTransformed);
|
|
5123
|
+
parts.push(renderDecl);
|
|
5124
|
+
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){}`);
|
|
5125
|
+
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; }`);
|
|
5126
|
+
parts.push(`export default __ns_sfc__`);
|
|
5127
|
+
let inlineCode = parts.filter(Boolean).join('\n');
|
|
5128
|
+
inlineCode = processCodeForDevice(inlineCode, false, true);
|
|
5129
|
+
try {
|
|
5130
|
+
inlineCode = ensureVersionedCoreImports(inlineCode, getServerOrigin(server), Number(ver));
|
|
5131
|
+
}
|
|
5132
|
+
catch { }
|
|
5133
|
+
try {
|
|
5134
|
+
inlineCode = ensureDestructureCoreImports(inlineCode);
|
|
5135
|
+
}
|
|
5136
|
+
catch { }
|
|
5137
|
+
// Replace legacy mutation pipeline with canonical assembler for reliability
|
|
5138
|
+
{
|
|
5139
|
+
// First: strip TypeScript robustly using Babel transform
|
|
5140
|
+
try {
|
|
5141
|
+
const tsRes = await babelCore.transformAsync(scriptTransformed, {
|
|
5142
|
+
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5143
|
+
ast: false,
|
|
5144
|
+
sourceType: 'module',
|
|
5145
|
+
configFile: false,
|
|
5146
|
+
babelrc: false,
|
|
5147
|
+
});
|
|
5148
|
+
if (tsRes?.code)
|
|
5149
|
+
scriptTransformed = tsRes.code;
|
|
5150
|
+
}
|
|
5151
|
+
catch (eTs) {
|
|
5152
|
+
if (verbose) {
|
|
5153
|
+
try {
|
|
5154
|
+
console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
|
|
5155
|
+
}
|
|
5156
|
+
catch { }
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
// Hoist imports + strip residual TS via AST
|
|
5160
|
+
let importLines = [];
|
|
5161
|
+
try {
|
|
5162
|
+
const astRes = astExtractImportsAndStripTypes(scriptTransformed);
|
|
5163
|
+
importLines = astRes.imports;
|
|
5164
|
+
scriptTransformed = astRes.body;
|
|
5165
|
+
if (astRes.diagnostics.length && verbose) {
|
|
5166
|
+
try {
|
|
5167
|
+
console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
|
|
5168
|
+
}
|
|
5169
|
+
catch { }
|
|
5170
|
+
}
|
|
5171
|
+
}
|
|
5172
|
+
catch (eAst) {
|
|
5173
|
+
if (verbose) {
|
|
5174
|
+
try {
|
|
5175
|
+
console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
|
|
5176
|
+
}
|
|
5177
|
+
catch { }
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
// Ensure renderDecl ends with closing brace ONLY for function declaration forms
|
|
5181
|
+
// Avoid appending to const-assignment forms like: const __ns_render = (function(){ ... })();
|
|
5182
|
+
if (renderDecl && /(^|\n)\s*(?:export\s+)?function\s+__ns_render\s*\(/.test(renderDecl) && !/\}\s*$/.test(renderDecl)) {
|
|
5183
|
+
renderDecl = renderDecl.trimEnd() + '\n}';
|
|
5184
|
+
}
|
|
5185
|
+
const outParts = [];
|
|
5186
|
+
outParts.push(`// [sfc-asm] ${base} (inline-compiled)`);
|
|
5187
|
+
outParts.push('// [sfc-asm][canonical]');
|
|
5188
|
+
if (importLines.length)
|
|
5189
|
+
outParts.push(Array.from(new Set(importLines)).join('\n'));
|
|
5190
|
+
// Place component script first so the component object exists before we attach render.
|
|
5191
|
+
outParts.push(scriptTransformed);
|
|
5192
|
+
// Prefer full template block to guarantee presence of all hoisted constants.
|
|
5193
|
+
if (inlineBlock) {
|
|
5194
|
+
outParts.push(inlineBlock);
|
|
5195
|
+
}
|
|
5196
|
+
else {
|
|
5197
|
+
if (helperBindings)
|
|
5198
|
+
outParts.push(helperBindings);
|
|
5199
|
+
if (renderDecl && renderDecl.trim())
|
|
5200
|
+
outParts.push(renderDecl);
|
|
5201
|
+
}
|
|
5202
|
+
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){}`);
|
|
5203
|
+
// Export named render as a function that resolves lazily
|
|
5204
|
+
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; }');
|
|
5205
|
+
outParts.push('export default __ns_sfc__');
|
|
5206
|
+
let inlineCode2 = outParts.filter(Boolean).join('\n');
|
|
5207
|
+
inlineCode2 = processCodeForDevice(inlineCode2, false, true);
|
|
5208
|
+
try {
|
|
5209
|
+
inlineCode2 = ensureVersionedCoreImports(inlineCode2, getServerOrigin(server), Number(ver));
|
|
5210
|
+
}
|
|
5211
|
+
catch { }
|
|
5212
|
+
try {
|
|
5213
|
+
inlineCode2 = ensureDestructureCoreImports(inlineCode2);
|
|
5214
|
+
}
|
|
5215
|
+
catch { }
|
|
5216
|
+
// Hoist any late imports that accidentally landed after render or script assembly
|
|
5217
|
+
try {
|
|
5218
|
+
const lateImportRe = /^(?!\/\/).*^\s*import\s+[^;]+;?$/gm;
|
|
5219
|
+
const allImports = [];
|
|
5220
|
+
inlineCode2 = inlineCode2.replace(lateImportRe, (imp) => {
|
|
5221
|
+
allImports.push(imp);
|
|
5222
|
+
return '';
|
|
5223
|
+
});
|
|
5224
|
+
if (allImports.length) {
|
|
5225
|
+
// Place after helperBindings sentinel
|
|
5226
|
+
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\]\n)/, `$1${Array.from(new Set(allImports)).join('\n')}\n/* [asm-fix] re-hoisted ${allImports.length} imports */\n`);
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
catch { }
|
|
5230
|
+
// After hoisting, re-run AST normalization and duplicate-binding verification.
|
|
5231
|
+
// This guards against freshly hoisted imports reintroducing identifiers that collide
|
|
5232
|
+
// with earlier destructures (e.g., __ns_core_ns_1), which would otherwise surface at device runtime.
|
|
5233
|
+
try {
|
|
5234
|
+
inlineCode2 = astNormalizeModuleImportsAndHelpers(inlineCode2);
|
|
5235
|
+
}
|
|
5236
|
+
catch { }
|
|
5237
|
+
try {
|
|
5238
|
+
inlineCode2 = astVerifyAndAnnotateDuplicates(inlineCode2);
|
|
5239
|
+
if (/^\s*\/\/ \[ast-verify\]\[duplicate-bindings\]/m.test(inlineCode2)) {
|
|
5240
|
+
const diagnosticLine = (inlineCode2.match(/^\s*\/\/ \[ast-verify\]\[duplicate-bindings\][^\n]*/m) || [])[0] || '// [ast-verify][duplicate-bindings]';
|
|
5241
|
+
const brief = diagnosticLine.replace(/^[^:]*:?\s?/, '');
|
|
5242
|
+
const escaped = brief.replace(/["\\]/g, '\\$&');
|
|
5243
|
+
const thrower = `throw new Error("[nsv-hmr] Duplicate top-level bindings detected post-hoist: ${escaped}");`;
|
|
5244
|
+
inlineCode2 = `${thrower}\n` + inlineCode2;
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
catch { }
|
|
5248
|
+
// Minimal cleanup only (avoid destructive type stripping breaking object literal property defaults)
|
|
5249
|
+
try {
|
|
5250
|
+
// Heal cases where a TS type strip earlier removed initializer: plain 'default' inside props objects
|
|
5251
|
+
// becomes 'default: undefined'. We only match when followed by ',' or '}' or newline to avoid 'export default'.
|
|
5252
|
+
inlineCode2 = inlineCode2.replace(/\bdefault\b\s*(?=\}|,|\n)/g, 'default: undefined');
|
|
5253
|
+
// Remove obvious leftover angle generic markers
|
|
5254
|
+
inlineCode2 = inlineCode2.replace(/<unknown>/g, '');
|
|
5255
|
+
// Fix accidental '}=> {' sequences
|
|
5256
|
+
inlineCode2 = inlineCode2.replace(/}\s*=>\s*\{/g, '');
|
|
5257
|
+
// No-op: removed prior broken normalization. Handlers are fixed in the dedicated passes below.
|
|
5258
|
+
}
|
|
5259
|
+
catch { }
|
|
5260
|
+
// Removed redundant render closure heal that could inject an extra '}' before component script.
|
|
5261
|
+
// Rewrite any remaining imports (e.g., relative app paths) to HTTP ESM endpoints
|
|
5262
|
+
try {
|
|
5263
|
+
inlineCode2 = rewriteImports(inlineCode2, base, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
5264
|
+
}
|
|
5265
|
+
catch { }
|
|
5266
|
+
// Final TS strip on the whole assembled module (safety net)
|
|
5267
|
+
try {
|
|
5268
|
+
const tsFinal = await babelCore.transformAsync(inlineCode2, {
|
|
5269
|
+
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5270
|
+
ast: false,
|
|
5271
|
+
sourceType: 'module',
|
|
5272
|
+
configFile: false,
|
|
5273
|
+
babelrc: false,
|
|
5274
|
+
});
|
|
5275
|
+
if (tsFinal?.code)
|
|
5276
|
+
inlineCode2 = tsFinal.code;
|
|
5277
|
+
}
|
|
5278
|
+
catch { }
|
|
5279
|
+
// Heal Vue v-model update handlers that lost the ": else" branch during transforms:
|
|
5280
|
+
// "onUpdate:modelValue": _cache[N] || (_cache[N] = $event => _isRef(name) ? name.value = $event)
|
|
5281
|
+
// → add else branch to keep syntax valid: : (name = $event)
|
|
5282
|
+
try {
|
|
5283
|
+
// Fix missing else branch on v-model handlers: support dotted expressions (e.g., $setup.acceptTerms)
|
|
5284
|
+
const reMissingElse = /\"onUpdate:modelValue\"\s*:\s*_cache\[(\d+)\]\s*\|\|\s*\(_cache\[\1\]\s*=\s*\$event\s*=>\s*_isRef\(\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\)\s*\?\s*\2\.value\s*=\s*\$event\s*\)/g;
|
|
5285
|
+
inlineCode2 = inlineCode2.replace(reMissingElse, (_m, idx, expr) => {
|
|
5286
|
+
return `\"onUpdate:modelValue\": _cache[${idx}] || (_cache[${idx}] = $event => (_isRef(${expr}) ? (${expr}.value = $event) : (${expr} = $event)))`;
|
|
5287
|
+
});
|
|
5288
|
+
// Repair malformed handlers without an arrow (introduced by previous transforms):
|
|
5289
|
+
// Convert pattern assigning to $event without an arrow into a proper arrow using the same target expression.
|
|
5290
|
+
const reMalformed = /\"onUpdate:modelValue\"\s*:\s*_cache\[(\d+)\]\s*\|\|\s*\(_cache\[\1\]\s*=\s*[^=]*\(\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\)[^=]*=\s*\$event\s*\)\s*\)/g;
|
|
5291
|
+
inlineCode2 = inlineCode2.replace(reMalformed, (_m, idx, expr) => {
|
|
5292
|
+
return `\"onUpdate:modelValue\": _cache[${idx}] || (_cache[${idx}] = $event => (_isRef(${expr}) ? (${expr}.value = $event) : (${expr} = $event)))`;
|
|
5293
|
+
});
|
|
5294
|
+
}
|
|
5295
|
+
catch { }
|
|
5296
|
+
// Structural heal: ensure balanced braces before the first import statement
|
|
5297
|
+
try {
|
|
5298
|
+
const idx = inlineCode2.search(/^[\t ]*import\b/m);
|
|
5299
|
+
if (idx > 0) {
|
|
5300
|
+
const prefix = inlineCode2.slice(0, idx);
|
|
5301
|
+
let open = 0, close = 0;
|
|
5302
|
+
let inS = false, inD = false, inT = false, inLC = false, inBC = false;
|
|
5303
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
5304
|
+
const ch = prefix[i], nx = prefix[i + 1];
|
|
5305
|
+
if (inLC) {
|
|
5306
|
+
if (ch === '\n')
|
|
5307
|
+
inLC = false;
|
|
5308
|
+
continue;
|
|
5309
|
+
}
|
|
5310
|
+
if (inBC) {
|
|
5311
|
+
if (ch === '*' && nx === '/') {
|
|
5312
|
+
inBC = false;
|
|
5313
|
+
i++;
|
|
5314
|
+
}
|
|
5315
|
+
continue;
|
|
5316
|
+
}
|
|
5317
|
+
if (inS) {
|
|
5318
|
+
if (ch === '\\') {
|
|
5319
|
+
i++;
|
|
5320
|
+
continue;
|
|
5321
|
+
}
|
|
5322
|
+
if (ch === "'")
|
|
5323
|
+
inS = false;
|
|
5324
|
+
continue;
|
|
5325
|
+
}
|
|
5326
|
+
if (inD) {
|
|
5327
|
+
if (ch === '\\') {
|
|
5328
|
+
i++;
|
|
5329
|
+
continue;
|
|
5330
|
+
}
|
|
5331
|
+
if (ch === '"')
|
|
5332
|
+
inD = false;
|
|
5333
|
+
continue;
|
|
5334
|
+
}
|
|
5335
|
+
if (inT) {
|
|
5336
|
+
if (ch === '\\') {
|
|
5337
|
+
i++;
|
|
5338
|
+
continue;
|
|
5339
|
+
}
|
|
5340
|
+
if (ch === '`')
|
|
5341
|
+
inT = false;
|
|
5342
|
+
continue;
|
|
5343
|
+
}
|
|
5344
|
+
if (ch === '/' && nx === '/') {
|
|
5345
|
+
inLC = true;
|
|
5346
|
+
i++;
|
|
5347
|
+
continue;
|
|
5348
|
+
}
|
|
5349
|
+
if (ch === '/' && nx === '*') {
|
|
5350
|
+
inBC = true;
|
|
5351
|
+
i++;
|
|
5352
|
+
continue;
|
|
5353
|
+
}
|
|
5354
|
+
if (ch === "'") {
|
|
5355
|
+
inS = true;
|
|
5356
|
+
continue;
|
|
5357
|
+
}
|
|
5358
|
+
if (ch === '"') {
|
|
5359
|
+
inD = true;
|
|
5360
|
+
continue;
|
|
5361
|
+
}
|
|
5362
|
+
if (ch === '`') {
|
|
5363
|
+
inT = true;
|
|
5364
|
+
continue;
|
|
5365
|
+
}
|
|
5366
|
+
if (ch === '{')
|
|
5367
|
+
open++;
|
|
5368
|
+
else if (ch === '}')
|
|
5369
|
+
close++;
|
|
5370
|
+
}
|
|
5371
|
+
const missing = open - close;
|
|
5372
|
+
if (missing > 0) {
|
|
5373
|
+
inlineCode2 = inlineCode2.slice(0, idx) + '}'.repeat(missing) + '\n' + inlineCode2.slice(idx);
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
catch { }
|
|
5378
|
+
// Final TS strip on the whole assembled module (safety net)
|
|
5379
|
+
try {
|
|
5380
|
+
const tsFinal = await babelCore.transformAsync(inlineCode2, {
|
|
5381
|
+
plugins: [[pluginTransformTypescript, { allowDeclareFields: true }]],
|
|
5382
|
+
ast: false,
|
|
5383
|
+
sourceType: 'module',
|
|
5384
|
+
configFile: false,
|
|
5385
|
+
babelrc: false,
|
|
5386
|
+
});
|
|
5387
|
+
if (tsFinal?.code)
|
|
5388
|
+
inlineCode2 = tsFinal.code;
|
|
5389
|
+
}
|
|
5390
|
+
catch { }
|
|
5391
|
+
inlineCode2 = ensureVariableDynamicImportHelper(inlineCode2);
|
|
5392
|
+
inlineCode2 = ensureGuardPlainDynamicImports(inlineCode2, origin);
|
|
5393
|
+
inlineCode2 = REQUIRE_GUARD_SNIPPET + inlineCode2;
|
|
5394
|
+
// If no render materialized, return a clear error module for deterministic failure
|
|
5395
|
+
try {
|
|
5396
|
+
const lacksRender = !/__ns_render\b/.test(inlineCode2) && !/__ns_sfc__\.render\s*=/.test(inlineCode2);
|
|
5397
|
+
if (lacksRender) {
|
|
5398
|
+
const err = `throw new Error(\"[sfc-asm] ${base}: no render generated by assembler\");\nexport default {};`;
|
|
5399
|
+
res.statusCode = 200;
|
|
5400
|
+
res.end(err);
|
|
5401
|
+
return;
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
catch { }
|
|
5405
|
+
// Cosmetic and parser-friendly: ensure a newline after the canonical banner
|
|
5406
|
+
try {
|
|
5407
|
+
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\])(?!\n)/, '$1\n');
|
|
5408
|
+
}
|
|
5409
|
+
catch { }
|
|
5410
|
+
// Bust device cache for runtime bridge so helpers are always current for this graph version
|
|
5411
|
+
try {
|
|
5412
|
+
const origin = getServerOrigin(server);
|
|
5413
|
+
inlineCode2 = ensureVersionedRtImports(inlineCode2, origin, Number(ver));
|
|
5414
|
+
inlineCode2 = ACTIVE_STRATEGY.ensureVersionedImports(inlineCode2, origin, Number(ver));
|
|
5415
|
+
inlineCode2 = ensureVersionedCoreImports(inlineCode2, origin, Number(ver));
|
|
5416
|
+
}
|
|
5417
|
+
catch { }
|
|
5418
|
+
// Normalize imports/helpers via AST to ensure _defineComponent and other helpers are bound once
|
|
5419
|
+
try {
|
|
5420
|
+
inlineCode2 = astNormalizeModuleImportsAndHelpers(inlineCode2);
|
|
5421
|
+
}
|
|
5422
|
+
catch { }
|
|
5423
|
+
// Guarantee a concrete component object exists before exporting default.
|
|
5424
|
+
try {
|
|
5425
|
+
// Detect an existing declaration of __ns_sfc__ even if it's appended after a semicolon on the same line
|
|
5426
|
+
// e.g., "import ...;let __ns_sfc__;" (no newline). Accept start-of-string, newline, or semicolon as anchors.
|
|
5427
|
+
const hasDecl = /(^|[\n;])\s*(?:const|let|var)\s+__ns_sfc__\b/.test(inlineCode2);
|
|
5428
|
+
if (!hasDecl) {
|
|
5429
|
+
inlineCode2 = inlineCode2.replace(/(\/\/ \[sfc-asm\]\[canonical\]\n)/, `$1let __ns_sfc__ = {};\n`);
|
|
5430
|
+
}
|
|
5431
|
+
// Heal empty declarations (e.g., "let __ns_sfc__;" → initialize to {}), also when preceded by a semicolon
|
|
5432
|
+
inlineCode2 = inlineCode2.replace(/(^|[\n;])\s*let\s+__ns_sfc__\s*;?/g, '$1let __ns_sfc__ = {};');
|
|
5433
|
+
inlineCode2 = inlineCode2.replace(/(^|[\n;])\s*var\s+__ns_sfc__\s*;?/g, '$1var __ns_sfc__ = {};');
|
|
5434
|
+
}
|
|
5435
|
+
catch { }
|
|
5436
|
+
if (!/export\s+default\s+__ns_sfc__/.test(inlineCode2) && /__ns_sfc__/.test(inlineCode2))
|
|
5437
|
+
inlineCode2 += '\nexport default __ns_sfc__';
|
|
5438
|
+
res.statusCode = 200;
|
|
5439
|
+
res.end(inlineCode2);
|
|
5440
|
+
return;
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
catch { }
|
|
5445
|
+
// Do not use compiled ?vue or variant fallbacks; assembler must succeed or emit an error
|
|
5446
|
+
// Prefer compiling template from source via compiler-sfc; fallback to variant extraction
|
|
5447
|
+
let inlineOk = false;
|
|
5448
|
+
let helperBindings = '';
|
|
5449
|
+
let renderDecl = '';
|
|
5450
|
+
let inlineBlock = undefined;
|
|
5451
|
+
try {
|
|
5452
|
+
const root = server.config?.root || process.cwd();
|
|
5453
|
+
const abs = path.join(root, base.replace(/^\//, ''));
|
|
5454
|
+
let sfcSrc = '';
|
|
5455
|
+
try {
|
|
5456
|
+
sfcSrc = readFileSync(abs, 'utf-8');
|
|
5457
|
+
}
|
|
5458
|
+
catch { }
|
|
5459
|
+
if (sfcSrc) {
|
|
5460
|
+
const { descriptor } = parse(sfcSrc, { filename: abs });
|
|
5461
|
+
const tpl = descriptor.template?.content || '';
|
|
5462
|
+
if (tpl) {
|
|
5463
|
+
const id = createHash('md5').update(abs).digest('hex').slice(0, 8);
|
|
5464
|
+
const ct = compileTemplate({
|
|
5465
|
+
source: tpl,
|
|
5466
|
+
id,
|
|
5467
|
+
filename: abs,
|
|
5468
|
+
isProd: false,
|
|
5469
|
+
ssr: false,
|
|
5470
|
+
compilerOptions: {
|
|
5471
|
+
isCustomElement: (tag) => NS_NATIVE_TAGS.has(tag),
|
|
5472
|
+
},
|
|
5473
|
+
});
|
|
5474
|
+
let compiled = (ct && (ct.code || '')) || '';
|
|
5475
|
+
if (compiled) {
|
|
5476
|
+
// Prefer a full inline template block preserving hoists
|
|
5477
|
+
inlineBlock = buildInlineTemplateBlock(compiled) || undefined;
|
|
5478
|
+
if (inlineBlock) {
|
|
5479
|
+
inlineOk = true;
|
|
5480
|
+
}
|
|
5481
|
+
else {
|
|
5482
|
+
const extracted = extractTemplateRender(compiled);
|
|
5483
|
+
inlineOk = extracted.ok;
|
|
5484
|
+
helperBindings = extracted.helperBindings;
|
|
5485
|
+
renderDecl = extracted.renderDecl;
|
|
5486
|
+
inlineBlock = extracted.inlineBlock;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
catch { }
|
|
5493
|
+
// If compiler-sfc path didn't succeed, attempt variant extraction once
|
|
5494
|
+
if (!inlineOk) {
|
|
5495
|
+
const extracted = extractTemplateRender(templateCode);
|
|
5496
|
+
inlineOk = extracted.ok;
|
|
5497
|
+
helperBindings = extracted.helperBindings;
|
|
5498
|
+
renderDecl = extracted.renderDecl;
|
|
5499
|
+
inlineBlock = extracted.inlineBlock;
|
|
5500
|
+
}
|
|
5501
|
+
let asm;
|
|
5502
|
+
if (inlineOk) {
|
|
5503
|
+
if (inlineBlock && inlineBlock.trim()) {
|
|
5504
|
+
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');
|
|
5505
|
+
}
|
|
5506
|
+
else {
|
|
5507
|
+
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');
|
|
5508
|
+
}
|
|
5509
|
+
}
|
|
5510
|
+
else {
|
|
5511
|
+
// Deterministic error path when template extraction failed
|
|
5512
|
+
res.statusCode = 500;
|
|
5513
|
+
res.end(`throw new Error('[sfc-asm] ${base}: template extraction failed');\nexport default {};`);
|
|
5514
|
+
return;
|
|
5515
|
+
}
|
|
5516
|
+
// Run full device processing so helper aliasing and globals are consistent in this path too
|
|
5517
|
+
let code = REQUIRE_GUARD_SNIPPET + asm;
|
|
5518
|
+
code = processCodeForDevice(code, false, true, /(?:^|\/)node_modules\//.test(base), base);
|
|
5519
|
+
try {
|
|
5520
|
+
code = ensureVersionedCoreImports(code, getServerOrigin(server), Number(ver));
|
|
5521
|
+
}
|
|
5522
|
+
catch { }
|
|
5523
|
+
code = rewriteImports(code, base, sfcFileMap, depFileMap, projectRoot, !!verbose, undefined, getServerOrigin(server));
|
|
5524
|
+
try {
|
|
5525
|
+
code = ensureDestructureCoreImports(code);
|
|
5526
|
+
}
|
|
5527
|
+
catch { }
|
|
5528
|
+
code = ensureVariableDynamicImportHelper(code);
|
|
5529
|
+
code = ensureGuardPlainDynamicImports(code, origin);
|
|
5530
|
+
try {
|
|
5531
|
+
const origin = getServerOrigin(server);
|
|
5532
|
+
code = ensureVersionedRtImports(code, origin, Number(ver));
|
|
5533
|
+
code = ACTIVE_STRATEGY.ensureVersionedImports(code, origin, Number(ver));
|
|
5534
|
+
code = ensureVersionedCoreImports(code, origin, Number(ver));
|
|
5535
|
+
}
|
|
5536
|
+
catch { }
|
|
5537
|
+
// Inline-template body path already runs processCodeForDevice (AST + sanitizers); no additional _defineComponent fix needed
|
|
5538
|
+
res.statusCode = 200;
|
|
5539
|
+
res.end(code);
|
|
5540
|
+
}
|
|
5541
|
+
catch (e) {
|
|
5542
|
+
res.statusCode = 500;
|
|
5543
|
+
res.end('export {}\n');
|
|
5544
|
+
}
|
|
3367
5545
|
});
|
|
3368
5546
|
wss.on('connection', async (ws) => {
|
|
3369
5547
|
if (verbose)
|