Skip to content

Commit

Permalink
fix: use strip-literal to strip string lterals
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed May 7, 2022
1 parent c7356e0 commit a2e557b
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 149 deletions.
1 change: 1 addition & 0 deletions packages/vite/package.json
Expand Up @@ -112,6 +112,7 @@
"source-map-js": "^1.0.2",
"source-map-support": "^0.5.21",
"strip-ansi": "^6.0.1",
"strip-literal": "^0.1.0",
"terser": "^5.13.1",
"tsconfck": "^1.2.2",
"tslib": "^2.4.0",
Expand Down
Binary file removed packages/vite/src/node/__tests__/cleanString.spec.ts
Binary file not shown.
144 changes: 2 additions & 142 deletions packages/vite/src/node/cleanString.ts
@@ -1,145 +1,5 @@
import type { RollupError } from 'rollup'
import { multilineCommentsRE, singlelineCommentsRE } from './utils'

// bank on the non-overlapping nature of regex matches and combine all filters into one giant regex
// /`([^`\$\{\}]|\$\{(`|\g<1>)*\})*`/g can match nested string template
// but js not support match expression(\g<0>). so clean string template(`...`) in other ways.
const stringsRE = /"([^"\r\n]|(?<=\\)")*"|'([^'\r\n]|(?<=\\)')*'/g
const regexRE = /\/.*?(?<!\\)\/[gimsuy]*/g
const cleanerRE = new RegExp(
`${stringsRE.source}|${multilineCommentsRE.source}|${singlelineCommentsRE.source}`,
'g'
)

const blankReplacer = (s: string) => ' '.repeat(s.length)
const stringBlankReplacer = (s: string) =>
`${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}`

export function emptyString(raw: string): string {
let res = raw
.replace(cleanerRE, (s: string) =>
s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s)
)
.replace(regexRE, (s) => stringBlankReplacer(s))

let lastEnd = 0
let start = 0
while ((start = res.indexOf('`', lastEnd)) >= 0) {
let clean
;[clean, lastEnd] = lexStringTemplateExpression(res, start)
res = replaceAt(res, start, lastEnd, clean)
}

return res
}
import { multilineCommentsRE } from './utils'

export function emptyCssComments(raw: string) {
return raw.replace(multilineCommentsRE, blankReplacer)
}

const enum LexerState {
// template string
inTemplateString,
inInterpolationExpression,
inObjectExpression,
// strings
inSingleQuoteString,
inDoubleQuoteString,
// comments
inMultilineCommentsRE,
inSinglelineCommentsRE
}

function replaceAt(
string: string,
start: number,
end: number,
replacement: string
): string {
return string.slice(0, start) + replacement + string.slice(end)
}

/**
* lex string template and clean it.
*/
function lexStringTemplateExpression(
code: string,
start: number
): [string, number] {
let state = LexerState.inTemplateString as LexerState
let clean = '`'
const opStack: LexerState[] = [state]

function pushStack(newState: LexerState) {
state = newState
opStack.push(state)
}

function popStack() {
opStack.pop()
state = opStack[opStack.length - 1]
}

let i = start + 1
outer: for (; i < code.length; i++) {
const char = code.charAt(i)
switch (state) {
case LexerState.inTemplateString:
if (char === '$' && code.charAt(i + 1) === '{') {
pushStack(LexerState.inInterpolationExpression)
clean += '${'
i++ // jump next
} else if (char === '`') {
popStack()
clean += char
if (opStack.length === 0) {
break outer
}
} else {
clean += '\0'
}
break
case LexerState.inInterpolationExpression:
if (char === '{') {
pushStack(LexerState.inObjectExpression)
clean += char
} else if (char === '}') {
popStack()
clean += char
} else if (char === '`') {
pushStack(LexerState.inTemplateString)
clean += char
} else {
clean += char
}
break
case LexerState.inObjectExpression:
if (char === '}') {
popStack()
clean += char
} else if (char === '`') {
pushStack(LexerState.inTemplateString)
clean += char
} else {
clean += char
}
break
default:
throw new Error('unknown string template lexer state')
}
}

if (opStack.length !== 0) {
error(start)
}

return [clean, i + 1]
}

function error(pos: number) {
const err = new Error(
`can not match string template expression.`
) as RollupError
err.pos = pos
throw err
return raw.replace(multilineCommentsRE, (s) => ' '.repeat(s.length))
}
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/assetImportMetaUrl.ts
Expand Up @@ -3,7 +3,7 @@ import MagicString from 'magic-string'
import path from 'path'
import { fileToUrl } from './asset'
import type { ResolvedConfig } from '../config'
import { emptyString } from '../cleanString'
import { stripLiteral } from 'strip-literal'

/**
* Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL
Expand All @@ -27,7 +27,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
let s: MagicString | undefined
const assetImportMetaUrlRE =
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g
const cleanString = emptyString(code)
const cleanString = stripLiteral(code)

let match: RegExpExecArray | null
while ((match = assetImportMetaUrlRE.exec(cleanString))) {
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/html.ts
Expand Up @@ -36,7 +36,7 @@ import type {
TextNode
} from '@vue/compiler-dom'
import { NodeTypes } from '@vue/compiler-dom'
import { emptyString } from '../cleanString'
import { stripLiteral } from 'strip-literal'

interface ScriptAssetsUrl {
start: number
Expand Down Expand Up @@ -308,7 +308,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
}
} else if (node.children.length) {
const scriptNode = node.children.pop()! as TextNode
const cleanCode = emptyString(scriptNode.content)
const cleanCode = stripLiteral(scriptNode.content)

let match: RegExpExecArray | null
while ((match = inlineImportRE.exec(cleanCode))) {
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/workerImportMetaUrl.ts
Expand Up @@ -10,7 +10,7 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
import MagicString from 'magic-string'
import type { ViteDevServer } from '..'
import type { RollupError } from 'rollup'
import { emptyString } from '../cleanString'
import { stripLiteral } from 'strip-literal'

type WorkerType = 'classic' | 'module' | 'ignore'
const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\//
Expand Down Expand Up @@ -110,7 +110,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
code.includes('new URL') &&
code.includes(`import.meta.url`)
) {
const cleanString = emptyString(code)
const cleanString = stripLiteral(code)
const workerImportMetaUrlRE =
/\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g

Expand Down
23 changes: 22 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a2e557b

Please sign in to comment.