Skip to content

Commit

Permalink
feat: pre transform direct imports before requests hit the server (#5037
Browse files Browse the repository at this point in the history
)
  • Loading branch information
yyx990803 committed Sep 23, 2021
1 parent fe25567 commit 57b9a37
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 33 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Expand Up @@ -10,6 +10,7 @@ on:
branches:
- main
- release/*
- feat/*
pull_request:
workflow_dispatch:

Expand Down
49 changes: 37 additions & 12 deletions packages/vite/src/node/plugins/importAnalysis.ts
Expand Up @@ -17,7 +17,9 @@ import {
isJSRequest,
prettifyUrl,
timeFrom,
normalizePath
normalizePath,
removeImportQuery,
unwrapId
} from '../utils'
import {
debugHmr,
Expand All @@ -40,6 +42,7 @@ import { transformImportGlob } from '../importGlob'
import { makeLegalIdentifier } from '@rollup/pluginutils'
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
import { performance } from 'perf_hooks'
import { transformRequest } from '../server/transformRequest'

const isDebug = !!process.env.DEBUG
const debug = createDebugger('vite:import-analysis')
Expand Down Expand Up @@ -159,6 +162,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
// have been loaded so its entry is guaranteed in the module graph.
const importerModule = moduleGraph.getModuleById(importer)!
const importedUrls = new Set<string>()
const staticImportedUrls = new Set<string>()
const acceptedUrls = new Set<{
url: string
start: number
Expand Down Expand Up @@ -289,18 +293,28 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
} else if (prop === '.glo' && source[end + 4] === 'b') {
// transform import.meta.glob()
// e.g. `import.meta.glob('glob:./dir/*.js')`
const { imports, importsString, exp, endIndex, base, pattern } =
await transformImportGlob(
source,
start,
importer,
index,
root,
normalizeUrl
)
const {
imports,
importsString,
exp,
endIndex,
base,
pattern,
isEager
} = await transformImportGlob(
source,
start,
importer,
index,
root,
normalizeUrl
)
str().prepend(importsString)
str().overwrite(expStart, endIndex, exp)
imports.forEach((url) => importedUrls.add(url.replace(base, '/')))
imports.forEach((url) => {
importedUrls.add(url)
if (isEager) staticImportedUrls.add(url)
})
if (!(importerModule.file! in server._globImporters)) {
server._globImporters[importerModule.file!] = {
module: importerModule,
Expand Down Expand Up @@ -397,7 +411,11 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {

// record for HMR import chain analysis
// make sure to normalize away base
importedUrls.add(url.replace(base, '/'))
importedUrls.add(url)
if (!isDynamicImport) {
// for pre-transforming
staticImportedUrls.add(url)
}
} else if (!importer.startsWith(clientDir) && !ssr) {
// check @vite-ignore which suppresses dynamic import warning
const hasViteIgnore = /\/\*\s*@vite-ignore\s*\*\//.test(rawUrl)
Expand Down Expand Up @@ -516,6 +534,13 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
)}`
)

// pre-transform known direct imports
if (staticImportedUrls.size) {
staticImportedUrls.forEach((url) => {
transformRequest(unwrapId(removeImportQuery(url)), server, { ssr })
})
}

if (s) {
return s.toString()
} else {
Expand Down
16 changes: 12 additions & 4 deletions packages/vite/src/node/server/index.ts
Expand Up @@ -34,8 +34,11 @@ import { errorMiddleware, prepareError } from './middlewares/error'
import { handleHMRUpdate, HmrOptions, handleFileAddUnlink } from './hmr'
import { openBrowser } from './openBrowser'
import launchEditorMiddleware from 'launch-editor-middleware'
import { TransformResult } from 'rollup'
import { TransformOptions, transformRequest } from './transformRequest'
import {
TransformOptions,
TransformResult,
transformRequest
} from './transformRequest'
import {
transformWithEsbuild,
ESBuildTransformResult
Expand Down Expand Up @@ -299,6 +302,10 @@ export interface ViteDevServer {
* @internal
*/
_pendingReload: Promise<void> | null
/**
* @internal
*/
_pendingRequests: Record<string, Promise<TransformResult | null> | null>
}

export async function createServer(
Expand Down Expand Up @@ -395,10 +402,11 @@ export async function createServer(
},
_optimizeDepsMetadata: null,
_ssrExternals: null,
_globImporters: {},
_globImporters: Object.create(null),
_isRunningOptimizer: false,
_registerMissingImport: null,
_pendingReload: null
_pendingReload: null,
_pendingRequests: Object.create(null)
}

server.transformIndexHtml = createDevHtmlTransformFn(server)
Expand Down
32 changes: 18 additions & 14 deletions packages/vite/src/node/server/middlewares/error.ts
Expand Up @@ -39,27 +39,31 @@ function cleanStack(stack: string) {
.join('\n')
}

export function logError(server: ViteDevServer, err: RollupError) {
const msg = buildErrorMessage(err, [
chalk.red(`Internal server error: ${err.message}`)
])

server.config.logger.error(msg, {
clear: true,
timestamp: true,
error: err
})

server.ws.send({
type: 'error',
err: prepareError(err)
})
}

export function errorMiddleware(
server: ViteDevServer,
allowNext = false
): Connect.ErrorHandleFunction {
// note the 4 args must be kept for connect to treat this as error middleware
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteErrorMiddleware(err: RollupError, _req, res, next) {
const msg = buildErrorMessage(err, [
chalk.red(`Internal server error: ${err.message}`)
])

server.config.logger.error(msg, {
clear: true,
timestamp: true,
error: err
})

server.ws.send({
type: 'error',
err: prepareError(err)
})
logError(server, err)

if (allowNext) {
next()
Expand Down
33 changes: 30 additions & 3 deletions packages/vite/src/node/server/transformRequest.ts
Expand Up @@ -38,23 +38,50 @@ export interface TransformOptions {
html?: boolean
}

export async function transformRequest(
export function transformRequest(
url: string,
server: ViteDevServer,
options: TransformOptions = {}
): Promise<TransformResult | null> {
const { config, pluginContainer, moduleGraph, watcher } = server
const pending = server._pendingRequests[url]
if (pending) {
debugTransform(
`[reuse pending] for ${prettifyUrl(url, server.config.root)}`
)
return pending
}
const result = doTransform(url, server, options)
server._pendingRequests[url] = result
const onDone = () => {
server._pendingRequests[url] = null
}
result.then(onDone, onDone)
return result
}

async function doTransform(
url: string,
server: ViteDevServer,
options: TransformOptions
) {
url = removeTimestampQuery(url)
const { config, pluginContainer, moduleGraph, watcher } = server
const { root, logger } = config
const prettyUrl = isDebug ? prettifyUrl(url, root) : ''
const ssr = !!options.ssr

const module = await server.moduleGraph.getModuleByUrl(url)

// check if we have a fresh cache
const module = await moduleGraph.getModuleByUrl(url)
const cached =
module && (ssr ? module.ssrTransformResult : module.transformResult)
if (cached) {
// TODO: check if the module is "partially invalidated" - i.e. an import
// down the chain has been fully invalidated, but this current module's
// content has not changed.
// in this case, we can reuse its previous cached result and only update
// its import timestamps.

isDebug && debugCache(`[memory] ${prettyUrl}`)
return cached
}
Expand Down

0 comments on commit 57b9a37

Please sign in to comment.