提交 b3cdc137 编写于 作者: fxy060608's avatar fxy060608

chore: update css, asset

上级 c8d90e5d
import { ModuleNode, ViteDevServer as OrigViteDevServer } from 'vite'
export { ResolveFn } from 'vite'
export interface ViteDevServer extends OrigViteDevServer {
_globImporters: Record<
string,
{
module: ModuleNode
importGlobs: {
base: string
pattern: string
}[]
}
>
}
/**
* https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/asset.ts
*/
import path from 'path'
import { parse as parseUrl } from 'url'
import fs, { promises as fsp } from 'fs'
import mime from 'mime/lite'
import { Plugin } from 'vite'
import { ResolvedConfig } from 'vite'
import { Plugin } from '../plugin'
import { ResolvedConfig } from '../config'
import { cleanUrl } from '../utils'
import { FS_PREFIX } from '../constants'
import { PluginContext, RenderedChunk } from 'rollup'
import { OutputOptions, PluginContext, RenderedChunk } from 'rollup'
import MagicString from 'magic-string'
import { createHash } from 'crypto'
......@@ -188,6 +185,82 @@ export function getAssetFilename(
return assetHashToFilenameMap.get(config)?.get(hash)
}
/**
* converts the source filepath of the asset to the output filename based on the assetFileNames option. \
* this function imitates the behavior of rollup.js. \
* https://rollupjs.org/guide/en/#outputassetfilenames
*
* @example
* ```ts
* const content = Buffer.from('text');
* const fileName = assetFileNamesToFileName(
* 'assets/[name].[hash][extname]',
* '/path/to/file.txt',
* getAssetHash(content),
* content
* )
* // fileName: 'assets/file.982d9e3e.txt'
* ```
*
* @param assetFileNames filename pattern. e.g. `'assets/[name].[hash][extname]'`
* @param file filepath of the asset
* @param contentHash hash of the asset. used for `'[hash]'` placeholder
* @param content content of the asset. passed to `assetFileNames` if `assetFileNames` is a function
* @returns output filename
*/
export function assetFileNamesToFileName(
assetFileNames: Exclude<OutputOptions['assetFileNames'], undefined>,
file: string,
contentHash: string,
content: string | Buffer
): string {
const basename = path.basename(file)
// placeholders for `assetFileNames`
// `hash` is slightly different from the rollup's one
const extname = path.extname(basename)
const ext = extname.substr(1)
const name = basename.slice(0, -extname.length)
const hash = contentHash
if (typeof assetFileNames === 'function') {
assetFileNames = assetFileNames({
name: file,
source: content,
type: 'asset',
})
if (typeof assetFileNames !== 'string') {
throw new TypeError('assetFileNames must return a string')
}
} else if (typeof assetFileNames !== 'string') {
throw new TypeError('assetFileNames must be a string or a function')
}
const fileName = assetFileNames.replace(
/\[\w+\]/g,
(placeholder: string): string => {
switch (placeholder) {
case '[ext]':
return ext
case '[extname]':
return extname
case '[hash]':
return hash
case '[name]':
return name
}
throw new Error(
`invalid placeholder ${placeholder} in assetFileNames "${assetFileNames}"`
)
}
)
return fileName
}
/**
* Register an asset to be emitted as part of the bundle (if necessary)
* and returns the resolved public URL
......@@ -231,11 +304,17 @@ async function fileToBuiltUrl(
const contentHash = getAssetHash(content)
const { search, hash } = parseUrl(id)
const postfix = (search || '') + (hash || '')
const basename = path.basename(file)
const ext = path.extname(basename)
const fileName = path.posix.join(
config.build.assetsDir,
`${basename.slice(0, -ext.length)}.${contentHash}${ext}`
const output = config.build?.rollupOptions?.output
const assetFileNames =
(output && !Array.isArray(output) ? output.assetFileNames : undefined) ??
// defaults to '<assetsDir>/[name].[hash][extname]'
// slightly different from rollup's one ('assets/[name]-[hash][extname]')
path.posix.join(config.build.assetsDir, '[name].[hash][extname]')
const fileName = assetFileNamesToFileName(
assetFileNames,
file,
contentHash,
content
)
if (!map.has(contentHash)) {
map.set(contentHash, fileName)
......
/**
* https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/css.ts
*/
import fs from 'fs'
import path from 'path'
import glob from 'fast-glob'
......@@ -15,8 +12,8 @@ import {
normalizePath,
processSrcSet,
} from '../utils'
import { Plugin } from 'vite'
import { ResolvedConfig } from 'vite'
import { Plugin } from '../plugin'
import { ResolvedConfig } from '../config'
import postcssrc from 'postcss-load-config'
import {
NormalizedOutputOptions,
......@@ -28,7 +25,7 @@ import {
import { dataToEsm } from '@rollup/pluginutils'
import chalk from 'chalk'
import { CLIENT_PUBLIC_PATH } from '../constants'
import { ResolveFn, ViteDevServer } from 'vite'
import { ResolveFn, ViteDevServer } from '../'
import {
getAssetFilename,
assetUrlRE,
......@@ -43,7 +40,9 @@ import type Sass from 'sass'
// and causes the CI tests fail, see: https://github.com/vitejs/vite/pull/2860
import type Stylus from 'stylus'
import type Less from 'less'
import { Alias } from '../../../../../types/alias'
import { Alias } from 'types/alias'
import type { ModuleNode } from '../server/moduleGraph'
import { transform, formatMessages } from 'esbuild'
// const debug = createDebugger('vite:css')
......@@ -88,6 +87,7 @@ export const cssLangRE = new RegExp(cssLangs)
const cssModuleRE = new RegExp(`\\.module${cssLangs}`)
const directRequestRE = /(\?|&)direct\b/
const commonjsProxyRE = /\?commonjs-proxy/
const inlineRE = /(\?|&)inline\b/
const enum PreprocessLang {
less = 'less',
......@@ -117,6 +117,11 @@ export const chunkToEmittedCssFileMap = new WeakMap<
Set<string>
>()
const postcssConfigCache = new WeakMap<
ResolvedConfig,
PostCSSConfigResult | null
>()
/**
* Plugin applied before user plugins
*/
......@@ -187,29 +192,37 @@ export function cssPlugin(config: ResolvedConfig): Plugin {
if (server) {
// server only logic for handling CSS @import dependency hmr
const { moduleGraph } = server
const thisModule = moduleGraph.getModuleById(id)!
// CSS modules cannot self-accept since it exports values
const isSelfAccepting = !modules
if (deps) {
// record deps in the module graph so edits to @import css can trigger
// main import to hot update
const depModules = new Set(
[...deps].map((file) => moduleGraph.createFileOnlyEntry(file))
)
moduleGraph.updateModuleInfo(
thisModule,
depModules,
// The root CSS proxy module is self-accepting and should not
// have an explicit accept list
new Set(),
isSelfAccepting
)
for (const file of deps) {
this.addWatchFile(file)
const thisModule = moduleGraph.getModuleById(id)
if (thisModule) {
// CSS modules cannot self-accept since it exports values
const isSelfAccepting = !modules
if (deps) {
// record deps in the module graph so edits to @import css can trigger
// main import to hot update
const depModules = new Set<string | ModuleNode>()
for (const file of deps) {
depModules.add(
cssLangRE.test(file)
? moduleGraph.createFileOnlyEntry(file)
: await moduleGraph.ensureEntryFromUrl(
await fileToUrl(file, config, this)
)
)
}
moduleGraph.updateModuleInfo(
thisModule,
depModules,
// The root CSS proxy module is self-accepting and should not
// have an explicit accept list
new Set(),
isSelfAccepting
)
for (const file of deps) {
this.addWatchFile(file)
}
} else {
thisModule.isSelfAccepting = isSelfAccepting
}
} else {
thisModule.isSelfAccepting = isSelfAccepting
}
}
......@@ -245,11 +258,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
hasEmitted = false
},
transform(css, id, ssr) {
async transform(css, id, ssr) {
if (!cssLangRE.test(id) || commonjsProxyRE.test(id)) {
return
}
const inlined = inlineRE.test(id)
const modules = cssModulesCache.get(config)!.get(id)
const modulesCode =
modules && dataToEsm(modules, { namedExports: true, preferConst: true })
......@@ -262,6 +276,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
if (ssr) {
return modulesCode || `export default ${JSON.stringify(css)}`
}
if (inlined) {
return `export default ${JSON.stringify(css)}`
}
return [
`import { updateStyle, removeStyle } from ${JSON.stringify(
path.posix.join(config.base, CLIENT_PUBLIC_PATH)
......@@ -279,14 +296,18 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
// build CSS handling ----------------------------------------------------
// record css
styles.set(id, css)
if (!inlined) {
styles.set(id, css)
} else {
css = await minifyCSS(css, config)
}
return {
code: '', //modulesCode || `export default ${JSON.stringify(css)}`,
code: modulesCode || `export default ${JSON.stringify(css)}`,
map: { mappings: '' },
// avoid the css module from being tree-shaken so that we can retrieve
// it in renderChunk()
moduleSideEffects: 'no-treeshake',
moduleSideEffects: inlined ? false : 'no-treeshake',
}
},
......@@ -353,23 +374,14 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
// this is a shared CSS-only chunk that is empty.
pureCssChunks.add(chunk.fileName)
}
if (
// fixed by xxxxxx support amd
opts.format === 'es' ||
opts.format === 'cjs' ||
opts.format === 'amd'
) {
if (opts.format === 'es' || opts.format === 'cjs') {
chunkCSS = await processChunkCSS(chunkCSS, {
inlined: false,
minify: true,
})
const name =
chunk.isDynamicEntry && chunk.fileName
? chunk.fileName.replace('.js', '')
: chunk.name
// emit corresponding css file
const fileHandle = this.emitFile({
name: name + '.css',
name: chunk.name + '.css',
type: 'asset',
source: chunkCSS,
})
......@@ -528,6 +540,12 @@ function createCSSResolvers(config: ResolvedConfig): CSSAtImportResolvers {
}
}
function getCssResolversKeys(
resolvers: CSSAtImportResolvers
): Array<keyof CSSAtImportResolvers> {
return Object.keys(resolvers) as unknown as Array<keyof CSSAtImportResolvers>
}
async function compileCSS(
id: string,
code: string,
......@@ -654,6 +672,16 @@ async function compileCSS(
modulesOptions.getJSON(cssFileName, _modules, outputFileName)
}
},
async resolve(id: string) {
for (const key of getCssResolversKeys(atImportResolvers)) {
const resolved = await atImportResolvers[key](id)
if (resolved) {
return path.resolve(resolved)
}
}
return id
},
})
)
}
......@@ -696,13 +724,13 @@ async function compileCSS(
}
if (server) {
// register glob importers so we can trigger updates on file add/remove
if (!(id in (server as any)._globImporters)) {
;(server as any)._globImporters[id] = {
if (!(id in server._globImporters)) {
server._globImporters[id] = {
module: server.moduleGraph.getModuleById(id)!,
importGlobs: [],
}
}
;(server as any)._globImporters[id].importGlobs.push({
server._globImporters[id].importGlobs.push({
base: config.root,
pattern,
})
......@@ -733,37 +761,40 @@ interface PostCSSConfigResult {
plugins: Postcss.Plugin[]
}
let cachedPostcssConfig: PostCSSConfigResult | null | undefined
async function resolvePostcssConfig(
config: ResolvedConfig
): Promise<PostCSSConfigResult | null> {
if (cachedPostcssConfig !== undefined) {
return cachedPostcssConfig
let result = postcssConfigCache.get(config)
if (result !== undefined) {
return result
}
// inline postcss config via vite config
const inlineOptions = config.css?.postcss
if (isObject(inlineOptions)) {
const result = {
options: { ...inlineOptions },
const options = { ...inlineOptions }
delete options.plugins
result = {
options,
plugins: inlineOptions.plugins || [],
}
delete result.options.plugins
return (cachedPostcssConfig = result)
}
try {
const searchPath =
typeof inlineOptions === 'string' ? inlineOptions : config.root
// @ts-ignore
return (cachedPostcssConfig = await postcssrc({}, searchPath))
} catch (e) {
if (!/No PostCSS Config found/.test(e.message)) {
throw e
} else {
try {
const searchPath =
typeof inlineOptions === 'string' ? inlineOptions : config.root
// @ts-ignore
result = await postcssrc({}, searchPath)
} catch (e) {
if (!/No PostCSS Config found/.test(e.message)) {
throw e
}
result = null
}
return (cachedPostcssConfig = null)
}
postcssConfigCache.set(config, result)
return result
}
type CssUrlReplacer = (
......@@ -852,31 +883,18 @@ async function doUrlReplace(
return `url(${wrap}${await replacer(rawUrl)}${wrap})`
}
let CleanCSS: any
async function minifyCSS(css: string, config: ResolvedConfig) {
CleanCSS = CleanCSS || (await import('clean-css')).default
const res = new CleanCSS({
rebase: false,
...config.build.cleanCssOptions,
}).minify(css)
if (res.errors && res.errors.length) {
config.logger.error(chalk.red(`error when minifying css:\n${res.errors}`))
throw res.errors[0]
}
// do not warn on remote @imports
const warnings =
res.warnings &&
res.warnings.filter((m: string) => !m.includes('remote @import'))
if (warnings && warnings.length) {
const { code, warnings } = await transform(css, {
loader: 'css',
minify: true,
})
if (warnings.length) {
const msgs = await formatMessages(warnings, { kind: 'warning' })
config.logger.warn(
chalk.yellow(`warnings when minifying css:\n${warnings.join('\n')}`)
chalk.yellow(`warnings when minifying css:\n${msgs.join('\n')}`)
)
}
return res.styles
return code
}
// #1845
......
{
"extends": "../../tsconfig.node.json",
"compilerOptions": {
"outDir": "dist"
"outDir": "dist",
"paths": {
"types/alias": ["types/alias.d.ts"]
}
},
"include": [
"src",
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册