Skip to content

Commit bae6a37

Browse files
authored
fix(ssr): this in exported function should be undefined (#18329)
1 parent 964a618 commit bae6a37

File tree

5 files changed

+86
-33
lines changed

5 files changed

+86
-33
lines changed

packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ test('named import', async () => {
2525
`import { ref } from 'vue';function foo() { return ref(0) }`,
2626
),
2727
).toMatchInlineSnapshot(`
28-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]});
29-
function foo() { return __vite_ssr_import_0__.ref(0) }"
28+
"const __vite_ssr_identity__ = v => v;
29+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]});
30+
function foo() { return __vite_ssr_identity__(__vite_ssr_import_0__.ref)(0) }"
3031
`)
3132
})
3233

@@ -36,8 +37,9 @@ test('named import: arbitrary module namespace specifier', async () => {
3637
`import { "some thing" as ref } from 'vue';function foo() { return ref(0) }`,
3738
),
3839
).toMatchInlineSnapshot(`
39-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["some thing"]});
40-
function foo() { return __vite_ssr_import_0__["some thing"](0) }"
40+
"const __vite_ssr_identity__ = v => v;
41+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["some thing"]});
42+
function foo() { return __vite_ssr_identity__(__vite_ssr_import_0__["some thing"])(0) }"
4143
`)
4244
})
4345

@@ -220,8 +222,9 @@ test('do not rewrite method definition', async () => {
220222
`import { fn } from 'vue';class A { fn() { fn() } }`,
221223
)
222224
expect(result?.code).toMatchInlineSnapshot(`
223-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]});
224-
class A { fn() { __vite_ssr_import_0__.fn() } }"
225+
"const __vite_ssr_identity__ = v => v;
226+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]});
227+
class A { fn() { __vite_ssr_identity__(__vite_ssr_import_0__.fn)() } }"
225228
`)
226229
expect(result?.deps).toEqual(['vue'])
227230
})
@@ -501,14 +504,15 @@ test('overwrite bindings', async () => {
501504
`function g() { const f = () => { const inject = true }; console.log(inject) }\n`,
502505
),
503506
).toMatchInlineSnapshot(`
504-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]});
507+
"const __vite_ssr_identity__ = v => v;
508+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]});
505509
const a = { inject: __vite_ssr_import_0__.inject }
506510
const b = { test: __vite_ssr_import_0__.inject }
507511
function c() { const { test: inject } = { test: true }; console.log(inject) }
508512
const d = __vite_ssr_import_0__.inject
509-
function f() { console.log(__vite_ssr_import_0__.inject) }
513+
function f() { console.log(__vite_ssr_identity__(__vite_ssr_import_0__.inject)) }
510514
function e() { const { inject } = { inject: true } }
511-
function g() { const f = () => { const inject = true }; console.log(__vite_ssr_import_0__.inject) }
515+
function g() { const f = () => { const inject = true }; console.log(__vite_ssr_identity__(__vite_ssr_import_0__.inject)) }
512516
"
513517
`)
514518
})
@@ -530,12 +534,13 @@ function c({ _ = bar() + foo() }) {}
530534
`,
531535
),
532536
).toMatchInlineSnapshot(`
533-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo","bar"]});
537+
"const __vite_ssr_identity__ = v => v;
538+
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo","bar"]});
534539
535540
536-
const a = ({ _ = __vite_ssr_import_0__.foo() }) => {}
537-
function b({ _ = __vite_ssr_import_0__.bar() }) {}
538-
function c({ _ = __vite_ssr_import_0__.bar() + __vite_ssr_import_0__.foo() }) {}
541+
const a = ({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.foo)() }) => {}
542+
function b({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.bar)() }) {}
543+
function c({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.bar)() + __vite_ssr_identity__(__vite_ssr_import_0__.foo)() }) {}
539544
"
540545
`)
541546
})
@@ -618,7 +623,8 @@ objRest()
618623
`,
619624
),
620625
).toMatchInlineSnapshot(`
621-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["remove","add","get","set","rest","objRest"]});
626+
"const __vite_ssr_identity__ = v => v;
627+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["remove","add","get","set","rest","objRest"]});
622628
623629
624630
@@ -638,12 +644,12 @@ objRest()
638644
objRest()
639645
}
640646
641-
__vite_ssr_import_0__.remove()
642-
__vite_ssr_import_0__.add()
643-
__vite_ssr_import_0__.get()
644-
__vite_ssr_import_0__.set()
645-
__vite_ssr_import_0__.rest()
646-
__vite_ssr_import_0__.objRest()
647+
__vite_ssr_identity__(__vite_ssr_import_0__.remove)()
648+
__vite_ssr_identity__(__vite_ssr_import_0__.add)()
649+
__vite_ssr_identity__(__vite_ssr_import_0__.get)()
650+
__vite_ssr_identity__(__vite_ssr_import_0__.set)()
651+
__vite_ssr_identity__(__vite_ssr_import_0__.rest)()
652+
__vite_ssr_identity__(__vite_ssr_import_0__.objRest)()
647653
"
648654
`)
649655
})
@@ -778,7 +784,8 @@ bbb()
778784
`,
779785
),
780786
).toMatchInlineSnapshot(`
781-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["aaa","bbb","ccc","ddd"]});
787+
"const __vite_ssr_identity__ = v => v;
788+
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["aaa","bbb","ccc","ddd"]});
782789
783790
784791
@@ -800,8 +807,8 @@ bbb()
800807
ccc()
801808
}
802809
803-
__vite_ssr_import_0__.aaa()
804-
__vite_ssr_import_0__.bbb()
810+
__vite_ssr_identity__(__vite_ssr_import_0__.aaa)()
811+
__vite_ssr_identity__(__vite_ssr_import_0__.bbb)()
805812
"
806813
`)
807814
})
@@ -823,11 +830,12 @@ test('jsx', async () => {
823830
const result = await transformWithEsbuild(code, id)
824831
expect(await ssrTransformSimpleCode(result.code, '/foo.jsx'))
825832
.toMatchInlineSnapshot(`
826-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]});
833+
"const __vite_ssr_identity__ = v => v;
834+
const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]});
827835
const __vite_ssr_import_1__ = await __vite_ssr_import__("foo", {"importedNames":["Foo","Slot"]});
828836
829837
830-
function Bar({ Slot: Slot2 = /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_import_1__.Foo, null) }) {
838+
function Bar({ Slot: Slot2 = /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_identity__(__vite_ssr_import_1__.Foo), null) }) {
831839
return /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_import_0__.default.Fragment, null, /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(Slot2, null));
832840
}
833841
"
@@ -899,12 +907,29 @@ import foo from "foo"`,
899907
),
900908
).toMatchInlineSnapshot(`
901909
"#!/usr/bin/env node
910+
const __vite_ssr_identity__ = v => v;
902911
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["default"]});
903-
console.log(__vite_ssr_import_0__.default);
912+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.default));
904913
"
905914
`)
906915
})
907916

917+
test('indentity function helper injected after hashbang', async () => {
918+
expect(
919+
await ssrTransformSimpleCode(
920+
`#!/usr/bin/env node
921+
import { foo } from "foo"
922+
foo()`,
923+
),
924+
).toMatchInlineSnapshot(`
925+
"#!/usr/bin/env node
926+
const __vite_ssr_identity__ = v => v;
927+
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
928+
929+
__vite_ssr_identity__(__vite_ssr_import_0__.foo)()"
930+
`)
931+
})
932+
908933
// #10289
909934
test('track scope by class, function, condition blocks', async () => {
910935
const code = `
@@ -935,7 +960,8 @@ export class Test {
935960
};`.trim()
936961

937962
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
938-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});
963+
"const __vite_ssr_identity__ = v => v;
964+
const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});
939965
940966
if (false) {
941967
const foo = 'foo'
@@ -944,8 +970,8 @@ export class Test {
944970
const [bar] = ['bar']
945971
console.log(bar)
946972
} else {
947-
console.log(__vite_ssr_import_0__.foo)
948-
console.log(__vite_ssr_import_0__.bar)
973+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.foo))
974+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.bar))
949975
}
950976
class Test {
951977
constructor() {
@@ -956,8 +982,8 @@ export class Test {
956982
const [bar] = ['bar']
957983
console.log(bar)
958984
} else {
959-
console.log(__vite_ssr_import_0__.foo)
960-
console.log(__vite_ssr_import_0__.bar)
985+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.foo))
986+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.bar))
961987
}
962988
}
963989
}
@@ -1068,11 +1094,12 @@ const Baz = class extends Foo {}
10681094
`,
10691095
)
10701096
expect(result?.code).toMatchInlineSnapshot(`
1071-
"const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["default","Bar"]});
1097+
"const __vite_ssr_identity__ = v => v;
1098+
const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["default","Bar"]});
10721099
10731100
10741101
1075-
console.log(__vite_ssr_import_0__.default, __vite_ssr_import_0__.Bar);
1102+
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.default), __vite_ssr_identity__(__vite_ssr_import_0__.Bar));
10761103
const obj = {
10771104
foo: class Foo {},
10781105
bar: class Bar {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function foo() {
2+
return this
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { foo } from './importee.js'
2+
3+
export const result = foo()

packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ describe('module runner initialization', async () => {
230230
const action = await mod.importAction('/fixtures/cyclic/action')
231231
expect(action).toBeDefined()
232232
})
233+
234+
it('this of the exported function should be undefined', async ({
235+
runner,
236+
}) => {
237+
const mod = await runner.import('/fixtures/no-this/importer.js')
238+
expect(mod.result).toBe(undefined)
239+
})
233240
})
234241

235242
describe('optimize-deps', async () => {

packages/vite/src/node/ssr/ssrTransform.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const ssrImportKey = `__vite_ssr_import__`
3939
export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__`
4040
export const ssrExportAllKey = `__vite_ssr_exportAll__`
4141
export const ssrImportMetaKey = `__vite_ssr_import_meta__`
42+
const ssrIdentityFunction = `__vite_ssr_identity__`
4243

4344
const hashbangRE = /^#!.*\n/
4445

@@ -303,6 +304,7 @@ async function ssrTransformScript(
303304
}
304305
}
305306

307+
let injectIdentityFunction = false
306308
// 3. convert references to import bindings & import.meta references
307309
walk(ast, {
308310
onIdentifier(id, parent, parentStack) {
@@ -332,6 +334,13 @@ async function ssrTransformScript(
332334
const topNode = parentStack[parentStack.length - 2]
333335
s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`)
334336
}
337+
} else if (parent.type === 'CallExpression') {
338+
s.update(id.start, id.end, binding)
339+
// wrap with identity function to avoid method binding `this`
340+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#method_binding
341+
s.prependRight(id.start, `${ssrIdentityFunction}(`)
342+
s.appendLeft(id.end, `)`)
343+
injectIdentityFunction = true
335344
} else if (
336345
// don't transform class name identifier
337346
!(parent.type === 'ClassExpression' && id === parent.id)
@@ -350,6 +359,10 @@ async function ssrTransformScript(
350359
},
351360
})
352361

362+
if (injectIdentityFunction) {
363+
s.prependLeft(hoistIndex, `const ${ssrIdentityFunction} = v => v;\n`)
364+
}
365+
353366
let map = s.generateMap({ hires: 'boundary' })
354367
map.sources = [path.basename(url)]
355368
// needs to use originalCode instead of code

0 commit comments

Comments
 (0)