From 1d468c8e6f02b0d0e362aa2d6542af1e1f55ab45 Mon Sep 17 00:00:00 2001 From: yoho Date: Thu, 21 Apr 2022 07:21:54 +0800 Subject: [PATCH] fix: escape character in string regexp match (#7834) --- .../src/node/__tests__/cleanString.spec.ts | 19 +++++++++++++++++++ packages/vite/src/node/cleanString.ts | 12 ++++++++++-- packages/vite/src/node/plugins/css.ts | 17 ++++++++++------- packages/vite/src/node/plugins/html.ts | 3 ++- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 1065a2d4985ceb..b77f4c0fb5fbad 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -32,6 +32,25 @@ test('strings', () => { expect(clean).toMatch('const b = "\0\0\0\0"') }) +test('escape character', () => { + const clean = emptyString(` + '1\\'1' + "1\\"1" + "1\\"1\\"1" + "1\\'1'\\"1" + "1'1'" + "1'\\'1\\''\\"1\\"\\"" + '1"\\"1\\""\\"1\\"\\"' + '""1""' + '"""1"""' + '""""1""""' + "''1''" + "'''1'''" + "''''1''''" + `) + expect(clean).not.toMatch('1') +}) + test('strings comment nested', () => { expect( emptyString(` diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 05163ea055b631..2b7cba41aef66b 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -2,7 +2,8 @@ import type { RollupError } from 'rollup' // 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 cleanerRE = /"[^"]*"|'[^']*'|\/\*(.|[\r\n])*?\*\/|\/\/.*/g +const cleanerRE = + /"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'|\/\*(.|[\r\n])*?\*\/|\/\/.*/g const blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => @@ -25,9 +26,16 @@ export function emptyString(raw: string): string { } const enum LexerState { + // template string inTemplateString, inInterpolationExpression, - inObjectExpression + inObjectExpression, + // strings + inSingleQuoteString, + inDoubleQuoteString, + // comments + inMultilineCommentsRE, + inSinglelineCommentsRE } function replaceAt( diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2f5ab3df58b46a..83e18aabecdb33 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1122,7 +1122,7 @@ export async function hoistAtRules(css: string) { // to top when multiple files are concatenated. // match until semicolon that's not in quotes s.replace( - /@import\s*(?:url\([^\)]*\)|"[^"]*"|'[^']*'|[^;]*).*?;/gm, + /@import\s*(?:url\([^\)]*\)|"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'|[^;]*).*?;/gm, (match) => { s.appendLeft(0, match) return '' @@ -1131,13 +1131,16 @@ export async function hoistAtRules(css: string) { // #6333 // CSS @charset must be the top-first in the file, hoist the first to top let foundCharset = false - s.replace(/@charset\s*(?:"[^"]*"|'[^']*'|[^;]*).*?;/gm, (match) => { - if (!foundCharset) { - s.prepend(match) - foundCharset = true + s.replace( + /@charset\s*(?:"([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*'|[^;]*).*?;/gm, + (match) => { + if (!foundCharset) { + s.prepend(match) + foundCharset = true + } + return '' } - return '' - }) + ) return s.toString() } diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 5c86b6c0ac6073..e8df0825ddb3fa 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -46,7 +46,8 @@ interface ScriptAssetsUrl { const htmlProxyRE = /\?html-proxy=?[&inline\-css]*&index=(\d+)\.(js|css)$/ const inlineCSSRE = /__VITE_INLINE_CSS__([^_]+_\d+)__/g // Do not allow preceding '.', but do allow preceding '...' for spread operations -const inlineImportRE = /(? htmlProxyRE.test(id)