| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- import path from 'path'
- import {
- ConstantTypes,
- createCompoundExpression,
- createSimpleExpression,
- NodeTransform,
- NodeTypes,
- SimpleExpressionNode
- } from '@vue/compiler-core'
- import {
- isRelativeUrl,
- parseUrl,
- isExternalUrl,
- isDataUrl
- } from './templateUtils'
- import {
- AssetURLOptions,
- defaultAssetUrlOptions
- } from './templateTransformAssetUrl'
- 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 createSrcsetTransformWithOptions = (
- options: Required<AssetURLOptions>
- ): NodeTransform => {
- return (node, context) =>
- (transformSrcset as Function)(node, context, options)
- }
- export const transformSrcset: NodeTransform = (
- node,
- context,
- options: Required<AssetURLOptions> = defaultAssetUrlOptions
- ) => {
- 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
- const value = attr.value.content
- if (!value) return
- 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 }
- })
- // for data url need recheck url
- for (let i = 0; i < imageCandidates.length; i++) {
- if (imageCandidates[i].url.trim().startsWith('data:')) {
- imageCandidates[i + 1].url =
- imageCandidates[i].url + ',' + imageCandidates[i + 1].url
- imageCandidates.splice(i, 1)
- }
- }
- // When srcset does not contain any relative URLs, skip transforming
- if (
- !options.includeAbsolute &&
- !imageCandidates.some(({ url }) => isRelativeUrl(url))
- ) {
- return
- }
- if (options.base) {
- const base = options.base
- const set: string[] = []
- imageCandidates.forEach(({ url, descriptor }) => {
- descriptor = descriptor ? ` ${descriptor}` : ``
- if (isRelativeUrl(url)) {
- set.push((path.posix || path).join(base, url) + descriptor)
- } else {
- set.push(url + descriptor)
- }
- })
- attr.value.content = set.join(', ')
- return
- }
- const compoundExpression = createCompoundExpression([], attr.loc)
- imageCandidates.forEach(({ url, descriptor }, index) => {
- if (
- !isExternalUrl(url) &&
- !isDataUrl(url) &&
- (options.includeAbsolute || isRelativeUrl(url))
- ) {
- const { path } = parseUrl(url)
- let exp: SimpleExpressionNode
- if (path) {
- const existingImportsIndex = context.imports.findIndex(
- i => i.path === path
- )
- if (existingImportsIndex > -1) {
- exp = createSimpleExpression(
- `_imports_${existingImportsIndex}`,
- false,
- attr.loc,
- ConstantTypes.CAN_HOIST
- )
- } else {
- exp = createSimpleExpression(
- `_imports_${context.imports.length}`,
- false,
- attr.loc,
- ConstantTypes.CAN_HOIST
- )
- context.imports.push({ exp, path })
- }
- compoundExpression.children.push(exp)
- }
- } else {
- const exp = createSimpleExpression(
- `"${url}"`,
- false,
- attr.loc,
- ConstantTypes.CAN_HOIST
- )
- 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(` + ', ' + `)
- }
- })
- const hoisted = context.hoist(compoundExpression)
- hoisted.constType = ConstantTypes.CAN_HOIST
- node.props[index] = {
- type: NodeTypes.DIRECTIVE,
- name: 'bind',
- arg: createSimpleExpression('srcset', true, attr.loc),
- exp: hoisted,
- modifiers: [],
- loc: attr.loc
- }
- }
- })
- }
- }
- }
|