Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/vite/hmr/frameworks/vue/server/strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const __hmrId = "foo";
export default {};
`;
const rewritten = vueServerStrategy.rewriteFrameworkImports(raw);
expect(rewritten).toContain('/@nativescript/vendor.mjs');
expect(rewritten).toContain('/ns/rt');

const post = vueServerStrategy.postClean(rewritten);
expect(post).not.toContain('__VUE_HMR_RUNTIME__.reload');
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/hmr/frameworks/vue/server/strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type { VueSfcRegistryEntry, VueSfcRegistryMessage, VueSfcRegistryUpdateMe
// imports this to inject it as the `processSfcCode` helper for Vue's hooks.
export const processSfcCode = createProcessSfcCode(processCodeForDevice);

const VENDOR_MJS = '/@nativescript/vendor.mjs';
const VUE_RUNTIME = '/ns/rt';
const VUE_HTTP_SFC_DIR = `${getProjectAppPath()}/sfc`;
const VUE_IMPORTS_TO_VENDOR: RegExp[] = [/(from\s+["'])vue(?:\/[A-Za-z0-9_\-\/]+)?(["'])/g, /(from\s+["'])nativescript-vue(?:\/[A-Za-z0-9_\-\/]+)?(["'])/g];

Expand Down Expand Up @@ -585,7 +585,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
rewriteFrameworkImports(code: string) {
let result = code;
for (const pattern of VUE_IMPORTS_TO_VENDOR) {
result = result.replace(pattern, `from "${VENDOR_MJS}"`);
result = result.replace(pattern, `from "${VUE_RUNTIME}"`);
}
return result;
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/hmr/helpers/ast-normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ export function astNormalizeModuleImportsAndHelpers(code: string, opts?: { under
const isRtLike = (src: string | null | undefined) => !!src && /(?:https?:\/\/[^"']+)?\/ns\/rt(?:\/[\d]+)?(?:\?p=[^"']+)?$/.test(src);
const isCoreLike = (src: string | null | undefined) => !!src && /(?:https?:\/\/[^"']+)?\/ns\/core(?:\/[\d]+)?(?:\?p=[^"']+)?$/.test(src);
const isSfcPath = (src: string | null | undefined) => !!src && /\.vue(?:\?|$)/.test(src);
const isVuePrebundle = (src: string | null | undefined) => !!src && /\.vite\/deps\/(?:vue|nativescript-vue)[^/]*\.js/.test(src);
const isVuePackage = (src: string | null | undefined) => !!src && (src === 'vue' || src.startsWith('vue/') || src.includes('nativescript-vue'));
const isVuePrebundle = (src: string | null | undefined) => !!src && /\.vite\/deps\/(?:vue|nativescript-vue)(?:_[^/]*)?\.js(?:[?#]|$)/.test(src);
const isVuePackage = (src: string | null | undefined) => !!src && (src === 'vue' || src.startsWith('vue/') || /(?:^|\/)nativescript-vue(?:\/|[?#]|$)/.test(src));
const isVueLike = (src: string | null | undefined) => (isVuePackage(src) || isVuePrebundle(src)) && !isSfcPath(src);
// True virtual modules only (`\0`/`__x00__`-marked). A plain `/@id/<id>`
// is Vite's URL form for any REAL resolved bare id that isn't a file
Expand Down
36 changes: 36 additions & 0 deletions packages/vite/hmr/server/vue-package-imports.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import { parse } from '@babel/parser';
import { vueServerStrategy } from '../frameworks/vue/server/strategy.js';
import { astNormalizeModuleImportsAndHelpers } from '../helpers/ast-normalizer.js';
import { cleanCode, processCodeForDevice, rewriteImports } from './websocket-device-transform.js';

describe('Vue package imports in HTTP modules', () => {
it('keeps native navigation helpers on the runtime bridge through the full transform', () => {
const source = `import { $navigateTo, ref } from 'nativescript-vue';\nexport const current = ref(null);\nexport function go(page) { $navigateTo(page); }`;
const cleaned = cleanCode(source, vueServerStrategy);
const processed = processCodeForDevice(cleaned, false, true, true, '/node_modules/native-router/dist/frame.js');
const output = rewriteImports(processed, '/node_modules/native-router/dist/frame.js', new Map(), new Map(), '/', false, undefined, 'http://localhost:5173');
expect(output).not.toContain('/@nativescript/vendor.mjs');
expect(output).toContain('/ns/rt');
expect(() => parse(output, { sourceType: 'module' })).not.toThrow();
});

it.each(['vue-router-ns', 'vue-router-ns_file-routing', 'vue-demi', 'nativescript-vue-router'])('does not redirect the %s prebundle to the Vue runtime', (name) => {
const url = `/node_modules/.vite/deps/${name}.js?v=abc`;
const output = astNormalizeModuleImportsAndHelpers(`import { createRouter } from '${url}'; export const router = createRouter();`);
expect(output).toContain(url);
expect(output).not.toContain('/ns/rt');
});

it.each(['vue', 'nativescript-vue', 'vue_dist_vue_runtime_esm-bundler'])('still redirects the %s runtime prebundle', (name) => {
const output = astNormalizeModuleImportsAndHelpers(`import { ref } from '/node_modules/.vite/deps/${name}.js?v=abc'; export const value = ref(0);`);
expect(output).toContain('/ns/rt');
expect(output).not.toContain('/node_modules/.vite/deps/');
});

it('preserves a similarly named bare plugin import', () => {
const output = astNormalizeModuleImportsAndHelpers(`import { createRouter } from 'nativescript-vue-router'; export const router = createRouter();`);
expect(output).toContain('nativescript-vue-router');
expect(output).not.toContain('/ns/rt');
});
});
Loading