From 18900fdd39d65da77846c01297e497995e234ece Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 10 Oct 2023 16:52:34 +0200 Subject: [PATCH] refactor(css): make `getEmptyChunkReplacer` for unit test (#14528) Signed-off-by: Ferdinand Thiessen Co-authored-by: bluwy --- .../src/node/__tests__/plugins/css.spec.ts | 55 +++++++++++++++++++ packages/vite/src/node/plugins/css.ts | 50 ++++++++++++----- 2 files changed, 91 insertions(+), 14 deletions(-) diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts index cccdda396f8df6..d206e5b73e90d1 100644 --- a/packages/vite/src/node/__tests__/plugins/css.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts @@ -7,6 +7,7 @@ import { convertTargets, cssPlugin, cssUrlRE, + getEmptyChunkReplacer, hoistAtRules, } from '../../plugins/css' @@ -258,3 +259,57 @@ describe('convertTargets', () => { }) }) }) + +describe('getEmptyChunkReplacer', () => { + test('replaces import call', () => { + const code = `\ +import "some-module"; +import "pure_css_chunk.js"; +import "other-module";` + + const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'es') + const replaced = replacer(code) + expect(replaced.length).toBe(code.length) + expect(replaced).toMatchInlineSnapshot(` + "import \\"some-module\\"; + /* empty css */import \\"other-module\\";" + `) + }) + + test('replaces import call without new lines', () => { + const code = `import "some-module";import "pure_css_chunk.js";import "other-module";` + + const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'es') + const replaced = replacer(code) + expect(replaced.length).toBe(code.length) + expect(replaced).toMatchInlineSnapshot( + '"import \\"some-module\\";/* empty css */import \\"other-module\\";"', + ) + }) + + test('replaces require call', () => { + const code = `\ +require("some-module"); +require("pure_css_chunk.js"); +require("other-module");` + + const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs') + const replaced = replacer(code) + expect(replaced.length).toBe(code.length) + expect(replaced).toMatchInlineSnapshot(` + "require(\\"some-module\\"); + /* empty css */require(\\"other-module\\");" + `) + }) + + test('replaces require call in minified code without new lines', () => { + const code = `require("some-module");require("pure_css_chunk.js");require("other-module");` + + const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs') + const replaced = replacer(code) + expect(replaced.length).toBe(code.length) + expect(replaced).toMatchInlineSnapshot( + '"require(\\"some-module\\");/* empty css */require(\\"other-module\\");"', + ) + }) +}) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 85ba5eea8e5b89..133370be37036d 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -6,6 +6,7 @@ import glob from 'fast-glob' import postcssrc from 'postcss-load-config' import type { ExistingRawSourceMap, + ModuleFormat, OutputChunk, RenderedChunk, RollupError, @@ -735,16 +736,11 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { (pureCssChunk) => prelimaryNameToChunkMap[pureCssChunk.fileName], ) - const emptyChunkFiles = pureCssChunkNames - .map((file) => path.basename(file)) - .join('|') - .replace(/\./g, '\\.') - const emptyChunkRE = new RegExp( - opts.format === 'es' - ? `\\bimport\\s*["'][^"']*(?:${emptyChunkFiles})["'];\n?` - : `\\brequire\\(\\s*["'][^"']*(?:${emptyChunkFiles})["']\\);\n?`, - 'g', + const replaceEmptyChunk = getEmptyChunkReplacer( + pureCssChunkNames, + opts.format, ) + for (const file in bundle) { const chunk = bundle[file] if (chunk.type === 'chunk') { @@ -766,13 +762,10 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } return true }) - chunk.code = chunk.code.replace( - emptyChunkRE, - // remove css import while preserving source map location - (m) => `/* empty css ${''.padEnd(m.length - 15)}*/`, - ) + chunk.code = replaceEmptyChunk(chunk.code) } } + const removedPureCssFiles = removedPureCssFilesCache.get(config)! pureCssChunkNames.forEach((fileName) => { removedPureCssFiles.set(fileName, bundle[fileName] as RenderedChunk) @@ -818,6 +811,35 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } } +/** + * Create a replacer function that takes code and replaces given pure CSS chunk imports + * @param pureCssChunkNames The chunks that only contain pure CSS and should be replaced + * @param outputFormat The module output format to decide whether to replace `import` or `require` + */ +export function getEmptyChunkReplacer( + pureCssChunkNames: string[], + outputFormat: ModuleFormat, +): (code: string) => string { + const emptyChunkFiles = pureCssChunkNames + .map((file) => path.basename(file)) + .join('|') + .replace(/\./g, '\\.') + + const emptyChunkRE = new RegExp( + outputFormat === 'es' + ? `\\bimport\\s*["'][^"']*(?:${emptyChunkFiles})["'];\n?` + : `\\brequire\\(\\s*["'][^"']*(?:${emptyChunkFiles})["']\\);\n?`, + 'g', + ) + + return (code: string) => + code.replace( + emptyChunkRE, + // remove css import while preserving source map location + (m) => `/* empty css ${''.padEnd(m.length - 15)}*/`, + ) +} + interface CSSAtImportResolvers { css: ResolveFn sass: ResolveFn