From 037a6c77da04aeec7442e11765619b0ea4d846f9 Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 6 Apr 2023 08:43:15 +0200 Subject: [PATCH] perf: parallelize imports processing in import analysis plugin (#12754) --- .../vite/src/node/plugins/importAnalysis.ts | 499 +++++++++--------- 1 file changed, 264 insertions(+), 235 deletions(-) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 677394f587a46f..0a93e270213d99 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -80,6 +80,12 @@ const hasViteIgnoreRE = /\/\*\s*@vite-ignore\s*\*\// const cleanUpRawUrlRE = /\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm const urlIsStringRE = /^(?:'.*'|".*"|`.*`)$/ +interface UrlPosition { + url: string + start: number + end: number +} + export function isExplicitImportRequired(url: string): boolean { return !isJSRequest(cleanUrl(url)) && !isCSSRequest(url) } @@ -271,13 +277,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { let s: MagicString | undefined const str = () => s || (s = new MagicString(source)) const importedUrls = new Set() - const acceptedUrls = new Set<{ - url: string - start: number - end: number - }>() let isPartiallySelfAccepting = false - const acceptedExports = new Set() const importedBindings = enablePartialAccept ? new Map>() : null @@ -409,268 +409,288 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { return [url, resolved.id] } - for (let index = 0; index < imports.length; index++) { - const { - s: start, - e: end, - ss: expStart, - se: expEnd, - d: dynamicIndex, - // #2083 User may use escape path, - // so use imports[index].n to get the unescaped string - n: specifier, - a: assertIndex, - } = imports[index] - - const rawUrl = source.slice(start, end) - - // check import.meta usage - if (rawUrl === 'import.meta') { - const prop = source.slice(end, end + 4) - if (prop === '.hot') { - hasHMR = true - const endHot = end + 4 + (source[end + 4] === '?' ? 1 : 0) - if (source.slice(endHot, endHot + 7) === '.accept') { - // further analyze accepted modules - if (source.slice(endHot, endHot + 14) === '.acceptExports') { - lexAcceptedHmrExports( - source, - source.indexOf('(', endHot + 14) + 1, - acceptedExports, - ) - isPartiallySelfAccepting = true - } else if ( - lexAcceptedHmrDeps( - source, - source.indexOf('(', endHot + 7) + 1, - acceptedUrls, - ) - ) { - isSelfAccepting = true + const orderedAcceptedUrls = new Array | undefined>( + imports.length, + ) + const orderedAcceptedExports = new Array | undefined>( + imports.length, + ) + + await Promise.all( + imports.map(async (importSpecifier, index) => { + const { + s: start, + e: end, + ss: expStart, + se: expEnd, + d: dynamicIndex, + // #2083 User may use escape path, + // so use imports[index].n to get the unescaped string + n: specifier, + a: assertIndex, + } = importSpecifier + + const rawUrl = source.slice(start, end) + + // check import.meta usage + if (rawUrl === 'import.meta') { + const prop = source.slice(end, end + 4) + if (prop === '.hot') { + hasHMR = true + const endHot = end + 4 + (source[end + 4] === '?' ? 1 : 0) + if (source.slice(endHot, endHot + 7) === '.accept') { + // further analyze accepted modules + if (source.slice(endHot, endHot + 14) === '.acceptExports') { + const importAcceptedExports = (orderedAcceptedExports[index] = + new Set()) + lexAcceptedHmrExports( + source, + source.indexOf('(', endHot + 14) + 1, + importAcceptedExports, + ) + isPartiallySelfAccepting = true + } else { + const importAcceptedUrls = (orderedAcceptedUrls[index] = + new Set()) + if ( + lexAcceptedHmrDeps( + source, + source.indexOf('(', endHot + 7) + 1, + importAcceptedUrls, + ) + ) { + isSelfAccepting = true + } + } } + } else if (prop === '.env') { + hasEnv = true } - } else if (prop === '.env') { - hasEnv = true + return } - continue - } - const isDynamicImport = dynamicIndex > -1 + const isDynamicImport = dynamicIndex > -1 - // strip import assertions as we can process them ourselves - if (!isDynamicImport && assertIndex > -1) { - str().remove(end + 1, expEnd) - } - - // static import or valid string in dynamic import - // If resolvable, let's resolve it - if (specifier) { - // skip external / data uri - if (isExternalUrl(specifier) || isDataUrl(specifier)) { - continue + // strip import assertions as we can process them ourselves + if (!isDynamicImport && assertIndex > -1) { + str().remove(end + 1, expEnd) } - // skip ssr external - if (ssr) { - if (config.legacy?.buildSsrCjsExternalHeuristics) { - if (cjsShouldExternalizeForSSR(specifier, server._ssrExternals)) { - continue + + // static import or valid string in dynamic import + // If resolvable, let's resolve it + if (specifier) { + // skip external / data uri + if (isExternalUrl(specifier) || isDataUrl(specifier)) { + return + } + // skip ssr external + if (ssr) { + if (config.legacy?.buildSsrCjsExternalHeuristics) { + if ( + cjsShouldExternalizeForSSR(specifier, server._ssrExternals) + ) { + return + } + } else if (shouldExternalizeForSSR(specifier, config)) { + return + } + if (isBuiltin(specifier)) { + return } - } else if (shouldExternalizeForSSR(specifier, config)) { - continue } - if (isBuiltin(specifier)) { - continue + // skip client + if (specifier === clientPublicPath) { + return } - } - // skip client - if (specifier === clientPublicPath) { - continue - } - // warn imports to non-asset /public files - if ( - specifier[0] === '/' && - !config.assetsInclude(cleanUrl(specifier)) && - !specifier.endsWith('.json') && - checkPublicFile(specifier, config) - ) { - throw new Error( - `Cannot import non-asset file ${specifier} which is inside /public.` + - `JS/CSS files inside /public are copied as-is on build and ` + - `can only be referenced via