Skip to content
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

fix(ssr): inject identity function at the top #18449

Merged
Merged
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
48 changes: 35 additions & 13 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ 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"]});
const __vite_ssr_identity__ = v => v;
"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 @@ -38,8 +38,8 @@ 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"]});
const __vite_ssr_identity__ = v => v;
"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 @@ -223,8 +223,8 @@ 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"]});
const __vite_ssr_identity__ = v => v;
"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 @@ -536,8 +536,8 @@ 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_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) }
Expand Down Expand Up @@ -862,8 +862,8 @@ 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_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"]});


Expand Down Expand Up @@ -955,8 +955,8 @@ foo()`,
),
).toMatchInlineSnapshot(`
"#!/usr/bin/env node
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
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)()"
`)
Expand Down Expand Up @@ -992,8 +992,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_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});

if (false) {
const foo = 'foo'
Expand Down Expand Up @@ -1183,3 +1183,25 @@ console.log(foo + 2)
"
`)
})

test('identity function is declared before used', async () => {
expect(
await ssrTransformSimpleCode(`
import { foo } from './foo'
export default foo()
export * as bar from './bar'
console.log(bar)
`),
).toMatchInlineSnapshot(`
"const __vite_ssr_identity__ = v => v;
const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]});


__vite_ssr_exports__.default = __vite_ssr_identity__(__vite_ssr_import_0__.foo)()
const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar");

Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }});
console.log(bar)
Comment on lines +1196 to +1204
Copy link
Member Author

@sapphi-red sapphi-red Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output without the fix is:

const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]});


__vite_ssr_exports__.default = __vite_ssr_identity__(__vite_ssr_import_0__.foo)()
const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar");
const __vite_ssr_identity__ = v => v;

Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }});
console.log(bar)

Note that __vite_ssr_identity__ is called before declaration.

"
`)
})
5 changes: 3 additions & 2 deletions packages/vite/src/node/ssr/ssrTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ async function ssrTransformScript(
const declaredConst = new Set<string>()

// hoist at the start of the file, after the hashbang
let hoistIndex = hashbangRE.exec(code)?.[0].length ?? 0
const fileStartIndex = hashbangRE.exec(code)?.[0].length ?? 0
let hoistIndex = fileStartIndex

function defineImport(
index: number,
Expand Down Expand Up @@ -375,7 +376,7 @@ async function ssrTransformScript(
})

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

let map = s.generateMap({ hires: 'boundary' })
Expand Down