Skip to content

Commit 70e2fe4

Browse files
committed
feat(compiler-sfc): transform srcset
1 parent ec2feea commit 70e2fe4

File tree

3 files changed

+166
-3
lines changed

3 files changed

+166
-3
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`compiler sfc: transform srcset transform srcset 1`] = `
4+
"import { createVNode, createBlock, Fragment, openBlock } from \\"vue\\"
5+
import _imports_0 from './logo.png'
6+
7+
8+
const _hoisted_1 = _imports_0
9+
const _hoisted_2 = _imports_0 + '2x'
10+
const _hoisted_3 = _imports_0 + '2x'
11+
const _hoisted_4 = _imports_0 + ', ' + _imports_0 + '2x'
12+
const _hoisted_5 = _imports_0 + '2x, ' + _imports_0
13+
const _hoisted_6 = _imports_0 + '2x, ' + _imports_0 + '3x'
14+
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + '2x, ' + _imports_0 + '3x'
15+
16+
export default function render() {
17+
const _ctx = this
18+
return (openBlock(), createBlock(Fragment, null, [
19+
createVNode(\\"img\\", {
20+
src: \\"./logo.png\\",
21+
srcset: _hoisted_1
22+
}),
23+
createVNode(\\"img\\", {
24+
src: \\"./logo.png\\",
25+
srcset: _hoisted_2
26+
}),
27+
createVNode(\\"img\\", {
28+
src: \\"./logo.png\\",
29+
srcset: _hoisted_3
30+
}),
31+
createVNode(\\"img\\", {
32+
src: \\"./logo.png\\",
33+
srcset: _hoisted_4
34+
}),
35+
createVNode(\\"img\\", {
36+
src: \\"./logo.png\\",
37+
srcset: _hoisted_5
38+
}),
39+
createVNode(\\"img\\", {
40+
src: \\"./logo.png\\",
41+
srcset: _hoisted_6
42+
}),
43+
createVNode(\\"img\\", {
44+
src: \\"./logo.png\\",
45+
srcset: _hoisted_7
46+
})
47+
]))
48+
}"
49+
`;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { generate, parse, transform } from '@vue/compiler-core'
2+
import { transformSrcset } from '../src/templateTransformSrcset'
3+
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
4+
import { transformBind } from '../../compiler-core/src/transforms/vBind'
5+
6+
function compileWithSrcset(template: string) {
7+
const ast = parse(template)
8+
transform(ast, {
9+
nodeTransforms: [transformSrcset, transformElement],
10+
directiveTransforms: {
11+
bind: transformBind
12+
}
13+
})
14+
return generate(ast, { mode: 'module' })
15+
}
16+
17+
describe('compiler sfc: transform srcset', () => {
18+
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+
`)
28+
29+
expect(result.code).toMatchSnapshot()
30+
})
31+
})
Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,88 @@
1-
import { NodeTransform } from '@vue/compiler-core'
1+
import {
2+
createCompoundExpression,
3+
createSimpleExpression,
4+
NodeTransform,
5+
NodeTypes,
6+
SimpleExpressionNode
7+
} from '@vue/compiler-core'
8+
import { parseUrl } from './templateUtils'
29

3-
export const transformSrcset: NodeTransform = () => {
4-
// TODO
10+
const srcsetTags = ['img', 'source']
11+
12+
interface ImageCandidate {
13+
url: string
14+
descriptor: string
15+
}
16+
17+
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
18+
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
19+
20+
export const transformSrcset: NodeTransform = (node, context) => {
21+
if (node.type === NodeTypes.ELEMENT) {
22+
if (srcsetTags.includes(node.tag) && node.props.length) {
23+
node.props.forEach((attr, index) => {
24+
if (attr.name === 'srcset' && attr.type === NodeTypes.ATTRIBUTE) {
25+
if (!attr.value) return
26+
// same logic as in transform-require.js
27+
const value = attr.value.content
28+
29+
const imageCandidates: ImageCandidate[] = value.split(',').map(s => {
30+
// The attribute value arrives here with all whitespace, except
31+
// normal spaces, represented by escape sequences
32+
const [url, descriptor] = s
33+
.replace(escapedSpaceCharacters, ' ')
34+
.trim()
35+
.split(' ', 2)
36+
return { url, descriptor }
37+
})
38+
39+
const compoundExpression = createCompoundExpression([], attr.loc)
40+
imageCandidates.forEach(({ url, descriptor }, index) => {
41+
const { path } = parseUrl(url)
42+
let exp: SimpleExpressionNode
43+
if (path) {
44+
const importsArray = Array.from(context.imports)
45+
const existingImportsIndex = importsArray.findIndex(
46+
i => i.path === path
47+
)
48+
if (existingImportsIndex > -1) {
49+
exp = createSimpleExpression(
50+
`_imports_${existingImportsIndex}`,
51+
false,
52+
attr.loc,
53+
true
54+
)
55+
} else {
56+
exp = createSimpleExpression(
57+
`_imports_${importsArray.length}`,
58+
false,
59+
attr.loc,
60+
true
61+
)
62+
context.imports.add({ exp, path })
63+
}
64+
compoundExpression.children.push(exp)
65+
}
66+
const isNotLast = imageCandidates.length - 1 > index
67+
if (descriptor && isNotLast) {
68+
compoundExpression.children.push(` + '${descriptor}, ' + `)
69+
} else if (descriptor) {
70+
compoundExpression.children.push(` + '${descriptor}'`)
71+
} else if (isNotLast) {
72+
compoundExpression.children.push(` + ', ' + `)
73+
}
74+
})
75+
76+
node.props[index] = {
77+
type: NodeTypes.DIRECTIVE,
78+
name: 'bind',
79+
arg: createSimpleExpression('srcset', true, attr.loc),
80+
exp: context.hoist(compoundExpression),
81+
modifiers: [],
82+
loc: attr.loc
83+
}
84+
}
85+
})
86+
}
87+
}
588
}

0 commit comments

Comments
 (0)