diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap
index 0af6bed6..4dfc212e 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap
@@ -1,5 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should not hoist srcset URLs in SSR mode 1`] = `
+"import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from \\"vue\\"
+import { ssrRenderAttr as _ssrRenderAttr, ssrRenderComponent as _ssrRenderComponent } from \\"vue/server-renderer\\"
+import _imports_0 from './img/foo.svg'
+import _imports_1 from './img/bar.svg'
+
+
+export function ssrRender(_ctx, _push, _parent, _attrs) {
+ const _component_router_link = _resolveComponent(\\"router-link\\")
+
+ _push(\`
\`)
+ _push(_ssrRenderComponent(_component_router_link, null, {
+ default: _withCtx((_, _push, _parent, _scopeId) => {
+ if (_push) {
+ _push(\`
\`)
+ } else {
+ return [
+ _createVNode(\\"picture\\", null, [
+ _createVNode(\\"source\\", {
+ srcset: _imports_1
+ }),
+ _createVNode(\\"img\\", { src: _imports_1 })
+ ])
+ ]
+ }
+ }),
+ _: 1 /* STABLE */
+ }, _parent))
+ _push(\`\`)
+}"
+`;
+
exports[`source map 1`] = `
Object {
"mappings": ";;;wBACE,oBAA8B;IAAzB,oBAAmB,4BAAbA,WAAM",
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
index c0fceece..ac09f57e 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap
@@ -38,11 +38,13 @@ import _imports_0 from '@svg/file.svg'
const _hoisted_1 = _imports_0 + '#fragment'
+const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"use\\", { href: _hoisted_1 }, null, -1 /* HOISTED */)
+const _hoisted_3 = /*#__PURE__*/_createElementVNode(\\"use\\", { href: _hoisted_1 }, null, -1 /* HOISTED */)
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
- _createElementVNode(\\"use\\", { href: _hoisted_1 }),
- _createElementVNode(\\"use\\", { href: _hoisted_1 })
+ _hoisted_2,
+ _hoisted_3
], 64 /* STABLE_FRAGMENT */))
}"
`;
diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap
index 89e487f0..1d9be468 100644
--- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap
+++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap
@@ -13,57 +13,69 @@ const _hoisted_5 = _imports_0 + ' 2x, ' + _imports_0
const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
const _hoisted_8 = \\"/logo.png\\" + ', ' + _imports_0 + ' 2x'
+const _hoisted_9 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_1
+}, null, -1 /* HOISTED */)
+const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_2
+}, null, -1 /* HOISTED */)
+const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_3
+}, null, -1 /* HOISTED */)
+const _hoisted_13 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_4
+}, null, -1 /* HOISTED */)
+const _hoisted_14 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_5
+}, null, -1 /* HOISTED */)
+const _hoisted_15 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_6
+}, null, -1 /* HOISTED */)
+const _hoisted_16 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_7
+}, null, -1 /* HOISTED */)
+const _hoisted_17 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: \\"/logo.png, /logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_18 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"https://example.com/logo.png\\",
+ srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_19 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: _hoisted_8
+}, null, -1 /* HOISTED */)
+const _hoisted_20 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"data:image/png;base64,i\\",
+ srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
+}, null, -1 /* HOISTED */)
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_1
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_2
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_3
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_4
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_5
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_6
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_7
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: \\"/logo.png, /logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"https://example.com/logo.png\\",
- srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: _hoisted_8
- }),
- _createElementVNode(\\"img\\", {
- src: \\"data:image/png;base64,i\\",
- srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
- })
+ _hoisted_9,
+ _hoisted_10,
+ _hoisted_11,
+ _hoisted_12,
+ _hoisted_13,
+ _hoisted_14,
+ _hoisted_15,
+ _hoisted_16,
+ _hoisted_17,
+ _hoisted_18,
+ _hoisted_19,
+ _hoisted_20
], 64 /* STABLE_FRAGMENT */))
}"
`;
@@ -71,56 +83,69 @@ export function render(_ctx, _cache) {
exports[`compiler sfc: transform srcset transform srcset w/ base 1`] = `
"import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\"
+const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_3 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_4 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_5 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png, /foo/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_6 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png 2x, /foo/logo.png\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_7 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png 2x, /foo/logo.png 3x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_8 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_9 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: \\"/logo.png, /logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"https://example.com/logo.png\\",
+ srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: \\"/logo.png, /foo/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"data:image/png;base64,i\\",
+ srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
+}, null, -1 /* HOISTED */)
+
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png, /foo/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png 2x, /foo/logo.png\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png 2x, /foo/logo.png 3x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"/foo/logo.png, /foo/logo.png 2x, /foo/logo.png 3x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: \\"/logo.png, /logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"https://example.com/logo.png\\",
- srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: \\"/logo.png, /foo/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"data:image/png;base64,i\\",
- srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
- })
+ _hoisted_1,
+ _hoisted_2,
+ _hoisted_3,
+ _hoisted_4,
+ _hoisted_5,
+ _hoisted_6,
+ _hoisted_7,
+ _hoisted_8,
+ _hoisted_9,
+ _hoisted_10,
+ _hoisted_11,
+ _hoisted_12
], 64 /* STABLE_FRAGMENT */))
}"
`;
@@ -140,57 +165,69 @@ const _hoisted_6 = _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
const _hoisted_7 = _imports_0 + ', ' + _imports_0 + ' 2x, ' + _imports_0 + ' 3x'
const _hoisted_8 = _imports_1 + ', ' + _imports_1 + ' 2x'
const _hoisted_9 = _imports_1 + ', ' + _imports_0 + ' 2x'
+const _hoisted_10 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: \\"\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_11 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_1
+}, null, -1 /* HOISTED */)
+const _hoisted_12 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_2
+}, null, -1 /* HOISTED */)
+const _hoisted_13 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_3
+}, null, -1 /* HOISTED */)
+const _hoisted_14 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_4
+}, null, -1 /* HOISTED */)
+const _hoisted_15 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_5
+}, null, -1 /* HOISTED */)
+const _hoisted_16 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_6
+}, null, -1 /* HOISTED */)
+const _hoisted_17 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"./logo.png\\",
+ srcset: _hoisted_7
+}, null, -1 /* HOISTED */)
+const _hoisted_18 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: _hoisted_8
+}, null, -1 /* HOISTED */)
+const _hoisted_19 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"https://example.com/logo.png\\",
+ srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
+}, null, -1 /* HOISTED */)
+const _hoisted_20 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"/logo.png\\",
+ srcset: _hoisted_9
+}, null, -1 /* HOISTED */)
+const _hoisted_21 = /*#__PURE__*/_createElementVNode(\\"img\\", {
+ src: \\"data:image/png;base64,i\\",
+ srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
+}, null, -1 /* HOISTED */)
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: \\"\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_1
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_2
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_3
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_4
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_5
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_6
- }),
- _createElementVNode(\\"img\\", {
- src: \\"./logo.png\\",
- srcset: _hoisted_7
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: _hoisted_8
- }),
- _createElementVNode(\\"img\\", {
- src: \\"https://example.com/logo.png\\",
- srcset: \\"https://example.com/logo.png, https://example.com/logo.png 2x\\"
- }),
- _createElementVNode(\\"img\\", {
- src: \\"/logo.png\\",
- srcset: _hoisted_9
- }),
- _createElementVNode(\\"img\\", {
- src: \\"data:image/png;base64,i\\",
- srcset: \\"data:image/png;base64,i 1x, data:image/png;base64,i 2x\\"
- })
+ _hoisted_10,
+ _hoisted_11,
+ _hoisted_12,
+ _hoisted_13,
+ _hoisted_14,
+ _hoisted_15,
+ _hoisted_16,
+ _hoisted_17,
+ _hoisted_18,
+ _hoisted_19,
+ _hoisted_20,
+ _hoisted_21
], 64 /* STABLE_FRAGMENT */))
}"
`;
diff --git a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
index af0f6600..2beda880 100644
--- a/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
+++ b/packages/compiler-sfc/__tests__/compileTemplate.spec.ts
@@ -153,3 +153,24 @@ test('should generate the correct imports expression', () => {
expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
})
+
+// #3874
+test('should not hoist srcset URLs in SSR mode', () => {
+ const { code } = compile({
+ filename: 'example.vue',
+ source: `
+
+
+
+
+
+
+
+
+
+
+ `,
+ ssr: true
+ })
+ expect(code).toMatchSnapshot()
+})
diff --git a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
index 44ef092a..0b0f138b 100644
--- a/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
+++ b/packages/compiler-sfc/__tests__/templateTransformAssetUrl.spec.ts
@@ -54,9 +54,12 @@ describe('compiler sfc: transform asset url', () => {
test('support uri fragment', () => {
const result = compileWithAssetUrls(
'' +
- ''
+ '',
+ {},
+ {
+ hoistStatic: true
+ }
)
-
expect(result.code).toMatchSnapshot()
})
diff --git a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts
index f88706c4..c2a61460 100644
--- a/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts
+++ b/packages/compiler-sfc/__tests__/templateTransformSrcset.spec.ts
@@ -26,6 +26,7 @@ function compileWithSrcset(
? createSrcsetTransformWithOptions(normalizeOptions(options))
: transformSrcset
transform(ast, {
+ hoistStatic: true,
nodeTransforms: [srcsetTransform, transformElement],
directiveTransforms: {
bind: transformBind
diff --git a/packages/compiler-sfc/src/templateTransformAssetUrl.ts b/packages/compiler-sfc/src/templateTransformAssetUrl.ts
index 2f6152b8..32bf33bc 100644
--- a/packages/compiler-sfc/src/templateTransformAssetUrl.ts
+++ b/packages/compiler-sfc/src/templateTransformAssetUrl.ts
@@ -176,6 +176,17 @@ function getImportsExpressionExp(
}
const hashExp = `${name} + '${hash}'`
+ const finalExp = createSimpleExpression(
+ hashExp,
+ false,
+ loc,
+ ConstantTypes.CAN_STRINGIFY
+ )
+
+ if (!context.hoistStatic) {
+ return finalExp
+ }
+
const existingHoistIndex = context.hoists.findIndex(h => {
return (
h &&
@@ -192,9 +203,7 @@ function getImportsExpressionExp(
ConstantTypes.CAN_STRINGIFY
)
}
- return context.hoist(
- createSimpleExpression(hashExp, false, loc, ConstantTypes.CAN_STRINGIFY)
- )
+ return context.hoist(finalExp)
} else {
return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_STRINGIFY)
}
diff --git a/packages/compiler-sfc/src/templateTransformSrcset.ts b/packages/compiler-sfc/src/templateTransformSrcset.ts
index ccdcc721..a2c36b2d 100644
--- a/packages/compiler-sfc/src/templateTransformSrcset.ts
+++ b/packages/compiler-sfc/src/templateTransformSrcset.ts
@@ -3,6 +3,7 @@ import {
ConstantTypes,
createCompoundExpression,
createSimpleExpression,
+ ExpressionNode,
NodeTransform,
NodeTypes,
SimpleExpressionNode
@@ -145,14 +146,17 @@ export const transformSrcset: NodeTransform = (
}
})
- const hoisted = context.hoist(compoundExpression)
- hoisted.constType = ConstantTypes.CAN_STRINGIFY
+ let exp: ExpressionNode = compoundExpression
+ if (context.hoistStatic) {
+ exp = context.hoist(compoundExpression)
+ exp.constType = ConstantTypes.CAN_STRINGIFY
+ }
node.props[index] = {
type: NodeTypes.DIRECTIVE,
name: 'bind',
arg: createSimpleExpression('srcset', true, attr.loc),
- exp: hoisted,
+ exp,
modifiers: [],
loc: attr.loc
}