Skip to content

fix(compiler-vapor): fix asset import from public directory #13630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: minor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/compiler-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
type NodeTransform,
type StructuralDirectiveTransform,
type DirectiveTransform,
type ImportItem,
} from './transform'
export {
generate,
Expand Down
18 changes: 9 additions & 9 deletions packages/compiler-sfc/src/compileTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ import {
type AssetURLOptions,
type AssetURLTagConfig,
createAssetUrlTransformWithOptions,
defaultAssetUrlOptions,
normalizeOptions,
transformAssetUrl,
} from './template/transformAssetUrl'
import {
createSrcsetTransformWithOptions,
transformSrcset,
} from './template/transformSrcset'
import { createSrcsetTransformWithOptions } from './template/transformSrcset'
import { generateCodeFrame, isObject } from '@vue/shared'
import * as CompilerDOM from '@vue/compiler-dom'
import * as CompilerVapor from '@vue/compiler-vapor'
Expand Down Expand Up @@ -185,14 +182,17 @@ function doCompileTemplate({
const warnings: CompilerError[] = []

let nodeTransforms: NodeTransform[] = []
if (isObject(transformAssetUrls)) {
const assetOptions = normalizeOptions(transformAssetUrls)
if (transformAssetUrls !== false) {
const assetOptions = isObject(transformAssetUrls)
? normalizeOptions(transformAssetUrls)
: defaultAssetUrlOptions

assetOptions.vapor = vapor

nodeTransforms = [
createAssetUrlTransformWithOptions(assetOptions),
createSrcsetTransformWithOptions(assetOptions),
]
} else if (transformAssetUrls !== false) {
nodeTransforms = [transformAssetUrl, transformSrcset]
}

if (ssr && !ssrCssVars) {
Expand Down
15 changes: 13 additions & 2 deletions packages/compiler-sfc/src/template/transformAssetUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface AssetURLOptions {
*/
includeAbsolute?: boolean
tags?: AssetURLTagConfig
vapor?: boolean
}

export const defaultAssetUrlOptions: Required<AssetURLOptions> = {
Expand All @@ -44,6 +45,7 @@ export const defaultAssetUrlOptions: Required<AssetURLOptions> = {
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href'],
},
vapor: false,
}

export const normalizeOptions = (
Expand Down Expand Up @@ -134,7 +136,13 @@ export const transformAssetUrl: NodeTransform = (
// otherwise, transform the url into an import.
// this assumes a bundler will resolve the import into the correct
// absolute url (e.g. webpack file-loader)
const exp = getImportsExpressionExp(url.path, url.hash, attr.loc, context)
const exp = getImportsExpressionExp(
url.path,
url.hash,
attr.loc,
context,
options.vapor,
)
node.props[index] = {
type: NodeTypes.DIRECTIVE,
name: 'bind',
Expand All @@ -152,6 +160,7 @@ function getImportsExpressionExp(
hash: string | null,
loc: SourceLocation,
context: TransformContext,
vapor = false,
): ExpressionNode {
if (path) {
let name: string
Expand Down Expand Up @@ -211,6 +220,8 @@ function getImportsExpressionExp(
}
return context.hoist(finalExp)
} else {
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
return vapor
? createSimpleExpression(`''`, true, loc, ConstantTypes.CAN_STRINGIFY)
: createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
}
}
57 changes: 52 additions & 5 deletions packages/compiler-sfc/src/template/transformSrcset.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'path'
import {
type CompoundExpressionNode,
ConstantTypes,
type ExpressionNode,
type NodeTransform,
Expand Down Expand Up @@ -36,6 +37,24 @@ export const createSrcsetTransformWithOptions = (
(transformSrcset as Function)(node, context, options)
}

export function flattenCompoundExpression(
compoundExpression: CompoundExpressionNode,
): string {
return compoundExpression.children
.map(child => {
if (typeof child === 'string') {
return child
} else if (typeof child === 'symbol') {
return child.description
} else if (child.type === NodeTypes.COMPOUND_EXPRESSION) {
return flattenCompoundExpression(child)
} else {
return child.content
}
})
.join('')
}

export const transformSrcset: NodeTransform = (
node,
context,
Expand All @@ -47,7 +66,25 @@ export const transformSrcset: NodeTransform = (
if (attr.name === 'srcset' && attr.type === NodeTypes.ATTRIBUTE) {
if (!attr.value) return
const value = attr.value.content
if (!value) return
if (!value) {
// Handle empty srcset
if (options.vapor) {
node.props[index] = {
type: NodeTypes.DIRECTIVE,
name: 'bind',
arg: createSimpleExpression('srcset', true, attr.loc),
exp: createSimpleExpression(
`''`,
true,
attr.loc,
ConstantTypes.CAN_STRINGIFY,
),
modifiers: [],
loc: attr.loc,
}
}
return
}
const imageCandidates: ImageCandidate[] = value.split(',').map(s => {
// The attribute value arrives here with all whitespace, except
// normal spaces, represented by escape sequences
Expand Down Expand Up @@ -151,10 +188,20 @@ export const transformSrcset: NodeTransform = (
}
})

let exp: ExpressionNode = compoundExpression
if (context.hoistStatic) {
exp = context.hoist(compoundExpression)
exp.constType = ConstantTypes.CAN_STRINGIFY
let exp: ExpressionNode
if (options.vapor) {
exp = createSimpleExpression(
flattenCompoundExpression(compoundExpression),
false,
attr.loc,
ConstantTypes.CAN_STRINGIFY,
)
} else {
exp = compoundExpression
if (context.hoistStatic) {
exp = context.hoist(compoundExpression)
exp.constType = ConstantTypes.CAN_STRINGIFY
}
}

node.props[index] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`compiler sfc: transform asset url > should allow for full base URLs, with paths 1`] = `
"import { template as _template } from 'vue';
const t0 = _template("<img src=\\"http://localhost:3000/src/logo.png\\">", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > should allow for full base URLs, without paths 1`] = `
"import { template as _template } from 'vue';
const t0 = _template("<img src=\\"http://localhost:3000/logo.png\\">", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > should allow for full base URLs, without port 1`] = `
"import { template as _template } from 'vue';
const t0 = _template("<img src=\\"http://localhost/logo.png\\">", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > should allow for full base URLs, without protocol 1`] = `
"import { template as _template } from 'vue';
const t0 = _template("<img src=\\"//localhost/logo.png\\">", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > support uri fragment 1`] = `
"import { template as _template } from 'vue';
import _imports_0 from '@svg/file.svg';
const t0 = _template("<use href=\\"" + _imports_0 + '#fragment' + "\\"></use>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
return [n0, n1]
}"
`;

exports[`compiler sfc: transform asset url > support uri is empty 1`] = `
"import { template as _template } from 'vue';
const t0 = _template("<use href=\\"\\"></use>", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > transform assetUrls 1`] = `
"import { template as _template } from 'vue';
import _imports_0 from './logo.png';
import _imports_1 from 'fixtures/logo.png';
import _imports_2 from '/fixtures/logo.png';
const t0 = _template("<img src=\\"" + _imports_0 + "\\">")
const t1 = _template("<img src=\\"" + _imports_1 + "\\">")
const t2 = _template("<img src=\\"http://example.com/fixtures/logo.png\\">")
const t3 = _template("<img src=\\"//example.com/fixtures/logo.png\\">")
const t4 = _template("<img src=\\"" + _imports_2 + "\\">")
const t5 = _template("<img src=\\"data:image/png;base64,i\\">")
export function render(_ctx) {
const n0 = t0()
const n1 = t1()
const n2 = t1()
const n3 = t2()
const n4 = t3()
const n5 = t4()
const n6 = t5()
return [n0, n1, n2, n3, n4, n5, n6]
}"
`;
exports[`compiler sfc: transform asset url > transform with stringify 1`] = `
"import { template as _template } from 'vue';
import _imports_0 from './bar.png';
import _imports_1 from '/bar.png';
const t0 = _template("<div><img src=\\"" + _imports_0 + "\\"><img src=\\"" + _imports_1 + "\\"><img src=\\"https://foo.bar/baz.png\\"><img src=\\"//foo.bar/baz.png\\"><img src=\\"" + _imports_0 + "\\"></div>", true)
export function render(_ctx) {
const n0 = t0()
return n0
}"
`;
exports[`compiler sfc: transform asset url > with explicit base 1`] = `
"import { template as _template } from 'vue';
import _imports_0 from 'bar.png';
import _imports_1 from '@theme/bar.png';
const t0 = _template("<img src=\\"/foo/bar.png\\">")
const t1 = _template("<img src=\\"" + _imports_0 + "\\">")
const t2 = _template("<img src=\\"" + _imports_1 + "\\">")
export function render(_ctx) {
const n0 = t0()
const n1 = t1()
const n2 = t1()
const n3 = t2()
return [n0, n1, n2, n3]
}"
`;
exports[`compiler sfc: transform asset url > with includeAbsolute: true 1`] = `
"import { template as _template } from 'vue';
import _imports_0 from './bar.png';
import _imports_1 from '/bar.png';
const t0 = _template("<img src=\\"" + _imports_0 + "\\">")
const t1 = _template("<img src=\\"" + _imports_1 + "\\">")
const t2 = _template("<img src=\\"https://foo.bar/baz.png\\">")
const t3 = _template("<img src=\\"//foo.bar/baz.png\\">")
export function render(_ctx) {
const n0 = t0()
const n1 = t1()
const n2 = t2()
const n3 = t3()
return [n0, n1, n2, n3]
}"
`;
Loading