diff --git a/packages/vite/src/node/ssr/fetchModule.ts b/packages/vite/src/node/ssr/fetchModule.ts index ae66d3af596072..60c0cb0a416b57 100644 --- a/packages/vite/src/node/ssr/fetchModule.ts +++ b/packages/vite/src/node/ssr/fetchModule.ts @@ -1,6 +1,5 @@ import { pathToFileURL } from 'node:url' import type { ModuleNode, TransformResult, ViteDevServer } from '..' -import type { PackageCache } from '../packages' import type { InternalResolveOptionsWithOverrideConditions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' import { isBuiltin, isExternalUrl, isFilePathESM } from '../utils' @@ -9,14 +8,8 @@ import { unwrapId } from '../../shared/utils' import { SOURCEMAPPING_URL, VITE_RUNTIME_SOURCEMAPPING_SOURCE, - VITE_RUNTIME_SOURCEMAPPING_URL, } from '../../shared/constants' - -interface NodeImportResolveOptions - extends InternalResolveOptionsWithOverrideConditions { - legacyProxySsrExternalModules?: boolean - packageCache?: PackageCache -} +import { genSourceMapUrl } from '../server/sourcemap' export interface FetchModuleOptions { inlineSourceMap?: boolean @@ -51,7 +44,7 @@ export async function fetchModule( } = server.config const overrideConditions = ssr.resolve?.externalConditions || [] - const resolveOptions: NodeImportResolveOptions = { + const resolveOptions: InternalResolveOptionsWithOverrideConditions = { mainFields: ['main'], conditions: [], overrideConditions: [...overrideConditions, 'production', 'development'], @@ -62,8 +55,6 @@ export async function fetchModule( isProduction, root, ssrConfig: ssr, - legacyProxySsrExternalModules: - server.config.legacy?.proxySsrExternalModules, packageCache: server.config.packageCache, } @@ -148,13 +139,10 @@ function inlineSourceMap( if (OTHER_SOURCE_MAP_REGEXP.test(code)) code = code.replace(OTHER_SOURCE_MAP_REGEXP, '') - const sourceMap = Buffer.from( - JSON.stringify(processSourceMap?.(map) || map), - 'utf-8', - ).toString('base64') + const sourceMap = processSourceMap?.(map) || map result.code = `${code.trimEnd()}\n//# sourceURL=${ mod.id - }\n${VITE_RUNTIME_SOURCEMAPPING_SOURCE}\n//# ${VITE_RUNTIME_SOURCEMAPPING_URL};base64,${sourceMap}\n` + }\n${VITE_RUNTIME_SOURCEMAPPING_SOURCE}\n//# ${SOURCEMAPPING_URL}=${genSourceMapUrl(sourceMap)}\n` return result } diff --git a/packages/vite/src/node/ssr/ssrFetchModule.ts b/packages/vite/src/node/ssr/ssrFetchModule.ts index 27f9213963918f..d0e1c98cca2569 100644 --- a/packages/vite/src/node/ssr/ssrFetchModule.ts +++ b/packages/vite/src/node/ssr/ssrFetchModule.ts @@ -1,15 +1,8 @@ import type { ViteDevServer } from '../server' import type { FetchResult } from '../../runtime/types' +import { asyncFunctionDeclarationPaddingLineCount } from '../../shared/utils' import { fetchModule } from './fetchModule' -// eslint-disable-next-line @typescript-eslint/no-empty-function -const AsyncFunction = async function () {}.constructor as typeof Function -const fnDeclarationLineCount = (() => { - const body = '/*code*/' - const source = new AsyncFunction('a', 'b', body).toString() - return source.slice(0, source.indexOf(body)).split('\n').length - 1 -})() - export function ssrFetchModule( server: ViteDevServer, id: string, @@ -19,9 +12,8 @@ export function ssrFetchModule( processSourceMap(map) { // this assumes that "new AsyncFunction" is used to create the module return Object.assign({}, map, { - // currently we need to offset the line - // https://github.com/nodejs/node/issues/43047#issuecomment-1180632750 - mappings: ';'.repeat(fnDeclarationLineCount) + map.mappings, + mappings: + ';'.repeat(asyncFunctionDeclarationPaddingLineCount) + map.mappings, }) }, }) diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index 42f023adb4a82a..49428987956904 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -7,8 +7,17 @@ import { transformRequest } from '../server/transformRequest' import type { InternalResolveOptionsWithOverrideConditions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' import { genSourceMapUrl } from '../server/sourcemap' -import type { PackageCache } from '../packages' -import { unwrapId } from '../../shared/utils' +import { + AsyncFunction, + asyncFunctionDeclarationPaddingLineCount, + unwrapId, +} from '../../shared/utils' +import { + type SSRImportBaseMetadata, + analyzeImportedModDifference, + proxyGuardOnlyEsm, +} from '../../shared/ssrTransform' +import { SOURCEMAPPING_URL } from '../../shared/constants' import { ssrDynamicImportKey, ssrExportAllKey, @@ -27,31 +36,6 @@ type SSRModule = Record interface NodeImportResolveOptions extends InternalResolveOptionsWithOverrideConditions { legacyProxySsrExternalModules?: boolean - packageCache?: PackageCache -} - -interface SSRImportMetadata { - isDynamicImport?: boolean - /** - * Imported names before being transformed to `ssrImportKey` - * - * import foo, { bar as baz, qux } from 'hello' - * => ['default', 'bar', 'qux'] - * - * import * as namespace from 'world - * => undefined - */ - importedNames?: string[] -} - -// eslint-disable-next-line @typescript-eslint/no-empty-function -const AsyncFunction = async function () {}.constructor as typeof Function -let fnDeclarationLineCount = 0 -{ - const body = '/*code*/' - const source = new AsyncFunction('a', 'b', body).toString() - fnDeclarationLineCount = - source.slice(0, source.indexOf(body)).split('\n').length - 1 } const pendingModules = new Map>() @@ -165,7 +149,7 @@ async function instantiateModule( // account for multiple pending deps and duplicate imports. const pendingDeps: string[] = [] - const ssrImport = async (dep: string, metadata?: SSRImportMetadata) => { + const ssrImport = async (dep: string, metadata?: SSRImportBaseMetadata) => { try { if (dep[0] !== '.' && dep[0] !== '/') { return await nodeImport(dep, mod.file!, resolveOptions, metadata) @@ -227,12 +211,11 @@ async function instantiateModule( let sourceMapSuffix = '' if (result.map && 'version' in result.map) { const moduleSourceMap = Object.assign({}, result.map, { - // currently we need to offset the line - // https://github.com/nodejs/node/issues/43047#issuecomment-1180632750 - mappings: ';'.repeat(fnDeclarationLineCount) + result.map.mappings, + mappings: + ';'.repeat(asyncFunctionDeclarationPaddingLineCount) + + result.map.mappings, }) - sourceMapSuffix = - '\n//# sourceMappingURL=' + genSourceMapUrl(moduleSourceMap) + sourceMapSuffix = `\n//# ${SOURCEMAPPING_URL}=${genSourceMapUrl(moduleSourceMap)}` } try { @@ -289,7 +272,7 @@ async function nodeImport( id: string, importer: string, resolveOptions: NodeImportResolveOptions, - metadata?: SSRImportMetadata, + metadata?: SSRImportBaseMetadata, ) { let url: string let filePath: string | undefined @@ -322,10 +305,11 @@ async function nodeImport( } else if (filePath) { analyzeImportedModDifference( mod, - filePath, id, + isFilePathESM(filePath, resolveOptions.packageCache) + ? 'module' + : undefined, metadata, - resolveOptions.packageCache, ) return proxyGuardOnlyEsm(mod, id) } else { @@ -358,63 +342,3 @@ function proxyESM(mod: any) { function isPrimitive(value: any) { return !value || (typeof value !== 'object' && typeof value !== 'function') } - -/** - * Vite converts `import { } from 'foo'` to `const _ = __vite_ssr_import__('foo')`. - * Top-level imports and dynamic imports work slightly differently in Node.js. - * This function normalizes the differences so it matches prod behaviour. - */ -function analyzeImportedModDifference( - mod: any, - filePath: string, - rawId: string, - metadata?: SSRImportMetadata, - packageCache?: PackageCache, -) { - // No normalization needed if the user already dynamic imports this module - if (metadata?.isDynamicImport) return - // If file path is ESM, everything should be fine - if (isFilePathESM(filePath, packageCache)) return - - // For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js. - // If the user named imports a specifier that can't be analyzed, error. - if (metadata?.importedNames?.length) { - const missingBindings = metadata.importedNames.filter((s) => !(s in mod)) - if (missingBindings.length) { - const lastBinding = missingBindings[missingBindings.length - 1] - // Copied from Node.js - throw new SyntaxError(`\ -[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports. -CommonJS modules can always be imported via the default export, for example using: - -import pkg from '${rawId}'; -const {${missingBindings.join(', ')}} = pkg; -`) - } - } -} - -/** - * Guard invalid named exports only, similar to how Node.js errors for top-level imports. - * But since we transform as dynamic imports, we need to emulate the error manually. - */ -function proxyGuardOnlyEsm( - mod: any, - rawId: string, - metadata?: SSRImportMetadata, -) { - // If the module doesn't import anything explicitly, e.g. `import 'foo'` or - // `import * as foo from 'foo'`, we can skip the proxy guard. - if (!metadata?.importedNames?.length) return mod - - return new Proxy(mod, { - get(mod, prop) { - if (prop !== 'then' && !(prop in mod)) { - throw new SyntaxError( - `[vite] The requested module '${rawId}' does not provide an export named '${prop.toString()}'`, - ) - } - return mod[prop] - }, - }) -} diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 3668a4a525a267..0c22577c888155 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -16,6 +16,7 @@ import { parseAstAsync as rollupParseAstAsync } from 'rollup/parseAst' import type { TransformResult } from '../server/transformRequest' import { combineSourcemaps, isDefined } from '../utils' import { isJSONRequest } from '../plugins/json' +import type { DefineImportMetadata } from '../../shared/ssrTransform' type Node = _Node & { start: number @@ -28,19 +29,6 @@ interface TransformOptions { } } -interface DefineImportMetadata { - /** - * Imported names of an import statement, e.g. - * - * import foo, { bar as baz, qux } from 'hello' - * => ['default', 'bar', 'qux'] - * - * import * as namespace from 'world - * => undefined - */ - importedNames?: string[] -} - export const ssrModuleExportsKey = `__vite_ssr_exports__` export const ssrImportKey = `__vite_ssr_import__` export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__` diff --git a/packages/vite/src/runtime/esmRunner.ts b/packages/vite/src/runtime/esmRunner.ts index 47078c370fb852..5d4c481c39e85a 100644 --- a/packages/vite/src/runtime/esmRunner.ts +++ b/packages/vite/src/runtime/esmRunner.ts @@ -1,3 +1,4 @@ +import { AsyncFunction } from '../shared/utils' import { ssrDynamicImportKey, ssrExportAllKey, @@ -7,9 +8,6 @@ import { } from './constants' import type { ViteModuleRunner, ViteRuntimeModuleContext } from './types' -// eslint-disable-next-line @typescript-eslint/no-empty-function -const AsyncFunction = async function () {}.constructor as typeof Function - export class ESModulesRunner implements ViteModuleRunner { async runViteModule( context: ViteRuntimeModuleContext, diff --git a/packages/vite/src/runtime/moduleCache.ts b/packages/vite/src/runtime/moduleCache.ts index 750e933e0e303c..3681e3db1ed78d 100644 --- a/packages/vite/src/runtime/moduleCache.ts +++ b/packages/vite/src/runtime/moduleCache.ts @@ -1,11 +1,11 @@ -import { isWindows, withTrailingSlash } from '../shared/utils' -import { VITE_RUNTIME_SOURCEMAPPING_URL } from '../shared/constants' +import { isWindows, slash, withTrailingSlash } from '../shared/utils' +import { SOURCEMAPPING_URL } from '../shared/constants' import { decodeBase64 } from './utils' import { DecodedMap } from './sourcemap/decoder' import type { ModuleCache } from './types' const VITE_RUNTIME_SOURCEMAPPING_REGEXP = new RegExp( - `//# ${VITE_RUNTIME_SOURCEMAPPING_URL};base64,(.+)`, + `//# ${SOURCEMAPPING_URL}=data:application/json;base64,(.+)`, ) export class ModuleCacheMap extends Map { @@ -180,8 +180,7 @@ function normalizeModuleId(file: string, root: string): string { if (prefixedBuiltins.has(file)) return file // unix style, but Windows path still starts with the drive letter to check the root - let unixFile = file - .replace(/\\/g, '/') + let unixFile = slash(file) .replace(/^\/@fs\//, isWindows ? '' : '/') .replace(/^node:/, '') .replace(/^\/+/, '/') diff --git a/packages/vite/src/runtime/runtime.ts b/packages/vite/src/runtime/runtime.ts index 008dd214a37ce5..5feff38352617f 100644 --- a/packages/vite/src/runtime/runtime.ts +++ b/packages/vite/src/runtime/runtime.ts @@ -4,9 +4,14 @@ import { cleanUrl, isPrimitive, isWindows, + slash, unwrapId, wrapId, } from '../shared/utils' +import { + analyzeImportedModDifference, + proxyGuardOnlyEsm, +} from '../shared/ssrTransform' import { ModuleCacheMap } from './moduleCache' import type { FetchResult, @@ -160,7 +165,7 @@ export class ViteRuntime { // 8 is the length of "file:///" url = url.slice(isWindows ? 8 : 7) } - url = url.replace(/\\/g, '/') + url = slash(url) const _root = this.options.root const root = _root[_root.length - 1] === '/' ? _root : `${_root}/` // strip root from the URL because fetchModule prefers a public served url path @@ -427,62 +432,3 @@ function exportAll(exports: any, sourceModule: any) { } } } - -/** - * Vite converts `import { } from 'foo'` to `const _ = __vite_ssr_import__('foo')`. - * Top-level imports and dynamic imports work slightly differently in Node.js. - * This function normalizes the differences so it matches prod behaviour. - */ -function analyzeImportedModDifference( - mod: any, - rawId: string, - moduleType: string | undefined, - metadata?: SSRImportMetadata, -) { - // No normalization needed if the user already dynamic imports this module - if (metadata?.isDynamicImport) return - // If file path is ESM, everything should be fine - if (moduleType === 'module') return - - // For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js. - // If the user named imports a specifier that can't be analyzed, error. - if (metadata?.importedNames?.length) { - const missingBindings = metadata.importedNames.filter((s) => !(s in mod)) - if (missingBindings.length) { - const lastBinding = missingBindings[missingBindings.length - 1] - // Copied from Node.js - throw new SyntaxError(`\ -[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports. -CommonJS modules can always be imported via the default export, for example using: - -import pkg from '${rawId}'; -const {${missingBindings.join(', ')}} = pkg; -`) - } - } -} - -/** - * Guard invalid named exports only, similar to how Node.js errors for top-level imports. - * But since we transform as dynamic imports, we need to emulate the error manually. - */ -function proxyGuardOnlyEsm( - mod: any, - rawId: string, - metadata?: SSRImportMetadata, -) { - // If the module doesn't import anything explicitly, e.g. `import 'foo'` or - // `import * as foo from 'foo'`, we can skip the proxy guard. - if (!metadata?.importedNames?.length) return mod - - return new Proxy(mod, { - get(mod, prop) { - if (prop !== 'then' && !(prop in mod)) { - throw new SyntaxError( - `[vite] The requested module '${rawId}' does not provide an export named '${prop.toString()}'`, - ) - } - return mod[prop] - }, - }) -} diff --git a/packages/vite/src/runtime/sourcemap/interceptor.ts b/packages/vite/src/runtime/sourcemap/interceptor.ts index 043287b5839168..58d324e79b943c 100644 --- a/packages/vite/src/runtime/sourcemap/interceptor.ts +++ b/packages/vite/src/runtime/sourcemap/interceptor.ts @@ -2,6 +2,7 @@ import type { OriginalMapping } from '@jridgewell/trace-mapping' import type { ViteRuntime } from '../runtime' import { posixDirname, posixResolve } from '../utils' import type { ModuleCacheMap } from '../moduleCache' +import { slash } from '../../shared/utils' import { DecodedMap, getOriginalPosition } from './decoder' interface RetrieveFileHandler { @@ -37,11 +38,11 @@ const createExecHandlers = any>( } const retrieveFileFromHandlers = createExecHandlers(retrieveFileHandlers) -const retrievSourceMapFromHandlers = createExecHandlers( +const retrieveSourceMapFromHandlers = createExecHandlers( retrieveSourceMapHandlers, ) -let overriden = false +let overridden = false const originalPrepare = Error.prepareStackTrace function resetInterceptor(runtime: ViteRuntime, options: InterceptorOptions) { @@ -51,7 +52,7 @@ function resetInterceptor(runtime: ViteRuntime, options: InterceptorOptions) { retrieveSourceMapHandlers.delete(options.retrieveSourceMap) if (moduleGraphs.size === 0) { Error.prepareStackTrace = originalPrepare - overriden = false + overridden = false } } @@ -59,9 +60,9 @@ export function interceptStackTrace( runtime: ViteRuntime, options: InterceptorOptions = {}, ): () => void { - if (!overriden) { + if (!overridden) { Error.prepareStackTrace = prepareStackTrace - overriden = true + overridden = true } moduleGraphs.add(runtime.moduleCache) if (options.retrieveFile) retrieveFileHandlers.add(options.retrieveFile) @@ -88,24 +89,21 @@ interface CachedMapEntry { // Support URLs relative to a directory, but be careful about a protocol prefix function supportRelativeURL(file: string, url: string) { if (!file) return url - const dir = posixDirname(file.replace(/\\/g, '/')) + const dir = posixDirname(slash(file)) const match = /^\w+:\/\/[^/]*/.exec(dir) let protocol = match ? match[0] : '' const startPath = dir.slice(protocol.length) if (protocol && /^\/\w:/.test(startPath)) { // handle file:///C:/ paths protocol += '/' - return ( - protocol + - posixResolve(dir.slice(protocol.length), url).replace(/\\/g, '/') - ) + return protocol + slash(posixResolve(startPath, url)) } - return protocol + posixResolve(dir.slice(protocol.length), url) + return protocol + posixResolve(startPath, url) } function getRuntimeSourceMap(position: OriginalMapping): CachedMapEntry | null { for (const moduleCache of moduleGraphs) { - const sourceMap = moduleCache.getSourceMap(position.source as string) + const sourceMap = moduleCache.getSourceMap(position.source!) if (sourceMap) { return { url: position.source, @@ -145,7 +143,7 @@ function retrieveSourceMapURL(source: string) { const reSourceMap = /^data:application\/json[^,]+base64,/ function retrieveSourceMap(source: string) { - const urlAndMap = retrievSourceMapFromHandlers(source) + const urlAndMap = retrieveSourceMapFromHandlers(source) if (urlAndMap) return urlAndMap let sourceMappingURL = retrieveSourceMapURL(source) diff --git a/packages/vite/src/runtime/types.ts b/packages/vite/src/runtime/types.ts index 56707005f8db50..730ed59630e26d 100644 --- a/packages/vite/src/runtime/types.ts +++ b/packages/vite/src/runtime/types.ts @@ -1,6 +1,10 @@ import type { ViteHotContext } from 'types/hot' import type { HMRPayload } from 'types/hmrPayload' import type { HMRConnection, HMRLogger } from '../shared/hmr' +import type { + DefineImportMetadata, + SSRImportBaseMetadata, +} from '../shared/ssrTransform' import type { ModuleCacheMap } from './moduleCache' import type { ssrDynamicImportKey, @@ -12,17 +16,9 @@ import type { import type { DecodedMap } from './sourcemap/decoder' import type { InterceptorOptions } from './sourcemap/interceptor' -export interface DefineImportMetadata { - /** - * Imported names before being transformed to `ssrImportKey` - * - * import foo, { bar as baz, qux } from 'hello' - * => ['default', 'bar', 'qux'] - * - * import * as namespace from 'world - * => undefined - */ - importedNames?: string[] +export type { DefineImportMetadata } +export interface SSRImportMetadata extends SSRImportBaseMetadata { + entrypoint?: boolean } export interface HMRRuntimeConnection extends HMRConnection { @@ -33,11 +29,6 @@ export interface HMRRuntimeConnection extends HMRConnection { onUpdate(callback: (payload: HMRPayload) => void): void } -export interface SSRImportMetadata extends DefineImportMetadata { - isDynamicImport?: boolean - entrypoint?: boolean -} - export interface ViteRuntimeImportMeta extends ImportMeta { url: string env: ImportMetaEnv diff --git a/packages/vite/src/shared/constants.ts b/packages/vite/src/shared/constants.ts index d60bbc9a1f8287..7c0e685d5abf6b 100644 --- a/packages/vite/src/shared/constants.ts +++ b/packages/vite/src/shared/constants.ts @@ -21,4 +21,3 @@ SOURCEMAPPING_URL += 'ppingURL' export const VITE_RUNTIME_SOURCEMAPPING_SOURCE = '//# sourceMappingSource=vite-runtime' -export const VITE_RUNTIME_SOURCEMAPPING_URL = `${SOURCEMAPPING_URL}=data:application/json;charset=utf-8` diff --git a/packages/vite/src/shared/ssrTransform.ts b/packages/vite/src/shared/ssrTransform.ts new file mode 100644 index 00000000000000..da23cd5d9f2ce5 --- /dev/null +++ b/packages/vite/src/shared/ssrTransform.ts @@ -0,0 +1,75 @@ +export interface DefineImportMetadata { + /** + * Imported names before being transformed to `ssrImportKey` + * + * import foo, { bar as baz, qux } from 'hello' + * => ['default', 'bar', 'qux'] + * + * import * as namespace from 'world + * => undefined + */ + importedNames?: string[] +} + +export interface SSRImportBaseMetadata extends DefineImportMetadata { + isDynamicImport?: boolean +} + +/** + * Vite converts `import { } from 'foo'` to `const _ = __vite_ssr_import__('foo')`. + * Top-level imports and dynamic imports work slightly differently in Node.js. + * This function normalizes the differences so it matches prod behaviour. + */ +export function analyzeImportedModDifference( + mod: any, + rawId: string, + moduleType: string | undefined, + metadata?: SSRImportBaseMetadata, +): void { + // No normalization needed if the user already dynamic imports this module + if (metadata?.isDynamicImport) return + // If file path is ESM, everything should be fine + if (moduleType === 'module') return + + // For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js. + // If the user named imports a specifier that can't be analyzed, error. + if (metadata?.importedNames?.length) { + const missingBindings = metadata.importedNames.filter((s) => !(s in mod)) + if (missingBindings.length) { + const lastBinding = missingBindings[missingBindings.length - 1] + // Copied from Node.js + throw new SyntaxError(`\ +[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports. +CommonJS modules can always be imported via the default export, for example using: + +import pkg from '${rawId}'; +const {${missingBindings.join(', ')}} = pkg; +`) + } + } +} + +/** + * Guard invalid named exports only, similar to how Node.js errors for top-level imports. + * But since we transform as dynamic imports, we need to emulate the error manually. + */ +export function proxyGuardOnlyEsm( + mod: any, + rawId: string, + metadata?: SSRImportBaseMetadata, +): any { + // If the module doesn't import anything explicitly, e.g. `import 'foo'` or + // `import * as foo from 'foo'`, we can skip the proxy guard. + if (!metadata?.importedNames?.length) return mod + + return new Proxy(mod, { + get(mod, prop) { + if (prop !== 'then' && !(prop in mod)) { + throw new SyntaxError( + `[vite] The requested module '${rawId}' does not provide an export named '${prop.toString()}'`, + ) + } + return mod[prop] + }, + }) +} diff --git a/packages/vite/src/shared/utils.ts b/packages/vite/src/shared/utils.ts index e3b9800edbc6bf..3b3507a8280c82 100644 --- a/packages/vite/src/shared/utils.ts +++ b/packages/vite/src/shared/utils.ts @@ -43,3 +43,14 @@ export function withTrailingSlash(path: string): string { } return path } + +// eslint-disable-next-line @typescript-eslint/no-empty-function +export const AsyncFunction = async function () {}.constructor as typeof Function + +// https://github.com/nodejs/node/issues/43047#issuecomment-1564068099 +export const asyncFunctionDeclarationPaddingLineCount = + /** #__PURE__ */ (() => { + const body = '/*code*/' + const source = new AsyncFunction('a', 'b', body).toString() + return source.slice(0, source.indexOf(body)).split('\n').length - 1 + })()