"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 29,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 35,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 34,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 36,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 35,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 26,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 39,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 50,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 32,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 29,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 45,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 39,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 34,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 12,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 13,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 30,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 13,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 14,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 15,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 22,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 16,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 33,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 29,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 16,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 15,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 19,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 20,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 18,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 23,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 24,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 23,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 24,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 24,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 22,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 37,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 16,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 26,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 41,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 26,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 31,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 39,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 38,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 37,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 26,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 26,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 30,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 53,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 20,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 32,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 40,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 35,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 31,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 30,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 29,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 30,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 41,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 41,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 41,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 42,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 42,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 45,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 45,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 42,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 35,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 42,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 29,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 37,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 31,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 30,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 34,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 34,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 28,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 25,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 16,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 27,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 3,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 7,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 5,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 8,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 37,
"directives": Array [],
"helpers": Array [],
"hoists": Array [],
+ "imports": Array [],
"loc": Object {
"end": Object {
"column": 7,
helpers: [],
components: [],
directives: [],
+ imports: [],
hoists: [],
cached: 0,
codegenNode: createSimpleExpression(`null`, false),
FRAGMENT
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
+import { ImportsOption } from './transform'
// Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific
components: string[]
directives: string[]
hoists: JSChildNode[]
+ imports: ImportsOption[]
cached: number
codegenNode: TemplateChildNode | JSChildNode | undefined
}
CREATE_COMMENT,
CREATE_TEXT
} from './runtimeHelpers'
+import { ImportsOption } from './transform'
type CodegenNode = TemplateChildNode | JSChildNode
if (hasHelpers) {
push(`import { ${ast.helpers.map(helper).join(', ')} } from "vue"\n`)
}
+ if (ast.imports.length) {
+ genImports(ast.imports, context)
+ newline()
+ }
genHoists(ast.hoists, context)
newline()
push(`export default `)
})
}
+function genImports(importsOptions: ImportsOption[], context: CodegenContext) {
+ if (!importsOptions.length) {
+ return
+ }
+ importsOptions.forEach(imports => {
+ context.push(`import `)
+ genNode(imports.exp, context)
+ context.push(` from '${imports.path}'`)
+ context.newline()
+ })
+}
+
function isText(n: string | CodegenNode) {
return (
isString(n) ||
components: [],
directives: [],
hoists: [],
+ imports: [],
cached: 0,
codegenNode: undefined,
loc: getSelection(context, start)
onError?: (error: CompilerError) => void
}
+export interface ImportsOption {
+ exp: string | ExpressionNode
+ path: string
+}
+
export interface TransformContext extends Required<TransformOptions> {
root: RootNode
helpers: Set<symbol>
components: Set<string>
directives: Set<string>
hoists: JSChildNode[]
+ imports: Set<ImportsOption>
cached: number
identifiers: { [name: string]: number | undefined }
scopes: {
components: new Set(),
directives: new Set(),
hoists: [],
+ imports: new Set(),
cached: 0,
identifiers: {},
scopes: {
root.helpers = [...context.helpers]
root.components = [...context.components]
root.directives = [...context.directives]
+ root.imports = [...context.imports]
root.hoists = context.hoists
root.cached = context.cached
}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`compiler sfc: transform asset url support uri fragment 1`] = `
+"import { createVNode, createBlock, openBlock } from \\"vue\\"
+import _imports_0 from '@svg/file.svg'
+
+
+const _hoisted_1 = _imports_0 + '#fragment'
+
+export default function render() {
+ const _ctx = this
+ return (openBlock(), createBlock(\\"use\\", { href: _hoisted_1 }))
+}"
+`;
+
+exports[`compiler sfc: transform asset url support uri is empty 1`] = `
+"import { createVNode, createBlock, openBlock } from \\"vue\\"
+
+export default function render() {
+ const _ctx = this
+ return (openBlock(), createBlock(\\"use\\", { href: '' }))
+}"
+`;
+
+exports[`compiler sfc: transform asset url transform assetUrls 1`] = `
+"import { createVNode, createBlock, Fragment, openBlock } from \\"vue\\"
+import _imports_0 from './logo.png'
+import _imports_1 from 'fixtures/logo.png'
+
+
+export default function render() {
+ const _ctx = this
+ return (openBlock(), createBlock(Fragment, null, [
+ createVNode(\\"img\\", { src: _imports_0 }),
+ createVNode(\\"img\\", { src: _imports_1 }),
+ createVNode(\\"img\\", { src: _imports_1 })
+ ]))
+}"
+`;
--- /dev/null
+import { generate, parse, transform } from '@vue/compiler-core'
+import { transformAssetUrl } from '../src/templateTransformAssetUrl'
+import { transformElement } from '../../compiler-core/src/transforms/transformElement'
+import { transformBind } from '../../compiler-core/src/transforms/vBind'
+
+function compileWithAssetUrls(template: string) {
+ const ast = parse(template)
+ transform(ast, {
+ nodeTransforms: [transformAssetUrl, transformElement],
+ directiveTransforms: {
+ bind: transformBind
+ }
+ })
+ return generate(ast, { mode: 'module' })
+}
+
+describe('compiler sfc: transform asset url', () => {
+ test('transform assetUrls', () => {
+ const result = compileWithAssetUrls(`
+ <img src="./logo.png"/>
+ <img src="~fixtures/logo.png"/>
+ <img src="~/fixtures/logo.png"/>
+ `)
+
+ expect(result.code).toMatchSnapshot()
+ })
+
+ /**
+ * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
+ */
+ test('support uri fragment', () => {
+ const result = compileWithAssetUrls(
+ '<use href="~@svg/file.svg#fragment"></use>'
+ )
+
+ expect(result.code).toMatchSnapshot()
+ })
+
+ /**
+ * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
+ */
+ test('support uri is empty', () => {
+ const result = compileWithAssetUrls('<use href="~"></use>')
+
+ expect(result.code).toMatchSnapshot()
+ })
+})
-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)
+ }
}
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)
}
/**