From 87c73e99d6aed0771f8c955ca9d5188ec22c90e7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 6 Dec 2021 01:19:06 +0800 Subject: [PATCH] fix(compiler-sfc): make asset url imports stringifiable --- .../src/transforms/stringifyStatic.ts | 19 ++++++++- .../templateTransformAssetUrl.spec.ts.snap | 16 ++++++++ .../templateTransformSrcset.spec.ts.snap | 25 ++++++++++++ .../templateTransformAssetUrl.spec.ts | 39 +++++++++++++++++-- .../__tests__/templateTransformSrcset.spec.ts | 32 +++++++++++++-- .../src/templateTransformAssetUrl.ts | 13 +++++-- .../src/templateTransformSrcset.ts | 8 ++-- 7 files changed, 136 insertions(+), 16 deletions(-) diff --git a/packages/compiler-dom/src/transforms/stringifyStatic.ts b/packages/compiler-dom/src/transforms/stringifyStatic.ts index 4f31f53cad7..877c4d011c2 100644 --- a/packages/compiler-dom/src/transforms/stringifyStatic.ts +++ b/packages/compiler-dom/src/transforms/stringifyStatic.ts @@ -38,6 +38,12 @@ export const enum StringifyThresholds { 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. * @@ -80,7 +86,7 @@ export const stringifyStatic: HoistTransform = (children, context, parent) => { const staticCall = createCallExpression(context.helper(CREATE_STATIC), [ JSON.stringify( currentChunk.map(node => stringifyNode(node, context)).join('') - ), + ).replace(expReplaceRE, `" + $1 + "`), // the 2nd argument indicates the number of DOM nodes this static vnode // will insert / hydrate String(currentChunk.length) @@ -273,8 +279,17 @@ function stringifyElement( res += `="${escapeHtml(p.value.content)}"` } } 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" - let evaluated = evaluateConstant(p.exp as SimpleExpressionNode) + let evaluated = evaluateConstant(exp) if (evaluated != null) { const arg = p.arg && (p.arg as SimpleExpressionNode).content if (arg === 'class') { diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap index 37545d772d3..c0fceecee6f 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap @@ -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(\\"\\", 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`] = ` "import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\" import _imports_0 from 'bar.png' diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap index 5cc0fe2a957..89e487f095f 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap @@ -194,3 +194,28 @@ export function render(_ctx, _cache) { ], 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(\\"\\", 12) +const _hoisted_22 = [ + _hoisted_10 +] + +export function render(_ctx, _cache) { + return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_22)) +}" +`; diff --git a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts index 18e2cb5e98e..15aff00bfea 100644 --- a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts +++ b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts @@ -1,4 +1,9 @@ -import { generate, baseParse, transform } from '@vue/compiler-core' +import { + generate, + baseParse, + transform, + TransformOptions +} from '@vue/compiler-core' import { transformAssetUrl, createAssetUrlTransformWithOptions, @@ -7,8 +12,13 @@ import { } from '../src/templateTransformAssetUrl' import { transformElement } from '../../compiler-core/src/transforms/transformElement' 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 t = options ? createAssetUrlTransformWithOptions(normalizeOptions(options)) @@ -17,7 +27,8 @@ function compileWithAssetUrls(template: string, options?: AssetURLOptions) { nodeTransforms: [t, transformElement], directiveTransforms: { bind: transformBind - } + }, + ...transformOptions }) return generate(ast, { mode: 'module' }) } @@ -131,4 +142,26 @@ describe('compiler sfc: transform asset url', () => { expect(code).toMatchSnapshot() }) + + test('transform with stringify', () => { + const { code } = compileWithAssetUrls( + `
` + + `` + + `` + + `` + + `` + + `` + + `
`, + { + includeAbsolute: true + }, + { + hoistStatic: true, + transformHoist: stringifyStatic + } + ) + console.log(code) + expect(code).toMatch(`_createStaticVNode`) + expect(code).toMatchSnapshot() + }) }) diff --git a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts index 465ca56704d..f88706c420b 100644 --- a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts +++ b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts @@ -1,4 +1,9 @@ -import { generate, baseParse, transform } from '@vue/compiler-core' +import { + generate, + baseParse, + transform, + TransformOptions +} from '@vue/compiler-core' import { transformSrcset, createSrcsetTransformWithOptions @@ -9,8 +14,13 @@ import { AssetURLOptions, normalizeOptions } 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 srcsetTransform = options ? createSrcsetTransformWithOptions(normalizeOptions(options)) @@ -19,7 +29,8 @@ function compileWithSrcset(template: string, options?: AssetURLOptions) { nodeTransforms: [srcsetTransform, transformElement], directiveTransforms: { bind: transformBind - } + }, + ...transformOptions }) return generate(ast, { mode: 'module' }) } @@ -59,4 +70,19 @@ describe('compiler sfc: transform srcset', () => { }).code ).toMatchSnapshot() }) + + test('transform srcset w/ stringify', () => { + const code = compileWithSrcset( + `
${src}
`, + { + includeAbsolute: true + }, + { + hoistStatic: true, + transformHoist: stringifyStatic + } + ).code + expect(code).toMatch(`_createStaticVNode`) + expect(code).toMatchSnapshot() + }) }) diff --git a/packages/compiler-sfc/src/templateTransformAssetUrl.ts b/packages/compiler-sfc/src/templateTransformAssetUrl.ts index 4246817dff8..2f6152b8eb6 100644 --- a/packages/compiler-sfc/src/templateTransformAssetUrl.ts +++ b/packages/compiler-sfc/src/templateTransformAssetUrl.ts @@ -162,7 +162,12 @@ function getImportsExpressionExp( exp = context.imports[existingIndex].exp as SimpleExpressionNode } else { 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 }) } @@ -184,13 +189,13 @@ function getImportsExpressionExp( `_hoisted_${existingHoistIndex + 1}`, false, loc, - ConstantTypes.CAN_HOIST + ConstantTypes.CAN_STRINGIFY ) } return context.hoist( - createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_HOIST) + createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_STRINGIFY) ) } else { - return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_HOIST) + return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY) } } diff --git a/packages/compiler-sfc/src/templateTransformSrcset.ts b/packages/compiler-sfc/src/templateTransformSrcset.ts index 02cda03abc6..edd00928f8a 100644 --- a/packages/compiler-sfc/src/templateTransformSrcset.ts +++ b/packages/compiler-sfc/src/templateTransformSrcset.ts @@ -113,14 +113,14 @@ export const transformSrcset: NodeTransform = ( `_imports_${existingImportsIndex}`, false, attr.loc, - ConstantTypes.CAN_HOIST + ConstantTypes.CAN_STRINGIFY ) } else { exp = createSimpleExpression( `_imports_${context.imports.length}`, false, attr.loc, - ConstantTypes.CAN_HOIST + ConstantTypes.CAN_STRINGIFY ) context.imports.push({ exp, path }) } @@ -131,7 +131,7 @@ export const transformSrcset: NodeTransform = ( `"${url}"`, false, attr.loc, - ConstantTypes.CAN_HOIST + ConstantTypes.CAN_STRINGIFY ) compoundExpression.children.push(exp) } @@ -146,7 +146,7 @@ export const transformSrcset: NodeTransform = ( }) const hoisted = context.hoist(compoundExpression) - hoisted.constType = ConstantTypes.CAN_HOIST + hoisted.constType = ConstantTypes.CAN_STRINGIFY node.props[index] = { type: NodeTypes.DIRECTIVE,