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: keep track of ssr version of imported modules separately #11973

Merged
merged 1 commit into from
Jun 15, 2023
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
25 changes: 21 additions & 4 deletions packages/vite/src/node/server/moduleGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export class ModuleNode {
info?: ModuleInfo
meta?: Record<string, any>
importers = new Set<ModuleNode>()
importedModules = new Set<ModuleNode>()
clientImportedModules = new Set<ModuleNode>()
ssrImportedModules = new Set<ModuleNode>()
acceptedHmrDeps = new Set<ModuleNode>()
acceptedHmrExports: Set<string> | null = null
importedBindings: Map<string, Set<string>> | null = null
Expand All @@ -46,6 +47,14 @@ export class ModuleNode {
this.isSelfAccepting = false
}
}

get importedModules(): Set<ModuleNode> {
const importedModules = new Set(this.clientImportedModules)
for (const module of this.ssrImportedModules) {
importedModules.add(module)
}
return importedModules
}
}

export type ResolvedUrl = [
Expand Down Expand Up @@ -169,7 +178,7 @@ export class ModuleGraph {
ssr?: boolean,
): Promise<Set<ModuleNode> | undefined> {
mod.isSelfAccepting = isSelfAccepting
const prevImports = mod.importedModules
const prevImports = ssr ? mod.ssrImportedModules : mod.clientImportedModules
let noLongerImported: Set<ModuleNode> | undefined

let resolvePromises = []
Expand All @@ -195,11 +204,19 @@ export class ModuleGraph {
await Promise.all(resolvePromises)
}

const nextImports = (mod.importedModules = new Set(resolveResults))
const nextImports = new Set(resolveResults)
if (ssr) {
mod.ssrImportedModules = nextImports
} else {
mod.clientImportedModules = nextImports
}

// remove the importer from deps that were imported but no longer are.
prevImports.forEach((dep) => {
if (!nextImports.has(dep)) {
if (
!mod.clientImportedModules.has(dep) &&
!mod.ssrImportedModules.has(dep)
) {
dep.importers.delete(mod)
if (!dep.importers.size) {
// dependency no longer imported
Expand Down
35 changes: 33 additions & 2 deletions playground/ssr-deps/__tests__/ssr-deps.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from 'vitest'
import { describe, expect, test } from 'vitest'
import { port } from './serve'
import { getColor, page } from '~utils'
import { editFile, getColor, isServe, page, untilUpdated } from '~utils'

const url = `http://localhost:${port}`

Expand Down Expand Up @@ -121,3 +121,34 @@ test('import css library', async () => {
await page.goto(url)
expect(await page.textContent('.module-condition')).toMatch('[success]')
})

describe.runIf(isServe)('hmr', () => {
test('handle isomorphic module updates', async () => {
await page.goto(url)

expect(await page.textContent('.isomorphic-module-server')).toMatch(
'[server]',
)
// Allowing additional time for this element to be filled in
// by a client script that is loaded using dynamic import
await untilUpdated(async () => {
return page.textContent('.isomorphic-module-browser')
}, '[browser]')

editFile('src/isomorphic-module-browser.js', (code) =>
code.replace('[browser]', '[browser-hmr]'),
)
await page.waitForNavigation()
await untilUpdated(async () => {
return page.textContent('.isomorphic-module-browser')
}, '[browser-hmr]')

editFile('src/isomorphic-module-server.js', (code) =>
code.replace('[server]', '[server-hmr]'),
)
await page.waitForNavigation()
await untilUpdated(async () => {
return page.textContent('.isomorphic-module-server')
}, '[server-hmr]')
})
})
10 changes: 10 additions & 0 deletions playground/ssr-deps/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,15 @@ <h1>SSR Dependencies</h1>
// hydration scripts
import '@vitejs/test-css-lib'
</script>
<script type="module">
// Using dynamic import, so the module is transformed when browser actually
// requests it. This essentially disables pre-transform optimization that's
// crucial to trigger a race condition, covered by the test case introduced
// in https://github.com/vitejs/vite/pull/11973
import('virtual:isomorphic-module').then(({ default: message }) => {
document.querySelector('.isomorphic-module-browser').textContent =
message
})
</script>
</body>
</html>
17 changes: 17 additions & 0 deletions playground/ssr-deps/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ export async function createServer(root = process.cwd(), hmrPort) {
}
},
},
{
name: 'virtual-isomorphic-module',
resolveId(id) {
if (id === 'virtual:isomorphic-module') {
return '\0virtual:isomorphic-module'
}
},
load(id, { ssr }) {
if (id === '\0virtual:isomorphic-module') {
if (ssr) {
return 'export { default } from "/src/isomorphic-module-server.js";'
} else {
return 'export { default } from "/src/isomorphic-module-browser.js";'
}
}
},
},
],
})
// use vite's connect instance as middleware
Expand Down
5 changes: 5 additions & 0 deletions playground/ssr-deps/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import optimizedCjsWithNestedExternal from '@vitejs/test-optimized-cjs-with-nest
import { setMessage } from '@vitejs/test-external-entry/entry'
setMessage('Hello World!')
import externalUsingExternalEntry from '@vitejs/test-external-using-external-entry'
import isomorphicModuleMessage from 'virtual:isomorphic-module'

export async function render(url, rootDir) {
let html = ''
Expand Down Expand Up @@ -90,5 +91,9 @@ export async function render(url, rootDir) {

html += `\n<p class="module-condition">${moduleConditionMessage}</p>`

html += `\n<p class="isomorphic-module-server">${isomorphicModuleMessage}</p>`

html += `\n<p class="isomorphic-module-browser"></p>`

return html + '\n'
}
3 changes: 3 additions & 0 deletions playground/ssr-deps/src/isomorphic-module-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const message = 'message from isomorphic-module (browser): [browser]'

export default message
3 changes: 3 additions & 0 deletions playground/ssr-deps/src/isomorphic-module-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const message = 'message from isomorphic-module (server): [server]'

export default message