diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index 2d3711fd8289c6..4d51d1cecaf493 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -205,6 +205,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { modernPolyfills ) await buildPolyfillChunk( + config.mode, modernPolyfills, bundle, facadeToModernPolyfillMap, @@ -237,6 +238,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { ) await buildPolyfillChunk( + config.mode, legacyPolyfills, bundle, facadeToLegacyPolyfillMap, @@ -615,6 +617,7 @@ function createBabelPresetEnvOptions( } async function buildPolyfillChunk( + mode: string, imports: Set, bundle: OutputBundle, facadeToChunkMap: Map, @@ -626,6 +629,7 @@ async function buildPolyfillChunk( let { minify, assetsDir } = buildOptions minify = minify ? 'terser' : false const res = await build({ + mode, // so that everything is resolved from here root: path.dirname(fileURLToPath(import.meta.url)), configFile: false, diff --git a/playground/legacy/__tests__/client-and-ssr/client-legacy-ssr-sequential-builds.spec.ts b/playground/legacy/__tests__/client-and-ssr/client-legacy-ssr-sequential-builds.spec.ts new file mode 100644 index 00000000000000..0980d722605347 --- /dev/null +++ b/playground/legacy/__tests__/client-and-ssr/client-legacy-ssr-sequential-builds.spec.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { port } from './serve' +import { isBuild, page } from '~utils' + +const url = `http://localhost:${port}` + +describe.runIf(isBuild)('client-legacy-ssr-sequential-builds', () => { + test('should work', async () => { + await page.goto(url) + expect(await page.textContent('#app')).toMatch('Hello') + }) + + test('import.meta.env.MODE', async () => { + // SSR build is always modern + expect(await page.textContent('#mode')).toMatch('test') + }) +}) diff --git a/playground/legacy/__tests__/client-and-ssr/serve.ts b/playground/legacy/__tests__/client-and-ssr/serve.ts new file mode 100644 index 00000000000000..becca5fc123b8b --- /dev/null +++ b/playground/legacy/__tests__/client-and-ssr/serve.ts @@ -0,0 +1,68 @@ +// this is automatically detected by playground/vitestSetup.ts and will replace +// the default e2e test serve behavior +import path from 'node:path' +import { ports, rootDir } from '~utils' + +export const port = ports['legacy/client-and-ssr'] + +export async function serve(): Promise<{ close(): Promise }> { + const { build } = await import('vite') + + // In a CLI app it is possible that you may run `build` several times one after another + // For example, you may want to override an option specifically for the SSR build + // And you may have a CLI app built for that purpose to make a more concise API + // An unexpected behaviour is for the plugin-legacy to override the process.env.NODE_ENV value + // And any build after the first client build that called plugin-legacy will misbehave and + // build with process.env.NODE_ENV=production, rather than your CLI's env: NODE_ENV=myWhateverEnv my-cli-app build + // The issue is with plugin-legacy's index.ts file not explicitly passing mode: process.env.NODE_ENV to vite's build function + // This causes vite to call resolveConfig with defaultMode = 'production' and mutate process.env.NODE_ENV to 'production' + + await build({ + mode: process.env.NODE_ENV, + root: rootDir, + logLevel: 'silent', + build: { + target: 'esnext', + outDir: 'dist/client' + } + }) + + await build({ + mode: process.env.NODE_ENV, + root: rootDir, + logLevel: 'silent', + build: { + target: 'esnext', + ssr: 'entry-server-sequential.js', + outDir: 'dist/server' + } + }) + + const { default: express } = await import('express') + const app = express() + + app.use('/', async (_req, res) => { + const { render } = await import( + path.resolve(rootDir, './dist/server/entry-server-sequential.mjs') + ) + const html = await render() + res.status(200).set({ 'Content-Type': 'text/html' }).end(html) + }) + + return new Promise((resolve, reject) => { + try { + const server = app.listen(port, () => { + resolve({ + // for test teardown + async close() { + await new Promise((resolve) => { + server.close(resolve) + }) + } + }) + }) + } catch (e) { + reject(e) + } + }) +} diff --git a/playground/legacy/entry-server-sequential.js b/playground/legacy/entry-server-sequential.js new file mode 100644 index 00000000000000..718dc84b8df6b0 --- /dev/null +++ b/playground/legacy/entry-server-sequential.js @@ -0,0 +1,7 @@ +// This counts as 'server-side' rendering, yes? +export async function render() { + return /* html */ ` +
Hello
+
${import.meta.env.MODE}
+ ` +} diff --git a/playground/test-utils.ts b/playground/test-utils.ts index 1364c56e477b10..c7c3288fafe2ff 100644 --- a/playground/test-utils.ts +++ b/playground/test-utils.ts @@ -21,6 +21,7 @@ export const ports = { 'legacy/ssr': 9520, lib: 9521, 'optimize-missing-deps': 9522, + 'legacy/client-and-ssr': 9523, 'ssr-deps': 9600, 'ssr-html': 9601, 'ssr-pug': 9602,