]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): transform srcset (#501)
authorlikui <2218301630@qq.com>
Tue, 3 Dec 2019 04:06:14 +0000 (12:06 +0800)
committerEvan You <yyx990803@gmail.com>
Tue, 3 Dec 2019 04:06:14 +0000 (23:06 -0500)
packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap [new file with mode: 0644]
packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts [new file with mode: 0644]
packages/compiler-sfc/src/templateTransformSrcset.ts

diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap
new file mode 100644 (file)
index 0000000..444b0ea
--- /dev/null
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`compiler sfc: transform srcset transform srcset 1`] = `
+"import { createVNode, createBlock, Fragment, openBlock } from \\"vue\\"
+import _imports_0 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'
+
+export default function render() {
+  const _ctx = this
+  return (openBlock(), createBlock(Fragment, null, [
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_1
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_2
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_3
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_4
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_5
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_6
+    }),
+    createVNode(\\"img\\", {
+      src: \\"./logo.png\\",
+      srcset: _hoisted_7
+    })
+  ]))
+}"
+`;
diff --git a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts
new file mode 100644 (file)
index 0000000..5aedc90
--- /dev/null
@@ -0,0 +1,31 @@
+import { generate, parse, transform } from '@vue/compiler-core'
+import { transformSrcset } from '../src/templateTransformSrcset'
+import { transformElement } from '../../compiler-core/src/transforms/transformElement'
+import { transformBind } from '../../compiler-core/src/transforms/vBind'
+
+function compileWithSrcset(template: string) {
+  const ast = parse(template)
+  transform(ast, {
+    nodeTransforms: [transformSrcset, transformElement],
+    directiveTransforms: {
+      bind: transformBind
+    }
+  })
+  return generate(ast, { mode: 'module' })
+}
+
+describe('compiler sfc: transform srcset', () => {
+  test('transform srcset', () => {
+    const result = compileWithSrcset(`
+                       <img src="./logo.png" srcset="./logo.png"/>
+                       <img src="./logo.png" srcset="./logo.png 2x"/>
+                       <img src="./logo.png" srcset="./logo.png 2x"/>
+                       <img src="./logo.png" srcset="./logo.png, ./logo.png 2x"/>
+                       <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, ./logo.png 2x, ./logo.png 3x"/>
+               `)
+
+    expect(result.code).toMatchSnapshot()
+  })
+})
index 144749ce28c7b2c89e4877f321e3679d78d4ad84..cf557ec255fd816a0fc94e576cf3fc09ff679ba6 100644 (file)
@@ -1,5 +1,88 @@
-import { NodeTransform } from '@vue/compiler-core'
+import {
+  createCompoundExpression,
+  createSimpleExpression,
+  NodeTransform,
+  NodeTypes,
+  SimpleExpressionNode
+} from '@vue/compiler-core'
+import { parseUrl } from './templateUtils'
 
-export const transformSrcset: NodeTransform = () => {
-  // TODO
+const srcsetTags = ['img', 'source']
+
+interface ImageCandidate {
+  url: string
+  descriptor: string
+}
+
+// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
+const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
+
+export const transformSrcset: NodeTransform = (node, context) => {
+  if (node.type === NodeTypes.ELEMENT) {
+    if (srcsetTags.includes(node.tag) && node.props.length) {
+      node.props.forEach((attr, index) => {
+        if (attr.name === 'srcset' && attr.type === NodeTypes.ATTRIBUTE) {
+          if (!attr.value) return
+          // same logic as in transform-require.js
+          const value = attr.value.content
+
+          const imageCandidates: ImageCandidate[] = value.split(',').map(s => {
+            // The attribute value arrives here with all whitespace, except
+            // normal spaces, represented by escape sequences
+            const [url, descriptor] = s
+              .replace(escapedSpaceCharacters, ' ')
+              .trim()
+              .split(' ', 2)
+            return { url, descriptor }
+          })
+
+          const compoundExpression = createCompoundExpression([], attr.loc)
+          imageCandidates.forEach(({ url, descriptor }, index) => {
+            const { path } = parseUrl(url)
+            let exp: SimpleExpressionNode
+            if (path) {
+              const importsArray = Array.from(context.imports)
+              const existingImportsIndex = importsArray.findIndex(
+                i => i.path === path
+              )
+              if (existingImportsIndex > -1) {
+                exp = createSimpleExpression(
+                  `_imports_${existingImportsIndex}`,
+                  false,
+                  attr.loc,
+                  true
+                )
+              } else {
+                exp = createSimpleExpression(
+                  `_imports_${importsArray.length}`,
+                  false,
+                  attr.loc,
+                  true
+                )
+                context.imports.add({ exp, path })
+              }
+              compoundExpression.children.push(exp)
+            }
+            const isNotLast = imageCandidates.length - 1 > index
+            if (descriptor && isNotLast) {
+              compoundExpression.children.push(` + '${descriptor}, ' + `)
+            } else if (descriptor) {
+              compoundExpression.children.push(` + '${descriptor}'`)
+            } else if (isNotLast) {
+              compoundExpression.children.push(` + ', ' + `)
+            }
+          })
+
+          node.props[index] = {
+            type: NodeTypes.DIRECTIVE,
+            name: 'bind',
+            arg: createSimpleExpression('srcset', true, attr.loc),
+            exp: context.hoist(compoundExpression),
+            modifiers: [],
+            loc: attr.loc
+          }
+        }
+      })
+    }
+  }
 }