feat(css)!: remove css default export

This commit is contained in:
sapphi-red 2023-08-16 20:54:32 +09:00 committed by 翠 / green
parent 9594c7021b
commit b6c44cd693
13 changed files with 46 additions and 255 deletions

View File

@ -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

View File

@ -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()
})

View File

@ -327,7 +327,6 @@ function esbuildScanPlugin(
id,
config.root,
resolve,
config.isProduction,
)
return result?.s.toString() || transpiledContents

View File

@ -164,7 +164,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'
@ -467,10 +466,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: '' } }
@ -499,22 +495,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 {

View File

@ -61,7 +61,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')
@ -528,35 +528,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

View File

@ -29,7 +29,6 @@ import {
slash,
transformStableResult,
} from '../utils'
import { isCSSRequest, isModuleCSSRequest } from './css'
const { isMatch, scan } = micromatch
@ -84,7 +83,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) {
@ -332,24 +330,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[]
@ -364,7 +344,6 @@ export async function transformGlobImport(
id: string,
root: string,
resolveId: IdResolver,
isProduction: boolean,
restoreQueryExtension = false,
): Promise<TransformGlobImportResult | null> {
id = slash(id)
@ -386,15 +365,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, {
@ -444,7 +415,6 @@ export async function transformGlobImport(
return { filePath, importPath }
}
let includesCSS = false
files.forEach((file, i) => {
const paths = resolvePaths(file)
const filePath = paths.filePath
@ -459,10 +429,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
@ -476,36 +442,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}`,
)
}
})
@ -518,20 +462,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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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`

View File

@ -121,8 +121,11 @@
</script>
<script type="module">
import.meta.glob('/tree-shake.css', { eager: true })
const results = import.meta.glob('/no-tree-shake.css', { eager: true })
import.meta.glob('/tree-shake.css', { eager: true, query: { inline: true } })
const results = import.meta.glob('/no-tree-shake.css', {
eager: true,
query: { inline: true },
})
document.querySelector('.no-tree-shake-eager-css-result').textContent =
results['/no-tree-shake.css'].default
</script>
@ -156,7 +159,3 @@
<script type="module">
import '@vitejs/test-import-meta-glob-pkg'
</script>
<script type="module">
console.log('Ran scripts')
</script>

View File

@ -349,7 +349,7 @@
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')
}