mirror of
https://github.com/vitejs/vite.git
synced 2024-11-21 22:59:10 +00:00
feat(css)!: remove css default export
This commit is contained in:
parent
9594c7021b
commit
b6c44cd693
48
packages/vite/client.d.ts
vendored
48
packages/vite/client.d.ts
vendored
@ -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
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -327,7 +327,6 @@ function esbuildScanPlugin(
|
||||
id,
|
||||
config.root,
|
||||
resolve,
|
||||
config.isProduction,
|
||||
)
|
||||
|
||||
return result?.s.toString() || transpiledContents
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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`
|
||||
|
@ -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>
|
||||
|
@ -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')
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user