feat(compiler-sfc): transform asset url (#500)
This commit is contained in:
@@ -1,5 +1,81 @@
|
||||
import { NodeTransform } from '@vue/compiler-core'
|
||||
import {
|
||||
AttributeNode,
|
||||
createSimpleExpression,
|
||||
ExpressionNode,
|
||||
NodeTransform,
|
||||
NodeTypes,
|
||||
SourceLocation,
|
||||
TransformContext
|
||||
} from '@vue/compiler-core'
|
||||
import { parseUrl } from './templateUtils'
|
||||
|
||||
export const transformAssetUrl: NodeTransform = () => {
|
||||
// TODO
|
||||
export interface AssetURLOptions {
|
||||
[name: string]: string[]
|
||||
}
|
||||
|
||||
const assetURLOptions: AssetURLOptions = {
|
||||
video: ['src', 'poster'],
|
||||
source: ['src'],
|
||||
img: ['src'],
|
||||
image: ['xlink:href', 'href'],
|
||||
use: ['xlink:href', 'href']
|
||||
}
|
||||
|
||||
export const transformAssetUrl: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT) {
|
||||
for (const tag in assetURLOptions) {
|
||||
if ((tag === '*' || node.tag === tag) && node.props.length) {
|
||||
const attributes = assetURLOptions[tag]
|
||||
attributes.forEach(item => {
|
||||
node.props.forEach((attr: AttributeNode, index) => {
|
||||
if (attr.type !== NodeTypes.ATTRIBUTE) return
|
||||
if (attr.name !== item) return
|
||||
if (!attr.value) return
|
||||
const url = parseUrl(attr.value.content)
|
||||
const exp = getImportsExpressionExp(
|
||||
url.path,
|
||||
url.hash,
|
||||
attr.loc,
|
||||
context
|
||||
)
|
||||
node.props[index] = {
|
||||
type: NodeTypes.DIRECTIVE,
|
||||
name: 'bind',
|
||||
arg: createSimpleExpression(item, true, attr.loc),
|
||||
exp,
|
||||
modifiers: [],
|
||||
loc: attr.loc
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getImportsExpressionExp(
|
||||
path: string | undefined,
|
||||
hash: string | undefined,
|
||||
loc: SourceLocation,
|
||||
context: TransformContext
|
||||
): ExpressionNode {
|
||||
if (path) {
|
||||
const importsArray = Array.from(context.imports)
|
||||
const existing = importsArray.find(i => i.path === path)
|
||||
if (existing) {
|
||||
return existing.exp as ExpressionNode
|
||||
}
|
||||
const name = `_imports_${importsArray.length}`
|
||||
const exp = createSimpleExpression(name, false, loc, true)
|
||||
context.imports.add({ exp, path })
|
||||
if (hash && path) {
|
||||
return context.hoist(
|
||||
createSimpleExpression(`${name} + '${hash}'`, false, loc, true)
|
||||
)
|
||||
} else {
|
||||
return exp
|
||||
}
|
||||
} else {
|
||||
return createSimpleExpression(`''`, false, loc, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
import { UrlWithStringQuery, parse as uriParse } from 'url'
|
||||
|
||||
// TODO use imports instead.
|
||||
// We need an extra transform context API for injecting arbitrary import
|
||||
// statements.
|
||||
export function urlToRequire(url: string): string {
|
||||
const returnValue = `"${url}"`
|
||||
export function parseUrl(url: string): UrlWithStringQuery {
|
||||
const firstChar = url.charAt(0)
|
||||
if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
|
||||
if (firstChar === '~') {
|
||||
const secondChar = url.charAt(1)
|
||||
url = url.slice(secondChar === '/' ? 2 : 1)
|
||||
}
|
||||
|
||||
const uriParts = parseUriParts(url)
|
||||
|
||||
if (!uriParts.hash) {
|
||||
return `require("${url}")`
|
||||
} else {
|
||||
// support uri fragment case by excluding it from
|
||||
// the require and instead appending it as string;
|
||||
// assuming that the path part is sufficient according to
|
||||
// the above caseing(t.i. no protocol-auth-host parts expected)
|
||||
return `require("${uriParts.path}") + "${uriParts.hash}"`
|
||||
}
|
||||
}
|
||||
return returnValue
|
||||
return parseUriParts(url)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user