Skip to content

Commit 6a0be88

Browse files
committed
feat(compiler-sfc): support transforming absolute asset urls
BREAKING CHANGE: `@vue/compiler-sfc`'s `transformAssetUrlsBase` option has been removed. It is merged into `trasnformAssetUrls` which now also accepts the format of ```ts { base?: string includeAbsolute?: string tags?: { [name: string]: string[] } } ```
1 parent f9a3766 commit 6a0be88

9 files changed

+332
-97
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,17 @@ export function render(_ctx, _cache) {
4949
], 64 /* STABLE_FRAGMENT */))
5050
}"
5151
`;
52+
53+
exports[`compiler sfc: transform asset url with includeAbsolute: true 1`] = `
54+
"import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
55+
import _imports_0 from './bar.png'
56+
import _imports_1 from '/bar.png'
57+
58+
59+
export function render(_ctx, _cache) {
60+
return (_openBlock(), _createBlock(_Fragment, null, [
61+
_createVNode(\\"img\\", { src: _imports_0 }),
62+
_createVNode(\\"img\\", { src: _imports_1 })
63+
], 64 /* STABLE_FRAGMENT */))
64+
}"
65+
`;

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

+112
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,115 @@ export function render(_ctx, _cache) {
5959
], 64 /* STABLE_FRAGMENT */))
6060
}"
6161
`;
62+
63+
exports[`compiler sfc: transform srcset transform srcset w/ base 1`] = `
64+
"import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
65+
66+
export function render(_ctx, _cache) {
67+
return (_openBlock(), _createBlock(_Fragment, null, [
68+
_createVNode(\\"img\\", {
69+
src: \\"./logo.png\\",
70+
srcset: \\"/foo/logo.png\\"
71+
}),
72+
_createVNode(\\"img\\", {
73+
src: \\"./logo.png\\",
74+
srcset: \\"/foo/logo.png 2x\\"
75+
}),
76+
_createVNode(\\"img\\", {
77+
src: \\"./logo.png\\",
78+
srcset: \\"/foo/logo.png 2x\\"
79+
}),
80+
_createVNode(\\"img\\", {
81+
src: \\"./logo.png\\",
82+
srcset: \\"/foo/logo.png, /foo/logo.png 2x\\"
83+
}),
84+
_createVNode(\\"img\\", {
85+
src: \\"./logo.png\\",
86+
srcset: \\"/foo/logo.png 2x, /foo/logo.png\\"
87+
}),
88+
_createVNode(\\"img\\", {
89+
src: \\"./logo.png\\",
90+
srcset: \\"/foo/logo.png 2x, /foo/logo.png 3x\\"
91+
}),
92+
_createVNode(\\"img\\", {
93+
src: \\"./logo.png\\",
94+
srcset: \\"/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x\\"
95+
}),
96+
_createVNode(\\"img\\", {
97+
src: \\"/logo.png\\",
98+
srcset: \\"/logo.png, /logo.png 2x\\"
99+
}),
100+
_createVNode(\\"img\\", {
101+
src: \\"https://example.com/logo.png\\",
102+
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
103+
}),
104+
_createVNode(\\"img\\", {
105+
src: \\"/logo.png\\",
106+
srcset: \\"/logo.png, /foo/logo.png 2x\\"
107+
})
108+
], 64 /* STABLE_FRAGMENT */))
109+
}"
110+
`;
111+
112+
exports[`compiler sfc: transform srcset transform srcset w/ includeAbsolute: true 1`] = `
113+
"import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
114+
import _imports_0 from './logo.png'
115+
import _imports_1 from '/logo.png'
116+
117+
118+
const _hoisted_1 = _imports_0
119+
const _hoisted_2 = _imports_0 + '2x'
120+
const _hoisted_3 = _imports_0 + '2x'
121+
const _hoisted_4 = _imports_0 + ', ' + _imports_0 + '2x'
122+
const _hoisted_5 = _imports_0 + '2x, ' + _imports_0
123+
const _hoisted_6 = _imports_0 + '2x, ' + _imports_0 + '3x'
124+
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + '2x, ' + _imports_0 + '3x'
125+
const _hoisted_8 = _imports_1 + ', ' + _imports_1 + '2x'
126+
const _hoisted_9 = \\"https://example.com/logo.png\\" + ', ' + \\"https://example.com/logo.png\\" + '2x'
127+
const _hoisted_10 = _imports_1 + ', ' + _imports_0 + '2x'
128+
129+
export function render(_ctx, _cache) {
130+
return (_openBlock(), _createBlock(_Fragment, null, [
131+
_createVNode(\\"img\\", {
132+
src: \\"./logo.png\\",
133+
srcset: _hoisted_1
134+
}),
135+
_createVNode(\\"img\\", {
136+
src: \\"./logo.png\\",
137+
srcset: _hoisted_2
138+
}),
139+
_createVNode(\\"img\\", {
140+
src: \\"./logo.png\\",
141+
srcset: _hoisted_3
142+
}),
143+
_createVNode(\\"img\\", {
144+
src: \\"./logo.png\\",
145+
srcset: _hoisted_4
146+
}),
147+
_createVNode(\\"img\\", {
148+
src: \\"./logo.png\\",
149+
srcset: _hoisted_5
150+
}),
151+
_createVNode(\\"img\\", {
152+
src: \\"./logo.png\\",
153+
srcset: _hoisted_6
154+
}),
155+
_createVNode(\\"img\\", {
156+
src: \\"./logo.png\\",
157+
srcset: _hoisted_7
158+
}),
159+
_createVNode(\\"img\\", {
160+
src: \\"/logo.png\\",
161+
srcset: _hoisted_8
162+
}),
163+
_createVNode(\\"img\\", {
164+
src: \\"https://example.com/logo.png\\",
165+
srcset: _hoisted_9
166+
}),
167+
_createVNode(\\"img\\", {
168+
src: \\"/logo.png\\",
169+
srcset: _hoisted_10
170+
})
171+
], 64 /* STABLE_FRAGMENT */))
172+
}"
173+
`;

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

+15-3
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,27 @@ test('transform asset url options', () => {
5454
// Object option
5555
const { code: code1 } = compileTemplate({
5656
...input,
57-
transformAssetUrls: { foo: ['bar'] }
57+
transformAssetUrls: {
58+
tags: { foo: ['bar'] }
59+
}
5860
})
5961
expect(code1).toMatch(`import _imports_0 from 'baz'\n`)
60-
// false option
62+
63+
// legacy object option (direct tags config)
6164
const { code: code2 } = compileTemplate({
65+
...input,
66+
transformAssetUrls: {
67+
foo: ['bar']
68+
}
69+
})
70+
expect(code2).toMatch(`import _imports_0 from 'baz'\n`)
71+
72+
// false option
73+
const { code: code3 } = compileTemplate({
6274
...input,
6375
transformAssetUrls: false
6476
})
65-
expect(code2).not.toMatch(`import _imports_0 from 'baz'\n`)
77+
expect(code3).not.toMatch(`import _imports_0 from 'baz'\n`)
6678
})
6779

6880
test('source map', () => {

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

+22-16
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import { generate, baseParse, transform } from '@vue/compiler-core'
22
import {
33
transformAssetUrl,
4-
createAssetUrlTransformWithOptions
4+
createAssetUrlTransformWithOptions,
5+
AssetURLOptions,
6+
normalizeOptions
57
} from '../src/templateTransformAssetUrl'
68
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
79
import { transformBind } from '../../compiler-core/src/transforms/vBind'
810

9-
function compileWithAssetUrls(template: string) {
11+
function compileWithAssetUrls(template: string, options?: AssetURLOptions) {
1012
const ast = baseParse(template)
13+
const t = options
14+
? createAssetUrlTransformWithOptions(normalizeOptions(options))
15+
: transformAssetUrl
1116
transform(ast, {
12-
nodeTransforms: [transformAssetUrl, transformElement],
17+
nodeTransforms: [t, transformElement],
1318
directiveTransforms: {
1419
bind: transformBind
1520
}
@@ -51,24 +56,25 @@ describe('compiler sfc: transform asset url', () => {
5156
})
5257

5358
test('with explicit base', () => {
54-
const ast = baseParse(
59+
const { code } = compileWithAssetUrls(
5560
`<img src="./bar.png"></img>` + // -> /foo/bar.png
5661
`<img src="~bar.png"></img>` + // -> /foo/bar.png
5762
`<img src="bar.png"></img>` + // -> bar.png (untouched)
58-
`<img src="@theme/bar.png"></img>` // -> @theme/bar.png (untouched)
63+
`<img src="@theme/bar.png"></img>`, // -> @theme/bar.png (untouched)
64+
{
65+
base: '/foo'
66+
}
5967
)
60-
transform(ast, {
61-
nodeTransforms: [
62-
createAssetUrlTransformWithOptions({
63-
base: '/foo'
64-
}),
65-
transformElement
66-
],
67-
directiveTransforms: {
68-
bind: transformBind
68+
expect(code).toMatchSnapshot()
69+
})
70+
71+
test('with includeAbsolute: true', () => {
72+
const { code } = compileWithAssetUrls(
73+
`<img src="./bar.png"></img>` + `<img src="/bar.png"></img>`,
74+
{
75+
includeAbsolute: true
6976
}
70-
})
71-
const { code } = generate(ast, { mode: 'module' })
77+
)
7278
expect(code).toMatchSnapshot()
7379
})
7480
})
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,60 @@
11
import { generate, baseParse, transform } from '@vue/compiler-core'
2-
import { transformSrcset } from '../src/templateTransformSrcset'
2+
import {
3+
transformSrcset,
4+
createSrcsetTransformWithOptions
5+
} from '../src/templateTransformSrcset'
36
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
47
import { transformBind } from '../../compiler-core/src/transforms/vBind'
8+
import {
9+
AssetURLOptions,
10+
normalizeOptions
11+
} from '../src/templateTransformAssetUrl'
512

6-
function compileWithSrcset(template: string) {
13+
function compileWithSrcset(template: string, options?: AssetURLOptions) {
714
const ast = baseParse(template)
15+
const srcsetTrasnform = options
16+
? createSrcsetTransformWithOptions(normalizeOptions(options))
17+
: transformSrcset
818
transform(ast, {
9-
nodeTransforms: [transformSrcset, transformElement],
19+
nodeTransforms: [srcsetTrasnform, transformElement],
1020
directiveTransforms: {
1121
bind: transformBind
1222
}
1323
})
1424
return generate(ast, { mode: 'module' })
1525
}
1626

27+
const src = `
28+
<img src="./logo.png" srcset="./logo.png"/>
29+
<img src="./logo.png" srcset="./logo.png 2x"/>
30+
<img src="./logo.png" srcset="./logo.png 2x"/>
31+
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x"/>
32+
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png"/>
33+
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x"/>
34+
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x"/>
35+
<img src="/logo.png" srcset="/logo.png, /logo.png 2x"/>
36+
<img src="https://example.com/logo.png" srcset="https://example.com/logo.png, https://example.com/logo.png 2x"/>
37+
<img src="/logo.png" srcset="/logo.png, ./logo.png 2x"/>
38+
`
39+
1740
describe('compiler sfc: transform srcset', () => {
1841
test('transform srcset', () => {
19-
const result = compileWithSrcset(`
20-
<img src="./logo.png" srcset="./logo.png"/>
21-
<img src="./logo.png" srcset="./logo.png 2x"/>
22-
<img src="./logo.png" srcset="./logo.png 2x"/>
23-
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x"/>
24-
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png"/>
25-
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x"/>
26-
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x"/>
27-
<img src="/logo.png" srcset="/logo.png, /logo.png 2x"/>
28-
<img src="https://example.com/logo.png" srcset="https://example.com/logo.png, https://example.com/logo.png 2x"/>
29-
<img src="/logo.png" srcset="/logo.png, ./logo.png 2x"/>
30-
`)
42+
expect(compileWithSrcset(src).code).toMatchSnapshot()
43+
})
44+
45+
test('transform srcset w/ base', () => {
46+
expect(
47+
compileWithSrcset(src, {
48+
base: '/foo'
49+
}).code
50+
).toMatchSnapshot()
51+
})
3152

32-
expect(result.code).toMatchSnapshot()
53+
test('transform srcset w/ includeAbsolute: true', () => {
54+
expect(
55+
compileWithSrcset(src, {
56+
includeAbsolute: true
57+
}).code
58+
).toMatchSnapshot()
3359
})
3460
})

packages/compiler-sfc/src/compileTemplate.ts

+19-25
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ import { SourceMapConsumer, SourceMapGenerator, RawSourceMap } from 'source-map'
1010
import {
1111
transformAssetUrl,
1212
AssetURLOptions,
13-
createAssetUrlTransformWithOptions
13+
createAssetUrlTransformWithOptions,
14+
AssetURLTagConfig,
15+
normalizeOptions
1416
} from './templateTransformAssetUrl'
15-
import { transformSrcset } from './templateTransformSrcset'
17+
import {
18+
transformSrcset,
19+
createSrcsetTransformWithOptions
20+
} from './templateTransformSrcset'
1621
import { isObject } from '@vue/shared'
1722
import * as CompilerDOM from '@vue/compiler-dom'
1823
import * as CompilerSSR from '@vue/compiler-ssr'
@@ -47,16 +52,10 @@ export interface SFCTemplateCompileOptions {
4752
*/
4853
preprocessCustomRequire?: (id: string) => any
4954
/**
50-
* Configure what tags/attributes to trasnform into relative asset url imports
51-
* in the form of `{ [tag: string]: string[] }`, or disable the transform with
52-
* `false`.
53-
*/
54-
transformAssetUrls?: AssetURLOptions | boolean
55-
/**
56-
* If base is provided, instead of transforming relative asset urls into
57-
* imports, they will be directly rewritten to absolute urls.
55+
* Configure what tags/attributes to trasnform into asset url imports,
56+
* or disable the transform altogether with `false`.
5857
*/
59-
transformAssetUrlsBase?: string
58+
transformAssetUrls?: AssetURLOptions | AssetURLTagConfig | boolean
6059
}
6160

6261
function preprocess(
@@ -144,24 +143,19 @@ function doCompileTemplate({
144143
ssr = false,
145144
compiler = ssr ? (CompilerSSR as TemplateCompiler) : CompilerDOM,
146145
compilerOptions = {},
147-
transformAssetUrls,
148-
transformAssetUrlsBase
146+
transformAssetUrls
149147
}: SFCTemplateCompileOptions): SFCTemplateCompileResults {
150148
const errors: CompilerError[] = []
151149

152150
let nodeTransforms: NodeTransform[] = []
153-
if (transformAssetUrls !== false) {
154-
if (transformAssetUrlsBase || isObject(transformAssetUrls)) {
155-
nodeTransforms = [
156-
createAssetUrlTransformWithOptions({
157-
base: transformAssetUrlsBase,
158-
tags: isObject(transformAssetUrls) ? transformAssetUrls : undefined
159-
}),
160-
transformSrcset
161-
]
162-
} else {
163-
nodeTransforms = [transformAssetUrl, transformSrcset]
164-
}
151+
if (isObject(transformAssetUrls)) {
152+
const assetOptions = normalizeOptions(transformAssetUrls)
153+
nodeTransforms = [
154+
createAssetUrlTransformWithOptions(assetOptions),
155+
createSrcsetTransformWithOptions(assetOptions)
156+
]
157+
} else if (transformAssetUrls !== false) {
158+
nodeTransforms = [transformAssetUrl, transformSrcset]
165159
}
166160

167161
let { code, map } = compiler.compile(source, {

0 commit comments

Comments
 (0)