From ca1ec6492a816eda1d9edc5404874c64c6ffb538 Mon Sep 17 00:00:00 2001 From: yoho Date: Fri, 8 Apr 2022 20:50:54 +0800 Subject: [PATCH 01/26] fix: comment nested string, string nested comment --- packages/vite/src/node/cleanString.ts | 71 +++++++++++++++++++ .../src/node/plugins/assetImportMetaUrl.ts | 24 +++---- .../src/node/plugins/workerImportMetaUrl.ts | 57 ++++++--------- packages/vite/src/node/utils.ts | 1 - 4 files changed, 102 insertions(+), 51 deletions(-) create mode 100644 packages/vite/src/node/cleanString.ts diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts new file mode 100644 index 00000000000000..8e8b6f3078d5e5 --- /dev/null +++ b/packages/vite/src/node/cleanString.ts @@ -0,0 +1,71 @@ +// bank on the non-overlapping nature of regex matches +// and combine all our current filters into one giant regex +// FIXME: nested string template (PS: `${`${}`}`) +const cleanerRE = /"[^"]*"|'[^']*'|`[^`]*`|\/\*(.|[\r\n])*?\*\/|\/\/.*/g + +const blankReplacer = (s: string) => '\0'.repeat(s.length) +const stringBlankReplacer = (s: string) => + `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` + +export class CleanCommentString extends String { + cleanComment = '' + raw = '' + + constructor(raw: string) { + super(raw.toString()) + this.raw = raw + this.cleanComment = raw.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : s + ) + } + + override toString() { + return this.cleanComment + } +} + +export class CleanString extends String { + cleanComment = '' + clean = '' + raw = '' + + constructor(raw: string | CleanCommentString) { + super(raw.toString()) + if (raw instanceof CleanCommentString) { + this.raw = raw.raw + this.cleanComment = raw.cleanComment + this.clean = raw.cleanComment.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) + ) + } else { + this.raw = raw + this.cleanComment = emptyCommentsString(raw).cleanComment + this.clean = this.cleanComment.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) + ) + } + } + + override toString() { + return this.clean + } +} + +export function emptyCommentsString(raw: string): CleanCommentString { + return new CleanCommentString(raw) +} + +export function emptyString(raw: string | CleanCommentString): CleanString { + return new CleanString(raw) +} + +export function findEmptyStringRawIndex( + raw: CleanString, + emptyFlag: string, + start: number +): [number, number] { + // FIXME: if there are sub-strings of the same length in the same string. (PS: fn(' ', ' ')) + const flagIndex = raw.clean.indexOf(emptyFlag, start) + const flagEndIndex = flagIndex + emptyFlag.length + return [flagIndex, flagEndIndex] +} diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index a3f8e441b0f933..d97d004a483623 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -3,12 +3,7 @@ import MagicString from 'magic-string' import path from 'path' import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' -import { - multilineCommentsRE, - singlelineCommentsRE, - stringsRE, - blankReplacer -} from '../utils' +import { emptyString, findEmptyStringRawIndex } from '../cleanString' /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL @@ -31,18 +26,18 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { ) { const importMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g - const noCommentsCode = code - .replace(multilineCommentsRE, blankReplacer) - .replace(singlelineCommentsRE, blankReplacer) - .replace(stringsRE, (m) => `'${'\0'.repeat(m.length - 2)}'`) + const cleanCode = emptyString(code) let s: MagicString | null = null let match: RegExpExecArray | null - while ((match = importMetaUrlRE.exec(noCommentsCode))) { + while ((match = importMetaUrlRE.exec(cleanCode.toString()))) { const { 0: exp, 1: emptyUrl, index } = match - const urlStart = exp.indexOf(emptyUrl) + index - const urlEnd = urlStart + emptyUrl.length + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanCode, + emptyUrl, + index + ) const rawUrl = code.slice(urlStart, urlEnd) if (!s) s = new MagicString(code) @@ -74,8 +69,9 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { // Get final asset URL. Catch error if the file does not exist, // in which we can resort to the initial URL and let it resolve in runtime const builtUrl = await fileToUrl(file, config, this).catch(() => { + const truthExp = cleanCode.raw.slice(index, index + exp.length) config.logger.warnOnce( - `\n${exp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) return url }) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index c9a2903d9a4142..ffc8aa17da1856 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -2,14 +2,7 @@ import JSON5 from 'json5' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { fileToUrl } from './asset' -import { - blankReplacer, - cleanUrl, - injectQuery, - multilineCommentsRE, - singlelineCommentsRE, - stringsRE -} from '../utils' +import { cleanUrl, injectQuery } from '../utils' import path from 'path' import { workerFileToUrl } from './worker' import { parseRequest } from '../utils' @@ -17,27 +10,25 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import type { RollupError } from 'rollup' +import type { CleanString } from '../cleanString' +import { emptyString, findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' const WORKER_FILE_ID = 'worker_url_file' -function getWorkerType( - code: string, - noCommentsCode: string, - i: number -): WorkerType { +function getWorkerType(cleanCode: CleanString, i: number): WorkerType { function err(e: string, pos: number) { const error = new Error(e) as RollupError error.pos = pos throw error } - const commaIndex = noCommentsCode.indexOf(',', i) + const commaIndex = cleanCode.indexOf(',', i) if (commaIndex === -1) { return 'classic' } - const endIndex = noCommentsCode.indexOf(')', i) + const endIndex = cleanCode.indexOf(')', i) // case: ') ... ,' mean no worker options params if (commaIndex > endIndex) { @@ -45,22 +36,22 @@ function getWorkerType( } // need to find in comment code - let workerOptsString = code.substring(commaIndex + 1, endIndex) + const workerOptString = cleanCode.raw.substring(commaIndex + 1, endIndex) - const hasViteIgnore = /\/\*\s*@vite-ignore\s*\*\//.test(workerOptsString) + const hasViteIgnore = /\/\*\s*@vite-ignore\s*\*\//.test(workerOptString) if (hasViteIgnore) { return 'ignore' } // need to find in no comment code - workerOptsString = noCommentsCode.substring(commaIndex + 1, endIndex) - if (!workerOptsString.trim().length) { + const cleanworkerOptString = cleanCode.substring(commaIndex + 1, endIndex) + if (!cleanworkerOptString.trim().length) { return 'classic' } let workerOpts: { type: WorkerType } = { type: 'classic' } try { - workerOpts = JSON5.parse(workerOptsString) + workerOpts = JSON5.parse(workerOptString) } catch (e) { // can't parse by JSON5, so the worker options had unexpect char. err( @@ -120,22 +111,20 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { ) { const importMetaUrlRE = /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g - const noCommentsCode = code - .replace(multilineCommentsRE, blankReplacer) - .replace(singlelineCommentsRE, blankReplacer) - - const noStringCode = noCommentsCode.replace( - stringsRE, - (m) => `'${' '.repeat(m.length - 2)}'` - ) + + const cleanCode = emptyString(code) + let match: RegExpExecArray | null let s: MagicString | null = null - while ((match = importMetaUrlRE.exec(noStringCode))) { + while ((match = importMetaUrlRE.exec(cleanCode.toString()))) { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index - const urlStart = allExp.indexOf(emptyUrl) + index - const urlEnd = urlStart + emptyUrl.length + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanCode, + emptyUrl, + index + ) const rawUrl = code.slice(urlStart, urlEnd) if (options?.ssr) { @@ -154,11 +143,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } s ||= new MagicString(code) - const workerType = getWorkerType( - code, - noCommentsCode, - index + allExp.length - ) + const workerType = getWorkerType(cleanCode, index + allExp.length) const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) let url: string if (isBuild) { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 8c7859e3850454..16391df8c73df3 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -733,4 +733,3 @@ export function parseRequest(id: string): Record | null { } export const blankReplacer = (match: string) => ' '.repeat(match.length) -export const stringsRE = /"[^"]*"|'[^']*'|`[^`]*`/g From 1b1d642f8f828849a0671d0fad691fa06a4bca03 Mon Sep 17 00:00:00 2001 From: yoho Date: Fri, 8 Apr 2022 20:54:25 +0800 Subject: [PATCH 02/26] chore: clean code --- packages/vite/src/node/cleanString.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 8e8b6f3078d5e5..dc3e42c8539ebe 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -8,24 +8,23 @@ const stringBlankReplacer = (s: string) => `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` export class CleanCommentString extends String { - cleanComment = '' + clean = '' raw = '' constructor(raw: string) { super(raw.toString()) this.raw = raw - this.cleanComment = raw.replace(cleanerRE, (s: string) => + this.clean = raw.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : s ) } override toString() { - return this.cleanComment + return this.clean } } export class CleanString extends String { - cleanComment = '' clean = '' raw = '' @@ -33,14 +32,12 @@ export class CleanString extends String { super(raw.toString()) if (raw instanceof CleanCommentString) { this.raw = raw.raw - this.cleanComment = raw.cleanComment - this.clean = raw.cleanComment.replace(cleanerRE, (s: string) => + this.clean = raw.clean.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) } else { this.raw = raw - this.cleanComment = emptyCommentsString(raw).cleanComment - this.clean = this.cleanComment.replace(cleanerRE, (s: string) => + this.clean = raw.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) } From a1b96cba6d8f43b5d7c401343848e122222cafe4 Mon Sep 17 00:00:00 2001 From: yoho Date: Sat, 9 Apr 2022 00:00:07 +0800 Subject: [PATCH 03/26] test: clean string --- CONTRIBUTING.md | 2 +- .../src/node/__tests__/cleanString.spec.ts | 75 +++++++++++++++++++ packages/vite/src/node/cleanString.ts | 3 +- 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 packages/vite/src/node/__tests__/cleanString.spec.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b105499159bebb..f086627de5cfe0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,7 +79,7 @@ Each test can be run under either dev server mode or build mode. - `pnpm run test-build` runs tests only under build mode. -- You can also use `pnpm run test-serve -- [match]` or `pnpm run test-build -- [match]` to run tests in a specific playground package, e.g. `pnpm run test-serve -- css` will run tests for both `playground/css` and `playground/css-codesplit` under serve mode. +- You can also use `pnpm run test-serve -- [match]` or `pnpm run test-build -- [match]` to run tests in a specific playground package, e.g. `pnpm run test-serve -- asset` will run tests for both `playground/asset` and `vite/src/node/__tests__/asset` under serve mode and `vite/src/node/__tests__/**/*` just run in serve mode. Note package matching is not available for the `pnpm test` script, which always runs all tests. diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts new file mode 100644 index 00000000000000..2a3208ce09f90a --- /dev/null +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -0,0 +1,75 @@ +import { emptyString, findEmptyStringRawIndex } from '../../node/cleanString' + +test('comments', () => { + const str = ` + // comment1 + /* coment2 */ + /* + // coment3 + */ + /* // coment3 */ + // comment 4 /* comment 5 */ + ` + const clean = emptyString(str) + expect(clean.clean.trim()).toBe('') +}) + +test('strings', () => { + const str = ` + // comment1 + const a = "aaaa" + /* coment2 */ + const b = "bbbb" + /* + // coment3 + */ + /* // coment3 */ + // comment 4 /* comment 5 */ + ` + const clean = emptyString(str) + expect(clean.clean).toMatch('const a = "\0\0\0\0"') + expect(clean.clean).toMatch('const b = "\0\0\0\0"') +}) + +test('strings comment nested', () => { + const commentNestedString = ` + // comment 1 " + const a = "a //" + // comment 2 " + ` + const commentNestedStringClean = emptyString(commentNestedString) + expect(commentNestedStringClean.clean).toMatch('const a = "\0\0\0\0"') + + const stringNestedComment = ` + const a = "a //" + console.log("console") + ` + const stringNestedCommentClean = emptyString(stringNestedComment) + + expect(stringNestedCommentClean.clean).toMatch('const a = "\0\0\0\0"') +}) + +test('empty string flag', () => { + const str = ` + const a = "aaaaa" + const b = "bbbbb" + ` + const clean = emptyString(str) + expect(clean.clean).toMatch('const a = "\0\0\0\0\0"') + expect(clean.clean).toMatch('const b = "\0\0\0\0\0"') + + const aIndex = str.indexOf('const a = "aaaaa"') + const a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) + expect(str.slice(a[0], a[1])).toMatch('aaaaa') + + const bIndex = str.indexOf('const b = "bbbbb"') + const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) + expect(str.slice(b[0], b[1])).toMatch('bbbbb') +}) + +// TODO +// describe('template string nested', () => { +// const str = "`##${ a + b + `##${c + `##${d}`}##`}##`" +// const clean = emptyString(str) +// expect(clean.clean).toMatch('\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') +// }) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index dc3e42c8539ebe..06d59c0e6e0baf 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -3,7 +3,7 @@ // FIXME: nested string template (PS: `${`${}`}`) const cleanerRE = /"[^"]*"|'[^']*'|`[^`]*`|\/\*(.|[\r\n])*?\*\/|\/\/.*/g -const blankReplacer = (s: string) => '\0'.repeat(s.length) +const blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` @@ -61,7 +61,6 @@ export function findEmptyStringRawIndex( emptyFlag: string, start: number ): [number, number] { - // FIXME: if there are sub-strings of the same length in the same string. (PS: fn(' ', ' ')) const flagIndex = raw.clean.indexOf(emptyFlag, start) const flagEndIndex = flagIndex + emptyFlag.length return [flagIndex, flagEndIndex] From 3bc7b4f2cf6054544b1a6ecababde0110696ff44 Mon Sep 17 00:00:00 2001 From: yoho Date: Sat, 9 Apr 2022 00:13:21 +0800 Subject: [PATCH 04/26] chore: clean --- packages/vite/src/node/cleanString.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 06d59c0e6e0baf..cd71a4a4deb65d 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -29,18 +29,12 @@ export class CleanString extends String { raw = '' constructor(raw: string | CleanCommentString) { - super(raw.toString()) - if (raw instanceof CleanCommentString) { - this.raw = raw.raw - this.clean = raw.clean.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) - ) - } else { - this.raw = raw - this.clean = raw.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) - ) - } + const cleaned = raw.toString() + super(cleaned) + this.raw = raw instanceof CleanCommentString ? raw.raw : cleaned + this.clean = cleaned.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) + ) } override toString() { From f70eb0261613ecb0a6df35d5ab8eca9a02e3633b Mon Sep 17 00:00:00 2001 From: yoho Date: Sat, 9 Apr 2022 09:44:09 +0800 Subject: [PATCH 05/26] refactor: clean string --- packages/vite/src/node/cleanString.ts | 83 ++++++++------ .../src/node/plugins/assetImportMetaUrl.ts | 102 +++++++++--------- .../src/node/plugins/workerImportMetaUrl.ts | 99 +++++++++-------- 3 files changed, 153 insertions(+), 131 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index cd71a4a4deb65d..db7ec16c2bb78c 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -7,47 +7,38 @@ const blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` -export class CleanCommentString extends String { - clean = '' - raw = '' - - constructor(raw: string) { - super(raw.toString()) - this.raw = raw - this.clean = raw.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : s - ) - } - - override toString() { - return this.clean - } +export interface CleanString { + clean: string + raw: string } -export class CleanString extends String { - clean = '' - raw = '' +function isCleanString(obj: any): obj is CleanString { + return obj.raw && obj.clean +} - constructor(raw: string | CleanCommentString) { - const cleaned = raw.toString() - super(cleaned) - this.raw = raw instanceof CleanCommentString ? raw.raw : cleaned - this.clean = cleaned.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) +export function emptyCommentsString(raw: string): CleanString { + const res: CleanString = { + raw: raw, + clean: raw.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : s ) } - - override toString() { - return this.clean - } + return res } -export function emptyCommentsString(raw: string): CleanCommentString { - return new CleanCommentString(raw) -} - -export function emptyString(raw: string | CleanCommentString): CleanString { - return new CleanString(raw) +export function emptyString(raw: string | CleanString): CleanString { + const res: CleanString = { raw: '', clean: '' } + if (isCleanString(raw)) { + res.raw = raw.raw + res.clean = raw.clean + } else { + res.raw = raw + res.clean = raw + } + res.clean = res.clean.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) + ) + return res } export function findEmptyStringRawIndex( @@ -59,3 +50,27 @@ export function findEmptyStringRawIndex( const flagEndIndex = flagIndex + emptyFlag.length return [flagIndex, flagEndIndex] } + +export async function walkCleanString( + re: RegExp, + raw: string, + callback: (match: RegExpExecArray, cleanString: CleanString) => Promise +): Promise { + const cleanString = emptyString(raw) + let match: RegExpExecArray | null + while ((match = re.exec(cleanString.clean))) { + await callback(match, cleanString) + } +} + +export function walkCleanStringSync( + re: RegExp, + raw: string, + callback: (match: RegExpExecArray, cleanString: CleanString) => void +): void { + const cleanString = emptyString(raw) + let match: RegExpExecArray | null + while ((match = re.exec(cleanString.clean))) { + callback(match, cleanString) + } +} diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index d97d004a483623..a8d9f8668c6517 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -3,7 +3,7 @@ import MagicString from 'magic-string' import path from 'path' import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' -import { emptyString, findEmptyStringRawIndex } from '../cleanString' +import { walkCleanString, findEmptyStringRawIndex } from '../cleanString' /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL @@ -26,62 +26,64 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { ) { const importMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g - const cleanCode = emptyString(code) + let s: MagicString | undefined - let s: MagicString | null = null - let match: RegExpExecArray | null - while ((match = importMetaUrlRE.exec(cleanCode.toString()))) { - const { 0: exp, 1: emptyUrl, index } = match + await walkCleanString( + importMetaUrlRE, + code, + async (match, cleanString) => { + const { 0: exp, 1: emptyUrl, index } = match - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanCode, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) - if (!s) s = new MagicString(code) + if (!s) s = new MagicString(code) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - const ast = this.parse(rawUrl) - const templateLiteral = (ast as any).body[0].expression - if (templateLiteral.expressions.length) { - const pattern = buildGlobPattern(templateLiteral) - // Note: native import.meta.url is not supported in the baseline - // target so we use the global location here. It can be - // window.location or self.location in case it is used in a Web Worker. - // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self - s.overwrite( - index, - index + exp.length, - `new URL(import.meta.globEagerDefault(${JSON.stringify( - pattern - )})[${rawUrl}], self.location)`, - { contentOnly: true } - ) - continue + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + const ast = this.parse(rawUrl) + const templateLiteral = (ast as any).body[0].expression + if (templateLiteral.expressions.length) { + const pattern = buildGlobPattern(templateLiteral) + // Note: native import.meta.url is not supported in the baseline + // target so we use the global location here. It can be + // window.location or self.location in case it is used in a Web Worker. + // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self + s.overwrite( + index, + index + exp.length, + `new URL(import.meta.globEagerDefault(${JSON.stringify( + pattern + )})[${rawUrl}], self.location)`, + { contentOnly: true } + ) + return + } } - } - const url = rawUrl.slice(1, -1) - const file = path.resolve(path.dirname(id), url) - // Get final asset URL. Catch error if the file does not exist, - // in which we can resort to the initial URL and let it resolve in runtime - const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = cleanCode.raw.slice(index, index + exp.length) - config.logger.warnOnce( - `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + const url = rawUrl.slice(1, -1) + const file = path.resolve(path.dirname(id), url) + // Get final asset URL. Catch error if the file does not exist, + // in which we can resort to the initial URL and let it resolve in runtime + const builtUrl = await fileToUrl(file, config, this).catch(() => { + const truthExp = cleanString.raw.slice(index, index + exp.length) + config.logger.warnOnce( + `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + ) + return url + }) + s.overwrite( + index, + index + exp.length, + `new URL(${JSON.stringify(builtUrl)}, self.location)`, + { contentOnly: true } ) - return url - }) - s.overwrite( - index, - index + exp.length, - `new URL(${JSON.stringify(builtUrl)}, self.location)`, - { contentOnly: true } - ) - } + } + ) if (s) { return { code: s.toString(), diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index ffc8aa17da1856..c9822a22ea8aad 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,3 +1,4 @@ +import type { CleanString } from './../cleanString' import JSON5 from 'json5' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' @@ -10,8 +11,7 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import type { RollupError } from 'rollup' -import type { CleanString } from '../cleanString' -import { emptyString, findEmptyStringRawIndex } from '../cleanString' +import { walkCleanString, findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' @@ -24,11 +24,11 @@ function getWorkerType(cleanCode: CleanString, i: number): WorkerType { throw error } - const commaIndex = cleanCode.indexOf(',', i) + const commaIndex = cleanCode.clean.indexOf(',', i) if (commaIndex === -1) { return 'classic' } - const endIndex = cleanCode.indexOf(')', i) + const endIndex = cleanCode.clean.indexOf(')', i) // case: ') ... ,' mean no worker options params if (commaIndex > endIndex) { @@ -44,7 +44,10 @@ function getWorkerType(cleanCode: CleanString, i: number): WorkerType { } // need to find in no comment code - const cleanworkerOptString = cleanCode.substring(commaIndex + 1, endIndex) + const cleanworkerOptString = cleanCode.clean.substring( + commaIndex + 1, + endIndex + ) if (!cleanworkerOptString.trim().length) { return 'classic' } @@ -112,51 +115,53 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const importMetaUrlRE = /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g - const cleanCode = emptyString(code) - - let match: RegExpExecArray | null - let s: MagicString | null = null - while ((match = importMetaUrlRE.exec(cleanCode.toString()))) { - const { 0: allExp, 2: exp, 3: emptyUrl, index } = match - const urlIndex = allExp.indexOf(exp) + index - - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanCode, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) - - if (options?.ssr) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in SSR.`, - urlIndex + let s: MagicString | undefined + await walkCleanString( + importMetaUrlRE, + code, + async (match, cleanString) => { + const { 0: allExp, 2: exp, 3: emptyUrl, index } = match + const urlIndex = allExp.indexOf(exp) + index + + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString, + emptyUrl, + index ) + const rawUrl = code.slice(urlStart, urlEnd) + + if (options?.ssr) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in SSR.`, + urlIndex + ) + } + + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + urlIndex + ) + } + + s ||= new MagicString(code) + const workerType = getWorkerType(cleanString, index + allExp.length) + const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) + let url: string + if (isBuild) { + url = await workerFileToUrl(this, config, file, query) + } else { + url = await fileToUrl(cleanUrl(file), config, this) + url = injectQuery(url, WORKER_FILE_ID) + url = injectQuery(url, `type=${workerType}`) + } + s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { + contentOnly: true + }) } + ) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - urlIndex - ) - } - - s ||= new MagicString(code) - const workerType = getWorkerType(cleanCode, index + allExp.length) - const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) - let url: string - if (isBuild) { - url = await workerFileToUrl(this, config, file, query) - } else { - url = await fileToUrl(cleanUrl(file), config, this) - url = injectQuery(url, WORKER_FILE_ID) - url = injectQuery(url, `type=${workerType}`) - } - s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { - contentOnly: true - }) - } if (s) { return { code: s.toString(), From 25ad3edb3d134b442abbfc7167156312b9e06ec7 Mon Sep 17 00:00:00 2001 From: yoho Date: Sat, 9 Apr 2022 23:55:11 +0800 Subject: [PATCH 06/26] chore: match string template in other ways --- packages/vite/src/node/__tests__/cleanString.spec.ts | 4 ++-- packages/vite/src/node/cleanString.ts | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 2a3208ce09f90a..81035b1a859014 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -67,9 +67,9 @@ test('empty string flag', () => { expect(str.slice(b[0], b[1])).toMatch('bbbbb') }) -// TODO // describe('template string nested', () => { // const str = "`##${ a + b + `##${c + `##${d}`}##`}##`" + // const clean = emptyString(str) -// expect(clean.clean).toMatch('\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') +// expect(clean.clean).toMatch('`\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`') // }) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index db7ec16c2bb78c..1b4df57147ecf8 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -1,7 +1,7 @@ -// bank on the non-overlapping nature of regex matches -// and combine all our current filters into one giant regex -// FIXME: nested string template (PS: `${`${}`}`) -const cleanerRE = /"[^"]*"|'[^']*'|`[^`]*`|\/\*(.|[\r\n])*?\*\/|\/\/.*/g +// bank on the non-overlapping nature of regex matches and combine all filters into one giant regex +// `([^`\$\{\}]|\$\{(`|\g<1>)*\})*` 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 blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => @@ -38,6 +38,7 @@ export function emptyString(raw: string | CleanString): CleanString { res.clean = res.clean.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) + // TODO replace string template return res } From 10d95629d469776495e78c103d2ee9840d337068 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 00:45:30 +0800 Subject: [PATCH 07/26] feat: full test --- .../src/node/__tests__/cleanString.spec.ts | 78 ++++++++++++++----- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 81035b1a859014..715571cda75051 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -1,23 +1,25 @@ import { emptyString, findEmptyStringRawIndex } from '../../node/cleanString' test('comments', () => { - const str = ` + expect( + emptyString(` + // comment1 // comment // comment1 /* coment2 */ /* // coment3 */ /* // coment3 */ + /* // coment3 */ // comment // comment 4 /* comment 5 */ - ` - const clean = emptyString(str) - expect(clean.clean.trim()).toBe('') + `).clean.trim() + ).toBe('') }) test('strings', () => { - const str = ` + const clean = emptyString(` // comment1 - const a = "aaaa" + const a = 'aaaa' /* coment2 */ const b = "bbbb" /* @@ -25,31 +27,69 @@ test('strings', () => { */ /* // coment3 */ // comment 4 /* comment 5 */ - ` - const clean = emptyString(str) - expect(clean.clean).toMatch('const a = "\0\0\0\0"') + `) + expect(clean.clean).toMatch("const a = '\0\0\0\0'") expect(clean.clean).toMatch('const b = "\0\0\0\0"') }) test('strings comment nested', () => { - const commentNestedString = ` + expect( + emptyString(` // comment 1 " const a = "a //" // comment 2 " - ` - const commentNestedStringClean = emptyString(commentNestedString) - expect(commentNestedStringClean.clean).toMatch('const a = "\0\0\0\0"') + `).clean + ).toMatch('const a = "\0\0\0\0"') + + expect( + emptyString(` + // comment 1 ' + const a = "a //" + // comment 2 ' + `).clean + ).toMatch('const a = "\0\0\0\0"') - const stringNestedComment = ` + expect( + emptyString(` + // comment 1 \` + const a = "a //" + // comment 2 \` + `).clean + ).toMatch('const a = "\0\0\0\0"') + + expect( + emptyString(` const a = "a //" console.log("console") - ` - const stringNestedCommentClean = emptyString(stringNestedComment) + `).clean + ).toMatch('const a = "\0\0\0\0"') + + expect( + emptyString(` + const a = "a /*" + console.log("console") + const b = "b */" + `).clean + ).toMatch('const a = "\0\0\0\0"') + + expect( + emptyString(` + const a = "a ' " + console.log("console") + const b = "b ' " + `).clean + ).toMatch('const a = "\0\0\0\0"') - expect(stringNestedCommentClean.clean).toMatch('const a = "\0\0\0\0"') + expect( + emptyString(` + const a = "a \` " + console.log("console") + const b = "b \` " + `).clean + ).toMatch('const a = "\0\0\0\0"') }) -test('empty string flag', () => { +test('find empty string flag in raw index', () => { const str = ` const a = "aaaaa" const b = "bbbbb" @@ -68,7 +108,7 @@ test('empty string flag', () => { }) // describe('template string nested', () => { -// const str = "`##${ a + b + `##${c + `##${d}`}##`}##`" +// const str = "`##${a + b + `##${c + `##${d}`}##`}##`" // const clean = emptyString(str) // expect(clean.clean).toMatch('`\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`') From be41a44c9c922170c685fe6d0c43525a485a9ff9 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 11:19:14 +0800 Subject: [PATCH 08/26] feat: clean code --- .../src/node/__tests__/cleanString.spec.ts | 16 +-- packages/vite/src/node/cleanString.ts | 30 +---- .../src/node/plugins/assetImportMetaUrl.ts | 106 +++++++++--------- .../src/node/plugins/workerImportMetaUrl.ts | 95 ++++++++-------- 4 files changed, 110 insertions(+), 137 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 715571cda75051..8a012acdab9180 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -35,25 +35,25 @@ test('strings', () => { test('strings comment nested', () => { expect( emptyString(` - // comment 1 " + // comment 1 /* " */ const a = "a //" - // comment 2 " + // comment 2 /* " */ `).clean ).toMatch('const a = "\0\0\0\0"') expect( emptyString(` - // comment 1 ' + // comment 1 /* ' */ const a = "a //" - // comment 2 ' + // comment 2 /* ' */ `).clean ).toMatch('const a = "\0\0\0\0"') expect( emptyString(` - // comment 1 \` + // comment 1 /* \` */ const a = "a //" - // comment 2 \` + // comment 2 /* \` */ `).clean ).toMatch('const a = "\0\0\0\0"') @@ -99,11 +99,11 @@ test('find empty string flag in raw index', () => { expect(clean.clean).toMatch('const b = "\0\0\0\0\0"') const aIndex = str.indexOf('const a = "aaaaa"') - const a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) + const a = findEmptyStringRawIndex(clean.clean, '\0\0\0\0\0', aIndex) expect(str.slice(a[0], a[1])).toMatch('aaaaa') const bIndex = str.indexOf('const b = "bbbbb"') - const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) + const b = findEmptyStringRawIndex(clean.clean, '\0\0\0\0\0', bIndex) expect(str.slice(b[0], b[1])).toMatch('bbbbb') }) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 1b4df57147ecf8..5477b49ca4fa9d 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -1,5 +1,5 @@ // bank on the non-overlapping nature of regex matches and combine all filters into one giant regex -// `([^`\$\{\}]|\$\{(`|\g<1>)*\})*` can match nested string template +// /`([^`\$\{\}]|\$\{(`|\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 @@ -43,35 +43,11 @@ export function emptyString(raw: string | CleanString): CleanString { } export function findEmptyStringRawIndex( - raw: CleanString, + clean: string, emptyFlag: string, start: number ): [number, number] { - const flagIndex = raw.clean.indexOf(emptyFlag, start) + const flagIndex = clean.indexOf(emptyFlag, start) const flagEndIndex = flagIndex + emptyFlag.length return [flagIndex, flagEndIndex] } - -export async function walkCleanString( - re: RegExp, - raw: string, - callback: (match: RegExpExecArray, cleanString: CleanString) => Promise -): Promise { - const cleanString = emptyString(raw) - let match: RegExpExecArray | null - while ((match = re.exec(cleanString.clean))) { - await callback(match, cleanString) - } -} - -export function walkCleanStringSync( - re: RegExp, - raw: string, - callback: (match: RegExpExecArray, cleanString: CleanString) => void -): void { - const cleanString = emptyString(raw) - let match: RegExpExecArray | null - while ((match = re.exec(cleanString.clean))) { - callback(match, cleanString) - } -} diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index a8d9f8668c6517..6ef5e0a20063cf 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -3,7 +3,10 @@ import MagicString from 'magic-string' import path from 'path' import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' -import { walkCleanString, findEmptyStringRawIndex } from '../cleanString' +import { emptyString, findEmptyStringRawIndex } from '../cleanString' + +const importMetaUrlRE = + /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL @@ -24,66 +27,61 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { code.includes('new URL') && code.includes(`import.meta.url`) ) { - const importMetaUrlRE = - /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g let s: MagicString | undefined + let match: RegExpExecArray | null + const cleanString = emptyString(code) + while ((match = importMetaUrlRE.exec(cleanString.clean))) { + const { 0: exp, 1: emptyUrl, index } = match - await walkCleanString( - importMetaUrlRE, - code, - async (match, cleanString) => { - const { 0: exp, 1: emptyUrl, index } = match - - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString.clean, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) - if (!s) s = new MagicString(code) + if (!s) s = new MagicString(code) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - const ast = this.parse(rawUrl) - const templateLiteral = (ast as any).body[0].expression - if (templateLiteral.expressions.length) { - const pattern = buildGlobPattern(templateLiteral) - // Note: native import.meta.url is not supported in the baseline - // target so we use the global location here. It can be - // window.location or self.location in case it is used in a Web Worker. - // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self - s.overwrite( - index, - index + exp.length, - `new URL(import.meta.globEagerDefault(${JSON.stringify( - pattern - )})[${rawUrl}], self.location)`, - { contentOnly: true } - ) - return - } + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + const ast = this.parse(rawUrl) + const templateLiteral = (ast as any).body[0].expression + if (templateLiteral.expressions.length) { + const pattern = buildGlobPattern(templateLiteral) + // Note: native import.meta.url is not supported in the baseline + // target so we use the global location here. It can be + // window.location or self.location in case it is used in a Web Worker. + // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self + s.overwrite( + index, + index + exp.length, + `new URL(import.meta.globEagerDefault(${JSON.stringify( + pattern + )})[${rawUrl}], self.location)`, + { contentOnly: true } + ) + continue } + } - const url = rawUrl.slice(1, -1) - const file = path.resolve(path.dirname(id), url) - // Get final asset URL. Catch error if the file does not exist, - // in which we can resort to the initial URL and let it resolve in runtime - const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = cleanString.raw.slice(index, index + exp.length) - config.logger.warnOnce( - `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` - ) - return url - }) - s.overwrite( - index, - index + exp.length, - `new URL(${JSON.stringify(builtUrl)}, self.location)`, - { contentOnly: true } + const url = rawUrl.slice(1, -1) + const file = path.resolve(path.dirname(id), url) + // Get final asset URL. Catch error if the file does not exist, + // in which we can resort to the initial URL and let it resolve in runtime + const builtUrl = await fileToUrl(file, config, this).catch(() => { + const truthExp = cleanString.raw.slice(index, index + exp.length) + config.logger.warnOnce( + `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) - } - ) + return url + }) + s.overwrite( + index, + index + exp.length, + `new URL(${JSON.stringify(builtUrl)}, self.location)`, + { contentOnly: true } + ) + } if (s) { return { code: s.toString(), diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index c9822a22ea8aad..6da855af19715a 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,4 +1,5 @@ import type { CleanString } from './../cleanString' +import { emptyString } from './../cleanString' import JSON5 from 'json5' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' @@ -11,9 +12,12 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import type { RollupError } from 'rollup' -import { walkCleanString, findEmptyStringRawIndex } from '../cleanString' +import { findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' +const importMetaUrlRE = + /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g +const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// const WORKER_FILE_ID = 'worker_url_file' @@ -38,7 +42,7 @@ function getWorkerType(cleanCode: CleanString, i: number): WorkerType { // need to find in comment code const workerOptString = cleanCode.raw.substring(commaIndex + 1, endIndex) - const hasViteIgnore = /\/\*\s*@vite-ignore\s*\*\//.test(workerOptString) + const hasViteIgnore = ignoreFlagRE.test(workerOptString) if (hasViteIgnore) { return 'ignore' } @@ -112,55 +116,50 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { code.includes('new URL') && code.includes(`import.meta.url`) ) { - const importMetaUrlRE = - /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g - let s: MagicString | undefined - await walkCleanString( - importMetaUrlRE, - code, - async (match, cleanString) => { - const { 0: allExp, 2: exp, 3: emptyUrl, index } = match - const urlIndex = allExp.indexOf(exp) + index - - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index + let match: RegExpExecArray | null + const cleanString = emptyString(code) + while ((match = importMetaUrlRE.exec(cleanString.clean))) { + const { 0: allExp, 2: exp, 3: emptyUrl, index } = match + const urlIndex = allExp.indexOf(exp) + index + + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString.clean, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) + + if (options?.ssr) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in SSR.`, + urlIndex + ) + } + + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + urlIndex ) - const rawUrl = code.slice(urlStart, urlEnd) - - if (options?.ssr) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in SSR.`, - urlIndex - ) - } - - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - urlIndex - ) - } - - s ||= new MagicString(code) - const workerType = getWorkerType(cleanString, index + allExp.length) - const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) - let url: string - if (isBuild) { - url = await workerFileToUrl(this, config, file, query) - } else { - url = await fileToUrl(cleanUrl(file), config, this) - url = injectQuery(url, WORKER_FILE_ID) - url = injectQuery(url, `type=${workerType}`) - } - s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { - contentOnly: true - }) } - ) + + s ||= new MagicString(code) + const workerType = getWorkerType(cleanString, index + allExp.length) + const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) + let url: string + if (isBuild) { + url = await workerFileToUrl(this, config, file, query) + } else { + url = await fileToUrl(cleanUrl(file), config, this) + url = injectQuery(url, WORKER_FILE_ID) + url = injectQuery(url, `type=${workerType}`) + } + s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { + contentOnly: true + }) + } if (s) { return { From 32558f036f122cd860377cb9385304f31c16e3b3 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 11:29:37 +0800 Subject: [PATCH 09/26] chore: reduce code cyclomatic complexity --- .../src/node/plugins/assetImportMetaUrl.ts | 115 +++++++++--------- .../src/node/plugins/workerImportMetaUrl.ts | 99 +++++++-------- 2 files changed, 108 insertions(+), 106 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 6ef5e0a20063cf..1da942cc302354 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -23,70 +23,71 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { name: 'vite:asset-import-meta-url', async transform(code, id, options) { if ( - !options?.ssr && - code.includes('new URL') && - code.includes(`import.meta.url`) + options?.ssr || + !code.includes('new URL') || + !code.includes(`import.meta.url`) ) { - let s: MagicString | undefined - let match: RegExpExecArray | null - const cleanString = emptyString(code) - while ((match = importMetaUrlRE.exec(cleanString.clean))) { - const { 0: exp, 1: emptyUrl, index } = match + return null + } + let s: MagicString | undefined + let match: RegExpExecArray | null + const cleanString = emptyString(code) + while ((match = importMetaUrlRE.exec(cleanString.clean))) { + const { 0: exp, 1: emptyUrl, index } = match - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString.clean, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString.clean, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) - if (!s) s = new MagicString(code) + if (!s) s = new MagicString(code) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - const ast = this.parse(rawUrl) - const templateLiteral = (ast as any).body[0].expression - if (templateLiteral.expressions.length) { - const pattern = buildGlobPattern(templateLiteral) - // Note: native import.meta.url is not supported in the baseline - // target so we use the global location here. It can be - // window.location or self.location in case it is used in a Web Worker. - // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self - s.overwrite( - index, - index + exp.length, - `new URL(import.meta.globEagerDefault(${JSON.stringify( - pattern - )})[${rawUrl}], self.location)`, - { contentOnly: true } - ) - continue - } + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + const ast = this.parse(rawUrl) + const templateLiteral = (ast as any).body[0].expression + if (templateLiteral.expressions.length) { + const pattern = buildGlobPattern(templateLiteral) + // Note: native import.meta.url is not supported in the baseline + // target so we use the global location here. It can be + // window.location or self.location in case it is used in a Web Worker. + // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self + s.overwrite( + index, + index + exp.length, + `new URL(import.meta.globEagerDefault(${JSON.stringify( + pattern + )})[${rawUrl}], self.location)`, + { contentOnly: true } + ) + continue } + } - const url = rawUrl.slice(1, -1) - const file = path.resolve(path.dirname(id), url) - // Get final asset URL. Catch error if the file does not exist, - // in which we can resort to the initial URL and let it resolve in runtime - const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = cleanString.raw.slice(index, index + exp.length) - config.logger.warnOnce( - `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` - ) - return url - }) - s.overwrite( - index, - index + exp.length, - `new URL(${JSON.stringify(builtUrl)}, self.location)`, - { contentOnly: true } + const url = rawUrl.slice(1, -1) + const file = path.resolve(path.dirname(id), url) + // Get final asset URL. Catch error if the file does not exist, + // in which we can resort to the initial URL and let it resolve in runtime + const builtUrl = await fileToUrl(file, config, this).catch(() => { + const truthExp = cleanString.raw.slice(index, index + exp.length) + config.logger.warnOnce( + `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) - } - if (s) { - return { - code: s.toString(), - map: config.build.sourcemap ? s.generateMap({ hires: true }) : null - } + return url + }) + s.overwrite( + index, + index + exp.length, + `new URL(${JSON.stringify(builtUrl)}, self.location)`, + { contentOnly: true } + ) + } + if (s) { + return { + code: s.toString(), + map: config.build.sourcemap ? s.generateMap({ hires: true }) : null } } return null diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 6da855af19715a..ecfbae944bf2c2 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -112,63 +112,64 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } } if ( - (code.includes('new Worker') || code.includes('new ShareWorker')) && - code.includes('new URL') && - code.includes(`import.meta.url`) + !(code.includes('new Worker') || code.includes('new ShareWorker')) || + !code.includes('new URL') || + !code.includes(`import.meta.url`) ) { - let s: MagicString | undefined - let match: RegExpExecArray | null - const cleanString = emptyString(code) - while ((match = importMetaUrlRE.exec(cleanString.clean))) { - const { 0: allExp, 2: exp, 3: emptyUrl, index } = match - const urlIndex = allExp.indexOf(exp) + index - - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString.clean, - emptyUrl, - index + return null + } + let s: MagicString | undefined + let match: RegExpExecArray | null + const cleanString = emptyString(code) + while ((match = importMetaUrlRE.exec(cleanString.clean))) { + const { 0: allExp, 2: exp, 3: emptyUrl, index } = match + const urlIndex = allExp.indexOf(exp) + index + + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString.clean, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) + + if (options?.ssr) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in SSR.`, + urlIndex ) - const rawUrl = code.slice(urlStart, urlEnd) - - if (options?.ssr) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in SSR.`, - urlIndex - ) - } + } - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - urlIndex - ) - } + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + urlIndex + ) + } - s ||= new MagicString(code) - const workerType = getWorkerType(cleanString, index + allExp.length) - const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) - let url: string - if (isBuild) { - url = await workerFileToUrl(this, config, file, query) - } else { - url = await fileToUrl(cleanUrl(file), config, this) - url = injectQuery(url, WORKER_FILE_ID) - url = injectQuery(url, `type=${workerType}`) - } - s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { - contentOnly: true - }) + s ||= new MagicString(code) + const workerType = getWorkerType(cleanString, index + allExp.length) + const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) + let url: string + if (isBuild) { + url = await workerFileToUrl(this, config, file, query) + } else { + url = await fileToUrl(cleanUrl(file), config, this) + url = injectQuery(url, WORKER_FILE_ID) + url = injectQuery(url, `type=${workerType}`) } + s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { + contentOnly: true + }) + } - if (s) { - return { - code: s.toString(), - map: config.build.sourcemap ? s.generateMap({ hires: true }) : null - } + if (s) { + return { + code: s.toString(), + map: config.build.sourcemap ? s.generateMap({ hires: true }) : null } - return null } + return null } } } From e25cd09424040bbe772a7426c98aeba4fba4802b Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 11:58:58 +0800 Subject: [PATCH 10/26] chore: named --- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 4 ++-- packages/vite/src/node/plugins/workerImportMetaUrl.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 1da942cc302354..7c14583bfd7427 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -5,7 +5,7 @@ import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' import { emptyString, findEmptyStringRawIndex } from '../cleanString' -const importMetaUrlRE = +const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g /** @@ -32,7 +32,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { let s: MagicString | undefined let match: RegExpExecArray | null const cleanString = emptyString(code) - while ((match = importMetaUrlRE.exec(cleanString.clean))) { + while ((match = assetImportMetaUrlRE.exec(cleanString.clean))) { const { 0: exp, 1: emptyUrl, index } = match const [urlStart, urlEnd] = findEmptyStringRawIndex( diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index ecfbae944bf2c2..e1ce0f8cafdbda 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -15,7 +15,7 @@ import type { RollupError } from 'rollup' import { findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' -const importMetaUrlRE = +const workerImportMetaUrlRE = /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// @@ -121,7 +121,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { let s: MagicString | undefined let match: RegExpExecArray | null const cleanString = emptyString(code) - while ((match = importMetaUrlRE.exec(cleanString.clean))) { + while ((match = workerImportMetaUrlRE.exec(cleanString.clean))) { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index From 93700fec80baceb2be46b7a3f4821b7d5e4ed843 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 11:59:30 +0800 Subject: [PATCH 11/26] chore: chore --- packages/vite/src/node/plugins/workerImportMetaUrl.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index e1ce0f8cafdbda..b72ed1e3dc42e6 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,5 +1,3 @@ -import type { CleanString } from './../cleanString' -import { emptyString } from './../cleanString' import JSON5 from 'json5' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' @@ -12,7 +10,8 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import type { RollupError } from 'rollup' -import { findEmptyStringRawIndex } from '../cleanString' +import type { CleanString } from '../cleanString' +import { emptyString, findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' const workerImportMetaUrlRE = From 993037423146d1a3f763bd46d5818654f132b547 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 12:10:09 +0800 Subject: [PATCH 12/26] fix: regexp re --- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 5 ++--- packages/vite/src/node/plugins/workerImportMetaUrl.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 7c14583bfd7427..e450cc0684f92d 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -5,9 +5,6 @@ import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' import { emptyString, findEmptyStringRawIndex } from '../cleanString' -const assetImportMetaUrlRE = - /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g - /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL * @@ -31,6 +28,8 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } let s: MagicString | undefined let match: RegExpExecArray | null + const assetImportMetaUrlRE = + /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g const cleanString = emptyString(code) while ((match = assetImportMetaUrlRE.exec(cleanString.clean))) { const { 0: exp, 1: emptyUrl, index } = match diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index b72ed1e3dc42e6..7656f1f18f5ecb 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -14,8 +14,6 @@ import type { CleanString } from '../cleanString' import { emptyString, findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' -const workerImportMetaUrlRE = - /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// const WORKER_FILE_ID = 'worker_url_file' @@ -120,6 +118,8 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { let s: MagicString | undefined let match: RegExpExecArray | null const cleanString = emptyString(code) + const workerImportMetaUrlRE = + /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g while ((match = workerImportMetaUrlRE.exec(cleanString.clean))) { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index From 60d066de1921d68faf075f133d5a7cc7b541036e Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 13:41:11 +0800 Subject: [PATCH 13/26] chore: clean string --- packages/vite/src/node/cleanString.ts | 33 ++++--------------- .../src/node/plugins/assetImportMetaUrl.ts | 6 ++-- .../src/node/plugins/workerImportMetaUrl.ts | 24 +++++++------- 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 5477b49ca4fa9d..f623897be5c78b 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -7,35 +7,14 @@ const blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` -export interface CleanString { - clean: string - raw: string -} - -function isCleanString(obj: any): obj is CleanString { - return obj.raw && obj.clean -} - -export function emptyCommentsString(raw: string): CleanString { - const res: CleanString = { - raw: raw, - clean: raw.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : s - ) - } - return res +export function emptyCommentsString(raw: string): string { + return raw.replace(cleanerRE, (s: string) => + s[0] === '/' ? blankReplacer(s) : s + ) } -export function emptyString(raw: string | CleanString): CleanString { - const res: CleanString = { raw: '', clean: '' } - if (isCleanString(raw)) { - res.raw = raw.raw - res.clean = raw.clean - } else { - res.raw = raw - res.clean = raw - } - res.clean = res.clean.replace(cleanerRE, (s: string) => +export function emptyString(raw: string): string { + const res = raw.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) // TODO replace string template diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index e450cc0684f92d..cf2238373199e3 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -31,11 +31,11 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g const cleanString = emptyString(code) - while ((match = assetImportMetaUrlRE.exec(cleanString.clean))) { + while ((match = assetImportMetaUrlRE.exec(cleanString))) { const { 0: exp, 1: emptyUrl, index } = match const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString.clean, + cleanString, emptyUrl, index ) @@ -70,7 +70,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { // Get final asset URL. Catch error if the file does not exist, // in which we can resort to the initial URL and let it resolve in runtime const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = cleanString.raw.slice(index, index + exp.length) + const truthExp = code.slice(index, index + exp.length) config.logger.warnOnce( `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 7656f1f18f5ecb..a035e24b304e52 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -10,7 +10,6 @@ import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import type { RollupError } from 'rollup' -import type { CleanString } from '../cleanString' import { emptyString, findEmptyStringRawIndex } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' @@ -18,18 +17,18 @@ const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// const WORKER_FILE_ID = 'worker_url_file' -function getWorkerType(cleanCode: CleanString, i: number): WorkerType { +function getWorkerType(raw: string, clean: string, i: number): WorkerType { function err(e: string, pos: number) { const error = new Error(e) as RollupError error.pos = pos throw error } - const commaIndex = cleanCode.clean.indexOf(',', i) + const commaIndex = clean.indexOf(',', i) if (commaIndex === -1) { return 'classic' } - const endIndex = cleanCode.clean.indexOf(')', i) + const endIndex = clean.indexOf(')', i) // case: ') ... ,' mean no worker options params if (commaIndex > endIndex) { @@ -37,7 +36,7 @@ function getWorkerType(cleanCode: CleanString, i: number): WorkerType { } // need to find in comment code - const workerOptString = cleanCode.raw.substring(commaIndex + 1, endIndex) + const workerOptString = raw.substring(commaIndex + 1, endIndex) const hasViteIgnore = ignoreFlagRE.test(workerOptString) if (hasViteIgnore) { @@ -45,10 +44,7 @@ function getWorkerType(cleanCode: CleanString, i: number): WorkerType { } // need to find in no comment code - const cleanworkerOptString = cleanCode.clean.substring( - commaIndex + 1, - endIndex - ) + const cleanworkerOptString = clean.substring(commaIndex + 1, endIndex) if (!cleanworkerOptString.trim().length) { return 'classic' } @@ -120,12 +116,12 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const cleanString = emptyString(code) const workerImportMetaUrlRE = /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g - while ((match = workerImportMetaUrlRE.exec(cleanString.clean))) { + while ((match = workerImportMetaUrlRE.exec(cleanString))) { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString.clean, + cleanString, emptyUrl, index ) @@ -147,7 +143,11 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } s ||= new MagicString(code) - const workerType = getWorkerType(cleanString, index + allExp.length) + const workerType = getWorkerType( + code, + cleanString, + index + allExp.length + ) const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) let url: string if (isBuild) { From 84aa548fcfa69f101079e08fd94e2b226392374e Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 13:50:07 +0800 Subject: [PATCH 14/26] fix: test --- .../src/node/__tests__/cleanString.spec.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 8a012acdab9180..a3b4062aa79145 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -12,7 +12,7 @@ test('comments', () => { /* // coment3 */ /* // coment3 */ // comment // comment 4 /* comment 5 */ - `).clean.trim() + `).trim() ).toBe('') }) @@ -28,8 +28,8 @@ test('strings', () => { /* // coment3 */ // comment 4 /* comment 5 */ `) - expect(clean.clean).toMatch("const a = '\0\0\0\0'") - expect(clean.clean).toMatch('const b = "\0\0\0\0"') + expect(clean).toMatch("const a = '\0\0\0\0'") + expect(clean).toMatch('const b = "\0\0\0\0"') }) test('strings comment nested', () => { @@ -38,7 +38,7 @@ test('strings comment nested', () => { // comment 1 /* " */ const a = "a //" // comment 2 /* " */ - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( @@ -46,7 +46,7 @@ test('strings comment nested', () => { // comment 1 /* ' */ const a = "a //" // comment 2 /* ' */ - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( @@ -54,14 +54,14 @@ test('strings comment nested', () => { // comment 1 /* \` */ const a = "a //" // comment 2 /* \` */ - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( emptyString(` const a = "a //" console.log("console") - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( @@ -69,7 +69,7 @@ test('strings comment nested', () => { const a = "a /*" console.log("console") const b = "b */" - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( @@ -77,7 +77,7 @@ test('strings comment nested', () => { const a = "a ' " console.log("console") const b = "b ' " - `).clean + `) ).toMatch('const a = "\0\0\0\0"') expect( @@ -85,7 +85,7 @@ test('strings comment nested', () => { const a = "a \` " console.log("console") const b = "b \` " - `).clean + `) ).toMatch('const a = "\0\0\0\0"') }) @@ -95,15 +95,15 @@ test('find empty string flag in raw index', () => { const b = "bbbbb" ` const clean = emptyString(str) - expect(clean.clean).toMatch('const a = "\0\0\0\0\0"') - expect(clean.clean).toMatch('const b = "\0\0\0\0\0"') + 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 a = findEmptyStringRawIndex(clean.clean, '\0\0\0\0\0', aIndex) + const a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) expect(str.slice(a[0], a[1])).toMatch('aaaaa') const bIndex = str.indexOf('const b = "bbbbb"') - const b = findEmptyStringRawIndex(clean.clean, '\0\0\0\0\0', bIndex) + const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) expect(str.slice(b[0], b[1])).toMatch('bbbbb') }) @@ -111,5 +111,5 @@ test('find empty string flag in raw index', () => { // const str = "`##${a + b + `##${c + `##${d}`}##`}##`" // const clean = emptyString(str) -// expect(clean.clean).toMatch('`\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`') +// expect(clean).toMatch('`\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`') // }) From 1bd6019e241c5beffceb7b9888220f1406a901d2 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 13:56:44 +0800 Subject: [PATCH 15/26] chore: revert reduce code cyclomatic complexity --- .../src/node/plugins/assetImportMetaUrl.ts | 121 +++++++++--------- .../src/node/plugins/workerImportMetaUrl.ts | 100 +++++++-------- 2 files changed, 110 insertions(+), 111 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index cf2238373199e3..7e6a062de4a969 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -20,76 +20,75 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { name: 'vite:asset-import-meta-url', async transform(code, id, options) { if ( - options?.ssr || - !code.includes('new URL') || - !code.includes(`import.meta.url`) + !options?.ssr && + code.includes('new URL') && + code.includes(`import.meta.url`) ) { - return null - } - let s: MagicString | undefined - let match: RegExpExecArray | null - const assetImportMetaUrlRE = - /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g - const cleanString = emptyString(code) - while ((match = assetImportMetaUrlRE.exec(cleanString))) { - const { 0: exp, 1: emptyUrl, index } = match + let s: MagicString | undefined + let match: RegExpExecArray | null + const assetImportMetaUrlRE = + /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g + const cleanString = emptyString(code) + while ((match = assetImportMetaUrlRE.exec(cleanString))) { + const { 0: exp, 1: emptyUrl, index } = match - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString, + emptyUrl, + index + ) + const rawUrl = code.slice(urlStart, urlEnd) - if (!s) s = new MagicString(code) + if (!s) s = new MagicString(code) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - const ast = this.parse(rawUrl) - const templateLiteral = (ast as any).body[0].expression - if (templateLiteral.expressions.length) { - const pattern = buildGlobPattern(templateLiteral) - // Note: native import.meta.url is not supported in the baseline - // target so we use the global location here. It can be - // window.location or self.location in case it is used in a Web Worker. - // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self - s.overwrite( - index, - index + exp.length, - `new URL(import.meta.globEagerDefault(${JSON.stringify( - pattern - )})[${rawUrl}], self.location)`, - { contentOnly: true } - ) - continue + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + const ast = this.parse(rawUrl) + const templateLiteral = (ast as any).body[0].expression + if (templateLiteral.expressions.length) { + const pattern = buildGlobPattern(templateLiteral) + // Note: native import.meta.url is not supported in the baseline + // target so we use the global location here. It can be + // window.location or self.location in case it is used in a Web Worker. + // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self + s.overwrite( + index, + index + exp.length, + `new URL(import.meta.globEagerDefault(${JSON.stringify( + pattern + )})[${rawUrl}], self.location)`, + { contentOnly: true } + ) + continue + } } - } - const url = rawUrl.slice(1, -1) - const file = path.resolve(path.dirname(id), url) - // Get final asset URL. Catch error if the file does not exist, - // in which we can resort to the initial URL and let it resolve in runtime - const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = code.slice(index, index + exp.length) - config.logger.warnOnce( - `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + const url = rawUrl.slice(1, -1) + const file = path.resolve(path.dirname(id), url) + // Get final asset URL. Catch error if the file does not exist, + // in which we can resort to the initial URL and let it resolve in runtime + const builtUrl = await fileToUrl(file, config, this).catch(() => { + const truthExp = code.slice(index, index + exp.length) + config.logger.warnOnce( + `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + ) + return url + }) + s.overwrite( + index, + index + exp.length, + `new URL(${JSON.stringify(builtUrl)}, self.location)`, + { contentOnly: true } ) - return url - }) - s.overwrite( - index, - index + exp.length, - `new URL(${JSON.stringify(builtUrl)}, self.location)`, - { contentOnly: true } - ) - } - if (s) { - return { - code: s.toString(), - map: config.build.sourcemap ? s.generateMap({ hires: true }) : null } + if (s) { + return { + code: s.toString(), + map: config.build.sourcemap ? s.generateMap({ hires: true }) : null + } + } + return null } - return null } } } diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index a035e24b304e52..eb96b6686f23aa 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -104,62 +104,62 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { code: injectEnv + code } } + let s: MagicString | undefined if ( - !(code.includes('new Worker') || code.includes('new ShareWorker')) || - !code.includes('new URL') || - !code.includes(`import.meta.url`) + (code.includes('new Worker') || code.includes('new ShareWorker')) && + code.includes('new URL') && + code.includes(`import.meta.url`) ) { - return null - } - let s: MagicString | undefined - let match: RegExpExecArray | null - const cleanString = emptyString(code) - const workerImportMetaUrlRE = - /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g - while ((match = workerImportMetaUrlRE.exec(cleanString))) { - const { 0: allExp, 2: exp, 3: emptyUrl, index } = match - const urlIndex = allExp.indexOf(exp) + index - - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index - ) - const rawUrl = code.slice(urlStart, urlEnd) - - if (options?.ssr) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in SSR.`, - urlIndex + let match: RegExpExecArray | null + const cleanString = emptyString(code) + const workerImportMetaUrlRE = + /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g + + while ((match = workerImportMetaUrlRE.exec(cleanString))) { + const { 0: allExp, 2: exp, 3: emptyUrl, index } = match + const urlIndex = allExp.indexOf(exp) + index + + const [urlStart, urlEnd] = findEmptyStringRawIndex( + cleanString, + emptyUrl, + index ) - } + const rawUrl = code.slice(urlStart, urlEnd) - // potential dynamic template string - if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - urlIndex - ) - } + if (options?.ssr) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in SSR.`, + urlIndex + ) + } + + // potential dynamic template string + if (rawUrl[0] === '`' && /\$\{/.test(rawUrl)) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + urlIndex + ) + } - s ||= new MagicString(code) - const workerType = getWorkerType( - code, - cleanString, - index + allExp.length - ) - const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) - let url: string - if (isBuild) { - url = await workerFileToUrl(this, config, file, query) - } else { - url = await fileToUrl(cleanUrl(file), config, this) - url = injectQuery(url, WORKER_FILE_ID) - url = injectQuery(url, `type=${workerType}`) + s ||= new MagicString(code) + const workerType = getWorkerType( + code, + cleanString, + index + allExp.length + ) + const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1)) + let url: string + if (isBuild) { + url = await workerFileToUrl(this, config, file, query) + } else { + url = await fileToUrl(cleanUrl(file), config, this) + url = injectQuery(url, WORKER_FILE_ID) + url = injectQuery(url, `type=${workerType}`) + } + s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { + contentOnly: true + }) } - s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { - contentOnly: true - }) } if (s) { From 4b7ae18d3deb29d4a7f88cfd99ddebe0ecc514bc Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 21:27:45 +0800 Subject: [PATCH 16/26] feat: lex string template expression --- .../src/node/__tests__/cleanString.spec.ts | 239 ++++++++++-------- packages/vite/src/node/cleanString.ts | 108 +++++++- 2 files changed, 240 insertions(+), 107 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index a3b4062aa79145..593a8b7f282458 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -1,115 +1,142 @@ import { emptyString, findEmptyStringRawIndex } from '../../node/cleanString' -test('comments', () => { - expect( - emptyString(` - // comment1 // comment - // comment1 - /* coment2 */ - /* - // coment3 - */ - /* // coment3 */ - /* // coment3 */ // comment - // comment 4 /* comment 5 */ - `).trim() - ).toBe('') -}) +// 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('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('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('strings comment nested', () => { +// expect( +// emptyString(` +// // comment 1 /* " */ +// const a = "a //" +// // comment 2 /* " */ +// `) +// ).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 a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) - expect(str.slice(a[0], a[1])).toMatch('aaaaa') - - const bIndex = str.indexOf('const b = "bbbbb"') - const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) - expect(str.slice(b[0], b[1])).toMatch('bbbbb') -}) +// 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"') -// describe('template string nested', () => { -// const str = "`##${a + b + `##${c + `##${d}`}##`}##`" +// 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('`\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`') +// 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 a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) +// expect(str.slice(a[0], a[1])).toMatch('aaaaa') + +// const bIndex = str.indexOf('const b = "bbbbb"') +// const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) +// expect(str.slice(b[0], b[1])).toMatch('bbbbb') // }) + +// test case +// `${ ( a = {}) }` +// +// `##${a + b + `##${c + `##${d}`}##`}##` + +test('template string nested', () => { + let str = 'const a = `aaaa`' + let res = 'const a = `\0\0\0\0`' + let clean = emptyString(str) + expect(clean).toMatch(res) + + str = 'const a = `aa${a}aa`' + res = 'const a = `\0\0${a}\0\0`' + clean = emptyString(str) + expect(clean).toMatch(res) + + str = 'const a = `aa${a + `a` + a}aa`' + res = 'const a = `\0\0${a + `\0` + a}\0\0`' + clean = emptyString(str) + expect(clean).toMatch(res) + + str = 'const a = `aa${a + `aaaa${c + (a = {b: 1}) + d}` + a}aa`' + res = 'const a = `\0\0${a + `\0\0\0\0${c + (a = {b: 1}) + d}` + a}\0\0`' + clean = emptyString(str) + expect(clean).toMatch(res) + + str = 'const a = `aaaa' + res = '' + try { + clean = emptyString(str) + } catch {} + expect(clean).toMatch(res) +}) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index f623897be5c78b..fd94bfdab7da56 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -1,3 +1,4 @@ +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. @@ -14,10 +15,15 @@ export function emptyCommentsString(raw: string): string { } export function emptyString(raw: string): string { - const res = raw.replace(cleanerRE, (s: string) => + let res = raw.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) + // TODO replace string template + const start = res.indexOf('`') + const [clean, end] = lexStringTemplateExpression(res, start) + res = replaceAt(res, start, end, clean) + return res } @@ -30,3 +36,103 @@ export function findEmptyStringRawIndex( const flagEndIndex = flagIndex + emptyFlag.length return [flagIndex, flagEndIndex] } + +const enum LexerState { + inTemplateString, + inInterpolationExpression, + inObjectExpression +} + +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 +} From 7600e2a3412145ec7746cee46a071b80b608431e Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 21:59:31 +0800 Subject: [PATCH 17/26] feat: test --- .../src/node/__tests__/cleanString.spec.ts | 251 +++++++++--------- packages/vite/src/node/cleanString.ts | 11 +- 2 files changed, 138 insertions(+), 124 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 593a8b7f282458..d96145fa43960f 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -1,142 +1,153 @@ import { emptyString, findEmptyStringRawIndex } 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('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 a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) -// expect(str.slice(a[0], a[1])).toMatch('aaaaa') - -// const bIndex = str.indexOf('const b = "bbbbb"') -// const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) -// expect(str.slice(b[0], b[1])).toMatch('bbbbb') -// }) - -// test case -// `${ ( a = {}) }` -// -// `##${a + b + `##${c + `##${d}`}##`}##` +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('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 a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) + expect(str.slice(a[0], a[1])).toMatch('aaaaa') + + const bIndex = str.indexOf('const b = "bbbbb"') + const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) + expect(str.slice(b[0], b[1])).toMatch('bbbbb') +}) test('template string nested', () => { - let str = 'const a = `aaaa`' - let res = 'const a = `\0\0\0\0`' + let str = '`aaaa`' + let res = '`\0\0\0\0`' let clean = emptyString(str) expect(clean).toMatch(res) - str = 'const a = `aa${a}aa`' - res = 'const a = `\0\0${a}\0\0`' + 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}aa`' + res = '`\0\0${a}\0\0`' clean = emptyString(str) expect(clean).toMatch(res) - str = 'const a = `aa${a + `a` + a}aa`' - res = 'const a = `\0\0${a + `\0` + a}\0\0`' + str = '`aa${a + `a` + a}aa`' + res = '`\0\0${a + `\0` + a}\0\0`' clean = emptyString(str) expect(clean).toMatch(res) - str = 'const a = `aa${a + `aaaa${c + (a = {b: 1}) + d}` + a}aa`' - res = 'const a = `\0\0${a + `\0\0\0\0${c + (a = {b: 1}) + d}` + a}\0\0`' + 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 = 'const a = `aaaa' + str = '`aaaa' res = '' try { clean = emptyString(str) } catch {} expect(clean).toMatch(res) + + str = + "" + res = `` + clean = emptyString(str) + expect(clean).toMatch(res) }) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index fd94bfdab7da56..b010a084b57843 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -19,10 +19,13 @@ export function emptyString(raw: string): string { s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) - // TODO replace string template - const start = res.indexOf('`') - const [clean, end] = lexStringTemplateExpression(res, start) - res = replaceAt(res, start, end, clean) + 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 } From bcfaf7d7407b8bdcbfeb2947af3458faa1bc27cb Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 22:08:47 +0800 Subject: [PATCH 18/26] chore: repeat test --- packages/vite/src/node/__tests__/cleanString.spec.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index d96145fa43960f..720f703ab0a2ca 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -123,11 +123,6 @@ test('template string nested', () => { 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) From 98832fd40e6f7a1d22a3c026e79d5de19f46b1e1 Mon Sep 17 00:00:00 2001 From: yoho Date: Sun, 10 Apr 2022 22:09:56 +0800 Subject: [PATCH 19/26] feat: test mulite string template test in one --- packages/vite/src/node/__tests__/cleanString.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 720f703ab0a2ca..f1808574caddb2 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -128,11 +128,23 @@ test('template string nested', () => { 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 { From 0dacca4979e7b4aaa7b403f026b1ec67a3228fb3 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 09:08:13 +0800 Subject: [PATCH 20/26] feat: revert string template --- .../src/node/__tests__/cleanString.spec.ts | 52 -------- packages/vite/src/node/cleanString.ts | 112 +----------------- 2 files changed, 1 insertion(+), 163 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index f1808574caddb2..4ae6871188ab25 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -106,55 +106,3 @@ test('find empty string flag in raw index', () => { const b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) expect(str.slice(b[0], b[1])).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 = - "" - res = `` - clean = emptyString(str) - expect(clean).toMatch(res) -}) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index b010a084b57843..c26816dc46d12a 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -1,4 +1,3 @@ -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. @@ -15,18 +14,9 @@ export function emptyCommentsString(raw: string): string { } export function emptyString(raw: string): string { - let res = raw.replace(cleanerRE, (s: string) => + const 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 } @@ -39,103 +29,3 @@ export function findEmptyStringRawIndex( const flagEndIndex = flagIndex + emptyFlag.length return [flagIndex, flagEndIndex] } - -const enum LexerState { - inTemplateString, - inInterpolationExpression, - inObjectExpression -} - -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 -} From a74bf6c128ff8a6101c4c788cdbd3bf73d06a263 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 12:19:53 +0800 Subject: [PATCH 21/26] chore: clean code --- packages/vite/src/node/cleanString.ts | 9 +-------- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 3 ++- packages/vite/src/node/plugins/workerImportMetaUrl.ts | 6 +++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index c26816dc46d12a..93f7c5abb40b12 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -7,17 +7,10 @@ const blankReplacer = (s: string) => ' '.repeat(s.length) const stringBlankReplacer = (s: string) => `${s[0]}${'\0'.repeat(s.length - 2)}${s[0]}` -export function emptyCommentsString(raw: string): string { - return raw.replace(cleanerRE, (s: string) => - s[0] === '/' ? blankReplacer(s) : s - ) -} - export function emptyString(raw: string): string { - const res = raw.replace(cleanerRE, (s: string) => + return raw.replace(cleanerRE, (s: string) => s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) - return res } export function findEmptyStringRawIndex( diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 7e6a062de4a969..32d06ea48c7b35 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -25,10 +25,11 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { code.includes(`import.meta.url`) ) { let s: MagicString | undefined - let match: RegExpExecArray | null const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*,?\s*\)/g const cleanString = emptyString(code) + + let match: RegExpExecArray | null while ((match = assetImportMetaUrlRE.exec(cleanString))) { const { 0: exp, 1: emptyUrl, index } = match diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index eb96b6686f23aa..d0bb0972a7422d 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -44,8 +44,8 @@ function getWorkerType(raw: string, clean: string, i: number): WorkerType { } // need to find in no comment code - const cleanworkerOptString = clean.substring(commaIndex + 1, endIndex) - if (!cleanworkerOptString.trim().length) { + const cleanWorkerOptString = clean.substring(commaIndex + 1, endIndex) + if (!cleanWorkerOptString.trim().length) { return 'classic' } @@ -110,11 +110,11 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { code.includes('new URL') && code.includes(`import.meta.url`) ) { - let match: RegExpExecArray | null const cleanString = emptyString(code) const workerImportMetaUrlRE = /\bnew\s+(Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/g + let match: RegExpExecArray | null while ((match = workerImportMetaUrlRE.exec(cleanString))) { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index From 44348cb3a10b0796333a61f1dced9d787bffd337 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 14:07:44 +0800 Subject: [PATCH 22/26] chore: nice named --- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 32d06ea48c7b35..6174e063be260e 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -69,9 +69,9 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { // Get final asset URL. Catch error if the file does not exist, // in which we can resort to the initial URL and let it resolve in runtime const builtUrl = await fileToUrl(file, config, this).catch(() => { - const truthExp = code.slice(index, index + exp.length) + const rawExp = code.slice(index, index + exp.length) config.logger.warnOnce( - `\n${truthExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` + `\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) return url }) From 0437df3bce7f1a6f5fc09040e7f445e6c6ab16ab Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 14:37:50 +0800 Subject: [PATCH 23/26] feat: clean code --- packages/vite/src/node/cleanString.ts | 10 ---------- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 9 +++------ packages/vite/src/node/plugins/workerImportMetaUrl.ts | 9 +++------ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/packages/vite/src/node/cleanString.ts b/packages/vite/src/node/cleanString.ts index 93f7c5abb40b12..d26274397124ff 100644 --- a/packages/vite/src/node/cleanString.ts +++ b/packages/vite/src/node/cleanString.ts @@ -12,13 +12,3 @@ export function emptyString(raw: string): string { s[0] === '/' ? blankReplacer(s) : stringBlankReplacer(s) ) } - -export function findEmptyStringRawIndex( - clean: string, - emptyFlag: string, - start: number -): [number, number] { - const flagIndex = clean.indexOf(emptyFlag, start) - const flagEndIndex = flagIndex + emptyFlag.length - return [flagIndex, flagEndIndex] -} diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 6174e063be260e..a9ce4cbc170fe5 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -3,7 +3,7 @@ import MagicString from 'magic-string' import path from 'path' import { fileToUrl } from './asset' import type { ResolvedConfig } from '../config' -import { emptyString, findEmptyStringRawIndex } from '../cleanString' +import { emptyString } from '../cleanString' /** * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL @@ -33,11 +33,8 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { while ((match = assetImportMetaUrlRE.exec(cleanString))) { const { 0: exp, 1: emptyUrl, index } = match - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index - ) + const urlStart = cleanString.indexOf(emptyUrl, index) + const urlEnd = urlStart + emptyUrl.length const rawUrl = code.slice(urlStart, urlEnd) if (!s) s = new MagicString(code) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index d0bb0972a7422d..8b10d4aaa69c3d 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -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, findEmptyStringRawIndex } from '../cleanString' +import { emptyString } from '../cleanString' type WorkerType = 'classic' | 'module' | 'ignore' const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\// @@ -119,11 +119,8 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const { 0: allExp, 2: exp, 3: emptyUrl, index } = match const urlIndex = allExp.indexOf(exp) + index - const [urlStart, urlEnd] = findEmptyStringRawIndex( - cleanString, - emptyUrl, - index - ) + const urlStart = cleanString.indexOf(emptyUrl, index) + const urlEnd = urlStart + emptyUrl.length const rawUrl = code.slice(urlStart, urlEnd) if (options?.ssr) { From cce2d7aebdd046f4e80c32b03db62433f91c354f Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 14:50:50 +0800 Subject: [PATCH 24/26] fix: test --- packages/vite/src/node/__tests__/cleanString.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/__tests__/cleanString.spec.ts b/packages/vite/src/node/__tests__/cleanString.spec.ts index 4ae6871188ab25..883dc67abfe2f9 100644 --- a/packages/vite/src/node/__tests__/cleanString.spec.ts +++ b/packages/vite/src/node/__tests__/cleanString.spec.ts @@ -1,4 +1,4 @@ -import { emptyString, findEmptyStringRawIndex } from '../../node/cleanString' +import { emptyString } from '../../node/cleanString' test('comments', () => { expect( @@ -99,10 +99,10 @@ test('find empty string flag in raw index', () => { expect(clean).toMatch('const b = "\0\0\0\0\0"') const aIndex = str.indexOf('const a = "aaaaa"') - const a = findEmptyStringRawIndex(clean, '\0\0\0\0\0', aIndex) - expect(str.slice(a[0], a[1])).toMatch('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 b = findEmptyStringRawIndex(clean, '\0\0\0\0\0', bIndex) - expect(str.slice(b[0], b[1])).toMatch('bbbbb') + const bStart = clean.indexOf('\0\0\0\0\0', bIndex) + expect(str.slice(bStart, bStart + 5)).toMatch('bbbbb') }) From fc0b75d792aa9175b52a7328d304e16d51c8238f Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 17:07:59 +0800 Subject: [PATCH 25/26] chore: return null --- packages/vite/src/node/plugins/assetImportMetaUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index a9ce4cbc170fe5..b8c16f76d2b93f 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -85,8 +85,8 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { map: config.build.sourcemap ? s.generateMap({ hires: true }) : null } } - return null } + return null } } } From 220bfc038bded8172e9a1e95601348c4f4076450 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 11 Apr 2022 17:10:13 +0800 Subject: [PATCH 26/26] chore: return null --- .../vite/src/node/plugins/workerImportMetaUrl.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 8b10d4aaa69c3d..c8ab20fe21694c 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -157,15 +157,16 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { contentOnly: true }) } - } - if (s) { - return { - code: s.toString(), - map: config.build.sourcemap ? s.generateMap({ hires: true }) : null + if (s) { + return { + code: s.toString(), + map: config.build.sourcemap ? s.generateMap({ hires: true }) : null + } } + + return null } - return null } } }