From 5451a34fe52df7b20fe69878d2caa34d82821ee0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Sat, 12 Nov 2022 15:17:10 +0800 Subject: [PATCH] fix(ssr): improve missing file error (#10880) --- .../vite/src/node/plugins/importAnalysis.ts | 4 +-- .../src/node/server/middlewares/transform.ts | 6 ++++- .../vite/src/node/server/transformRequest.ts | 25 +++++++++++-------- .../fixtures/modules/has-invalid-import.js | 4 +++ .../node/ssr/__tests__/ssrLoadModule.spec.ts | 23 +++++++++++++++++ 5 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 packages/vite/src/node/ssr/__tests__/fixtures/modules/has-invalid-import.js create mode 100644 packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index d26b0e219085d9..80ee54e501a26e 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -9,7 +9,6 @@ import { parse as parseJS } from 'acorn' import type { Node } from 'estree' import { findStaticImports, parseStaticImport } from 'mlly' import { makeLegalIdentifier } from '@rollup/pluginutils' -import { getDepOptimizationConfig } from '..' import type { ViteDevServer } from '..' import { CLIENT_DIR, @@ -44,6 +43,7 @@ import { unwrapId, wrapId } from '../utils' +import { getDepOptimizationConfig } from '../config' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { @@ -712,7 +712,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // These requests will also be registered in transformRequest to be awaited // by the deps optimizer if (config.server.preTransformRequests && staticImportedUrls.size) { - staticImportedUrls.forEach(({ url, id }) => { + staticImportedUrls.forEach(({ url }) => { url = removeImportQuery(url) transformRequest(url, server, { ssr }).catch((e) => { if (e?.code === ERR_OUTDATED_OPTIMIZED_DEP) { diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index 929c6856d34748..831ea2c2139a2b 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -18,7 +18,7 @@ import { unwrapId } from '../../utils' import { send } from '../send' -import { transformRequest } from '../transformRequest' +import { ERR_LOAD_URL, transformRequest } from '../transformRequest' import { isHTMLProxy } from '../../plugins/html' import { DEP_VERSION_RE, @@ -224,6 +224,10 @@ export function transformMiddleware( // error but a normal part of the missing deps discovery flow return } + if (e?.code === ERR_LOAD_URL) { + // Let other middleware handle if we can't load the url via transformRequest + return next() + } return next(e) } diff --git a/packages/vite/src/node/server/transformRequest.ts b/packages/vite/src/node/server/transformRequest.ts index f2b78516e63670..b7bed216348504 100644 --- a/packages/vite/src/node/server/transformRequest.ts +++ b/packages/vite/src/node/server/transformRequest.ts @@ -21,6 +21,9 @@ import { getDepsOptimizer } from '../optimizer' import { injectSourcesContent } from './sourcemap' import { isFileServingAllowed } from './middlewares/static' +export const ERR_LOAD_URL = 'ERR_LOAD_URL' +export const ERR_LOAD_PUBLIC_URL = 'ERR_LOAD_PUBLIC_URL' + const debugLoad = createDebugger('vite:load') const debugTransform = createDebugger('vite:transform') const debugCache = createDebugger('vite:cache') @@ -215,18 +218,18 @@ async function loadAndTransform( } } if (code == null) { - if (checkPublicFile(url, config)) { - throw new Error( - `Failed to load url ${url} (resolved id: ${id}). ` + - `This file is in /public and will be copied as-is during build without ` + - `going through the plugin transforms, and therefore should not be ` + - `imported from source code. It can only be referenced via HTML tags.` - ) - } else { - return null - } + const isPublicFile = checkPublicFile(url, config) + const msg = isPublicFile + ? `This file is in /public and will be copied as-is during build without ` + + `going through the plugin transforms, and therefore should not be ` + + `imported from source code. It can only be referenced via HTML tags.` + : `Does the file exist?` + const err: any = new Error( + `Failed to load url ${url} (resolved id: ${id}). ${msg}` + ) + err.code = isPublicFile ? ERR_LOAD_PUBLIC_URL : ERR_LOAD_URL + throw err } - // ensure module in graph after successful load const mod = await moduleGraph.ensureEntryFromUrl(url, ssr) ensureWatchedFile(watcher, mod.file, root) diff --git a/packages/vite/src/node/ssr/__tests__/fixtures/modules/has-invalid-import.js b/packages/vite/src/node/ssr/__tests__/fixtures/modules/has-invalid-import.js new file mode 100644 index 00000000000000..81ab2b0fab83c9 --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/fixtures/modules/has-invalid-import.js @@ -0,0 +1,4 @@ +// eslint-disable-next-line node/no-missing-import +import { foo } from './non-existent.js' + +export const hello = 'world' diff --git a/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts new file mode 100644 index 00000000000000..1df5f2b128da5d --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts @@ -0,0 +1,23 @@ +import { fileURLToPath } from 'node:url' +import { expect, test } from 'vitest' +import { createServer } from '../../server' + +const root = fileURLToPath(new URL('./', import.meta.url)) + +async function createDevServer() { + const server = await createServer({ configFile: false, root }) + server.pluginContainer.buildStart({}) + return server +} + +test('ssrLoad', async () => { + expect.assertions(1) + const server = await createDevServer() + try { + await server.ssrLoadModule('/fixtures/modules/has-invalid-import.js') + } catch (e) { + expect(e.message).toBe( + 'Failed to load url ./non-existent.js (resolved id: ./non-existent.js). Does the file exist?' + ) + } +})