fix(compiler-sfc): make asset url imports stringifiable
This commit is contained in:
parent
3e5ed6c1fc
commit
87c73e99d6
@ -38,6 +38,12 @@ export const enum StringifyThresholds {
|
|||||||
|
|
||||||
type StringifiableNode = PlainElementNode | TextCallNode
|
type StringifiableNode = PlainElementNode | TextCallNode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex for replacing placeholders for embedded constant variables
|
||||||
|
* (e.g. import URL string constants generated by compiler-sfc)
|
||||||
|
*/
|
||||||
|
const expReplaceRE = /__VUE_EXP_START__(.*?)__VUE_EXP_END__/g
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn eligible hoisted static trees into stringified static nodes, e.g.
|
* Turn eligible hoisted static trees into stringified static nodes, e.g.
|
||||||
*
|
*
|
||||||
@ -80,7 +86,7 @@ export const stringifyStatic: HoistTransform = (children, context, parent) => {
|
|||||||
const staticCall = createCallExpression(context.helper(CREATE_STATIC), [
|
const staticCall = createCallExpression(context.helper(CREATE_STATIC), [
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
currentChunk.map(node => stringifyNode(node, context)).join('')
|
currentChunk.map(node => stringifyNode(node, context)).join('')
|
||||||
),
|
).replace(expReplaceRE, `" + $1 + "`),
|
||||||
// the 2nd argument indicates the number of DOM nodes this static vnode
|
// the 2nd argument indicates the number of DOM nodes this static vnode
|
||||||
// will insert / hydrate
|
// will insert / hydrate
|
||||||
String(currentChunk.length)
|
String(currentChunk.length)
|
||||||
@ -273,8 +279,17 @@ function stringifyElement(
|
|||||||
res += `="${escapeHtml(p.value.content)}"`
|
res += `="${escapeHtml(p.value.content)}"`
|
||||||
}
|
}
|
||||||
} else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
|
} else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
|
||||||
|
const exp = p.exp as SimpleExpressionNode
|
||||||
|
if (exp.content[0] === '_') {
|
||||||
|
// internally generated string constant references
|
||||||
|
// e.g. imported URL strings via compiler-sfc transformAssetUrl plugin
|
||||||
|
res += ` ${(p.arg as SimpleExpressionNode).content}="__VUE_EXP_START__${
|
||||||
|
exp.content
|
||||||
|
}__VUE_EXP_END__"`
|
||||||
|
continue
|
||||||
|
}
|
||||||
// constant v-bind, e.g. :foo="1"
|
// constant v-bind, e.g. :foo="1"
|
||||||
let evaluated = evaluateConstant(p.exp as SimpleExpressionNode)
|
let evaluated = evaluateConstant(exp)
|
||||||
if (evaluated != null) {
|
if (evaluated != null) {
|
||||||
const arg = p.arg && (p.arg as SimpleExpressionNode).content
|
const arg = p.arg && (p.arg as SimpleExpressionNode).content
|
||||||
if (arg === 'class') {
|
if (arg === 'class') {
|
||||||
|
@ -74,6 +74,22 @@ export function render(_ctx, _cache) {
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler sfc: transform asset url transform with stringify 1`] = `
|
||||||
|
"import { createElementVNode as _createElementVNode, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
|
import _imports_0 from './bar.png'
|
||||||
|
import _imports_1 from '/bar.png'
|
||||||
|
|
||||||
|
|
||||||
|
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<img src=\\\\\\"\\" + _imports_0 + \\"\\\\\\"><img src=\\\\\\"\\" + _imports_1 + \\"\\\\\\"><img src=\\\\\\"https://foo.bar/baz.png\\\\\\"><img src=\\\\\\"//foo.bar/baz.png\\\\\\"><img src=\\\\\\"\\" + _imports_0 + \\"\\\\\\">\\", 5)
|
||||||
|
const _hoisted_6 = [
|
||||||
|
_hoisted_1
|
||||||
|
]
|
||||||
|
|
||||||
|
export function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_6))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler sfc: transform asset url with explicit base 1`] = `
|
exports[`compiler sfc: transform asset url with explicit base 1`] = `
|
||||||
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
import _imports_0 from 'bar.png'
|
import _imports_0 from 'bar.png'
|
||||||
|
@ -194,3 +194,28 @@ export function render(_ctx, _cache) {
|
|||||||
], 64 /* STABLE_FRAGMENT */))
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler sfc: transform srcset transform srcset w/ stringify 1`] = `
|
||||||
|
"import { createElementVNode as _createElementVNode, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
|
||||||
|
import _imports_0 from './logo.png'
|
||||||
|
import _imports_1 from '/logo.png'
|
||||||
|
|
||||||
|
|
||||||
|
const _hoisted_1 = _imports_0
|
||||||
|
const _hoisted_2 = _imports_0 + ' 2x'
|
||||||
|
const _hoisted_3 = _imports_0 + ' 2x'
|
||||||
|
const _hoisted_4 = _imports_0 + ', ' + _imports_0 + ' 2x'
|
||||||
|
const _hoisted_5 = _imports_0 + ' 2x, ' + _imports_0
|
||||||
|
const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
|
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
|
||||||
|
const _hoisted_8 = _imports_1 + ', ' + _imports_1 + ' 2x'
|
||||||
|
const _hoisted_9 = _imports_1 + ', ' + _imports_0 + ' 2x'
|
||||||
|
const _hoisted_10 = /*#__PURE__*/_createStaticVNode(\\"<img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_1 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_2 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_3 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_4 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_5 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_6 + \\"\\\\\\"><img src=\\\\\\"./logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_7 + \\"\\\\\\"><img src=\\\\\\"/logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_8 + \\"\\\\\\"><img src=\\\\\\"https://example.com/logo.png\\\\\\" srcset=\\\\\\"https://example.com/logo.png, https://example.com/logo.png 2x\\\\\\"><img src=\\\\\\"/logo.png\\\\\\" srcset=\\\\\\"\\" + _hoisted_9 + \\"\\\\\\"><img src=\\\\\\"data:image/png;base64,i\\\\\\" srcset=\\\\\\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\\\\\">\\", 12)
|
||||||
|
const _hoisted_22 = [
|
||||||
|
_hoisted_10
|
||||||
|
]
|
||||||
|
|
||||||
|
export function render(_ctx, _cache) {
|
||||||
|
return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_22))
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { generate, baseParse, transform } from '@vue/compiler-core'
|
import {
|
||||||
|
generate,
|
||||||
|
baseParse,
|
||||||
|
transform,
|
||||||
|
TransformOptions
|
||||||
|
} from '@vue/compiler-core'
|
||||||
import {
|
import {
|
||||||
transformAssetUrl,
|
transformAssetUrl,
|
||||||
createAssetUrlTransformWithOptions,
|
createAssetUrlTransformWithOptions,
|
||||||
@ -7,8 +12,13 @@ import {
|
|||||||
} from '../src/templateTransformAssetUrl'
|
} from '../src/templateTransformAssetUrl'
|
||||||
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
|
import { transformElement } from '../../compiler-core/src/transforms/transformElement'
|
||||||
import { transformBind } from '../../compiler-core/src/transforms/vBind'
|
import { transformBind } from '../../compiler-core/src/transforms/vBind'
|
||||||
|
import { stringifyStatic } from '../../compiler-dom/src/transforms/stringifyStatic'
|
||||||
|
|
||||||
function compileWithAssetUrls(template: string, options?: AssetURLOptions) {
|
function compileWithAssetUrls(
|
||||||
|
template: string,
|
||||||
|
options?: AssetURLOptions,
|
||||||
|
transformOptions?: TransformOptions
|
||||||
|
) {
|
||||||
const ast = baseParse(template)
|
const ast = baseParse(template)
|
||||||
const t = options
|
const t = options
|
||||||
? createAssetUrlTransformWithOptions(normalizeOptions(options))
|
? createAssetUrlTransformWithOptions(normalizeOptions(options))
|
||||||
@ -17,7 +27,8 @@ function compileWithAssetUrls(template: string, options?: AssetURLOptions) {
|
|||||||
nodeTransforms: [t, transformElement],
|
nodeTransforms: [t, transformElement],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
bind: transformBind
|
bind: transformBind
|
||||||
}
|
},
|
||||||
|
...transformOptions
|
||||||
})
|
})
|
||||||
return generate(ast, { mode: 'module' })
|
return generate(ast, { mode: 'module' })
|
||||||
}
|
}
|
||||||
@ -131,4 +142,26 @@ describe('compiler sfc: transform asset url', () => {
|
|||||||
|
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('transform with stringify', () => {
|
||||||
|
const { code } = compileWithAssetUrls(
|
||||||
|
`<div>` +
|
||||||
|
`<img src="./bar.png"/>` +
|
||||||
|
`<img src="/bar.png"/>` +
|
||||||
|
`<img src="https://foo.bar/baz.png"/>` +
|
||||||
|
`<img src="//foo.bar/baz.png"/>` +
|
||||||
|
`<img src="./bar.png"/>` +
|
||||||
|
`</div>`,
|
||||||
|
{
|
||||||
|
includeAbsolute: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hoistStatic: true,
|
||||||
|
transformHoist: stringifyStatic
|
||||||
|
}
|
||||||
|
)
|
||||||
|
console.log(code)
|
||||||
|
expect(code).toMatch(`_createStaticVNode`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { generate, baseParse, transform } from '@vue/compiler-core'
|
import {
|
||||||
|
generate,
|
||||||
|
baseParse,
|
||||||
|
transform,
|
||||||
|
TransformOptions
|
||||||
|
} from '@vue/compiler-core'
|
||||||
import {
|
import {
|
||||||
transformSrcset,
|
transformSrcset,
|
||||||
createSrcsetTransformWithOptions
|
createSrcsetTransformWithOptions
|
||||||
@ -9,8 +14,13 @@ import {
|
|||||||
AssetURLOptions,
|
AssetURLOptions,
|
||||||
normalizeOptions
|
normalizeOptions
|
||||||
} from '../src/templateTransformAssetUrl'
|
} from '../src/templateTransformAssetUrl'
|
||||||
|
import { stringifyStatic } from '../../compiler-dom/src/transforms/stringifyStatic'
|
||||||
|
|
||||||
function compileWithSrcset(template: string, options?: AssetURLOptions) {
|
function compileWithSrcset(
|
||||||
|
template: string,
|
||||||
|
options?: AssetURLOptions,
|
||||||
|
transformOptions?: TransformOptions
|
||||||
|
) {
|
||||||
const ast = baseParse(template)
|
const ast = baseParse(template)
|
||||||
const srcsetTransform = options
|
const srcsetTransform = options
|
||||||
? createSrcsetTransformWithOptions(normalizeOptions(options))
|
? createSrcsetTransformWithOptions(normalizeOptions(options))
|
||||||
@ -19,7 +29,8 @@ function compileWithSrcset(template: string, options?: AssetURLOptions) {
|
|||||||
nodeTransforms: [srcsetTransform, transformElement],
|
nodeTransforms: [srcsetTransform, transformElement],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
bind: transformBind
|
bind: transformBind
|
||||||
}
|
},
|
||||||
|
...transformOptions
|
||||||
})
|
})
|
||||||
return generate(ast, { mode: 'module' })
|
return generate(ast, { mode: 'module' })
|
||||||
}
|
}
|
||||||
@ -59,4 +70,19 @@ describe('compiler sfc: transform srcset', () => {
|
|||||||
}).code
|
}).code
|
||||||
).toMatchSnapshot()
|
).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('transform srcset w/ stringify', () => {
|
||||||
|
const code = compileWithSrcset(
|
||||||
|
`<div>${src}</div>`,
|
||||||
|
{
|
||||||
|
includeAbsolute: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hoistStatic: true,
|
||||||
|
transformHoist: stringifyStatic
|
||||||
|
}
|
||||||
|
).code
|
||||||
|
expect(code).toMatch(`_createStaticVNode`)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -162,7 +162,12 @@ function getImportsExpressionExp(
|
|||||||
exp = context.imports[existingIndex].exp as SimpleExpressionNode
|
exp = context.imports[existingIndex].exp as SimpleExpressionNode
|
||||||
} else {
|
} else {
|
||||||
name = `_imports_${context.imports.length}`
|
name = `_imports_${context.imports.length}`
|
||||||
exp = createSimpleExpression(name, false, loc, ConstantTypes.CAN_HOIST)
|
exp = createSimpleExpression(
|
||||||
|
name,
|
||||||
|
false,
|
||||||
|
loc,
|
||||||
|
ConstantTypes.CAN_STRINGIFY
|
||||||
|
)
|
||||||
context.imports.push({ exp, path })
|
context.imports.push({ exp, path })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,13 +189,13 @@ function getImportsExpressionExp(
|
|||||||
`_hoisted_${existingHoistIndex + 1}`,
|
`_hoisted_${existingHoistIndex + 1}`,
|
||||||
false,
|
false,
|
||||||
loc,
|
loc,
|
||||||
ConstantTypes.CAN_HOIST
|
ConstantTypes.CAN_STRINGIFY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return context.hoist(
|
return context.hoist(
|
||||||
createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_HOIST)
|
createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_STRINGIFY)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_HOIST)
|
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,14 +113,14 @@ export const transformSrcset: NodeTransform = (
|
|||||||
`_imports_${existingImportsIndex}`,
|
`_imports_${existingImportsIndex}`,
|
||||||
false,
|
false,
|
||||||
attr.loc,
|
attr.loc,
|
||||||
ConstantTypes.CAN_HOIST
|
ConstantTypes.CAN_STRINGIFY
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
exp = createSimpleExpression(
|
exp = createSimpleExpression(
|
||||||
`_imports_${context.imports.length}`,
|
`_imports_${context.imports.length}`,
|
||||||
false,
|
false,
|
||||||
attr.loc,
|
attr.loc,
|
||||||
ConstantTypes.CAN_HOIST
|
ConstantTypes.CAN_STRINGIFY
|
||||||
)
|
)
|
||||||
context.imports.push({ exp, path })
|
context.imports.push({ exp, path })
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ export const transformSrcset: NodeTransform = (
|
|||||||
`"${url}"`,
|
`"${url}"`,
|
||||||
false,
|
false,
|
||||||
attr.loc,
|
attr.loc,
|
||||||
ConstantTypes.CAN_HOIST
|
ConstantTypes.CAN_STRINGIFY
|
||||||
)
|
)
|
||||||
compoundExpression.children.push(exp)
|
compoundExpression.children.push(exp)
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ export const transformSrcset: NodeTransform = (
|
|||||||
})
|
})
|
||||||
|
|
||||||
const hoisted = context.hoist(compoundExpression)
|
const hoisted = context.hoist(compoundExpression)
|
||||||
hoisted.constType = ConstantTypes.CAN_HOIST
|
hoisted.constType = ConstantTypes.CAN_STRINGIFY
|
||||||
|
|
||||||
node.props[index] = {
|
node.props[index] = {
|
||||||
type: NodeTypes.DIRECTIVE,
|
type: NodeTypes.DIRECTIVE,
|
||||||
|
Loading…
Reference in New Issue
Block a user