templateTransformAssetUrl.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import {
  2. AttributeNode,
  3. createSimpleExpression,
  4. ExpressionNode,
  5. NodeTransform,
  6. NodeTypes,
  7. SourceLocation,
  8. TransformContext
  9. } from '@vue/compiler-core'
  10. import { isRelativeUrl, parseUrl } from './templateUtils'
  11. export interface AssetURLOptions {
  12. [name: string]: string[]
  13. }
  14. const defaultOptions: AssetURLOptions = {
  15. video: ['src', 'poster'],
  16. source: ['src'],
  17. img: ['src'],
  18. image: ['xlink:href', 'href'],
  19. use: ['xlink:href', 'href']
  20. }
  21. export const createAssetUrlTransformWithOptions = (
  22. options: AssetURLOptions
  23. ): NodeTransform => {
  24. const mergedOptions = {
  25. ...defaultOptions,
  26. ...options
  27. }
  28. return (node, context) =>
  29. (transformAssetUrl as Function)(node, context, mergedOptions)
  30. }
  31. export const transformAssetUrl: NodeTransform = (
  32. node,
  33. context,
  34. options: AssetURLOptions = defaultOptions
  35. ) => {
  36. if (node.type === NodeTypes.ELEMENT) {
  37. for (const tag in options) {
  38. if ((tag === '*' || node.tag === tag) && node.props.length) {
  39. const attributes = options[tag]
  40. attributes.forEach(item => {
  41. node.props.forEach((attr: AttributeNode, index) => {
  42. if (attr.type !== NodeTypes.ATTRIBUTE) return
  43. if (attr.name !== item) return
  44. if (!attr.value) return
  45. if (!isRelativeUrl(attr.value.content)) return
  46. const url = parseUrl(attr.value.content)
  47. const exp = getImportsExpressionExp(
  48. url.path,
  49. url.hash,
  50. attr.loc,
  51. context
  52. )
  53. node.props[index] = {
  54. type: NodeTypes.DIRECTIVE,
  55. name: 'bind',
  56. arg: createSimpleExpression(item, true, attr.loc),
  57. exp,
  58. modifiers: [],
  59. loc: attr.loc
  60. }
  61. })
  62. })
  63. }
  64. }
  65. }
  66. }
  67. function getImportsExpressionExp(
  68. path: string | undefined,
  69. hash: string | undefined,
  70. loc: SourceLocation,
  71. context: TransformContext
  72. ): ExpressionNode {
  73. if (path) {
  74. const importsArray = Array.from(context.imports)
  75. const existing = importsArray.find(i => i.path === path)
  76. if (existing) {
  77. return existing.exp as ExpressionNode
  78. }
  79. const name = `_imports_${importsArray.length}`
  80. const exp = createSimpleExpression(name, false, loc, true)
  81. context.imports.add({ exp, path })
  82. if (hash && path) {
  83. return context.hoist(
  84. createSimpleExpression(`${name} + '${hash}'`, false, loc, true)
  85. )
  86. } else {
  87. return exp
  88. }
  89. } else {
  90. return createSimpleExpression(`''`, false, loc, true)
  91. }
  92. }