fix(compiler-sfc): only transform relative asset URLs (#628)
This commit is contained in:
parent
787ac5f74e
commit
c71ca354b9
@ -33,7 +33,9 @@ export function render() {
|
|||||||
return (openBlock(), createBlock(Fragment, null, [
|
return (openBlock(), createBlock(Fragment, null, [
|
||||||
createVNode(\\"img\\", { src: _imports_0 }),
|
createVNode(\\"img\\", { src: _imports_0 }),
|
||||||
createVNode(\\"img\\", { src: _imports_1 }),
|
createVNode(\\"img\\", { src: _imports_1 }),
|
||||||
createVNode(\\"img\\", { src: _imports_1 })
|
createVNode(\\"img\\", { src: _imports_1 }),
|
||||||
|
createVNode(\\"img\\", { src: \\"http://example.com/fixtures/logo.png\\" }),
|
||||||
|
createVNode(\\"img\\", { src: \\"/fixtures/logo.png\\" })
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -12,6 +12,7 @@ const _hoisted_4 = _imports_0 + ', ' + _imports_0 + '2x'
|
|||||||
const _hoisted_5 = _imports_0 + '2x, ' + _imports_0
|
const _hoisted_5 = _imports_0 + '2x, ' + _imports_0
|
||||||
const _hoisted_6 = _imports_0 + '2x, ' + _imports_0 + '3x'
|
const _hoisted_6 = _imports_0 + '2x, ' + _imports_0 + '3x'
|
||||||
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + '2x, ' + _imports_0 + '3x'
|
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + '2x, ' + _imports_0 + '3x'
|
||||||
|
const _hoisted_8 = \\"/logo.png\\" + ', ' + _imports_0 + '2x'
|
||||||
|
|
||||||
export function render() {
|
export function render() {
|
||||||
const _ctx = this
|
const _ctx = this
|
||||||
@ -43,6 +44,18 @@ export function render() {
|
|||||||
createVNode(\\"img\\", {
|
createVNode(\\"img\\", {
|
||||||
src: \\"./logo.png\\",
|
src: \\"./logo.png\\",
|
||||||
srcset: _hoisted_7
|
srcset: _hoisted_7
|
||||||
|
}),
|
||||||
|
createVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: \\"/logo.png, /logo.png 2x\\"
|
||||||
|
}),
|
||||||
|
createVNode(\\"img\\", {
|
||||||
|
src: \\"https://example.com/logo.png\\",
|
||||||
|
srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
|
||||||
|
}),
|
||||||
|
createVNode(\\"img\\", {
|
||||||
|
src: \\"/logo.png\\",
|
||||||
|
srcset: _hoisted_8
|
||||||
})
|
})
|
||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
|
@ -50,7 +50,7 @@ test('warn missing preprocessor', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('transform asset url options', () => {
|
test('transform asset url options', () => {
|
||||||
const input = { source: `<foo bar="baz"/>`, filename: 'example.vue' }
|
const input = { source: `<foo bar="~baz"/>`, filename: 'example.vue' }
|
||||||
// Object option
|
// Object option
|
||||||
const { code: code1 } = compileTemplate({
|
const { code: code1 } = compileTemplate({
|
||||||
...input,
|
...input,
|
||||||
|
@ -20,6 +20,8 @@ describe('compiler sfc: transform asset url', () => {
|
|||||||
<img src="./logo.png"/>
|
<img src="./logo.png"/>
|
||||||
<img src="~fixtures/logo.png"/>
|
<img src="~fixtures/logo.png"/>
|
||||||
<img src="~/fixtures/logo.png"/>
|
<img src="~/fixtures/logo.png"/>
|
||||||
|
<img src="http://example.com/fixtures/logo.png"/>
|
||||||
|
<img src="/fixtures/logo.png"/>
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(result.code).toMatchSnapshot()
|
expect(result.code).toMatchSnapshot()
|
||||||
|
@ -24,6 +24,9 @@ describe('compiler sfc: transform srcset', () => {
|
|||||||
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png"/>
|
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png"/>
|
||||||
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x"/>
|
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x"/>
|
||||||
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x"/>
|
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x"/>
|
||||||
|
<img src="/logo.png" srcset="/logo.png, /logo.png 2x"/>
|
||||||
|
<img src="https://example.com/logo.png" srcset="https://example.com/logo.png, https://example.com/logo.png 2x"/>
|
||||||
|
<img src="/logo.png" srcset="/logo.png, ./logo.png 2x"/>
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(result.code).toMatchSnapshot()
|
expect(result.code).toMatchSnapshot()
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
SourceLocation,
|
SourceLocation,
|
||||||
TransformContext
|
TransformContext
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { parseUrl } from './templateUtils'
|
import { isRelativeUrl, parseUrl } from './templateUtils'
|
||||||
|
|
||||||
export interface AssetURLOptions {
|
export interface AssetURLOptions {
|
||||||
[name: string]: string[]
|
[name: string]: string[]
|
||||||
@ -46,6 +46,7 @@ export const transformAssetUrl: NodeTransform = (
|
|||||||
if (attr.type !== NodeTypes.ATTRIBUTE) return
|
if (attr.type !== NodeTypes.ATTRIBUTE) return
|
||||||
if (attr.name !== item) return
|
if (attr.name !== item) return
|
||||||
if (!attr.value) return
|
if (!attr.value) return
|
||||||
|
if (!isRelativeUrl(attr.value.content)) return
|
||||||
const url = parseUrl(attr.value.content)
|
const url = parseUrl(attr.value.content)
|
||||||
const exp = getImportsExpressionExp(
|
const exp = getImportsExpressionExp(
|
||||||
url.path,
|
url.path,
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
NodeTypes,
|
NodeTypes,
|
||||||
SimpleExpressionNode
|
SimpleExpressionNode
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { parseUrl } from './templateUtils'
|
import { isRelativeUrl, parseUrl } from './templateUtils'
|
||||||
|
|
||||||
const srcsetTags = ['img', 'source']
|
const srcsetTags = ['img', 'source']
|
||||||
|
|
||||||
@ -36,31 +36,44 @@ export const transformSrcset: NodeTransform = (node, context) => {
|
|||||||
return { url, descriptor }
|
return { url, descriptor }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// When srcset does not contain any relative URLs, skip transforming
|
||||||
|
if (!imageCandidates.some(({ url }) => isRelativeUrl(url))) return
|
||||||
|
|
||||||
const compoundExpression = createCompoundExpression([], attr.loc)
|
const compoundExpression = createCompoundExpression([], attr.loc)
|
||||||
imageCandidates.forEach(({ url, descriptor }, index) => {
|
imageCandidates.forEach(({ url, descriptor }, index) => {
|
||||||
const { path } = parseUrl(url)
|
if (isRelativeUrl(url)) {
|
||||||
let exp: SimpleExpressionNode
|
const { path } = parseUrl(url)
|
||||||
if (path) {
|
let exp: SimpleExpressionNode
|
||||||
const importsArray = Array.from(context.imports)
|
if (path) {
|
||||||
const existingImportsIndex = importsArray.findIndex(
|
const importsArray = Array.from(context.imports)
|
||||||
i => i.path === path
|
const existingImportsIndex = importsArray.findIndex(
|
||||||
)
|
i => i.path === path
|
||||||
if (existingImportsIndex > -1) {
|
|
||||||
exp = createSimpleExpression(
|
|
||||||
`_imports_${existingImportsIndex}`,
|
|
||||||
false,
|
|
||||||
attr.loc,
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
} else {
|
if (existingImportsIndex > -1) {
|
||||||
exp = createSimpleExpression(
|
exp = createSimpleExpression(
|
||||||
`_imports_${importsArray.length}`,
|
`_imports_${existingImportsIndex}`,
|
||||||
false,
|
false,
|
||||||
attr.loc,
|
attr.loc,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
context.imports.add({ exp, path })
|
} else {
|
||||||
|
exp = createSimpleExpression(
|
||||||
|
`_imports_${importsArray.length}`,
|
||||||
|
false,
|
||||||
|
attr.loc,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
context.imports.add({ exp, path })
|
||||||
|
}
|
||||||
|
compoundExpression.children.push(exp)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const exp = createSimpleExpression(
|
||||||
|
`"${url}"`,
|
||||||
|
false,
|
||||||
|
attr.loc,
|
||||||
|
true
|
||||||
|
)
|
||||||
compoundExpression.children.push(exp)
|
compoundExpression.children.push(exp)
|
||||||
}
|
}
|
||||||
const isNotLast = imageCandidates.length - 1 > index
|
const isNotLast = imageCandidates.length - 1 > index
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { UrlWithStringQuery, parse as uriParse } from 'url'
|
import { UrlWithStringQuery, parse as uriParse } from 'url'
|
||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
|
|
||||||
|
export function isRelativeUrl(url: string): boolean {
|
||||||
|
const firstChar = url.charAt(0)
|
||||||
|
return firstChar === '.' || firstChar === '~' || firstChar === '@'
|
||||||
|
}
|
||||||
|
|
||||||
// We need an extra transform context API for injecting arbitrary import
|
// We need an extra transform context API for injecting arbitrary import
|
||||||
// statements.
|
// statements.
|
||||||
export function parseUrl(url: string): UrlWithStringQuery {
|
export function parseUrl(url: string): UrlWithStringQuery {
|
||||||
const firstChar = url.charAt(0)
|
const firstChar = url.charAt(0)
|
||||||
if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
|
if (firstChar === '~') {
|
||||||
if (firstChar === '~') {
|
const secondChar = url.charAt(1)
|
||||||
const secondChar = url.charAt(1)
|
url = url.slice(secondChar === '/' ? 2 : 1)
|
||||||
url = url.slice(secondChar === '/' ? 2 : 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return parseUriParts(url)
|
return parseUriParts(url)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user