Skip to content

Commit

Permalink
perf: importsRE quadratic moves
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Nov 13, 2022
1 parent 68be9c7 commit 14a0e88
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 12 deletions.
7 changes: 3 additions & 4 deletions packages/vite/src/node/__tests__/scan.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from 'vitest'
import { commentRE, importsRE, scriptRE } from '../optimizer/scan'
import { commentRE, extractImportPaths, scriptRE } from '../optimizer/scan'
import { multilineCommentsRE, singlelineCommentsRE } from '../utils'

describe('optimizer-scan:script-test', () => {
Expand Down Expand Up @@ -89,8 +89,7 @@ describe('optimizer-scan:script-test', () => {
]

shouldMatchArray.forEach((str) => {
importsRE.lastIndex = 0
expect(importsRE.exec(str)[1]).toEqual("'vue'")
expect(extractImportPaths(str)).toStrictEqual(["import 'vue'"])
})

const shouldFailArray = [
Expand All @@ -102,7 +101,7 @@ describe('optimizer-scan:script-test', () => {
`import type Bar from 'foo'`
]
shouldFailArray.forEach((str) => {
expect(importsRE.test(str)).toBe(false)
expect(extractImportPaths(str)).toStrictEqual([])
})
})

Expand Down
19 changes: 11 additions & 8 deletions packages/vite/src/node/optimizer/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ const htmlTypesRE = /\.(html|vue|svelte|astro|imba)$/
// use Acorn because it's slow. Luckily this doesn't have to be bullet proof
// since even missed imports can be caught at runtime, and false positives will
// simply be ignored.
export const importsRE =
// eslint-disable-next-line regexp/no-super-linear-move -- TODO: FIXME backtracking
/(?<!\/\/.*)(?<=^|;|\*\/)\s*import(?!\s+type)(?:[\w*{}\n\r\t, ]+from)?\s*("[^"]+"|'[^']+')\s*(?=$|;|\/\/|\/\*)/gm
// `[ \f\t\v\u00a0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]` is same with
// `[\r\n\u2028\u2029]` removed from `\s`
const importsRE =
/(?<=^|;|\*\/)[ \f\t\v\u00a0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]*import(?!\s+type)(?:[\w*{}\n\r\t, ]+from)?\s*("[^"]+"|'[^']+')\s*(?=$|;|\/\/|\/\*)/gm

export async function scanImports(config: ResolvedConfig): Promise<{
deps: Record<string, string>
Expand Down Expand Up @@ -325,7 +326,9 @@ function esbuildScanPlugin(
// since they may be used in the template
const contents =
content +
(loader.startsWith('ts') ? extractImportPaths(content) : '')
(loader.startsWith('ts')
? extractImportPaths(content).join('\n')
: '')

const key = `${path}?id=${scriptId++}`
if (contents.includes('import.meta.glob')) {
Expand Down Expand Up @@ -524,22 +527,22 @@ function esbuildScanPlugin(
* the solution is to add `import 'x'` for every source to force
* esbuild to keep crawling due to potential side effects.
*/
function extractImportPaths(code: string) {
export function extractImportPaths(code: string): string[] {
// empty singleline & multiline comments to avoid matching comments
code = code
.replace(multilineCommentsRE, '/* */')
.replace(singlelineCommentsRE, '')

let js = ''
const imports = []
let m
while ((m = importsRE.exec(code)) != null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === importsRE.lastIndex) {
importsRE.lastIndex++
}
js += `\nimport ${m[1]}`
imports.push(`import ${m[1]}`)
}
return js
return imports
}

function shouldExternalizeDep(resolvedId: string, rawId: string): boolean {
Expand Down

0 comments on commit 14a0e88

Please sign in to comment.