Skip to content

Commit

Permalink
wip: automatic ssr externals inference
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 18, 2021
1 parent bb631bc commit b813b00
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 21 deletions.
3 changes: 1 addition & 2 deletions packages/plugin-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
define: {
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
},
ssrExternal: ['vue', '@vue/server-renderer']
}
}
},

Expand Down
8 changes: 4 additions & 4 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { TransformOptions } from 'esbuild'
import { CleanCSS } from 'types/clean-css'
import { dataURIPlugin } from './plugins/dataUri'
import { buildImportAnalysisPlugin } from './plugins/importAnaysisBuild'
import { resolveSSRExternal } from './ssrExternal'

export interface BuildOptions {
/**
Expand Down Expand Up @@ -290,10 +291,9 @@ async function doBuild(

// inject ssrExternal if present
const userExternal = options.rollupOptions?.external
const external =
options.ssr && config.ssrExternal
? resolveExternal(config.ssrExternal, userExternal)
: userExternal
const external = options.ssr
? resolveExternal(resolveSSRExternal(config.root), userExternal)
: userExternal

const rollup = require('rollup') as typeof Rollup

Expand Down
5 changes: 0 additions & 5 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ export interface UserConfig {
* Default: true
*/
clearScreen?: boolean
/**
* Externalize deps for SSR. These deps must provide a CommonJS build that
* can be `required()` and has the same module signature as its ESM build.
*/
ssrExternal?: string[]
}

export interface InlineConfig extends UserConfig {
Expand Down
7 changes: 5 additions & 2 deletions packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
continue
}
// skip ssr external
if (ssr && config.ssrExternal?.includes(url)) {
if (ssr && server._ssrExternals?.includes(url)) {
continue
}
// skip client
Expand Down Expand Up @@ -459,7 +459,10 @@ function isSupportedDynamicImport(url: string) {

function isOptimizedCjs(
id: string,
{ optimizeDepsMetadata, config: { optimizeCacheDir } }: ViteDevServer
{
_optimizeDepsMetadata: optimizeDepsMetadata,
config: { optimizeCacheDir }
}: ViteDevServer
): boolean {
if (optimizeDepsMetadata && optimizeCacheDir) {
const relative = path.relative(optimizeCacheDir, cleanUrl(id))
Expand Down
8 changes: 4 additions & 4 deletions packages/vite/src/node/plugins/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ export function tryNodeResolve(
if (
deepMatch &&
server &&
server.optimizeDepsMetadata &&
pkg.data.name in server.optimizeDepsMetadata.map &&
server._optimizeDepsMetadata &&
pkg.data.name in server._optimizeDepsMetadata.map &&
!isCSSRequest(id) &&
!server.config.assetsInclude(id)
) {
Expand Down Expand Up @@ -316,7 +316,7 @@ export function tryNodeResolve(
// files actually inside node_modules so that locally linked packages
// in monorepos are not cached this way.
if (resolved.includes('node_modules')) {
const versionHash = server?.optimizeDepsMetadata?.hash
const versionHash = server?._optimizeDepsMetadata?.hash
if (versionHash) {
resolved = injectQuery(resolved, `v=${versionHash}`)
}
Expand All @@ -330,7 +330,7 @@ export function tryOptimizedResolve(
server: ViteDevServer
): string | undefined {
const cacheDir = server.config.optimizeCacheDir
const depData = server.optimizeDepsMetadata
const depData = server._optimizeDepsMetadata
if (cacheDir && depData) {
const [id, q] = rawId.split(`?`, 2)
const query = q ? `?${q}` : ``
Expand Down
18 changes: 14 additions & 4 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
import { TransformOptions as EsbuildTransformOptions } from 'esbuild'
import { DepOptimizationMetadata, optimizeDeps } from '../optimizer'
import { ssrLoadModule } from './ssrModuleLoader'
import { resolveSSRExternal } from '../ssrExternal'

export interface ServerOptions {
host?: string
Expand Down Expand Up @@ -197,7 +198,12 @@ export interface ViteDevServer {
/**
* @internal
*/
optimizeDepsMetadata: DepOptimizationMetadata | null
_optimizeDepsMetadata: DepOptimizationMetadata | null
/**
* Deps that are extenralized
* @internal
*/
_ssrExternals: string[] | null
}

export async function createServer(
Expand Down Expand Up @@ -239,12 +245,14 @@ export async function createServer(
pluginContainer: container,
ws,
moduleGraph,
optimizeDepsMetadata: null,
transformWithEsbuild,
transformRequest(url, options) {
return transformRequest(url, server, options)
},
ssrLoadModule(url) {
if (!server._ssrExternals) {
server._ssrExternals = resolveSSRExternal(config.root)
}
return ssrLoadModule(url, server)
},
listen(port?: number) {
Expand All @@ -257,7 +265,9 @@ export async function createServer(
container.close(),
closeHttpServer()
])
}
},
_optimizeDepsMetadata: null,
_ssrExternals: null
}

process.once('SIGTERM', async () => {
Expand Down Expand Up @@ -373,7 +383,7 @@ export async function createServer(
// after optimization, read updated optimization metadata
const dataPath = path.resolve(config.optimizeCacheDir, 'metadata.json')
if (fs.existsSync(dataPath)) {
server.optimizeDepsMetadata = JSON.parse(
server._optimizeDepsMetadata = JSON.parse(
fs.readFileSync(dataPath, 'utf-8')
)
}
Expand Down
56 changes: 56 additions & 0 deletions packages/vite/src/node/ssrExternal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import fs from 'fs'
import path from 'path'
import { tryNodeResolve } from './plugins/resolve'
import { lookupFile, resolveFrom } from './utils'

/**
* Heuristics for determining whether a dependency should be externalized for
* server-side rendering.
*/
export function resolveSSRExternal(root: string): string[] {
const pkgContent = lookupFile(root, ['package.json'])
if (!pkgContent) {
return []
}
const pkg = JSON.parse(pkgContent)
const ssrExternals = Object.keys(pkg.devDependencies || {})
const deps = Object.keys(pkg.dependencies || {})
for (const dep of deps) {
const entry = tryNodeResolve(dep, root, false)?.id
let requireEntry
try {
requireEntry = require.resolve(dep, { paths: [root] })
} catch (e) {
continue
}
if (!entry) {
// no esm entry but has require entry (is this even possible?)
ssrExternals.push(dep)
continue
}
// node resolve and esm resolve resolves to the same file
if (path.extname(entry) !== '.js') {
// entry is not js, cannot externalize
continue
}
if (!entry.includes('node_modules')) {
// entry is not a node dep, possibly linked - don't externalize
// instead, trace its dependencies.
const depRoot = path.dirname(resolveFrom(`${dep}/package.json`, root))
ssrExternals.push(...resolveSSRExternal(depRoot))
continue
}
if (entry !== requireEntry) {
// has separate esm/require entry, assume require entry is cjs
ssrExternals.push(dep)
} else {
// node resolve and esm resolve resolves to the same file.
// check if the entry is cjs
const content = fs.readFileSync(entry, 'utf-8')
if (/\bmodule\.exports\b|\bexports[.\[]|\brequire\s*\(/.test(content)) {
ssrExternals.push(dep)
}
}
}
return [...new Set(ssrExternals)]
}

0 comments on commit b813b00

Please sign in to comment.