mirror of
https://github.com/vitejs/vite.git
synced 2024-11-22 07:09:05 +00:00
fix: use strip-literal
to strip string lterals (#8054)
This commit is contained in:
parent
b0e9234251
commit
b6fc3cdccf
@ -3465,6 +3465,35 @@ Repository: chalk/strip-ansi
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## strip-literal
|
||||
License: MIT
|
||||
By: Anthony Fu
|
||||
Repository: git+https://github.com/antfu/strip-literal.git
|
||||
|
||||
> MIT License
|
||||
>
|
||||
> Copyright (c) 2022 Anthony Fu <https://github.com/antfu>
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in all
|
||||
> copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
> SOFTWARE.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## to-regex-range
|
||||
License: MIT
|
||||
By: Jon Schlinkert, Rouven Weßling
|
||||
|
@ -112,6 +112,7 @@
|
||||
"source-map-js": "^1.0.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strip-literal": "^0.2.0",
|
||||
"terser": "^5.13.1",
|
||||
"tsconfck": "^1.2.2",
|
||||
"tslib": "^2.4.0",
|
||||
|
@ -1,190 +0,0 @@
|
||||
import { assetAttrsConfig } from './../plugins/html'
|
||||
import { emptyString } from '../../node/cleanString'
|
||||
|
||||
test('comments', () => {
|
||||
expect(
|
||||
emptyString(`
|
||||
// comment1 // comment
|
||||
// comment1
|
||||
/* coment2 */
|
||||
/*
|
||||
// coment3
|
||||
*/
|
||||
/* // coment3 */
|
||||
/* // coment3 */ // comment
|
||||
// comment 4 /* comment 5 */
|
||||
`).trim()
|
||||
).toBe('')
|
||||
})
|
||||
|
||||
test('strings', () => {
|
||||
const clean = emptyString(`
|
||||
// comment1
|
||||
const a = 'aaaa'
|
||||
/* coment2 */
|
||||
const b = "bbbb"
|
||||
/*
|
||||
// coment3
|
||||
*/
|
||||
/* // coment3 */
|
||||
// comment 4 /* comment 5 */
|
||||
`)
|
||||
expect(clean).toMatch("const a = '\0\0\0\0'")
|
||||
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('regexp affect', () => {
|
||||
const clean = emptyString(`
|
||||
/'/
|
||||
'1'
|
||||
/"/
|
||||
"1"
|
||||
`)
|
||||
expect(clean).not.toMatch('1')
|
||||
})
|
||||
|
||||
test('strings comment nested', () => {
|
||||
expect(
|
||||
emptyString(`
|
||||
// comment 1 /* " */
|
||||
const a = "a //"
|
||||
// comment 2 /* " */
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
// comment 1 /* ' */
|
||||
const a = "a //"
|
||||
// comment 2 /* ' */
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
// comment 1 /* \` */
|
||||
const a = "a //"
|
||||
// comment 2 /* \` */
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
const a = "a //"
|
||||
console.log("console")
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
const a = "a /*"
|
||||
console.log("console")
|
||||
const b = "b */"
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
const a = "a ' "
|
||||
console.log("console")
|
||||
const b = "b ' "
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
|
||||
expect(
|
||||
emptyString(`
|
||||
const a = "a \` "
|
||||
console.log("console")
|
||||
const b = "b \` "
|
||||
`)
|
||||
).toMatch('const a = "\0\0\0\0"')
|
||||
})
|
||||
|
||||
test('find empty string flag in raw index', () => {
|
||||
const str = `
|
||||
const a = "aaaaa"
|
||||
const b = "bbbbb"
|
||||
`
|
||||
const clean = emptyString(str)
|
||||
expect(clean).toMatch('const a = "\0\0\0\0\0"')
|
||||
expect(clean).toMatch('const b = "\0\0\0\0\0"')
|
||||
|
||||
const aIndex = str.indexOf('const a = "aaaaa"')
|
||||
const aStart = clean.indexOf('\0\0\0\0\0', aIndex)
|
||||
expect(str.slice(aStart, aStart + 5)).toMatch('aaaaa')
|
||||
|
||||
const bIndex = str.indexOf('const b = "bbbbb"')
|
||||
const bStart = clean.indexOf('\0\0\0\0\0', bIndex)
|
||||
expect(str.slice(bStart, bStart + 5)).toMatch('bbbbb')
|
||||
})
|
||||
|
||||
test('template string nested', () => {
|
||||
let str = '`aaaa`'
|
||||
let res = '`\0\0\0\0`'
|
||||
let clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aaaa` `aaaa`'
|
||||
res = '`\0\0\0\0` `\0\0\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aa${a}aa`'
|
||||
res = '`\0\0${a}\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aa${a + `a` + a}aa`'
|
||||
res = '`\0\0${a + `\0` + a}\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aa${a + `a` + a}aa` `aa${a + `a` + a}aa`'
|
||||
res = '`\0\0${a + `\0` + a}\0\0` `\0\0${a + `\0` + a}\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aa${a + `aaaa${c + (a = {b: 1}) + d}` + a}aa`'
|
||||
res = '`\0\0${a + `\0\0\0\0${c + (a = {b: 1}) + d}` + a}\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str =
|
||||
'`aa${a + `aaaa${c + (a = {b: 1}) + d}` + a}aa` `aa${a + `aaaa${c + (a = {b: 1}) + d}` + a}aa`'
|
||||
res =
|
||||
'`\0\0${a + `\0\0\0\0${c + (a = {b: 1}) + d}` + a}\0\0` `\0\0${a + `\0\0\0\0${c + (a = {b: 1}) + d}` + a}\0\0`'
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str = '`aaaa'
|
||||
res = ''
|
||||
try {
|
||||
clean = emptyString(str)
|
||||
} catch {}
|
||||
expect(clean).toMatch(res)
|
||||
|
||||
str =
|
||||
"<img src=\"${new URL('../assets/images/loading/loading.gif', import.meta.url).href}\" alt=''>"
|
||||
res = `<img src="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" alt=''>`
|
||||
clean = emptyString(str)
|
||||
expect(clean).toMatch(res)
|
||||
})
|
@ -1,142 +0,0 @@
|
||||
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 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)
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -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
|
||||
@ -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))) {
|
||||
|
@ -49,7 +49,7 @@ import { transform, formatMessages } from 'esbuild'
|
||||
import { addToHTMLProxyTransformResult } from './html'
|
||||
import { injectSourcesContent, getCodeWithSourcemap } from '../server/sourcemap'
|
||||
import type { RawSourceMap } from '@ampproject/remapping'
|
||||
import { emptyCssComments } from '../cleanString'
|
||||
import { emptyCssComments } from '../utils'
|
||||
|
||||
// const debug = createDebugger('vite:css')
|
||||
|
||||
|
@ -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
|
||||
@ -307,7 +307,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))) {
|
||||
|
@ -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*\*\//
|
||||
@ -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
|
||||
|
||||
|
@ -785,3 +785,7 @@ function gracefulRename(
|
||||
if (cb) cb(er)
|
||||
})
|
||||
}
|
||||
|
||||
export function emptyCssComments(raw: string) {
|
||||
return raw.replace(multilineCommentsRE, (s) => ' '.repeat(s.length))
|
||||
}
|
||||
|
@ -893,6 +893,7 @@ importers:
|
||||
source-map-js: ^1.0.2
|
||||
source-map-support: ^0.5.21
|
||||
strip-ansi: ^6.0.1
|
||||
strip-literal: ^0.2.0
|
||||
terser: ^5.13.1
|
||||
tsconfck: ^1.2.2
|
||||
tslib: ^2.4.0
|
||||
@ -966,6 +967,7 @@ importers:
|
||||
source-map-js: 1.0.2
|
||||
source-map-support: 0.5.21
|
||||
strip-ansi: 6.0.1
|
||||
strip-literal: 0.2.0
|
||||
terser: 5.13.1
|
||||
tsconfck: 1.2.2_typescript@4.5.4
|
||||
tslib: 2.4.0
|
||||
@ -8959,6 +8961,12 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/strip-literal/0.2.0:
|
||||
resolution: {integrity: sha512-pqhiiFRDifA2CRVH0Gmv6MDbd2b27MS0oIqqy7JCqfL5m2sh68223lmEK2eoBXp4vNaq8G1Wzwd9dfQcWszUlg==}
|
||||
dependencies:
|
||||
acorn: 8.7.1
|
||||
dev: true
|
||||
|
||||
/stylis/4.0.13:
|
||||
resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user