mirror of https://github.com/vuejs/core.git
Merge da82083df6
into 56be3dd4db
This commit is contained in:
commit
38a6c6a250
|
@ -114,6 +114,12 @@ describe('compiler sfc: transform asset url', () => {
|
||||||
expect(code).toMatch(`"xlink:href": "#myCircle"`)
|
expect(code).toMatch(`"xlink:href": "#myCircle"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #9919
|
||||||
|
test('should transform subpath import paths', () => {
|
||||||
|
const { code } = compileWithAssetUrls(`<img src="#src/assets/vue.svg" />`)
|
||||||
|
expect(code).toContain(`_imports_0 from '#src/assets/vue.svg'`)
|
||||||
|
})
|
||||||
|
|
||||||
test('should allow for full base URLs, with paths', () => {
|
test('should allow for full base URLs, with paths', () => {
|
||||||
const { code } = compileWithAssetUrls(`<img src="./logo.png" />`, {
|
const { code } = compileWithAssetUrls(`<img src="./logo.png" />`, {
|
||||||
base: 'http://localhost:3000/src/',
|
base: 'http://localhost:3000/src/',
|
||||||
|
|
|
@ -3,7 +3,12 @@ import { isString } from '@vue/shared'
|
||||||
|
|
||||||
export function isRelativeUrl(url: string): boolean {
|
export function isRelativeUrl(url: string): boolean {
|
||||||
const firstChar = url.charAt(0)
|
const firstChar = url.charAt(0)
|
||||||
return firstChar === '.' || firstChar === '~' || firstChar === '@'
|
return (
|
||||||
|
firstChar === '.' ||
|
||||||
|
firstChar === '~' ||
|
||||||
|
firstChar === '@' ||
|
||||||
|
firstChar === '#'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const externalRE = /^(https?:)?\/\//
|
const externalRE = /^(https?:)?\/\//
|
||||||
|
|
|
@ -101,13 +101,19 @@ export const transformAssetUrl: NodeTransform = (
|
||||||
|
|
||||||
const assetAttrs = (attrs || []).concat(wildCardAttrs || [])
|
const assetAttrs = (attrs || []).concat(wildCardAttrs || [])
|
||||||
node.props.forEach((attr, index) => {
|
node.props.forEach((attr, index) => {
|
||||||
|
const isHashFragment =
|
||||||
|
node.tag === 'use' &&
|
||||||
|
attr.type === NodeTypes.ATTRIBUTE &&
|
||||||
|
(attr.name === 'href' || attr.name === 'xlink:href') &&
|
||||||
|
attr.value?.content[0] === '#'
|
||||||
|
|
||||||
if (
|
if (
|
||||||
attr.type !== NodeTypes.ATTRIBUTE ||
|
attr.type !== NodeTypes.ATTRIBUTE ||
|
||||||
!assetAttrs.includes(attr.name) ||
|
!assetAttrs.includes(attr.name) ||
|
||||||
!attr.value ||
|
!attr.value ||
|
||||||
isExternalUrl(attr.value.content) ||
|
isExternalUrl(attr.value.content) ||
|
||||||
isDataUrl(attr.value.content) ||
|
isDataUrl(attr.value.content) ||
|
||||||
attr.value.content[0] === '#' ||
|
isHashFragment ||
|
||||||
(!options.includeAbsolute && !isRelativeUrl(attr.value.content))
|
(!options.includeAbsolute && !isRelativeUrl(attr.value.content))
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
|
@ -147,70 +153,110 @@ export const transformAssetUrl: NodeTransform = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves or registers an import for the given source path
|
||||||
|
* @param source - Path to resolve import for
|
||||||
|
* @param loc - Source location
|
||||||
|
* @param context - Transform context
|
||||||
|
* @returns Object containing import name and expression
|
||||||
|
*/
|
||||||
|
function resolveOrRegisterImport(
|
||||||
|
source: string,
|
||||||
|
loc: SourceLocation,
|
||||||
|
context: TransformContext,
|
||||||
|
): {
|
||||||
|
name: string
|
||||||
|
exp: SimpleExpressionNode
|
||||||
|
} {
|
||||||
|
const existingIndex = context.imports.findIndex(i => i.path === source)
|
||||||
|
if (existingIndex > -1) {
|
||||||
|
return {
|
||||||
|
name: `_imports_${existingIndex}`,
|
||||||
|
exp: context.imports[existingIndex].exp as SimpleExpressionNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `_imports_${context.imports.length}`
|
||||||
|
const exp = createSimpleExpression(
|
||||||
|
name,
|
||||||
|
false,
|
||||||
|
loc,
|
||||||
|
ConstantTypes.CAN_STRINGIFY,
|
||||||
|
)
|
||||||
|
|
||||||
|
// We need to ensure the path is not encoded (to %2F),
|
||||||
|
// so we decode it back in case it is encoded
|
||||||
|
context.imports.push({
|
||||||
|
exp,
|
||||||
|
path: decodeURIComponent(source),
|
||||||
|
})
|
||||||
|
|
||||||
|
return { name, exp }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms asset URLs into import expressions or string literals
|
||||||
|
*/
|
||||||
function getImportsExpressionExp(
|
function getImportsExpressionExp(
|
||||||
path: string | null,
|
path: string | null,
|
||||||
hash: string | null,
|
hash: string | null,
|
||||||
loc: SourceLocation,
|
loc: SourceLocation,
|
||||||
context: TransformContext,
|
context: TransformContext,
|
||||||
): ExpressionNode {
|
): ExpressionNode {
|
||||||
if (path) {
|
// Neither path nor hash - return empty string
|
||||||
let name: string
|
if (!path && !hash) {
|
||||||
let exp: SimpleExpressionNode
|
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
|
||||||
const existingIndex = context.imports.findIndex(i => i.path === path)
|
}
|
||||||
if (existingIndex > -1) {
|
|
||||||
name = `_imports_${existingIndex}`
|
|
||||||
exp = context.imports[existingIndex].exp as SimpleExpressionNode
|
|
||||||
} else {
|
|
||||||
name = `_imports_${context.imports.length}`
|
|
||||||
exp = createSimpleExpression(
|
|
||||||
name,
|
|
||||||
false,
|
|
||||||
loc,
|
|
||||||
ConstantTypes.CAN_STRINGIFY,
|
|
||||||
)
|
|
||||||
|
|
||||||
// We need to ensure the path is not encoded (to %2F),
|
// Only hash without path - treat hash as the import source (likely a subpath import)
|
||||||
// so we decode it back in case it is encoded
|
if (!path && hash) {
|
||||||
context.imports.push({
|
const { exp } = resolveOrRegisterImport(hash, loc, context)
|
||||||
exp,
|
return exp
|
||||||
path: decodeURIComponent(path),
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hash) {
|
// Only path without hash - straightforward import
|
||||||
return exp
|
if (path && !hash) {
|
||||||
}
|
const { exp } = resolveOrRegisterImport(path, loc, context)
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
const hashExp = `${name} + '${hash}'`
|
// At this point, we know we have both path and hash components
|
||||||
const finalExp = createSimpleExpression(
|
const { name } = resolveOrRegisterImport(path!, loc, context)
|
||||||
hashExp,
|
|
||||||
|
// Combine path import with hash
|
||||||
|
const hashExp = `${name} + '${hash}'`
|
||||||
|
const finalExp = createSimpleExpression(
|
||||||
|
hashExp,
|
||||||
|
false,
|
||||||
|
loc,
|
||||||
|
ConstantTypes.CAN_STRINGIFY,
|
||||||
|
)
|
||||||
|
|
||||||
|
// No hoisting needed
|
||||||
|
if (!context.hoistStatic) {
|
||||||
|
return finalExp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing hoisted expression
|
||||||
|
const existingHoistIndex = context.hoists.findIndex(h => {
|
||||||
|
return (
|
||||||
|
h &&
|
||||||
|
h.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||||
|
!h.isStatic &&
|
||||||
|
h.content === hashExp
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return existing hoisted expression if found
|
||||||
|
if (existingHoistIndex > -1) {
|
||||||
|
return createSimpleExpression(
|
||||||
|
`_hoisted_${existingHoistIndex + 1}`,
|
||||||
false,
|
false,
|
||||||
loc,
|
loc,
|
||||||
ConstantTypes.CAN_STRINGIFY,
|
ConstantTypes.CAN_STRINGIFY,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!context.hoistStatic) {
|
|
||||||
return finalExp
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingHoistIndex = context.hoists.findIndex(h => {
|
|
||||||
return (
|
|
||||||
h &&
|
|
||||||
h.type === NodeTypes.SIMPLE_EXPRESSION &&
|
|
||||||
!h.isStatic &&
|
|
||||||
h.content === hashExp
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if (existingHoistIndex > -1) {
|
|
||||||
return createSimpleExpression(
|
|
||||||
`_hoisted_${existingHoistIndex + 1}`,
|
|
||||||
false,
|
|
||||||
loc,
|
|
||||||
ConstantTypes.CAN_STRINGIFY,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return context.hoist(finalExp)
|
|
||||||
} else {
|
|
||||||
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hoist the expression and return the hoisted expression
|
||||||
|
return context.hoist(finalExp)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue