], 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 */))
+}"
+`;
}"
`;
+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'
expect(code).toMatchSnapshot()
})
+ test('with preserveTilde: true', () => {
+ const { code } = compileWithAssetUrls(
+ `<img src="~/app/bar.png"/>` + `<img src="~app/bar.png"/>`,
+ {
+ preserveTilde: true,
+ },
+ )
+ expect(code).toMatchSnapshot()
+ })
+
// vitejs/vite#298
test('should not transform hash fragments', () => {
const { code } = compileWithAssetUrls(
).code
expect(code).toMatchSnapshot()
})
+
+ test('srcset w/ preserveTilde: true', () => {
+ const code = compileWithSrcset(
+ `
+ <img srcset="~/app/logo.png, ~app/logo.png 2x"/>
+ <img srcset="~app/logo.png 1x, ~/app/logo.png 2x"/>
+ `,
+ { preserveTilde: true },
+ ).code
+ expect(code).toMatchSnapshot()
+ })
})
/**
* 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)
}
*/
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<AssetURLOptions> = {
base: null,
includeAbsolute: false,
+ preserveTilde: false,
tags: {
video: ['src', 'poster'],
source: ['src'],
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 || '/'
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(