From 67e7bf29cec3e76eb69aa19dcaffc4e3ccc598bd Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 20 Oct 2022 15:38:11 +0800 Subject: [PATCH] fix(css): remove `?direct` in id for postcss process (#10514) --- .../src/node/__tests__/plugins/css.spec.ts | 134 +++++++++++------- packages/vite/src/node/plugins/css.ts | 6 +- packages/vite/src/node/utils.ts | 4 + 3 files changed, 91 insertions(+), 53 deletions(-) diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts index efad549a30102f..d951178783073b 100644 --- a/packages/vite/src/node/__tests__/plugins/css.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts @@ -2,6 +2,7 @@ import fs from 'node:fs' import path from 'node:path' import { describe, expect, test, vi } from 'vitest' import { resolveConfig } from '../../config' +import type { InlineConfig } from '../../config' import { cssPlugin, cssUrlRE, hoistAtRules } from '../../plugins/css' describe('search css url function', () => { @@ -46,13 +47,17 @@ describe('search css url function', () => { }) }) -describe('css path resolutions', () => { - const mockedProjectPath = path.join(process.cwd(), '/foo/bar/project') - const mockedBarCssRelativePath = '/css/bar.module.css' - const mockedFooCssRelativePath = '/css/foo.module.css' - - test('cssmodule compose/from path resolutions', async () => { - const config = await resolveConfig( +describe('css modules', () => { + test('css module compose/from path resolutions', async () => { + const mockedProjectPath = path.join(process.cwd(), '/foo/bar/project') + const { transform, resetMock } = await createCssPluginTransform( + { + [path.join(mockedProjectPath, '/css/bar.module.css')]: `\ +.bar { +display: block; +background: #f0f; +}` + }, { resolve: { alias: [ @@ -62,57 +67,48 @@ describe('css path resolutions', () => { } ] } - }, - 'serve' + } ) - const { transform, buildStart } = cssPlugin(config) - - await buildStart.call({}) - - const mockFs = vi - .spyOn(fs, 'readFile') - // @ts-ignore vi.spyOn not recognize override `fs.readFile` definition. - .mockImplementationOnce((p, encoding, callback) => { - expect(p).toBe(path.join(mockedProjectPath, mockedBarCssRelativePath)) - expect(encoding).toBe('utf-8') - callback( - null, - Buffer.from(` -.bar { - display: block; - background: #f0f; -} - `) - ) - }) - - const { code } = await transform.call( - { - addWatchFile() { - return - } - }, - ` + const result = await transform( + `\ .foo { - position: fixed; - composes: bar from '@${mockedBarCssRelativePath}'; -} - `, - path.join(mockedProjectPath, mockedFooCssRelativePath) +position: fixed; +composes: bar from '@/css/bar.module.css'; +}`, + '/css/foo.module.css' ) - expect(code).toBe(` -._bar_soicv_2 { - display: block; - background: #f0f; -} -._foo_sctn3_2 { - position: fixed; + expect(result.code).toBe( + `\ +._bar_1csqm_1 { +display: block; +background: #f0f; } - `) +._foo_86148_1 { +position: fixed; +}` + ) + + resetMock() + }) - mockFs.mockReset() + test('custom generateScopedName', async () => { + const { transform, resetMock } = await createCssPluginTransform(undefined, { + css: { + modules: { + generateScopedName: 'custom__[hash:base64:5]' + } + } + }) + const css = `\ +.foo { + color: red; +}` + const result1 = await transform(css, '/foo.module.css') // server + const result2 = await transform(css, '/foo.module.css?direct') // client + expect(result1.code).toBe(result2.code) + resetMock() }) }) @@ -205,3 +201,39 @@ describe('hoist @ rules', () => { `) }) }) + +async function createCssPluginTransform( + files?: Record, + inlineConfig: InlineConfig = {} +) { + const config = await resolveConfig(inlineConfig, 'serve') + const { transform, buildStart } = cssPlugin(config) + + // @ts-expect-error + await buildStart.call({}) + + const mockFs = vi + .spyOn(fs, 'readFile') + // @ts-expect-error vi.spyOn not recognize override `fs.readFile` definition. + .mockImplementationOnce((p, encoding, callback) => { + callback(null, Buffer.from(files?.[p] ?? '')) + }) + + return { + async transform(code: string, id: string) { + // @ts-expect-error + return await transform.call( + { + addWatchFile() { + return + } + }, + code, + id + ) + }, + resetMock() { + mockFs.mockReset() + } + } +} diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index dad328164d476b..194259ba0e6c96 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -42,6 +42,7 @@ import { normalizePath, parseRequest, processSrcSet, + removeDirectQuery, requireResolveFromRootWithFallback } from '../utils' import type { Logger } from '../logger' @@ -914,13 +915,14 @@ async function compileCSS( let postcssResult: PostCSS.Result try { + const source = removeDirectQuery(id) // postcss is an unbundled dep and should be lazy imported postcssResult = await (await import('postcss')) .default(postcssPlugins) .process(code, { ...postcssOptions, - to: id, - from: id, + to: source, + from: source, ...(devSourcemap ? { map: { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 2711a29b7eb703..47028cc9d8889f 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -303,6 +303,7 @@ export function getPotentialTsSrcPaths(filePath: string): string[] { } const importQueryRE = /(\?|&)import=?(?:&|$)/ +const directRequestRE = /(\?|&)direct=?(?:&|$)/ const internalPrefixes = [ FS_PREFIX, VALID_ID_PREFIX, @@ -318,6 +319,9 @@ export const isInternalRequest = (url: string): boolean => export function removeImportQuery(url: string): string { return url.replace(importQueryRE, '$1').replace(trailingSeparatorRE, '') } +export function removeDirectQuery(url: string): string { + return url.replace(directRequestRE, '$1').replace(trailingSeparatorRE, '') +} export function injectQuery(url: string, queryToInject: string): string { // encode percents for consistent behavior with pathToFileURL