Skip to content

Commit b896fd4

Browse files
committed
feat(compiler-sfc): add preserveTilde option for asset URL and srcset transformations
1 parent cdffaf6 commit b896fd4

File tree

7 files changed

+67
-5
lines changed

7 files changed

+67
-5
lines changed

packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,17 @@ export function render(_ctx, _cache) {
118118
], 64 /* STABLE_FRAGMENT */))
119119
}"
120120
`;
121+
122+
exports[`compiler sfc: transform asset url > with preserveTilde: true 1`] = `
123+
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
124+
import _imports_0 from '~/app/bar.png'
125+
import _imports_1 from '~app/bar.png'
126+
127+
128+
export function render(_ctx, _cache) {
129+
return (_openBlock(), _createElementBlock(_Fragment, null, [
130+
_createElementVNode("img", { src: _imports_0 }),
131+
_createElementVNode("img", { src: _imports_1 })
132+
], 64 /* STABLE_FRAGMENT */))
133+
}"
134+
`;

packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ export function render(_ctx, _cache) {
1616
}"
1717
`;
1818

19+
exports[`compiler sfc: transform srcset > srcset w/ preserveTilde: true 1`] = `
20+
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
21+
import _imports_0 from '~/app/logo.png'
22+
import _imports_1 from '~app/logo.png'
23+
24+
25+
const _hoisted_1 = _imports_0 + ', ' + _imports_1 + ' 2x'
26+
const _hoisted_2 = _imports_1 + ' 1x, ' + _imports_0 + ' 2x'
27+
28+
export function render(_ctx, _cache) {
29+
return (_openBlock(), _createElementBlock(_Fragment, null, [
30+
_cache[0] || (_cache[0] = _createElementVNode("img", { srcset: _hoisted_1 }, null, -1 /* CACHED */)),
31+
_cache[1] || (_cache[1] = _createElementVNode("img", { srcset: _hoisted_2 }, null, -1 /* CACHED */))
32+
], 64 /* STABLE_FRAGMENT */))
33+
}"
34+
`;
35+
1936
exports[`compiler sfc: transform srcset > transform srcset 1`] = `
2037
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
2138
import _imports_0 from './logo.png'

packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ describe('compiler sfc: transform asset url', () => {
100100
expect(code).toMatchSnapshot()
101101
})
102102

103+
test('with preserveTilde: true', () => {
104+
const { code } = compileWithAssetUrls(
105+
`<img src="~/app/bar.png"/>` + `<img src="~app/bar.png"/>`,
106+
{
107+
preserveTilde: true,
108+
},
109+
)
110+
expect(code).toMatchSnapshot()
111+
})
112+
103113
// vitejs/vite#298
104114
test('should not transform hash fragments', () => {
105115
const { code } = compileWithAssetUrls(

packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,15 @@ describe('compiler sfc: transform srcset', () => {
9898
).code
9999
expect(code).toMatchSnapshot()
100100
})
101+
102+
test('srcset w/ preserveTilde: true', () => {
103+
const code = compileWithSrcset(
104+
`
105+
<img srcset="~/app/logo.png, ~app/logo.png 2x"/>
106+
<img srcset="~app/logo.png 1x, ~/app/logo.png 2x"/>
107+
`,
108+
{ preserveTilde: true },
109+
).code
110+
expect(code).toMatchSnapshot()
111+
})
101112
})

packages/compiler-sfc/src/template/templateUtils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ export function isDataUrl(url: string): boolean {
1919
/**
2020
* Parses string url into URL object.
2121
*/
22-
export function parseUrl(url: string): UrlWithStringQuery {
22+
export function parseUrl(
23+
url: string,
24+
preserveTilde?: boolean,
25+
): UrlWithStringQuery {
2326
const firstChar = url.charAt(0)
24-
if (firstChar === '~') {
27+
if (firstChar === '~' && !preserveTilde) {
2528
const secondChar = url.charAt(1)
2629
url = url.slice(secondChar === '/' ? 2 : 1)
2730
}

packages/compiler-sfc/src/template/transformAssetUrl.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,18 @@ export interface AssetURLOptions {
3232
*/
3333
includeAbsolute?: boolean
3434
tags?: AssetURLTagConfig
35+
/**
36+
* Whether to preserve the tilde (~) in asset URLs.
37+
* Nuxt uses ~ as alias for the /app directory.
38+
* see #13460
39+
*/
40+
preserveTilde?: boolean
3541
}
3642

3743
export const defaultAssetUrlOptions: Required<AssetURLOptions> = {
3844
base: null,
3945
includeAbsolute: false,
46+
preserveTilde: false,
4047
tags: {
4148
video: ['src', 'poster'],
4249
source: ['src'],
@@ -113,12 +120,12 @@ export const transformAssetUrl: NodeTransform = (
113120
return
114121
}
115122

116-
const url = parseUrl(attr.value.content)
123+
const url = parseUrl(attr.value.content, options.preserveTilde)
117124
if (options.base && attr.value.content[0] === '.') {
118125
// explicit base - directly rewrite relative urls into absolute url
119126
// to avoid generating extra imports
120127
// Allow for full hostnames provided in options.base
121-
const base = parseUrl(options.base)
128+
const base = parseUrl(options.base, options.preserveTilde)
122129
const protocol = base.protocol || ''
123130
const host = base.host ? protocol + '//' + base.host : ''
124131
const basePath = base.path || '/'

packages/compiler-sfc/src/template/transformSrcset.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const transformSrcset: NodeTransform = (
108108
const compoundExpression = createCompoundExpression([], attr.loc)
109109
imageCandidates.forEach(({ url, descriptor }, index) => {
110110
if (shouldProcessUrl(url)) {
111-
const { path } = parseUrl(url)
111+
const { path } = parseUrl(url, options.preserveTilde)
112112
let exp: SimpleExpressionNode
113113
if (path) {
114114
const existingImportsIndex = context.imports.findIndex(

0 commit comments

Comments
 (0)