Skip to content

Commit

Permalink
fix(ssr): fix ssr transform edge cases
Browse files Browse the repository at this point in the history
fix #1646
  • Loading branch information
yyx990803 committed Jan 23, 2021
1 parent 38524f6 commit f22ddbd
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/playground/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"include": ["."],
"exclude": ["**/dist/**"],
"compilerOptions": {
"target": "esnext",
"outDir": "dist",
"allowJs": true,
"esModuleInterop": true,
Expand Down
131 changes: 131 additions & 0 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { traverseHtml } from '../../plugins/html'
import { ssrTransform } from '../ssrTransform'

test('default import', async () => {
expect(
(await ssrTransform(`import foo from 'vue';console.log(foo.bar)`, null))
.code
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
console.log(__vite_ssr_import_0__.default.bar)"
`)
})

test('named import', async () => {
expect(
(
await ssrTransform(
`import { ref } from 'vue';function foo() { return ref(0) }`,
null
)
).code
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
function foo() { return __vite_ssr_import_0__.ref(0) }"
`)
})

test('namespace import', async () => {
expect(
(
await ssrTransform(
`import * as vue from 'vue';function foo() { return vue.ref(0) }`,
null
)
).code
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
function foo() { return __vite_ssr_import_0__.ref(0) }"
`)
})

test('export function decl', async () => {
expect((await ssrTransform(`export function foo() {}`, null)).code)
.toMatchInlineSnapshot(`
"function foo() {}
Object.defineProperty(__vite_ssr_exports__, \\"foo\\", { get(){ return foo }})"
`)
})

test('export class decl', async () => {
expect((await ssrTransform(`export class foo {}`, null)).code)
.toMatchInlineSnapshot(`
"class foo {}
Object.defineProperty(__vite_ssr_exports__, \\"foo\\", { get(){ return foo }})"
`)
})

test('export var decl', async () => {
expect((await ssrTransform(`export const a = 1, b = 2`, null)).code)
.toMatchInlineSnapshot(`
"const a = 1, b = 2
Object.defineProperty(__vite_ssr_exports__, \\"a\\", { get(){ return a }})
Object.defineProperty(__vite_ssr_exports__, \\"b\\", { get(){ return b }})"
`)
})

test('export named', async () => {
expect(
(await ssrTransform(`const a = 1, b = 2; export { a, b as c }`, null)).code
).toMatchInlineSnapshot(`
"const a = 1, b = 2;
Object.defineProperty(__vite_ssr_exports__, \\"a\\", { get(){ return a }})
Object.defineProperty(__vite_ssr_exports__, \\"c\\", { get(){ return b }})"
`)
})

test('export named from', async () => {
expect(
(await ssrTransform(`export { ref, computed as c } from 'vue'`, null)).code
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
Object.defineProperty(__vite_ssr_exports__, \\"ref\\", { get(){ return __vite_ssr_import_0__.ref }})
Object.defineProperty(__vite_ssr_exports__, \\"c\\", { get(){ return __vite_ssr_import_0__.computed }})"
`)
})

test('named exports of imported binding', async () => {
expect(
(
await ssrTransform(
`import {createApp} from 'vue';export {createApp}`,
null
)
).code
).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
Object.defineProperty(__vite_ssr_exports__, \\"createApp\\", { get(){ return __vite_ssr_import_0__.createApp }})"
`)
})

test('export * from', async () => {
expect((await ssrTransform(`export * from 'vue'`, null)).code)
.toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\")
__vite_ssr_exportAll__(__vite_ssr_import_0__)"
`)
})

test('export default', async () => {
expect(
(await ssrTransform(`export default {}`, null)).code
).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = {}"`)
})

test('import.meta', async () => {
expect(
(await ssrTransform(`console.log(import.meta.url)`, null)).code
).toMatchInlineSnapshot(`"console.log(__vite_ssr_import_meta__.url)"`)
})

test('dynamic import', async () => {
expect(
(await ssrTransform(`export const i = () => import('./foo')`, null)).code
).toMatchInlineSnapshot(`
"const i = () => __vite_ssr_dynamic_import__('./foo')
Object.defineProperty(__vite_ssr_exports__, \\"i\\", { get(){ return i }})"
`)
})
15 changes: 13 additions & 2 deletions packages/vite/src/node/ssr/ssrTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export async function ssrTransform(
)
}

// 1. check all import/export statements
// 1. check all import statements and record id -> importName map
for (const node of ast.body as Node[]) {
// import foo from 'foo' --> foo -> __import_foo__.default
// import { baz } from 'foo' --> baz -> __import_foo__.baz
Expand All @@ -78,7 +78,10 @@ export async function ssrTransform(
}
s.remove(node.start, node.end)
}
}

// 2. check all export statements and define exports
for (const node of ast.body as Node[]) {
// named exports
if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
Expand All @@ -104,10 +107,13 @@ export async function ssrTransform(
for (const spec of node.specifiers) {
defineExport(spec.exported.name, `${importId}.${spec.local.name}`)
}
s.remove(node.start, node.end)
} else {
// export { foo, bar }
for (const spec of node.specifiers) {
defineExport(spec.exported.name, spec.local.name)
const local = spec.local.name
const binding = idToImportMap.get(local)
defineExport(spec.exported.name, binding || local)
}
s.remove(node.start, node.end)
}
Expand All @@ -125,6 +131,7 @@ export async function ssrTransform(
// export * from './foo'
if (node.type === 'ExportAllDeclaration') {
const importId = defineImport(node, node.source.value as string)
s.remove(node.start, node.end)
s.append(`\n${ssrExportAllKey}(${importId})`)
}
}
Expand Down Expand Up @@ -319,6 +326,10 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) {
return false
}

if (parent.type === 'ExportSpecifier') {
return false
}

// is a special keyword but parsed as identifier
if (id.name === 'arguments') {
return false
Expand Down

0 comments on commit f22ddbd

Please sign in to comment.