diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap index 18ec05da2b5..6d95091ddb2 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap @@ -118,3 +118,17 @@ export function render(_ctx, _cache) { ], 64 /* STABLE_FRAGMENT */)) }" `; + +exports[`compiler sfc: transform asset url > with preserveTilde: true 1`] = ` +"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue" +import _imports_0 from '~/app/bar.png' +import _imports_1 from '~app/bar.png' + + +export function render(_ctx, _cache) { + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createElementVNode("img", { src: _imports_0 }), + _createElementVNode("img", { src: _imports_1 }) + ], 64 /* STABLE_FRAGMENT */)) +}" +`; diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap index 0469ffaba88..ade723deac6 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap @@ -16,6 +16,23 @@ export function render(_ctx, _cache) { }" `; +exports[`compiler sfc: transform srcset > srcset w/ preserveTilde: true 1`] = ` +"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue" +import _imports_0 from '~/app/logo.png' +import _imports_1 from '~app/logo.png' + + +const _hoisted_1 = _imports_0 + ', ' + _imports_1 + ' 2x' +const _hoisted_2 = _imports_1 + ' 1x, ' + _imports_0 + ' 2x' + +export function render(_ctx, _cache) { + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _cache[0] || (_cache[0] = _createElementVNode("img", { srcset: _hoisted_1 }, null, -1 /* CACHED */)), + _cache[1] || (_cache[1] = _createElementVNode("img", { srcset: _hoisted_2 }, null, -1 /* CACHED */)) + ], 64 /* STABLE_FRAGMENT */)) +}" +`; + exports[`compiler sfc: transform srcset > transform srcset 1`] = ` "import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue" import _imports_0 from './logo.png' diff --git a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts index a1f62f43a3c..cfedf13fd7a 100644 --- a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts +++ b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts @@ -100,6 +100,16 @@ describe('compiler sfc: transform asset url', () => { expect(code).toMatchSnapshot() }) + test('with preserveTilde: true', () => { + const { code } = compileWithAssetUrls( + `` + ``, + { + preserveTilde: true, + }, + ) + expect(code).toMatchSnapshot() + }) + // vitejs/vite#298 test('should not transform hash fragments', () => { const { code } = compileWithAssetUrls( diff --git a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts index 491731f94e5..55342002101 100644 --- a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts +++ b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts @@ -98,4 +98,15 @@ describe('compiler sfc: transform srcset', () => { ).code expect(code).toMatchSnapshot() }) + + test('srcset w/ preserveTilde: true', () => { + const code = compileWithSrcset( + ` + + + `, + { preserveTilde: true }, + ).code + expect(code).toMatchSnapshot() + }) }) diff --git a/packages/compiler-sfc/src/template/templateUtils.ts b/packages/compiler-sfc/src/template/templateUtils.ts index c1414d1ecbd..7ac5e872780 100644 --- a/packages/compiler-sfc/src/template/templateUtils.ts +++ b/packages/compiler-sfc/src/template/templateUtils.ts @@ -19,9 +19,12 @@ export function isDataUrl(url: string): boolean { /** * Parses string url into URL object. */ -export function parseUrl(url: string): UrlWithStringQuery { +export function parseUrl( + url: string, + preserveTilde?: boolean, +): UrlWithStringQuery { const firstChar = url.charAt(0) - if (firstChar === '~') { + if (firstChar === '~' && !preserveTilde) { const secondChar = url.charAt(1) url = url.slice(secondChar === '/' ? 2 : 1) } diff --git a/packages/compiler-sfc/src/template/transformAssetUrl.ts b/packages/compiler-sfc/src/template/transformAssetUrl.ts index 6291e21bbba..1deb015cbae 100644 --- a/packages/compiler-sfc/src/template/transformAssetUrl.ts +++ b/packages/compiler-sfc/src/template/transformAssetUrl.ts @@ -32,11 +32,18 @@ export interface AssetURLOptions { */ includeAbsolute?: boolean tags?: AssetURLTagConfig + /** + * Whether to preserve the tilde (~) in asset URLs. + * Nuxt uses ~ as alias for the /app directory. + * see #13460 + */ + preserveTilde?: boolean } export const defaultAssetUrlOptions: Required = { base: null, includeAbsolute: false, + preserveTilde: false, tags: { video: ['src', 'poster'], source: ['src'], @@ -113,12 +120,12 @@ export const transformAssetUrl: NodeTransform = ( return } - const url = parseUrl(attr.value.content) + const url = parseUrl(attr.value.content, options.preserveTilde) if (options.base && attr.value.content[0] === '.') { // explicit base - directly rewrite relative urls into absolute url // to avoid generating extra imports // Allow for full hostnames provided in options.base - const base = parseUrl(options.base) + const base = parseUrl(options.base, options.preserveTilde) const protocol = base.protocol || '' const host = base.host ? protocol + '//' + base.host : '' const basePath = base.path || '/' diff --git a/packages/compiler-sfc/src/template/transformSrcset.ts b/packages/compiler-sfc/src/template/transformSrcset.ts index 8f00f86e3c3..aefc4895bc5 100644 --- a/packages/compiler-sfc/src/template/transformSrcset.ts +++ b/packages/compiler-sfc/src/template/transformSrcset.ts @@ -108,7 +108,7 @@ export const transformSrcset: NodeTransform = ( const compoundExpression = createCompoundExpression([], attr.loc) imageCandidates.forEach(({ url, descriptor }, index) => { if (shouldProcessUrl(url)) { - const { path } = parseUrl(url) + const { path } = parseUrl(url, options.preserveTilde) let exp: SimpleExpressionNode if (path) { const existingImportsIndex = context.imports.findIndex(