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(