templateTransformSrcset.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import {
  2. createCompoundExpression,
  3. createSimpleExpression,
  4. NodeTransform,
  5. NodeTypes,
  6. SimpleExpressionNode
  7. } from '@vue/compiler-core'
  8. import { parseUrl } from './templateUtils'
  9. const srcsetTags = ['img', 'source']
  10. interface ImageCandidate {
  11. url: string
  12. descriptor: string
  13. }
  14. // http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
  15. const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
  16. export const transformSrcset: NodeTransform = (node, context) => {
  17. if (node.type === NodeTypes.ELEMENT) {
  18. if (srcsetTags.includes(node.tag) && node.props.length) {
  19. node.props.forEach((attr, index) => {
  20. if (attr.name === 'srcset' && attr.type === NodeTypes.ATTRIBUTE) {
  21. if (!attr.value) return
  22. // same logic as in transform-require.js
  23. const value = attr.value.content
  24. const imageCandidates: ImageCandidate[] = value.split(',').map(s => {
  25. // The attribute value arrives here with all whitespace, except
  26. // normal spaces, represented by escape sequences
  27. const [url, descriptor] = s
  28. .replace(escapedSpaceCharacters, ' ')
  29. .trim()
  30. .split(' ', 2)
  31. return { url, descriptor }
  32. })
  33. const compoundExpression = createCompoundExpression([], attr.loc)
  34. imageCandidates.forEach(({ url, descriptor }, index) => {
  35. const { path } = parseUrl(url)
  36. let exp: SimpleExpressionNode
  37. if (path) {
  38. const importsArray = Array.from(context.imports)
  39. const existingImportsIndex = importsArray.findIndex(
  40. i => i.path === path
  41. )
  42. if (existingImportsIndex > -1) {
  43. exp = createSimpleExpression(
  44. `_imports_${existingImportsIndex}`,
  45. false,
  46. attr.loc,
  47. true
  48. )
  49. } else {
  50. exp = createSimpleExpression(
  51. `_imports_${importsArray.length}`,
  52. false,
  53. attr.loc,
  54. true
  55. )
  56. context.imports.add({ exp, path })
  57. }
  58. compoundExpression.children.push(exp)
  59. }
  60. const isNotLast = imageCandidates.length - 1 > index
  61. if (descriptor && isNotLast) {
  62. compoundExpression.children.push(` + '${descriptor}, ' + `)
  63. } else if (descriptor) {
  64. compoundExpression.children.push(` + '${descriptor}'`)
  65. } else if (isNotLast) {
  66. compoundExpression.children.push(` + ', ' + `)
  67. }
  68. })
  69. node.props[index] = {
  70. type: NodeTypes.DIRECTIVE,
  71. name: 'bind',
  72. arg: createSimpleExpression('srcset', true, attr.loc),
  73. exp: context.hoist(compoundExpression),
  74. modifiers: [],
  75. loc: attr.loc
  76. }
  77. }
  78. })
  79. }
  80. }
  81. }