Skip to content

Commit f8c92d1

Browse files
authoredMay 29, 2022
feat: default esm SSR build, simplified externalization (#8348)
1 parent 5161ecd commit f8c92d1

34 files changed

+344
-156
lines changed
 

Diff for: ‎docs/config/ssr-options.md

+8
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,11 @@ Prevent listed dependencies from being externalized for SSR. If `true`, no depen
2424
- **Default:** `node`
2525

2626
Build target for the SSR server.
27+
28+
## ssr.format
29+
30+
- **Type:** `'esm' | 'cjs'`
31+
- **Default:** `esm`
32+
- **Experimental**
33+
34+
Build format for the SSR server. Since Vite v3 the SSR build generates ESM by default. `'cjs'` can be selected to generate a CJS build, but it isn't recommended. The option is left marked as experimental to give users more time to update to ESM. CJS builds requires complex externalization heuristics that aren't present in the ESM format.

Diff for: ‎docs/vite.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vite'
2+
3+
export default defineConfig({
4+
ssr: {
5+
format: 'cjs'
6+
}
7+
})

Diff for: ‎packages/vite/src/node/build.ts

+50-34
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import { manifestPlugin } from './plugins/manifest'
3131
import type { Logger } from './logger'
3232
import { dataURIPlugin } from './plugins/dataUri'
3333
import { buildImportAnalysisPlugin } from './plugins/importAnalysisBuild'
34-
import { resolveSSRExternal, shouldExternalizeForSSR } from './ssr/ssrExternal'
34+
import {
35+
cjsShouldExternalizeForSSR,
36+
cjsSsrResolveExternals
37+
} from './ssr/ssrExternal'
3538
import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
3639
import type { DepOptimizationMetadata } from './optimizer'
3740
import {
@@ -342,7 +345,6 @@ async function doBuild(
342345
const config = await resolveConfig(inlineConfig, 'build', 'production')
343346
const options = config.build
344347
const ssr = !!options.ssr
345-
const esm = config.ssr?.format === 'es' || !ssr
346348
const libOptions = options.lib
347349

348350
config.logger.info(
@@ -374,27 +376,14 @@ async function doBuild(
374376
ssr ? config.plugins.map((p) => injectSsrFlagToHooks(p)) : config.plugins
375377
) as Plugin[]
376378

377-
// inject ssrExternal if present
378379
const userExternal = options.rollupOptions?.external
379380
let external = userExternal
380-
if (ssr) {
381-
// see if we have cached deps data available
382-
let knownImports: string[] | undefined
383-
const dataPath = path.join(getDepsCacheDir(config), '_metadata.json')
384-
try {
385-
const data = JSON.parse(
386-
fs.readFileSync(dataPath, 'utf-8')
387-
) as DepOptimizationMetadata
388-
knownImports = Object.keys(data.optimized)
389-
} catch (e) {}
390-
if (!knownImports) {
391-
// no dev deps optimization data, do a fresh scan
392-
knownImports = await findKnownImports(config)
393-
}
394-
external = resolveExternal(
395-
resolveSSRExternal(config, knownImports),
396-
userExternal
397-
)
381+
382+
// In CJS, we can pass the externals to rollup as is. In ESM, we need to
383+
// do it in the resolve plugin so we can add the resolved extension for
384+
// deep node_modules imports
385+
if (ssr && config.ssr?.format === 'cjs') {
386+
external = await cjsSsrResolveExternal(config, userExternal)
398387
}
399388

400389
if (isDepsOptimizerEnabled(config) && !ssr) {
@@ -432,10 +421,12 @@ async function doBuild(
432421

433422
try {
434423
const buildOutputOptions = (output: OutputOptions = {}): OutputOptions => {
424+
const cjsSsrBuild = ssr && config.ssr?.format === 'cjs'
435425
return {
436426
dir: outDir,
437-
format: esm ? 'es' : 'cjs',
438-
exports: esm ? 'auto' : 'named',
427+
// Default format is 'es' for regular and for SSR builds
428+
format: cjsSsrBuild ? 'cjs' : 'es',
429+
exports: cjsSsrBuild ? 'named' : 'auto',
439430
sourcemap: options.sourcemap,
440431
name: libOptions ? libOptions.name : undefined,
441432
generatedCode: 'es2015',
@@ -697,26 +688,51 @@ export function onRollupWarning(
697688
}
698689
}
699690

700-
function resolveExternal(
701-
ssrExternals: string[],
691+
async function cjsSsrResolveExternal(
692+
config: ResolvedConfig,
702693
user: ExternalOption | undefined
703-
): ExternalOption {
694+
): Promise<ExternalOption> {
695+
// see if we have cached deps data available
696+
let knownImports: string[] | undefined
697+
const dataPath = path.join(getDepsCacheDir(config), '_metadata.json')
698+
try {
699+
const data = JSON.parse(
700+
fs.readFileSync(dataPath, 'utf-8')
701+
) as DepOptimizationMetadata
702+
knownImports = Object.keys(data.optimized)
703+
} catch (e) {}
704+
if (!knownImports) {
705+
// no dev deps optimization data, do a fresh scan
706+
knownImports = await findKnownImports(config)
707+
}
708+
const ssrExternals = cjsSsrResolveExternals(config, knownImports)
709+
704710
return (id, parentId, isResolved) => {
705-
if (shouldExternalizeForSSR(id, ssrExternals)) {
711+
const isExternal = cjsShouldExternalizeForSSR(id, ssrExternals)
712+
if (isExternal) {
706713
return true
707714
}
708715
if (user) {
709-
if (typeof user === 'function') {
710-
return user(id, parentId, isResolved)
711-
} else if (Array.isArray(user)) {
712-
return user.some((test) => isExternal(id, test))
713-
} else {
714-
return isExternal(id, user)
715-
}
716+
return resolveUserExternal(user, id, parentId, isResolved)
716717
}
717718
}
718719
}
719720

721+
function resolveUserExternal(
722+
user: ExternalOption,
723+
id: string,
724+
parentId: string | undefined,
725+
isResolved: boolean
726+
) {
727+
if (typeof user === 'function') {
728+
return user(id, parentId, isResolved)
729+
} else if (Array.isArray(user)) {
730+
return user.some((test) => isExternal(id, test))
731+
} else {
732+
return isExternal(id, user)
733+
}
734+
}
735+
720736
function isExternal(id: string, test: string | RegExp) {
721737
if (typeof test === 'string') {
722738
return id === test

Diff for: ‎packages/vite/src/node/config.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ export interface ExperimentalOptions {
230230

231231
export type SSRTarget = 'node' | 'webworker'
232232

233+
export type SSRFormat = 'esm' | 'cjs'
234+
233235
export interface SSROptions {
234236
external?: string[]
235237
noExternal?: string | RegExp | (string | RegExp)[] | true
@@ -239,12 +241,14 @@ export interface SSROptions {
239241
* Default: 'node'
240242
*/
241243
target?: SSRTarget
242-
243244
/**
244-
* Define the module format for the ssr build.
245-
* Default: 'cjs'
245+
* Define the format for the ssr build. Since Vite v3 the SSR build generates ESM by default.
246+
* `'cjs'` can be selected to generate a CJS build, but it isn't recommended. This option is
247+
* left marked as experimental to give users more time to update to ESM. CJS builds requires
248+
* complex externalization heuristics that aren't present in the ESM format.
249+
* @experimental
246250
*/
247-
format?: 'es' | 'cjs'
251+
format?: SSRFormat
248252
}
249253

250254
export interface ResolveWorkerOptions {

Diff for: ‎packages/vite/src/node/plugins/importAnalysis.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ import {
4141
} from '../utils'
4242
import type { ResolvedConfig } from '../config'
4343
import type { Plugin } from '../plugin'
44-
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
44+
import {
45+
cjsShouldExternalizeForSSR,
46+
shouldExternalizeForSSR
47+
} from '../ssr/ssrExternal'
4548
import { transformRequest } from '../server/transformRequest'
4649
import {
4750
getDepsCacheDir,
@@ -362,10 +365,11 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
362365
}
363366
// skip ssr external
364367
if (ssr) {
365-
if (
366-
server._ssrExternals &&
367-
shouldExternalizeForSSR(specifier, server._ssrExternals)
368-
) {
368+
if (config.ssr?.format === 'cjs') {
369+
if (cjsShouldExternalizeForSSR(specifier, server._ssrExternals)) {
370+
continue
371+
}
372+
} else if (shouldExternalizeForSSR(specifier, config)) {
369373
continue
370374
}
371375
if (isBuiltin(specifier)) {

Diff for: ‎packages/vite/src/node/plugins/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ResolvedConfig } from '../config'
33
import { isDepsOptimizerEnabled } from '../config'
44
import type { Plugin } from '../plugin'
55
import { getDepsOptimizer } from '../optimizer'
6+
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
67
import { jsonPlugin } from './json'
78
import { resolvePlugin } from './resolve'
89
import { optimizedDepsBuildPlugin, optimizedDepsPlugin } from './optimizedDeps'
@@ -61,7 +62,11 @@ export async function resolvePlugins(
6162
packageCache: config.packageCache,
6263
ssrConfig: config.ssr,
6364
asSrc: true,
64-
getDepsOptimizer: () => getDepsOptimizer(config)
65+
getDepsOptimizer: () => getDepsOptimizer(config),
66+
shouldExternalize:
67+
isBuild && config.build.ssr && config.ssr?.format !== 'cjs'
68+
? (id) => shouldExternalizeForSSR(id, config)
69+
: undefined
6570
}),
6671
htmlInlineProxyPlugin(config),
6772
cssPlugin(config),

Diff for: ‎packages/vite/src/node/plugins/resolve.ts

+35-9
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export interface InternalResolveOptions extends ResolveOptions {
8383
scan?: boolean
8484
// Resolve using esbuild deps optimization
8585
getDepsOptimizer?: () => DepsOptimizer | undefined
86+
shouldExternalize?: (id: string) => boolean | undefined
8687
}
8788

8889
export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin {
@@ -105,6 +106,7 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin {
105106
const depsOptimizer = baseOptions.getDepsOptimizer?.()
106107

107108
const ssr = resolveOpts?.ssr === true
109+
108110
if (id.startsWith(browserExternalId)) {
109111
return id
110112
}
@@ -258,7 +260,10 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin {
258260

259261
// bare package imports, perform node resolve
260262
if (bareImportRE.test(id)) {
263+
const external = options.shouldExternalize?.(id)
264+
261265
if (
266+
!external &&
262267
asSrc &&
263268
depsOptimizer &&
264269
!ssr &&
@@ -270,7 +275,13 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin {
270275

271276
if (
272277
targetWeb &&
273-
(res = tryResolveBrowserMapping(id, importer, options, false))
278+
(res = tryResolveBrowserMapping(
279+
id,
280+
importer,
281+
options,
282+
false,
283+
external
284+
))
274285
) {
275286
return res
276287
}
@@ -282,7 +293,8 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin {
282293
options,
283294
targetWeb,
284295
depsOptimizer,
285-
ssr
296+
ssr,
297+
external
286298
))
287299
) {
288300
return res
@@ -523,7 +535,8 @@ export function tryNodeResolve(
523535
options: InternalResolveOptions,
524536
targetWeb: boolean,
525537
depsOptimizer?: DepsOptimizer,
526-
ssr?: boolean
538+
ssr?: boolean,
539+
externalize?: boolean
527540
): PartialResolvedId | undefined {
528541
const { root, dedupe, isBuild, preserveSymlinks, packageCache } = options
529542

@@ -591,7 +604,8 @@ export function tryNodeResolve(
591604

592605
let resolveId = resolvePackageEntry
593606
let unresolvedId = pkgId
594-
if (unresolvedId !== nestedPath) {
607+
const isDeepImport = unresolvedId !== nestedPath
608+
if (isDeepImport) {
595609
resolveId = resolveDeepImport
596610
unresolvedId = '.' + nestedPath.slice(pkgId.length)
597611
}
@@ -616,15 +630,25 @@ export function tryNodeResolve(
616630
return
617631
}
618632

633+
const processResult = (resolved: PartialResolvedId) => {
634+
if (!externalize) {
635+
return resolved
636+
}
637+
const resolvedExt = path.extname(resolved.id)
638+
const resolvedId =
639+
isDeepImport && path.extname(id) !== resolvedExt ? id + resolvedExt : id
640+
return { ...resolved, id: resolvedId, external: true }
641+
}
642+
619643
// link id to pkg for browser field mapping check
620644
idToPkgMap.set(resolved, pkg)
621-
if (isBuild && !depsOptimizer) {
645+
if ((isBuild && !depsOptimizer) || externalize) {
622646
// Resolve package side effects for build so that rollup can better
623647
// perform tree-shaking
624-
return {
648+
return processResult({
625649
id: resolved,
626650
moduleSideEffects: pkg.hasSideEffects(resolved)
627-
}
651+
})
628652
}
629653

630654
if (
@@ -940,7 +964,8 @@ function tryResolveBrowserMapping(
940964
id: string,
941965
importer: string | undefined,
942966
options: InternalResolveOptions,
943-
isFilePath: boolean
967+
isFilePath: boolean,
968+
externalize?: boolean
944969
) {
945970
let res: string | undefined
946971
const pkg = importer && idToPkgMap.get(importer)
@@ -953,10 +978,11 @@ function tryResolveBrowserMapping(
953978
isDebug &&
954979
debug(`[browser mapped] ${colors.cyan(id)} -> ${colors.dim(res)}`)
955980
idToPkgMap.set(res, pkg)
956-
return {
981+
const result = {
957982
id: res,
958983
moduleSideEffects: pkg.hasSideEffects(res)
959984
}
985+
return externalize ? { ...result, external: true } : result
960986
}
961987
} else if (browserMappedPath === false) {
962988
return browserExternalId

Diff for: ‎packages/vite/src/node/plugins/ssrRequireHook.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import { arraify } from '../utils'
1212
export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
1313
if (
1414
config.command !== 'build' ||
15+
!config.build.ssr ||
1516
!config.resolve.dedupe?.length ||
1617
config.ssr?.noExternal === true ||
18+
config.ssr?.format !== 'cjs' ||
1719
isBuildOutputEsm(config)
1820
) {
1921
return null

Diff for: ‎packages/vite/src/node/server/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
resolveHostname
2323
} from '../utils'
2424
import { ssrLoadModule } from '../ssr/ssrModuleLoader'
25-
import { resolveSSRExternal } from '../ssr/ssrExternal'
25+
import { cjsSsrResolveExternals } from '../ssr/ssrExternal'
2626
import {
2727
rebindErrorStacktrace,
2828
ssrRewriteStacktrace
@@ -330,7 +330,7 @@ export async function createServer(
330330
...Object.keys(depsOptimizer.metadata.discovered)
331331
]
332332
}
333-
server._ssrExternals = resolveSSRExternal(config, knownImports)
333+
server._ssrExternals = cjsSsrResolveExternals(config, knownImports)
334334
}
335335
return ssrLoadModule(
336336
url,

Diff for: ‎packages/vite/src/node/ssr/ssrExternal.ts

+108-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { createFilter } from '@rollup/pluginutils'
55
import type { InternalResolveOptions } from '../plugins/resolve'
66
import { tryNodeResolve } from '../plugins/resolve'
77
import {
8+
bareImportRE,
89
createDebugger,
10+
isBuiltin,
911
isDefined,
1012
lookupFile,
1113
normalizePath,
@@ -29,7 +31,7 @@ export function stripNesting(packages: string[]): string[] {
2931
* Heuristics for determining whether a dependency should be externalized for
3032
* server-side rendering.
3133
*/
32-
export function resolveSSRExternal(
34+
export function cjsSsrResolveExternals(
3335
config: ResolvedConfig,
3436
knownImports: string[]
3537
): string[] {
@@ -49,7 +51,7 @@ export function resolveSSRExternal(
4951
seen.add(id)
5052
})
5153

52-
collectExternals(
54+
cjsSsrCollectExternals(
5355
config.root,
5456
config.resolve.preserveSymlinks,
5557
ssrExternals,
@@ -86,8 +88,98 @@ const CJS_CONTENT_RE =
8688
// TODO: use import()
8789
const _require = createRequire(import.meta.url)
8890

89-
// do we need to do this ahead of time or could we do it lazily?
90-
function collectExternals(
91+
const isSsrExternalCache = new WeakMap<
92+
ResolvedConfig,
93+
(id: string) => boolean | undefined
94+
>()
95+
96+
export function shouldExternalizeForSSR(
97+
id: string,
98+
config: ResolvedConfig
99+
): boolean | undefined {
100+
let isSsrExternal = isSsrExternalCache.get(config)
101+
if (!isSsrExternal) {
102+
isSsrExternal = createIsSsrExternal(config)
103+
isSsrExternalCache.set(config, isSsrExternal)
104+
}
105+
return isSsrExternal(id)
106+
}
107+
108+
function createIsSsrExternal(
109+
config: ResolvedConfig
110+
): (id: string) => boolean | undefined {
111+
const processedIds = new Map<string, boolean | undefined>()
112+
113+
const { ssr, root } = config
114+
115+
const noExternal = ssr?.noExternal
116+
const noExternalFilter =
117+
noExternal !== 'undefined' &&
118+
typeof noExternal !== 'boolean' &&
119+
createFilter(undefined, noExternal, { resolve: false })
120+
121+
const isConfiguredAsExternal = (id: string) => {
122+
const { ssr } = config
123+
if (!ssr || ssr.external?.includes(id)) {
124+
return true
125+
}
126+
if (typeof noExternal === 'boolean') {
127+
return !noExternal
128+
}
129+
if (noExternalFilter) {
130+
return noExternalFilter(id)
131+
}
132+
return true
133+
}
134+
135+
const resolveOptions: InternalResolveOptions = {
136+
root,
137+
preserveSymlinks: config.resolve.preserveSymlinks,
138+
isProduction: false,
139+
isBuild: true
140+
}
141+
142+
const isPackageEntry = (id: string) => {
143+
if (!bareImportRE.test(id) || id.includes('\0')) {
144+
return false
145+
}
146+
if (
147+
tryNodeResolve(
148+
id,
149+
undefined,
150+
resolveOptions,
151+
ssr?.target === 'webworker',
152+
undefined,
153+
true
154+
)
155+
) {
156+
return true
157+
}
158+
try {
159+
// no main entry, but deep imports may be allowed
160+
if (resolveFrom(`${id}/package.json`, root)) {
161+
return true
162+
}
163+
} catch {}
164+
return false
165+
}
166+
167+
return (id: string) => {
168+
if (processedIds.has(id)) {
169+
return processedIds.get(id)
170+
}
171+
const external =
172+
!id.startsWith('.') &&
173+
!path.isAbsolute(id) &&
174+
(isBuiltin(id) || (isConfiguredAsExternal(id) && isPackageEntry(id)))
175+
processedIds.set(id, external)
176+
return external
177+
}
178+
}
179+
180+
// When ssr.format is 'cjs', this function is used reverting to the Vite 2.9
181+
// SSR externalization heuristics
182+
function cjsSsrCollectExternals(
91183
root: string,
92184
preserveSymlinks: boolean | undefined,
93185
ssrExternals: Set<string>,
@@ -192,14 +284,23 @@ function collectExternals(
192284
}
193285

194286
for (const depRoot of depsToTrace) {
195-
collectExternals(depRoot, preserveSymlinks, ssrExternals, seen, logger)
287+
cjsSsrCollectExternals(
288+
depRoot,
289+
preserveSymlinks,
290+
ssrExternals,
291+
seen,
292+
logger
293+
)
196294
}
197295
}
198296

199-
export function shouldExternalizeForSSR(
297+
export function cjsShouldExternalizeForSSR(
200298
id: string,
201-
externals: string[]
299+
externals: string[] | null
202300
): boolean {
301+
if (!externals) {
302+
return false
303+
}
203304
const should = externals.some((e) => {
204305
if (id === e) {
205306
return true

Diff for: ‎playground/ssr-deps/__tests__/serve.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const port = ports['ssr-deps']
1010
export async function serve(): Promise<{ close(): Promise<void> }> {
1111
await kill(port)
1212

13-
const { createServer } = require(path.resolve(rootDir, 'server.js'))
13+
const { createServer } = await import(path.resolve(rootDir, 'server.js'))
1414
const { app, vite } = await createServer(rootDir, hmrPorts['ssr-deps'])
1515

1616
return new Promise((resolve, reject) => {

Diff for: ‎playground/ssr-deps/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-deps",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "node server",
78
"serve": "cross-env NODE_ENV=production node server",

Diff for: ‎playground/ssr-deps/server.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
// @ts-check
2-
const fs = require('fs')
3-
const path = require('path')
4-
const express = require('express')
2+
import fs from 'fs'
3+
import path from 'path'
4+
import { fileURLToPath } from 'url'
5+
import express from 'express'
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
58

69
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
710

8-
async function createServer(root = process.cwd(), hmrPort) {
11+
export async function createServer(root = process.cwd(), hmrPort) {
912
const resolve = (p) => path.resolve(__dirname, p)
1013

1114
const app = express()
1215

1316
/**
1417
* @type {import('vite').ViteDevServer}
1518
*/
16-
const vite = await require('vite').createServer({
19+
const vite = await (
20+
await import('vite')
21+
).createServer({
1722
root,
1823
logLevel: isTest ? 'error' : 'info',
1924
server: {
@@ -63,6 +68,3 @@ if (!isTest) {
6368
})
6469
)
6570
}
66-
67-
// for test use
68-
exports.createServer = createServer

Diff for: ‎playground/ssr-html/__tests__/serve.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const port = ports['ssr-html']
1010
export async function serve(): Promise<{ close(): Promise<void> }> {
1111
await kill(port)
1212

13-
const { createServer } = require(path.resolve(rootDir, 'server.js'))
13+
const { createServer } = await import(path.resolve(rootDir, 'server.js'))
1414
const { app, vite } = await createServer(rootDir, hmrPorts['ssr-html'])
1515

1616
return new Promise((resolve, reject) => {

Diff for: ‎playground/ssr-html/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-html",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "node server",
78
"serve": "cross-env NODE_ENV=production node server",

Diff for: ‎playground/ssr-html/server.js

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// @ts-check
2-
const fs = require('fs')
3-
const path = require('path')
4-
const express = require('express')
1+
import fs from 'fs'
2+
import path from 'path'
3+
import { fileURLToPath } from 'url'
4+
import express from 'express'
55

6+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
67
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
78

89
const DYNAMIC_SCRIPTS = `
@@ -22,16 +23,17 @@ const DYNAMIC_STYLES = `
2223
</style>
2324
`
2425

25-
async function createServer(root = process.cwd(), hmrPort) {
26+
export async function createServer(root = process.cwd(), hmrPort) {
2627
const resolve = (p) => path.resolve(__dirname, p)
2728

2829
const app = express()
2930

3031
/**
3132
* @type {import('vite').ViteDevServer}
3233
*/
33-
let vite
34-
vite = await require('vite').createServer({
34+
const vite = await (
35+
await import('vite')
36+
).createServer({
3537
root,
3638
logLevel: isTest ? 'error' : 'info',
3739
server: {
@@ -93,6 +95,3 @@ if (!isTest) {
9395
})
9496
)
9597
}
96-
97-
// for test use
98-
exports.createServer = createServer

Diff for: ‎playground/ssr-pug/__tests__/serve.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const port = ports['ssr-pug']
1010
export async function serve(): Promise<{ close(): Promise<void> }> {
1111
await kill(port)
1212

13-
const { createServer } = require(path.resolve(rootDir, 'server.js'))
13+
const { createServer } = await import(path.resolve(rootDir, 'server.js'))
1414
const { app, vite } = await createServer(rootDir, hmrPorts['ssr-pug'])
1515

1616
return new Promise((resolve, reject) => {

Diff for: ‎playground/ssr-pug/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-pug",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "node server",
78
"serve": "cross-env NODE_ENV=production node server",

Diff for: ‎playground/ssr-pug/server.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// @ts-check
2-
const path = require('path')
3-
const pug = require('pug')
4-
const express = require('express')
2+
import path from 'path'
3+
import { fileURLToPath } from 'url'
4+
import pug from 'pug'
5+
import express from 'express'
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
58

69
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
710

@@ -14,16 +17,17 @@ const DYNAMIC_SCRIPTS = `
1417
<script type="module" src="/src/app.js"></script>
1518
`
1619

17-
async function createServer(root = process.cwd(), hmrPort) {
20+
export async function createServer(root = process.cwd(), hmrPort) {
1821
const resolve = (p) => path.resolve(__dirname, p)
1922

2023
const app = express()
2124

2225
/**
2326
* @type {import('vite').ViteDevServer}
2427
*/
25-
let vite
26-
vite = await require('vite').createServer({
28+
const vite = await (
29+
await import('vite')
30+
).createServer({
2731
root,
2832
logLevel: isTest ? 'error' : 'info',
2933
server: {
@@ -71,6 +75,3 @@ if (!isTest) {
7175
})
7276
)
7377
}
74-
75-
// for test use
76-
exports.createServer = createServer

Diff for: ‎playground/ssr-react/__tests__/serve.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,19 @@ export async function serve(): Promise<{ close(): Promise<void> }> {
2929
build: {
3030
target: 'esnext',
3131
ssr: 'src/entry-server.jsx',
32-
outDir: 'dist/server'
32+
outDir: 'dist/server',
33+
rollupOptions: {
34+
output: {
35+
entryFileNames: 'entry-server.js'
36+
}
37+
}
3338
}
3439
})
3540
}
3641

3742
await kill(port)
3843

39-
const { createServer } = require(path.resolve(rootDir, 'server.js'))
44+
const { createServer } = await import(path.resolve(rootDir, 'server.js'))
4045
const { app, vite } = await createServer(
4146
rootDir,
4247
isBuild,

Diff for: ‎playground/ssr-react/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-react",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "node server",
78
"build": "npm run build:client && npm run build:server",

Diff for: ‎playground/ssr-react/prerender.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// Pre-render the app into static HTML.
22
// run `yarn generate` and then `dist/static` can be served as a static site.
33

4-
const fs = require('fs')
5-
const path = require('path')
4+
import fs from 'fs'
5+
import path from 'path'
6+
import { fileURLToPath } from 'url'
67

8+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
79
const toAbsolute = (p) => path.resolve(__dirname, p)
810

911
const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
10-
const { render } = require('./dist/server/entry-server.js')
12+
const { render } = await import('./dist/server/entry-server.js')
1113

1214
// determine routes to pre-render from src/pages
1315
const routesToPrerender = fs

Diff for: ‎playground/ssr-react/server.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
// @ts-check
2-
const fs = require('fs')
3-
const path = require('path')
4-
const express = require('express')
1+
import fs from 'fs'
2+
import path from 'path'
3+
import { fileURLToPath } from 'url'
4+
import express from 'express'
5+
6+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
57

68
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
79

810
process.env.MY_CUSTOM_SECRET = 'API_KEY_qwertyuiop'
911

10-
async function createServer(
12+
export async function createServer(
1113
root = process.cwd(),
1214
isProd = process.env.NODE_ENV === 'production',
1315
hmrPort
@@ -25,7 +27,9 @@ async function createServer(
2527
*/
2628
let vite
2729
if (!isProd) {
28-
vite = await require('vite').createServer({
30+
vite = await (
31+
await import('vite')
32+
).createServer({
2933
root,
3034
logLevel: isTest ? 'error' : 'info',
3135
server: {
@@ -44,9 +48,9 @@ async function createServer(
4448
// use vite's connect instance as middleware
4549
app.use(vite.middlewares)
4650
} else {
47-
app.use(require('compression')())
51+
app.use((await import('compression')).default())
4852
app.use(
49-
require('serve-static')(resolve('dist/client'), {
53+
(await import('serve-static')).default(resolve('dist/client'), {
5054
index: false
5155
})
5256
)
@@ -65,7 +69,7 @@ async function createServer(
6569
} else {
6670
template = indexProd
6771
// @ts-ignore
68-
render = require('./dist/server/entry-server.js').render
72+
render = (await import('./dist/server/entry-server.js')).render
6973
}
7074

7175
const context = {}
@@ -96,6 +100,3 @@ if (!isTest) {
96100
})
97101
)
98102
}
99-
100-
// for test use
101-
exports.createServer = createServer

Diff for: ‎playground/ssr-react/vite.config.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
const react = require('@vitejs/plugin-react')
1+
import { defineConfig } from 'vite'
2+
import react from '@vitejs/plugin-react'
23

3-
/**
4-
* @type {import('vite').UserConfig}
5-
*/
6-
module.exports = {
4+
export default defineConfig({
75
plugins: [react()],
86
build: {
97
minify: false
108
}
11-
}
9+
})

Diff for: ‎playground/ssr-vue/__tests__/serve.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export async function serve(): Promise<{ close(): Promise<void> }> {
3838

3939
await kill(port)
4040

41-
const { createServer } = require(path.resolve(rootDir, 'server.js'))
41+
const { createServer } = await import(path.resolve(rootDir, 'server.js'))
4242
const { app, vite } = await createServer(
4343
rootDir,
4444
isBuild,

Diff for: ‎playground/ssr-vue/__tests__/ssr-vue.spec.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,14 @@ test.runIf(isBuild)('dynamic css file should be preloaded', async () => {
186186
const re =
187187
/link rel="modulepreload".*?href="\/test\/assets\/(Home\.\w{8}\.js)"/
188188
const filename = re.exec(homeHtml)[1]
189-
const manifest = require(resolve(
190-
process.cwd(),
191-
'./playground-temp/ssr-vue/dist/client/ssr-manifest.json'
192-
))
189+
const manifest = (
190+
await import(
191+
resolve(
192+
process.cwd(),
193+
'./playground-temp/ssr-vue/dist/client/ssr-manifest.json'
194+
)
195+
)
196+
).default
193197
const depFile = manifest[filename]
194198
for (const file of depFile) {
195199
expect(homeHtml).toMatch(file)

Diff for: ‎playground/ssr-vue/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-vue",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "node server",
78
"build": "npm run build:client && npm run build:server",

Diff for: ‎playground/ssr-vue/prerender.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// Pre-render the app into static HTML.
22
// run `npm run generate` and then `dist/static` can be served as a static site.
33

4-
const fs = require('fs')
5-
const path = require('path')
4+
import fs from 'fs'
5+
import path from 'path'
66

77
const toAbsolute = (p) => path.resolve(__dirname, p)
88

9-
const manifest = require('./dist/static/ssr-manifest.json')
9+
const manifest = (await import('./dist/static/ssr-manifest.json')).default
1010
const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
11-
const { render } = require('./dist/server/entry-server.js')
11+
const { render } = await import('./dist/server/entry-server.js')
1212

1313
// determine routes to pre-render from src/pages
1414
const routesToPrerender = fs

Diff for: ‎playground/ssr-vue/server.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
// @ts-check
2-
const fs = require('fs')
3-
const path = require('path')
4-
const express = require('express')
2+
import fs from 'fs'
3+
import path from 'path'
4+
import { fileURLToPath } from 'url'
5+
import express from 'express'
56

67
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
78

8-
async function createServer(
9+
export async function createServer(
910
root = process.cwd(),
1011
isProd = process.env.NODE_ENV === 'production',
1112
hmrPort
1213
) {
14+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1315
const resolve = (p) => path.resolve(__dirname, p)
1416

1517
const indexProd = isProd
@@ -18,7 +20,7 @@ async function createServer(
1820

1921
const manifest = isProd
2022
? // @ts-ignore
21-
require('./dist/client/ssr-manifest.json')
23+
(await import('./dist/client/ssr-manifest.json')).default
2224
: {}
2325

2426
const app = express()
@@ -28,7 +30,9 @@ async function createServer(
2830
*/
2931
let vite
3032
if (!isProd) {
31-
vite = await require('vite').createServer({
33+
vite = await (
34+
await import('vite')
35+
).createServer({
3236
base: '/test/',
3337
root,
3438
logLevel: isTest ? 'error' : 'info',
@@ -48,10 +52,10 @@ async function createServer(
4852
// use vite's connect instance as middleware
4953
app.use(vite.middlewares)
5054
} else {
51-
app.use(require('compression')())
55+
app.use((await import('compression')).default())
5256
app.use(
5357
'/test/',
54-
require('serve-static')(resolve('dist/client'), {
58+
(await import('serve-static')).default(resolve('dist/client'), {
5559
index: false
5660
})
5761
)
@@ -70,7 +74,7 @@ async function createServer(
7074
} else {
7175
template = indexProd
7276
// @ts-ignore
73-
render = require('./dist/server/entry-server.js').render
77+
render = (await import('./dist/server/entry-server.js')).render
7478
}
7579

7680
const [appHtml, preloadLinks] = await render(url, manifest)
@@ -97,6 +101,3 @@ if (!isTest) {
97101
})
98102
)
99103
}
100-
101-
// for test use
102-
exports.createServer = createServer

Diff for: ‎playground/ssr-vue/vite.config.js

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
const vuePlugin = require('@vitejs/plugin-vue')
2-
const vueJsx = require('@vitejs/plugin-vue-jsx')
1+
import { defineConfig } from 'vite'
2+
import vuePlugin from '@vitejs/plugin-vue'
3+
import vueJsx from '@vitejs/plugin-vue-jsx'
34
const virtualFile = '@virtual-file'
45
const virtualId = '\0' + virtualFile
56
const nestedVirtualFile = '@nested-virtual-file'
67
const nestedVirtualId = '\0' + nestedVirtualFile
78

8-
/**
9-
* @type {import('vite').UserConfig}
10-
*/
11-
module.exports = {
9+
export default defineConfig({
1210
base: '/test/',
1311
plugins: [
1412
vuePlugin(),
@@ -56,4 +54,4 @@ module.exports = {
5654
optimizeDeps: {
5755
exclude: ['example-external-component']
5856
}
59-
}
57+
})

Diff for: ‎playground/ssr-webworker/__tests__/serve.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export async function serve(): Promise<{ close(): Promise<void> }> {
2525
}
2626
})
2727

28-
const { createServer } = require(path.resolve(rootDir, 'worker.js'))
28+
const { createServer } = await import(path.resolve(rootDir, 'worker.js'))
2929
const { app } = await createServer(rootDir, isBuild)
3030

3131
return new Promise((resolve, reject) => {

Diff for: ‎playground/ssr-webworker/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "test-ssr-webworker",
33
"private": true,
44
"version": "0.0.0",
5+
"type": "module",
56
"scripts": {
67
"dev": "DEV=1 node worker",
78
"build:worker": "vite build --ssr src/entry-worker.jsx --outDir dist/worker"

Diff for: ‎playground/ssr-webworker/vite.config.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
/**
2-
* @type {import('vite').UserConfig}
3-
*/
4-
module.exports = {
1+
import { defineConfig } from 'vite'
2+
3+
export default defineConfig({
54
build: {
65
minify: false
76
},
@@ -32,4 +31,4 @@ module.exports = {
3231
}
3332
}
3433
]
35-
}
34+
})

Diff for: ‎playground/ssr-webworker/worker.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
// @ts-check
2-
const path = require('path')
3-
const { Miniflare } = require('miniflare')
1+
import { fileURLToPath } from 'url'
2+
import path from 'path'
3+
import { Miniflare } from 'miniflare'
4+
5+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
46

57
const isTest = !!process.env.TEST
68

7-
async function createServer() {
9+
export async function createServer() {
810
const mf = new Miniflare({
911
scriptPath: path.resolve(__dirname, 'dist/worker/entry-worker.js')
1012
})
@@ -21,6 +23,3 @@ if (!isTest) {
2123
})
2224
)
2325
}
24-
25-
// for test use
26-
exports.createServer = createServer

0 commit comments

Comments
 (0)
Please sign in to comment.