From 8b27d742c9423601f3ceaadb35f7ea00367f7c4b Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 20 Apr 2022 17:28:16 +0800 Subject: [PATCH 1/2] wip: clean string for comment and quote string --- packages/vite/src/node/__tests__/cleanString.spec.ts | 7 +++++++ packages/vite/src/node/cleanString.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 1065a2d4985ceb..d5bdb3f43edbc9 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -32,6 +32,13 @@ test('strings', () => { expect(clean).toMatch('const b = "\0\0\0\0"') }) +test('Escape character', () => { + const clean = emptyString(` + const a = '\'' + const b = "\"" + `) +}) + test('strings comment nested', () => { expect( emptyString(` diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 05163ea055b631..e04de760e971d4 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -25,9 +25,16 @@ export function emptyString(raw: string): string { } const enum LexerState { + // template string inTemplateString, inInterpolationExpression, - inObjectExpression + inObjectExpression, + // strings + inSingleQuoteString, + inDoubleQuoteString, + // comments + inMultilineCommentsRE, + inSinglelineCommentsRE } function replaceAt( From 61b35991c32892d3011f26ec81f591593ba37df3 Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 20 Apr 2022 20:22:33 +0800 Subject: [PATCH 2/2] fix: regexp --- .../src/node/__tests__/cleanString.spec.ts | 18 +++++++++++++++--- packages/vite/src/node/cleanString.ts | 3 ++- packages/vite/src/node/plugins/css.ts | 17 ++++++++++------- packages/vite/src/node/plugins/html.ts | 3 ++- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index d5bdb3f43edbc9..b77f4c0fb5fbad 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -32,11 +32,23 @@ test('strings', () => { expect(clean).toMatch('const b = "\0\0\0\0"') }) -test('Escape character', () => { +test('escape character', () => { const clean = emptyString(` - const a = '\'' - const b = "\"" + '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', () => { diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index e04de760e971d4..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) => 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)