Skip to content

Commit

Permalink
fix(ssr): this in exported function should be undefined (#18329)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red authored Oct 18, 2024
1 parent 964a618 commit bae6a37
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 33 deletions.
93 changes: 60 additions & 33 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ test('named import', async () => {
`import { ref } from 'vue';function foo() { return ref(0) }`,
),
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]});
function foo() { return __vite_ssr_import_0__.ref(0) }"
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]});
function foo() { return __vite_ssr_identity__(__vite_ssr_import_0__.ref)(0) }"
`)
})

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

Expand Down Expand Up @@ -220,8 +222,9 @@ test('do not rewrite method definition', async () => {
`import { fn } from 'vue';class A { fn() { fn() } }`,
)
expect(result?.code).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]});
class A { fn() { __vite_ssr_import_0__.fn() } }"
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]});
class A { fn() { __vite_ssr_identity__(__vite_ssr_import_0__.fn)() } }"
`)
expect(result?.deps).toEqual(['vue'])
})
Expand Down Expand Up @@ -501,14 +504,15 @@ test('overwrite bindings', async () => {
`function g() { const f = () => { const inject = true }; console.log(inject) }\n`,
),
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]});
const a = { inject: __vite_ssr_import_0__.inject }
const b = { test: __vite_ssr_import_0__.inject }
function c() { const { test: inject } = { test: true }; console.log(inject) }
const d = __vite_ssr_import_0__.inject
function f() { console.log(__vite_ssr_import_0__.inject) }
function f() { console.log(__vite_ssr_identity__(__vite_ssr_import_0__.inject)) }
function e() { const { inject } = { inject: true } }
function g() { const f = () => { const inject = true }; console.log(__vite_ssr_import_0__.inject) }
function g() { const f = () => { const inject = true }; console.log(__vite_ssr_identity__(__vite_ssr_import_0__.inject)) }
"
`)
})
Expand All @@ -530,12 +534,13 @@ function c({ _ = bar() + foo() }) {}
`,
),
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo","bar"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo","bar"]});
const a = ({ _ = __vite_ssr_import_0__.foo() }) => {}
function b({ _ = __vite_ssr_import_0__.bar() }) {}
function c({ _ = __vite_ssr_import_0__.bar() + __vite_ssr_import_0__.foo() }) {}
const a = ({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.foo)() }) => {}
function b({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.bar)() }) {}
function c({ _ = __vite_ssr_identity__(__vite_ssr_import_0__.bar)() + __vite_ssr_identity__(__vite_ssr_import_0__.foo)() }) {}
"
`)
})
Expand Down Expand Up @@ -618,7 +623,8 @@ objRest()
`,
),
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["remove","add","get","set","rest","objRest"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["remove","add","get","set","rest","objRest"]});
Expand All @@ -638,12 +644,12 @@ objRest()
objRest()
}
__vite_ssr_import_0__.remove()
__vite_ssr_import_0__.add()
__vite_ssr_import_0__.get()
__vite_ssr_import_0__.set()
__vite_ssr_import_0__.rest()
__vite_ssr_import_0__.objRest()
__vite_ssr_identity__(__vite_ssr_import_0__.remove)()
__vite_ssr_identity__(__vite_ssr_import_0__.add)()
__vite_ssr_identity__(__vite_ssr_import_0__.get)()
__vite_ssr_identity__(__vite_ssr_import_0__.set)()
__vite_ssr_identity__(__vite_ssr_import_0__.rest)()
__vite_ssr_identity__(__vite_ssr_import_0__.objRest)()
"
`)
})
Expand Down Expand Up @@ -778,7 +784,8 @@ bbb()
`,
),
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["aaa","bbb","ccc","ddd"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["aaa","bbb","ccc","ddd"]});
Expand All @@ -800,8 +807,8 @@ bbb()
ccc()
}
__vite_ssr_import_0__.aaa()
__vite_ssr_import_0__.bbb()
__vite_ssr_identity__(__vite_ssr_import_0__.aaa)()
__vite_ssr_identity__(__vite_ssr_import_0__.bbb)()
"
`)
})
Expand All @@ -823,11 +830,12 @@ test('jsx', async () => {
const result = await transformWithEsbuild(code, id)
expect(await ssrTransformSimpleCode(result.code, '/foo.jsx'))
.toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]});
const __vite_ssr_import_1__ = await __vite_ssr_import__("foo", {"importedNames":["Foo","Slot"]});
function Bar({ Slot: Slot2 = /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_import_1__.Foo, null) }) {
function Bar({ Slot: Slot2 = /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_identity__(__vite_ssr_import_1__.Foo), null) }) {
return /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_import_0__.default.Fragment, null, /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(Slot2, null));
}
"
Expand Down Expand Up @@ -899,12 +907,29 @@ import foo from "foo"`,
),
).toMatchInlineSnapshot(`
"#!/usr/bin/env node
const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["default"]});
console.log(__vite_ssr_import_0__.default);
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.default));
"
`)
})

test('indentity function helper injected after hashbang', async () => {
expect(
await ssrTransformSimpleCode(
`#!/usr/bin/env node
import { foo } from "foo"
foo()`,
),
).toMatchInlineSnapshot(`
"#!/usr/bin/env node
const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
__vite_ssr_identity__(__vite_ssr_import_0__.foo)()"
`)
})

// #10289
test('track scope by class, function, condition blocks', async () => {
const code = `
Expand Down Expand Up @@ -935,7 +960,8 @@ export class Test {
};`.trim()

expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});
if (false) {
const foo = 'foo'
Expand All @@ -944,8 +970,8 @@ export class Test {
const [bar] = ['bar']
console.log(bar)
} else {
console.log(__vite_ssr_import_0__.foo)
console.log(__vite_ssr_import_0__.bar)
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.foo))
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.bar))
}
class Test {
constructor() {
Expand All @@ -956,8 +982,8 @@ export class Test {
const [bar] = ['bar']
console.log(bar)
} else {
console.log(__vite_ssr_import_0__.foo)
console.log(__vite_ssr_import_0__.bar)
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.foo))
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.bar))
}
}
}
Expand Down Expand Up @@ -1068,11 +1094,12 @@ const Baz = class extends Foo {}
`,
)
expect(result?.code).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["default","Bar"]});
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["default","Bar"]});
console.log(__vite_ssr_import_0__.default, __vite_ssr_import_0__.Bar);
console.log(__vite_ssr_identity__(__vite_ssr_import_0__.default), __vite_ssr_identity__(__vite_ssr_import_0__.Bar));
const obj = {
foo: class Foo {},
bar: class Bar {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function foo() {
return this
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { foo } from './importee.js'

export const result = foo()
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ describe('module runner initialization', async () => {
const action = await mod.importAction('/fixtures/cyclic/action')
expect(action).toBeDefined()
})

it('this of the exported function should be undefined', async ({
runner,
}) => {
const mod = await runner.import('/fixtures/no-this/importer.js')
expect(mod.result).toBe(undefined)
})
})

describe('optimize-deps', async () => {
Expand Down
13 changes: 13 additions & 0 deletions packages/vite/src/node/ssr/ssrTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const ssrImportKey = `__vite_ssr_import__`
export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__`
export const ssrExportAllKey = `__vite_ssr_exportAll__`
export const ssrImportMetaKey = `__vite_ssr_import_meta__`
const ssrIdentityFunction = `__vite_ssr_identity__`

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

Expand Down Expand Up @@ -303,6 +304,7 @@ async function ssrTransformScript(
}
}

let injectIdentityFunction = false
// 3. convert references to import bindings & import.meta references
walk(ast, {
onIdentifier(id, parent, parentStack) {
Expand Down Expand Up @@ -332,6 +334,13 @@ async function ssrTransformScript(
const topNode = parentStack[parentStack.length - 2]
s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`)
}
} else if (parent.type === 'CallExpression') {
s.update(id.start, id.end, binding)
// wrap with identity function to avoid method binding `this`
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#method_binding
s.prependRight(id.start, `${ssrIdentityFunction}(`)
s.appendLeft(id.end, `)`)
injectIdentityFunction = true
} else if (
// don't transform class name identifier
!(parent.type === 'ClassExpression' && id === parent.id)
Expand All @@ -350,6 +359,10 @@ async function ssrTransformScript(
},
})

if (injectIdentityFunction) {
s.prependLeft(hoistIndex, `const ${ssrIdentityFunction} = v => v;\n`)
}

let map = s.generateMap({ hires: 'boundary' })
map.sources = [path.basename(url)]
// needs to use originalCode instead of code
Expand Down

0 comments on commit bae6a37

Please sign in to comment.