From d87a0475b76e0ec799403165f9337f4b8c80ef27 Mon Sep 17 00:00:00 2001 From: sapphi-red Date: Wed, 16 Aug 2023 20:54:32 +0900 Subject: [PATCH 1/6] feat(css)!: remove css default export --- packages/vite/client.d.ts | 48 ++--------- .../plugins/importGlob/fixture.test.ts | 13 +-- packages/vite/src/node/optimizer/scan.ts | 1 - packages/vite/src/node/plugins/css.ts | 29 +++---- .../vite/src/node/plugins/importAnalysis.ts | 31 +------ .../vite/src/node/plugins/importMetaGlob.ts | 83 ++----------------- playground/css/__tests__/css.spec.ts | 32 +------ playground/css/main.js | 7 +- .../css/postcss-caching/blue-app/main.js | 3 +- .../css/postcss-caching/green-app/main.js | 3 +- .../glob-import/__tests__/glob-import.spec.ts | 38 +-------- playground/glob-import/index.html | 11 ++- playground/resolve/index.html | 2 +- 13 files changed, 46 insertions(+), 255 deletions(-) diff --git a/packages/vite/client.d.ts b/packages/vite/client.d.ts index b73389ec373f1f..b03e94768785af 100644 --- a/packages/vite/client.d.ts +++ b/packages/vite/client.d.ts @@ -38,60 +38,28 @@ declare module '*.module.sss' { // CSS declare module '*.css' { - /** - * @deprecated Use `import style from './style.css?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.scss' { - /** - * @deprecated Use `import style from './style.scss?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.sass' { - /** - * @deprecated Use `import style from './style.sass?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.less' { - /** - * @deprecated Use `import style from './style.less?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.styl' { - /** - * @deprecated Use `import style from './style.styl?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.stylus' { - /** - * @deprecated Use `import style from './style.stylus?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.pcss' { - /** - * @deprecated Use `import style from './style.pcss?inline'` instead. - */ - const css: string - export default css + export {} } declare module '*.sss' { - /** - * @deprecated Use `import style from './style.sss?inline'` instead. - */ - const css: string - export default css + export {} } // Built-in asset types diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts index 22bacfa77edf6e..6179b53cad35b6 100644 --- a/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts +++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts @@ -18,17 +18,13 @@ describe('fixture', async () => { ).code expect( - ( - await transformGlobImport(code, id, root, resolveId, true) - )?.s.toString(), + (await transformGlobImport(code, id, root, resolveId))?.s.toString(), ).toMatchSnapshot() }) it('preserve line count', async () => { const getTransformedLineCount = async (code: string) => - ( - await transformGlobImport(code, 'virtual:module', root, resolveId, true) - )?.s + (await transformGlobImport(code, 'virtual:module', root, resolveId))?.s .toString() .split('\n').length @@ -52,7 +48,7 @@ describe('fixture', async () => { ].join('\n') expect( ( - await transformGlobImport(code, 'virtual:module', root, resolveId, true) + await transformGlobImport(code, 'virtual:module', root, resolveId) )?.s.toString(), ).toMatchSnapshot() @@ -62,7 +58,6 @@ describe('fixture', async () => { 'virtual:module', root, resolveId, - true, ) expect('no error').toBe('should throw an error') } catch (err) { @@ -80,7 +75,7 @@ describe('fixture', async () => { expect( ( - await transformGlobImport(code, id, root, resolveId, true, true) + await transformGlobImport(code, id, root, resolveId, true) )?.s.toString(), ).toMatchSnapshot() }) diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 1a4dd46c61ccad..b27f8e0e8962fd 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -327,7 +327,6 @@ function esbuildScanPlugin( id, config.root, resolve, - config.isProduction, ) return result?.s.toString() || transpiledContents diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 3b5a8a6d09a0e6..73fa453d5d262d 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -170,7 +170,6 @@ const commonjsProxyRE = /\?commonjs-proxy/ const inlineRE = /[?&]inline\b/ const inlineCSSRE = /[?&]inline-css\b/ const styleAttrRE = /[?&]style-attr\b/ -const usedRE = /[?&]used\b/ const varRE = /^var\(/i const cssBundleName = 'style.css' @@ -473,10 +472,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { `const __vite__css = ${JSON.stringify(cssContent)}`, `__vite__updateStyle(__vite__id, __vite__css)`, // css modules exports change on edit so it can't self accept - `${ - modulesCode || - `import.meta.hot.accept()\nexport default __vite__css` - }`, + `${modulesCode || 'import.meta.hot.accept()'}`, `import.meta.hot.prune(() => __vite__removeStyle(__vite__id))`, ].join('\n') return { code, map: { mappings: '' } } @@ -505,22 +501,17 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } let code: string - if (usedRE.test(id)) { - if (modulesCode) { - code = modulesCode - } else { - let content = css - if (config.build.cssMinify) { - content = await minifyCSS(content, config, true) - } - code = `export default ${JSON.stringify(content)}` + if (modulesCode) { + code = modulesCode + } else if (inlined) { + let content = css + if (config.build.cssMinify) { + content = await minifyCSS(content, config, true) } + code = `export default ${JSON.stringify(content)}` } else { - // if moduleCode exists return it **even if** it does not have `?used` - // this will disable tree-shake to work with `import './foo.module.css'` but this usually does not happen - // this is a limitation of the current approach by `?used` to make tree-shake work - // See #8936 for more details - code = modulesCode || `export default ''` + // empty module when it's not a CSS module nor `?inline` + code = 'export {}' } return { diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index c33cd519fb4da7..57f409b49c55be 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -60,7 +60,7 @@ import { ERR_OUTDATED_OPTIMIZED_DEP, throwOutdatedRequest, } from './optimizedDeps' -import { isCSSRequest, isDirectCSSRequest, isModuleCSSRequest } from './css' +import { isCSSRequest, isDirectCSSRequest } from './css' import { browserExternalId } from './resolve' const debug = createDebugger('vite:import-analysis') @@ -527,35 +527,6 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // normalize const [url, resolvedId] = await normalizeUrl(specifier, start) - if ( - !isDynamicImport && - specifier && - !specifier.includes('?') && // ignore custom queries - isCSSRequest(resolvedId) && - !isModuleCSSRequest(resolvedId) - ) { - const sourceExp = source.slice(expStart, start) - if ( - sourceExp.includes('from') && // check default and named imports - !sourceExp.includes('__vite_glob_') // glob handles deprecation message itself - ) { - const newImport = - sourceExp + specifier + `?inline` + source.slice(end, expEnd) - this.warn( - `\n` + - colors.cyan(importerModule.file) + - `\n` + - colors.reset(generateCodeFrame(source, start)) + - `\n` + - colors.yellow( - `Default and named imports from CSS files are deprecated. ` + - `Use the ?inline query instead. ` + - `For example: ${newImport}`, - ), - ) - } - } - // record as safe modules // safeModulesPath should not include the base prefix. // See https://github.com/vitejs/vite/issues/9438#issuecomment-1465270409 diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 60172e0de3a143..54c7b16c80ca19 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -29,7 +29,6 @@ import { slash, transformStableResult, } from '../utils' -import { isCSSRequest, isModuleCSSRequest } from './css' const { isMatch, scan } = micromatch @@ -86,7 +85,6 @@ export function importGlobPlugin(config: ResolvedConfig): Plugin { config.root, (im, _, options) => this.resolve(im, id, options).then((i) => i?.id || im), - config.isProduction, config.experimental.importGlobRestoreExtension, ) if (result) { @@ -338,24 +336,6 @@ const importPrefix = '__vite_glob_' const { basename, dirname, relative, join } = posix -const warnedCSSDefaultImportVarName = '__vite_warned_css_default_import' -const jsonStringifyInOneline = (input: any) => - JSON.stringify(input).replace(/[{,:]/g, '$& ').replace(/\}/g, ' }') -const createCssDefaultImportWarning = ( - globs: string[], - options: GeneralImportGlobOptions, -) => - `if (!${warnedCSSDefaultImportVarName}) {` + - `${warnedCSSDefaultImportVarName} = true;` + - `console.warn(${JSON.stringify( - 'Default import of CSS without `?inline` is deprecated. ' + - "Add the `{ query: '?inline' }` glob option to fix this.\n" + - `For example: \`import.meta.glob(${jsonStringifyInOneline( - globs.length === 1 ? globs[0] : globs, - )}, ${jsonStringifyInOneline({ ...options, query: '?inline' })})\``, - )});` + - `}` - export interface TransformGlobImportResult { s: MagicString matches: ParsedImportGlob[] @@ -370,7 +350,6 @@ export async function transformGlobImport( id: string, root: string, resolveId: IdResolver, - isProduction: boolean, restoreQueryExtension = false, ): Promise { id = slash(id) @@ -401,15 +380,7 @@ export async function transformGlobImport( const staticImports = ( await Promise.all( matches.map( - async ({ - globs, - globsResolved, - isRelative, - options, - index, - start, - end, - }) => { + async ({ globsResolved, isRelative, options, index, start, end }) => { const cwd = getCommonBase(globsResolved) ?? root const files = ( await fg(globsResolved, { @@ -459,7 +430,6 @@ export async function transformGlobImport( return { filePath, importPath } } - let includesCSS = false files.forEach((file, i) => { const paths = resolvePaths(file) const filePath = paths.filePath @@ -474,10 +444,6 @@ export async function transformGlobImport( importPath = `${importPath}${importQuery}` - const isCSS = - !query && isCSSRequest(file) && !isModuleCSSRequest(file) - includesCSS ||= isCSS - const importKey = options.import && options.import !== '*' ? options.import @@ -491,36 +457,14 @@ export async function transformGlobImport( staticImports.push( `import ${expression} from ${JSON.stringify(importPath)}`, ) - if (!isProduction && isCSS) { - objectProps.push( - `get ${JSON.stringify( - filePath, - )}() { ${createCssDefaultImportWarning( - globs, - options, - )} return ${variableName} }`, - ) - } else { - objectProps.push(`${JSON.stringify(filePath)}: ${variableName}`) - } + objectProps.push(`${JSON.stringify(filePath)}: ${variableName}`) } else { let importStatement = `import(${JSON.stringify(importPath)})` if (importKey) importStatement += `.then(m => m[${JSON.stringify(importKey)}])` - if (!isProduction && isCSS) { - objectProps.push( - `${JSON.stringify( - filePath, - )}: () => { ${createCssDefaultImportWarning( - globs, - options, - )} return ${importStatement}}`, - ) - } else { - objectProps.push( - `${JSON.stringify(filePath)}: () => ${importStatement}`, - ) - } + objectProps.push( + `${JSON.stringify(filePath)}: () => ${importStatement}`, + ) } }) @@ -533,20 +477,9 @@ export async function transformGlobImport( ? '\n'.repeat(originalLineBreakCount) : '' - let replacement: string - if (!isProduction && includesCSS) { - replacement = - '/* #__PURE__ */ Object.assign(' + - '(() => {' + - `let ${warnedCSSDefaultImportVarName} = false;` + - `return {${objectProps.join(',')}${lineBreaks}};` + - '})()' + - ')' - } else { - replacement = `/* #__PURE__ */ Object.assign({${objectProps.join( - ',', - )}${lineBreaks}})` - } + const replacement = `/* #__PURE__ */ Object.assign({${objectProps.join( + ',', + )}${lineBreaks}})` s.overwrite(start, end, replacement) return staticImports diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index 5c85f24fe8820b..a08f50763d88f5 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -1,12 +1,11 @@ import { readFileSync } from 'node:fs' -import { describe, expect, test } from 'vitest' +import { expect, test } from 'vitest' import { editFile, findAssetFile, getBg, getColor, isBuild, - isServe, page, removeFile, serverLogs, @@ -24,14 +23,6 @@ test('imported css', async () => { expect(globEager).toContain('.dir-import') }) -test('inline imported css', async () => { - const css = await page.textContent('.imported-css') - expect(css).toMatch(/\.imported ?\{/) - if (isBuild) { - expect(css.trim()).not.toContain('\n') // check minified - } -}) - test('linked css', async () => { const linked = await page.$('.linked') const atImport = await page.$('.linked-at-import') @@ -463,28 +454,11 @@ test('PostCSS source.input.from includes query', async () => { // should resolve assets expect(code).toContain( isBuild - ? '/postcss-source-input.css?used&query=foo' - : '/postcss-source-input.css?query=foo', + ? '/postcss-source-input.css?used&inline&query=foo' + : '/postcss-source-input.css?inline&query=foo', ) }) -describe.runIf(isServe)('deprecate default/named imports from CSS', () => { - test('css file', () => { - const actual = serverLogs.some((log) => - /Use the \?inline query instead.+imported\.css/.test(log), - ) - expect(actual).toBe(true) - }) - - test('js file ending with .css.js', async () => { - const message = await page.textContent('.jsfile-css-js') - expect(message).toMatch('from jsfile.css.js') - serverLogs.forEach((log) => { - expect(log).not.toMatch(/Use the \?inline query instead.+jsfile\.css/) - }) - }) -}) - test('aliased css has content', async () => { expect(await getColor('.aliased')).toBe('blue') // skipped: currently not supported see #8936 diff --git a/playground/css/main.js b/playground/css/main.js index 4664ee1d2d8dde..62fc9f4d2fe8ff 100644 --- a/playground/css/main.js +++ b/playground/css/main.js @@ -1,15 +1,10 @@ import './minify.css' -// eslint-disable-next-line import/no-duplicates import './imported.css' import './sugarss.sss' import './sass.scss' import './less.less' import './stylus.styl' -// eslint-disable-next-line import/no-duplicates -import css from './imported.css' -text('.imported-css', css) // deprecated, but leave this as-is to make sure it works - import rawCss from './raw-imported.css?raw' text('.raw-imported-css', rawCss) @@ -99,7 +94,7 @@ const globEager = import.meta.glob('./glob-import/*.css', { }) text('.imported-css-globEager', JSON.stringify(globEager, null, 2)) -import postcssSourceInput from './postcss-source-input.css?query=foo' +import postcssSourceInput from './postcss-source-input.css?inline&query=foo' text('.postcss-source-input', postcssSourceInput) // The file is jsfile.css.js, and we should be able to import it without extension diff --git a/playground/css/postcss-caching/blue-app/main.js b/playground/css/postcss-caching/blue-app/main.js index 53286e882053fa..8556576f10e5f3 100644 --- a/playground/css/postcss-caching/blue-app/main.js +++ b/playground/css/postcss-caching/blue-app/main.js @@ -1,4 +1,5 @@ -import css from './imported.css' +import './imported.css' +import css from './imported.css?inline' text('.imported-css', css) function text(el, text) { diff --git a/playground/css/postcss-caching/green-app/main.js b/playground/css/postcss-caching/green-app/main.js index 53286e882053fa..8556576f10e5f3 100644 --- a/playground/css/postcss-caching/green-app/main.js +++ b/playground/css/postcss-caching/green-app/main.js @@ -1,4 +1,5 @@ -import css from './imported.css' +import './imported.css' +import css from './imported.css?inline' text('.imported-css', css) function text(el, text) { diff --git a/playground/glob-import/__tests__/glob-import.spec.ts b/playground/glob-import/__tests__/glob-import.spec.ts index 239278477eea2c..fd5215bd2bc39f 100644 --- a/playground/glob-import/__tests__/glob-import.spec.ts +++ b/playground/glob-import/__tests__/glob-import.spec.ts @@ -5,14 +5,10 @@ import { addFile, editFile, findAssetFile, - getColor, isBuild, - isServe, page, removeFile, - untilBrowserLogAfter, untilUpdated, - viteTestUrl, withRetry, } from '~utils' @@ -47,13 +43,7 @@ const allResult = { default: 'hi', }, '/dir/baz.json': json, - '/dir/foo.css': isBuild - ? { - default: '.foo{color:#00f}', - } - : { - default: '.foo {\n color: blue;\n}\n', - }, + '/dir/foo.css': {}, '/dir/foo.js': { msg: 'foo', }, @@ -216,8 +206,6 @@ if (!isBuild) { } test('tree-shake eager css', async () => { - expect(await getColor('.tree-shake-eager-css')).toBe('orange') - expect(await getColor('.no-tree-shake-eager-css')).toBe('orange') expect(await page.textContent('.no-tree-shake-eager-css-result')).toMatch( '.no-tree-shake-eager-css', ) @@ -228,30 +216,6 @@ test('tree-shake eager css', async () => { } }) -test('warn CSS default import', async () => { - const logs = await untilBrowserLogAfter( - () => page.goto(viteTestUrl), - 'Ran scripts', - ) - const noTreeshakeCSSMessage = - 'For example: `import.meta.glob("/no-tree-shake.css", { "eager": true, "query": "?inline" })`' - const treeshakeCSSMessage = - 'For example: `import.meta.glob("/tree-shake.css", { "eager": true, "query": "?inline" })`' - - expect( - logs.some((log) => log.includes(noTreeshakeCSSMessage)), - `expected logs to include a message including ${JSON.stringify( - noTreeshakeCSSMessage, - )}`, - ).toBe(isServe) - expect( - logs.every((log) => !log.includes(treeshakeCSSMessage)), - `expected logs not to include a message including ${JSON.stringify( - treeshakeCSSMessage, - )}`, - ).toBe(true) -}) - test('escapes special chars in globs without mangling user supplied glob suffix', async () => { // the escape dir contains subdirectories where each has a name that needs escaping for glob safety // inside each of them is a glob.js that exports the result of a relative glob `./**/*.js` diff --git a/playground/glob-import/index.html b/playground/glob-import/index.html index 3b72430668e14d..8c0c9b607f7b6d 100644 --- a/playground/glob-import/index.html +++ b/playground/glob-import/index.html @@ -121,8 +121,11 @@

In package

@@ -156,7 +159,3 @@

In package

- - diff --git a/playground/resolve/index.html b/playground/resolve/index.html index 64d9306266a68d..026d79fd33c844 100644 --- a/playground/resolve/index.html +++ b/playground/resolve/index.html @@ -349,7 +349,7 @@

resolve package that contains # in path

import '@vitejs/test-resolve-browser-field/multiple.dot.path' // css entry - import css from 'normalize.css' + import css from 'normalize.css/normalize.css?inline' if (typeof css === 'string') { text('.css', '[success] resolve package with css entry file') } From ded43c971eb94e2a597cbf19b67461a3f936c380 Mon Sep 17 00:00:00 2001 From: sapphi-red Date: Wed, 16 Aug 2023 21:27:21 +0900 Subject: [PATCH 2/6] feat(css): stop injecting `?used` --- .../src/node/plugins/importAnalysisBuild.ts | 25 +------------------ playground/css/__tests__/css.spec.ts | 6 +---- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 1d046eb49f581e..b602de8c4c760d 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -7,7 +7,6 @@ import colors from 'picocolors' import type { RawSourceMap } from '@ampproject/remapping' import convertSourceMap from 'convert-source-map' import { - bareImportRE, cleanUrl, combineSourcemaps, generateCodeFrame, @@ -23,8 +22,7 @@ import type { ResolvedConfig } from '../config' import { toOutputFilePathInJS } from '../build' import { genSourceMapUrl } from '../server/sourcemap' import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer' -import { SPECIAL_QUERY_RE } from '../constants' -import { isCSSRequest, removedPureCssFilesCache } from './css' +import { removedPureCssFilesCache } from './css' import { interopNamedImports } from './importAnalysis' /** @@ -379,27 +377,6 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { } } } - - // Differentiate CSS imports that use the default export from those that - // do not by injecting a ?used query - this allows us to avoid including - // the CSS string when unnecessary (esbuild has trouble tree-shaking - // them) - if ( - specifier && - isCSSRequest(specifier) && - // always inject ?used query when it is a dynamic import - // because there is no way to check whether the default export is used - (source.slice(expStart, start).includes('from') || isDynamicImport) && - // already has ?used query (by import.meta.glob) - !specifier.match(/\?used(&|$)/) && - // don't append ?used when SPECIAL_QUERY_RE exists - !specifier.match(SPECIAL_QUERY_RE) && - // edge case for package names ending with .css (e.g normalize.css) - !(bareImportRE.test(specifier) && !specifier.includes('/')) - ) { - const url = specifier.replace(/\?|$/, (m) => `?used${m ? '&' : ''}`) - str().update(start, end, isDynamicImport ? `'${url}'` : url) - } } if ( diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index a08f50763d88f5..a33377295faf29 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -452,11 +452,7 @@ test("relative path rewritten in Less's data-uri", async () => { test('PostCSS source.input.from includes query', async () => { const code = await page.textContent('.postcss-source-input') // should resolve assets - expect(code).toContain( - isBuild - ? '/postcss-source-input.css?used&inline&query=foo' - : '/postcss-source-input.css?inline&query=foo', - ) + expect(code).toContain('/postcss-source-input.css?inline&query=foo') }) test('aliased css has content', async () => { From 7ca3597e91f89921fa52b12d689a90c2ec8e8743 Mon Sep 17 00:00:00 2001 From: sapphi-red Date: Sun, 20 Aug 2023 00:46:50 +0900 Subject: [PATCH 3/6] docs: update about CSS default exports --- docs/guide/features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/features.md b/docs/guide/features.md index 392bafb8569509..598c05c654c88a 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -178,7 +178,7 @@ export default defineConfig({ ## CSS -Importing `.css` files will inject its content to the page via a `