]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): make asset url imports stringifiable
authorEvan You <yyx990803@gmail.com>
Sun, 5 Dec 2021 17:19:06 +0000 (01:19 +0800)
committerEvan You <yyx990803@gmail.com>
Sun, 5 Dec 2021 17:19:06 +0000 (01:19 +0800)
packages/compiler-dom/src/transforms/stringifyStatic.ts
packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap
packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts
packages/compiler-sfc/src/templateTransformAssetUrl.ts
packages/compiler-sfc/src/templateTransformSrcset.ts

index 4f31f53cad7391e4189133441abf368a81e4a217..877c4d011c2701036d9c9a37165aed3d68e811f6 100644 (file)
@@ -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') {
index 37545d772d3eec1cb9e92c315a70d51b369be871..c0fceecee6fc5bd8a75b004c83dab1cf39e0eca3 100644 (file)
@@ -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`] = `
 "import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
 import _imports_0 from 'bar.png'
index 5cc0fe2a957e40dcbc57aeca4967a6084759a696..89e487f095febbece991594ca74e740223c57f3c 100644 (file)
@@ -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(\\"<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))
+}"
+`;
index 18e2cb5e98e0f40790424071a120052c79ecdf9a..15aff00bfea196585caacd3c6dc122dffc354d9b 100644 (file)
@@ -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(
+      `<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()
+  })
 })
index 465ca56704d5f2c064483f46a8cb5ac602d203f5..f88706c420bd3303bb2e0fb21cde4ba26ccd6590 100644 (file)
@@ -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(
+      `<div>${src}</div>`,
+      {
+        includeAbsolute: true
+      },
+      {
+        hoistStatic: true,
+        transformHoist: stringifyStatic
+      }
+    ).code
+    expect(code).toMatch(`_createStaticVNode`)
+    expect(code).toMatchSnapshot()
+  })
 })
index 4246817dff8d7bf25cae896a65884e8e520b7276..2f6152b8eb6d3bce1e74567c4dc75ca92d517e36 100644 (file)
@@ -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)
   }
 }
index 02cda03abc6ee88b83ff2bbd882a63627958ebd3..edd00928f8af61d9065a9713b708500b35408ce7 100644 (file)
@@ -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,