Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qianlong66
uni-app
提交
6da01482
U
uni-app
项目概览
qianlong66
/
uni-app
与 Fork 源项目一致
Fork自
DCloud / uni-app
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-app
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
6da01482
编写于
11月 22, 2021
作者:
fxy060608
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: preprocess css
上级
a5c3f1fb
变更
8
显示空白变更内容
内联
并排
Showing
8 changed file
with
1708 addition
and
12 deletion
+1708
-12
packages/uni-app-plus/dist/uni-app-service.es.js
packages/uni-app-plus/dist/uni-app-service.es.js
+2
-2
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/css.ts
...ges/uni-cli-shared/src/vite/plugins/vitejs/plugins/css.ts
+16
-5
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Asset.ts
...uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Asset.ts
+368
-0
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Css.ts
...s/uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Css.ts
+1289
-0
packages/uni-cli-shared/src/vite/utils/plugin.ts
packages/uni-cli-shared/src/vite/utils/plugin.ts
+17
-0
packages/uni-h5-vite/src/plugin/index.ts
packages/uni-h5-vite/src/plugin/index.ts
+10
-1
packages/uni-h5/dist/uni-h5.cjs.js
packages/uni-h5/dist/uni-h5.cjs.js
+3
-2
packages/uni-h5/dist/uni-h5.es.js
packages/uni-h5/dist/uni-h5.es.js
+3
-2
未找到文件。
packages/uni-app-plus/dist/uni-app-service.es.js
浏览文件 @
6da01482
...
@@ -2686,9 +2686,9 @@ var serviceContext = (function (vue) {
...
@@ -2686,9 +2686,9 @@ var serviceContext = (function (vue) {
}
}
function parseRedirectInfo() {
function parseRedirectInfo() {
const weexPlus = weex.requireModule('plus');
const weexPlus = weex.requireModule('plus');
const { path, query, extraData, userAction } = weexPlus.getRedirectInfo() || {};
const { path, query, extraData, userAction
, fromAppid
} = weexPlus.getRedirectInfo() || {};
const referrerInfo = {
const referrerInfo = {
appId:
''
,
appId:
fromAppid
,
extraData: {},
extraData: {},
};
};
if (extraData) {
if (extraData) {
...
...
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/css.ts
浏览文件 @
6da01482
...
@@ -28,6 +28,7 @@ import type Stylus from 'stylus'
...
@@ -28,6 +28,7 @@ import type Stylus from 'stylus'
import
type
Less
from
'
less
'
import
type
Less
from
'
less
'
import
type
{
Alias
}
from
'
types/alias
'
import
type
{
Alias
}
from
'
types/alias
'
import
{
transform
,
formatMessages
}
from
'
esbuild
'
import
{
transform
,
formatMessages
}
from
'
esbuild
'
import
{
preCss
}
from
'
../../../../preprocess
'
// const debug = createDebugger('vite:css')
// const debug = createDebugger('vite:css')
export
interface
CSSOptions
{
export
interface
CSSOptions
{
...
@@ -866,6 +867,12 @@ const sass: SassStylePreprocessor = (source, root, options, aliasResolver) =>
...
@@ -866,6 +867,12 @@ const sass: SassStylePreprocessor = (source, root, options, aliasResolver) =>
aliasResolver
aliasResolver
)
)
export
function
preprocessCss
(
content
:
string
)
{
if
(
content
.
includes
(
'
#endif
'
))
{
return
preCss
(
content
)
}
return
content
}
/**
/**
* relative url() inside \@imported sass and less files must be rebased to use
* relative url() inside \@imported sass and less files must be rebased to use
* root file as base.
* root file as base.
...
@@ -876,18 +883,22 @@ async function rebaseUrls(
...
@@ -876,18 +883,22 @@ async function rebaseUrls(
alias
:
Alias
[]
alias
:
Alias
[]
):
Promise
<
Sass
.
ImporterReturnType
>
{
):
Promise
<
Sass
.
ImporterReturnType
>
{
file
=
path
.
resolve
(
file
)
// ensure os-specific flashes
file
=
path
.
resolve
(
file
)
// ensure os-specific flashes
// 条件编译
const
contents
=
preprocessCss
(
fs
.
readFileSync
(
file
,
'
utf-8
'
))
// in the same dir, no need to rebase
// in the same dir, no need to rebase
const
fileDir
=
path
.
dirname
(
file
)
const
fileDir
=
path
.
dirname
(
file
)
const
rootDir
=
path
.
dirname
(
rootFile
)
const
rootDir
=
path
.
dirname
(
rootFile
)
if
(
fileDir
===
rootDir
)
{
if
(
fileDir
===
rootDir
)
{
return
{
file
}
return
{
file
,
contents
}
}
}
// no url()
// no url()
const
content
=
fs
.
readFileSync
(
file
,
'
utf-8
'
)
if
(
!
cssUrlRE
.
test
(
contents
))
{
if
(
!
cssUrlRE
.
test
(
content
))
{
return
{
file
,
contents
}
return
{
file
}
}
}
const
rebased
=
await
rewriteCssUrls
(
content
,
(
url
)
=>
{
const
rebased
=
await
rewriteCssUrls
(
content
s
,
(
url
)
=>
{
if
(
url
.
startsWith
(
'
/
'
))
return
url
if
(
url
.
startsWith
(
'
/
'
))
return
url
// match alias, no need to rewrite
// match alias, no need to rewrite
for
(
const
{
find
}
of
alias
)
{
for
(
const
{
find
}
of
alias
)
{
...
...
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Asset.ts
0 → 100644
浏览文件 @
6da01482
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
'
../plugin
'
import
{
ResolvedConfig
}
from
'
../config
'
import
{
cleanUrl
}
from
'
../utils
'
import
{
FS_PREFIX
}
from
'
../constants
'
import
{
OutputOptions
,
PluginContext
,
RenderedChunk
}
from
'
rollup
'
import
MagicString
from
'
magic-string
'
import
{
createHash
}
from
'
crypto
'
import
{
normalizePath
}
from
'
../utils
'
export
const
assetUrlRE
=
/__VITE_ASSET__
([
a-z
\d]{8})
__
(?:\$
_
(
.*
?)
__
)?
/g
const
rawRE
=
/
(\?
|&
)
raw
(?:
&|$
)
/
const
urlRE
=
/
(\?
|&
)
url
(?:
&|$
)
/
export
const
chunkToEmittedAssetsMap
=
new
WeakMap
<
RenderedChunk
,
Set
<
string
>>
()
const
assetCache
=
new
WeakMap
<
ResolvedConfig
,
Map
<
string
,
string
>>
()
const
assetHashToFilenameMap
=
new
WeakMap
<
ResolvedConfig
,
Map
<
string
,
string
>
>
()
// save hashes of the files that has been emitted in build watch
const
emittedHashMap
=
new
WeakMap
<
ResolvedConfig
,
Set
<
string
>>
()
/**
* Also supports loading plain strings with import text from './foo.txt?raw'
*/
export
function
assetPlugin
(
config
:
ResolvedConfig
):
Plugin
{
// assetHashToFilenameMap initialization in buildStart causes getAssetFilename to return undefined
assetHashToFilenameMap
.
set
(
config
,
new
Map
())
return
{
name
:
'
vite:asset
'
,
buildStart
()
{
assetCache
.
set
(
config
,
new
Map
())
emittedHashMap
.
set
(
config
,
new
Set
())
},
resolveId
(
id
)
{
if
(
!
config
.
assetsInclude
(
cleanUrl
(
id
)))
{
return
}
// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const
publicFile
=
checkPublicFile
(
id
,
config
)
if
(
publicFile
)
{
return
id
}
},
async
load
(
id
)
{
if
(
id
.
startsWith
(
'
\
0
'
))
{
// Rollup convention, this id should be handled by the
// plugin that marked it with \0
return
}
// raw requests, read from disk
if
(
rawRE
.
test
(
id
))
{
const
file
=
checkPublicFile
(
id
,
config
)
||
cleanUrl
(
id
)
// raw query, read file and return as string
return
`export default
${
JSON
.
stringify
(
await
fsp
.
readFile
(
file
,
'
utf-8
'
)
)}
`
}
if
(
!
config
.
assetsInclude
(
cleanUrl
(
id
))
&&
!
urlRE
.
test
(
id
))
{
return
}
id
=
id
.
replace
(
urlRE
,
'
$1
'
).
replace
(
/
[\?
&
]
$/
,
''
)
const
url
=
await
fileToUrl
(
id
,
config
,
this
)
return
`export default
${
JSON
.
stringify
(
url
)}
`
},
renderChunk
(
code
,
chunk
)
{
let
match
:
RegExpExecArray
|
null
let
s
:
MagicString
|
undefined
// Urls added with JS using e.g.
// imgElement.src = "my/file.png" are using quotes
// Urls added in CSS that is imported in JS end up like
// var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
// In both cases, the wrapping should already be fine
while
((
match
=
assetUrlRE
.
exec
(
code
)))
{
s
=
s
||
(
s
=
new
MagicString
(
code
))
const
[
full
,
hash
,
postfix
=
''
]
=
match
// some internal plugins may still need to emit chunks (e.g. worker) so
// fallback to this.getFileName for that.
const
file
=
getAssetFilename
(
hash
,
config
)
||
this
.
getFileName
(
hash
)
registerAssetToChunk
(
chunk
,
file
)
const
outputFilepath
=
config
.
base
+
file
+
postfix
s
.
overwrite
(
match
.
index
,
match
.
index
+
full
.
length
,
outputFilepath
)
}
if
(
s
)
{
return
{
code
:
s
.
toString
(),
map
:
config
.
build
.
sourcemap
?
s
.
generateMap
({
hires
:
true
})
:
null
,
}
}
else
{
return
null
}
},
generateBundle
(
_
,
bundle
)
{
// do not emit assets for SSR build
if
(
config
.
command
===
'
build
'
&&
config
.
build
.
ssr
)
{
for
(
const
file
in
bundle
)
{
if
(
bundle
[
file
].
type
===
'
asset
'
&&
!
file
.
includes
(
'
ssr-manifest.json
'
)
)
{
delete
bundle
[
file
]
}
}
}
},
}
}
export
function
registerAssetToChunk
(
chunk
:
RenderedChunk
,
file
:
string
):
void
{
let
emitted
=
chunkToEmittedAssetsMap
.
get
(
chunk
)
if
(
!
emitted
)
{
emitted
=
new
Set
()
chunkToEmittedAssetsMap
.
set
(
chunk
,
emitted
)
}
emitted
.
add
(
cleanUrl
(
file
))
}
export
function
checkPublicFile
(
url
:
string
,
{
publicDir
}:
ResolvedConfig
):
string
|
undefined
{
// note if the file is in /public, the resolver would have returned it
// as-is so it's not going to be a fully resolved path.
if
(
!
publicDir
||
!
url
.
startsWith
(
'
/
'
))
{
return
}
const
publicFile
=
path
.
join
(
publicDir
,
cleanUrl
(
url
))
if
(
fs
.
existsSync
(
publicFile
))
{
return
publicFile
}
else
{
return
}
}
export
function
fileToUrl
(
id
:
string
,
config
:
ResolvedConfig
,
ctx
:
PluginContext
):
string
|
Promise
<
string
>
{
if
(
config
.
command
===
'
serve
'
)
{
return
fileToDevUrl
(
id
,
config
)
}
else
{
return
fileToBuiltUrl
(
id
,
config
,
ctx
)
}
}
function
fileToDevUrl
(
id
:
string
,
config
:
ResolvedConfig
)
{
let
rtn
:
string
if
(
checkPublicFile
(
id
,
config
))
{
// in public dir, keep the url as-is
rtn
=
id
}
else
if
(
id
.
startsWith
(
config
.
root
))
{
// in project root, infer short public path
rtn
=
'
/
'
+
path
.
posix
.
relative
(
config
.
root
,
id
)
}
else
{
// outside of project root, use absolute fs path
// (this is special handled by the serve static middleware
rtn
=
path
.
posix
.
join
(
FS_PREFIX
+
id
)
}
const
origin
=
config
.
server
?.
origin
??
''
return
origin
+
config
.
base
+
rtn
.
replace
(
/^
\/
/
,
''
)
}
export
function
getAssetFilename
(
hash
:
string
,
config
:
ResolvedConfig
):
string
|
undefined
{
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
*/
async
function
fileToBuiltUrl
(
id
:
string
,
config
:
ResolvedConfig
,
pluginContext
:
PluginContext
,
skipPublicCheck
=
false
):
Promise
<
string
>
{
if
(
!
skipPublicCheck
&&
checkPublicFile
(
id
,
config
))
{
return
config
.
base
+
id
.
slice
(
1
)
}
const
cache
=
assetCache
.
get
(
config
)
!
const
cached
=
cache
.
get
(
id
)
if
(
cached
)
{
return
cached
}
const
file
=
cleanUrl
(
id
)
const
content
=
await
fsp
.
readFile
(
file
)
let
url
:
string
if
(
config
.
build
.
lib
||
(
!
file
.
endsWith
(
'
.svg
'
)
&&
content
.
length
<
Number
(
config
.
build
.
assetsInlineLimit
))
)
{
// base64 inlined as a string
url
=
`data:
${
mime
.
getType
(
file
)}
;base64,
${
content
.
toString
(
'
base64
'
)}
`
}
else
{
// emit as asset
// rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
// that uses runtime url sniffing and it can be verbose when targeting
// non-module format. It also fails to cascade the asset content change
// into the chunk's hash, so we have to do our own content hashing here.
// https://bundlers.tooling.report/hashing/asset-cascade/
// https://github.com/rollup/rollup/issues/3415
const
map
=
assetHashToFilenameMap
.
get
(
config
)
!
const
contentHash
=
getAssetHash
(
content
)
const
{
search
,
hash
}
=
parseUrl
(
id
)
const
postfix
=
(
search
||
''
)
+
(
hash
||
''
)
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
)
}
const
emittedSet
=
emittedHashMap
.
get
(
config
)
!
if
(
!
emittedSet
.
has
(
contentHash
))
{
const
name
=
normalizePath
(
path
.
relative
(
config
.
root
,
file
))
pluginContext
.
emitFile
({
name
,
fileName
,
type
:
'
asset
'
,
source
:
content
,
})
emittedSet
.
add
(
contentHash
)
}
url
=
`__VITE_ASSET__
${
contentHash
}
__
${
postfix
?
`$_
${
postfix
}
__`
:
``
}
`
}
cache
.
set
(
id
,
url
)
return
url
}
export
function
getAssetHash
(
content
:
Buffer
):
string
{
return
createHash
(
'
sha256
'
).
update
(
content
).
digest
(
'
hex
'
).
slice
(
0
,
8
)
}
export
async
function
urlToBuiltUrl
(
url
:
string
,
importer
:
string
,
config
:
ResolvedConfig
,
pluginContext
:
PluginContext
):
Promise
<
string
>
{
if
(
checkPublicFile
(
url
,
config
))
{
return
config
.
base
+
url
.
slice
(
1
)
}
const
file
=
url
.
startsWith
(
'
/
'
)
?
path
.
join
(
config
.
root
,
url
)
:
path
.
join
(
path
.
dirname
(
importer
),
url
)
return
fileToBuiltUrl
(
file
,
config
,
pluginContext
,
// skip public check since we just did it above
true
)
}
packages/uni-cli-shared/src/vite/plugins/vitejs/plugins/h5Css.ts
0 → 100644
浏览文件 @
6da01482
import
fs
from
'
fs
'
import
path
from
'
path
'
import
glob
from
'
fast-glob
'
import
{
// createDebugger,
isExternalUrl
,
asyncReplace
,
cleanUrl
,
generateCodeFrame
,
isDataUrl
,
isObject
,
normalizePath
,
processSrcSet
,
}
from
'
../utils
'
import
{
Plugin
}
from
'
../plugin
'
import
{
ResolvedConfig
}
from
'
../config
'
import
postcssrc
from
'
postcss-load-config
'
import
{
NormalizedOutputOptions
,
OutputChunk
,
RenderedChunk
,
RollupError
,
SourceMap
,
}
from
'
rollup
'
import
{
dataToEsm
}
from
'
@rollup/pluginutils
'
import
chalk
from
'
chalk
'
import
{
CLIENT_PUBLIC_PATH
}
from
'
../constants
'
import
{
ResolveFn
,
ViteDevServer
}
from
'
../
'
import
{
getAssetFilename
,
assetUrlRE
,
registerAssetToChunk
,
fileToUrl
,
checkPublicFile
,
}
from
'
./h5Asset
'
import
MagicString
from
'
magic-string
'
import
*
as
Postcss
from
'
postcss
'
import
type
Sass
from
'
sass
'
// We need to disable check of extraneous import which is buggy for stylus,
// 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
type
{
ModuleNode
}
from
'
../server/moduleGraph
'
import
{
transform
,
formatMessages
}
from
'
esbuild
'
import
{
preprocessCss
}
from
'
./css
'
// const debug = createDebugger('vite:css')
export
interface
CSSOptions
{
/**
* https://github.com/css-modules/postcss-modules
*/
modules
?:
CSSModulesOptions
|
false
preprocessorOptions
?:
Record
<
string
,
any
>
postcss
?:
|
string
|
(
Postcss
.
ProcessOptions
&
{
plugins
?:
Postcss
.
Plugin
[]
})
}
export
interface
CSSModulesOptions
{
getJSON
?:
(
cssFileName
:
string
,
json
:
Record
<
string
,
string
>
,
outputFileName
:
string
)
=>
void
scopeBehaviour
?:
'
global
'
|
'
local
'
globalModulePaths
?:
RegExp
[]
generateScopedName
?:
|
string
|
((
name
:
string
,
filename
:
string
,
css
:
string
)
=>
string
)
hashPrefix
?:
string
/**
* default: null
*/
localsConvention
?:
|
'
camelCase
'
|
'
camelCaseOnly
'
|
'
dashes
'
|
'
dashesOnly
'
|
null
}
const
cssLangs
=
`\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)`
const
cssLangRE
=
new
RegExp
(
cssLangs
)
const
cssModuleRE
=
new
RegExp
(
`\\.module
${
cssLangs
}
`
)
const
directRequestRE
=
/
(\?
|&
)
direct
\b
/
const
commonjsProxyRE
=
/
\?
commonjs-proxy/
const
inlineRE
=
/
(\?
|&
)
inline
\b
/
const
usedRE
=
/
(\?
|&
)
used
\b
/
const
enum
PreprocessLang
{
less
=
'
less
'
,
sass
=
'
sass
'
,
scss
=
'
scss
'
,
styl
=
'
styl
'
,
stylus
=
'
stylus
'
,
}
const
enum
PureCssLang
{
css
=
'
css
'
,
}
type
CssLang
=
keyof
typeof
PureCssLang
|
keyof
typeof
PreprocessLang
export
const
isCSSRequest
=
(
request
:
string
):
boolean
=>
cssLangRE
.
test
(
request
)
export
const
isDirectCSSRequest
=
(
request
:
string
):
boolean
=>
cssLangRE
.
test
(
request
)
&&
directRequestRE
.
test
(
request
)
export
const
isDirectRequest
=
(
request
:
string
):
boolean
=>
directRequestRE
.
test
(
request
)
const
cssModulesCache
=
new
WeakMap
<
ResolvedConfig
,
Map
<
string
,
Record
<
string
,
string
>>
>
()
export
const
chunkToEmittedCssFileMap
=
new
WeakMap
<
RenderedChunk
,
Set
<
string
>
>
()
export
const
removedPureCssFilesCache
=
new
WeakMap
<
ResolvedConfig
,
Map
<
string
,
RenderedChunk
>
>
()
const
postcssConfigCache
=
new
WeakMap
<
ResolvedConfig
,
PostCSSConfigResult
|
null
>
()
/**
* Plugin applied before user plugins
*/
export
function
cssPlugin
(
config
:
ResolvedConfig
):
Plugin
{
let
server
:
ViteDevServer
let
moduleCache
:
Map
<
string
,
Record
<
string
,
string
>>
const
resolveUrl
=
config
.
createResolver
({
preferRelative
:
true
,
tryIndex
:
false
,
extensions
:
[],
})
const
atImportResolvers
=
createCSSResolvers
(
config
)
return
{
name
:
'
vite:css
'
,
configureServer
(
_server
)
{
server
=
_server
},
buildStart
()
{
// Ensure a new cache for every build (i.e. rebuilding in watch mode)
moduleCache
=
new
Map
<
string
,
Record
<
string
,
string
>>
()
cssModulesCache
.
set
(
config
,
moduleCache
)
removedPureCssFilesCache
.
set
(
config
,
new
Map
<
string
,
RenderedChunk
>
())
},
async
transform
(
raw
,
id
)
{
if
(
!
isCSSRequest
(
id
)
||
commonjsProxyRE
.
test
(
id
))
{
return
}
const
urlReplacer
:
CssUrlReplacer
=
async
(
url
,
importer
)
=>
{
if
(
checkPublicFile
(
url
,
config
))
{
return
config
.
base
+
url
.
slice
(
1
)
}
const
resolved
=
await
resolveUrl
(
url
,
importer
)
if
(
resolved
)
{
return
fileToUrl
(
resolved
,
config
,
this
)
}
return
url
}
const
{
code
:
css
,
modules
,
deps
,
}
=
await
compileCSS
(
id
,
raw
,
config
,
urlReplacer
,
atImportResolvers
,
server
)
if
(
modules
)
{
moduleCache
.
set
(
id
,
modules
)
}
// track deps for build watch mode
if
(
config
.
command
===
'
build
'
&&
config
.
build
.
watch
&&
deps
)
{
for
(
const
file
of
deps
)
{
this
.
addWatchFile
(
file
)
}
}
// dev
if
(
server
)
{
// server only logic for handling CSS @import dependency hmr
const
{
moduleGraph
}
=
server
const
thisModule
=
moduleGraph
.
getModuleById
(
id
)
if
(
thisModule
)
{
// CSS modules cannot self-accept since it exports values
const
isSelfAccepting
=
!
modules
&&
!
inlineRE
.
test
(
id
)
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
(
isCSSRequest
(
file
)
?
moduleGraph
.
createFileOnlyEntry
(
file
)
:
await
moduleGraph
.
ensureEntryFromUrl
(
(
await
fileToUrl
(
file
,
config
,
this
)
).
replace
(
(
config
.
server
?.
origin
??
''
)
+
config
.
base
,
'
/
'
)
)
)
}
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
}
}
}
return
{
code
:
css
,
// TODO CSS source map
map
:
{
mappings
:
''
},
}
},
}
}
/**
* Plugin applied after user plugins
*/
export
function
cssPostPlugin
(
config
:
ResolvedConfig
):
Plugin
{
// styles initialization in buildStart causes a styling loss in watch
const
styles
:
Map
<
string
,
string
>
=
new
Map
<
string
,
string
>
()
let
pureCssChunks
:
Set
<
string
>
// when there are multiple rollup outputs and extracting CSS, only emit once,
// since output formats have no effect on the generated CSS.
let
outputToExtractedCSSMap
:
Map
<
NormalizedOutputOptions
,
string
>
let
hasEmitted
=
false
return
{
name
:
'
vite:css-post
'
,
buildStart
()
{
// Ensure new caches for every build (i.e. rebuilding in watch mode)
pureCssChunks
=
new
Set
<
string
>
()
outputToExtractedCSSMap
=
new
Map
<
NormalizedOutputOptions
,
string
>
()
hasEmitted
=
false
},
async
transform
(
css
,
id
,
options
)
{
if
(
!
isCSSRequest
(
id
)
||
commonjsProxyRE
.
test
(
id
))
{
return
}
const
ssr
=
typeof
options
===
'
boolean
'
?
options
:
(
options
&&
(
options
as
any
).
ssr
)
===
true
const
inlined
=
inlineRE
.
test
(
id
)
const
modules
=
cssModulesCache
.
get
(
config
)
!
.
get
(
id
)
const
modulesCode
=
modules
&&
dataToEsm
(
modules
,
{
namedExports
:
true
,
preferConst
:
true
})
if
(
config
.
command
===
'
serve
'
)
{
if
(
isDirectCSSRequest
(
id
))
{
return
css
}
else
{
// server only
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
)
)}
`
,
`const id =
${
JSON
.
stringify
(
id
)}
`
,
`const css =
${
JSON
.
stringify
(
css
)}
`
,
`updateStyle(id, css)`
,
// css modules exports change on edit so it can't self accept
`
${
modulesCode
||
`import.meta.hot.accept()\nexport default css`
}
`
,
`import.meta.hot.prune(() => removeStyle(id))`
,
].
join
(
'
\n
'
)
}
}
// build CSS handling ----------------------------------------------------
// record css
if
(
!
inlined
)
{
styles
.
set
(
id
,
css
)
}
return
{
code
:
modulesCode
||
(
usedRE
.
test
(
id
)
?
`export default
${
JSON
.
stringify
(
inlined
?
await
minifyCSS
(
css
,
config
)
:
css
)}
`
:
`export default ''`
),
map
:
{
mappings
:
''
},
// avoid the css module from being tree-shaken so that we can retrieve
// it in renderChunk()
moduleSideEffects
:
inlined
?
false
:
'
no-treeshake
'
,
}
},
async
renderChunk
(
code
,
chunk
,
opts
)
{
let
chunkCSS
=
''
let
isPureCssChunk
=
true
const
ids
=
Object
.
keys
(
chunk
.
modules
)
for
(
const
id
of
ids
)
{
if
(
!
isCSSRequest
(
id
)
||
cssModuleRE
.
test
(
id
)
||
commonjsProxyRE
.
test
(
id
)
)
{
isPureCssChunk
=
false
}
if
(
styles
.
has
(
id
))
{
chunkCSS
+=
styles
.
get
(
id
)
}
}
if
(
!
chunkCSS
)
{
return
null
}
// resolve asset URL placeholders to their built file URLs and perform
// minification if necessary
const
processChunkCSS
=
async
(
css
:
string
,
{
inlined
,
minify
,
}:
{
inlined
:
boolean
minify
:
boolean
}
)
=>
{
// replace asset url references with resolved url.
const
isRelativeBase
=
config
.
base
===
''
||
config
.
base
.
startsWith
(
'
.
'
)
css
=
css
.
replace
(
assetUrlRE
,
(
_
,
fileHash
,
postfix
=
''
)
=>
{
const
filename
=
getAssetFilename
(
fileHash
,
config
)
+
postfix
registerAssetToChunk
(
chunk
,
filename
)
if
(
!
isRelativeBase
||
inlined
)
{
// absolute base or relative base but inlined (injected as style tag into
// index.html) use the base as-is
return
config
.
base
+
filename
}
else
{
// relative base + extracted CSS - asset file will be in the same dir
return
`./
${
path
.
posix
.
basename
(
filename
)}
`
}
})
// only external @imports should exist at this point - and they need to
// be hoisted to the top of the CSS chunk per spec (#1845)
if
(
css
.
includes
(
'
@import
'
))
{
css
=
await
hoistAtImports
(
css
)
}
if
(
minify
&&
config
.
build
.
minify
)
{
css
=
await
minifyCSS
(
css
,
config
)
}
return
css
}
if
(
config
.
build
.
cssCodeSplit
)
{
if
(
isPureCssChunk
)
{
// this is a shared CSS-only chunk that is empty.
pureCssChunks
.
add
(
chunk
.
fileName
)
}
if
(
opts
.
format
===
'
es
'
||
opts
.
format
===
'
cjs
'
)
{
chunkCSS
=
await
processChunkCSS
(
chunkCSS
,
{
inlined
:
false
,
minify
:
true
,
})
// emit corresponding css file
const
fileHandle
=
this
.
emitFile
({
name
:
chunk
.
name
+
'
.css
'
,
type
:
'
asset
'
,
source
:
chunkCSS
,
})
chunkToEmittedCssFileMap
.
set
(
chunk
,
new
Set
([
this
.
getFileName
(
fileHandle
)])
)
}
else
if
(
!
config
.
build
.
ssr
)
{
// legacy build, inline css
chunkCSS
=
await
processChunkCSS
(
chunkCSS
,
{
inlined
:
true
,
minify
:
true
,
})
const
style
=
`__vite_style__`
const
injectCode
=
`var
${
style
}
= document.createElement('style');`
+
`
${
style
}
.innerHTML =
${
JSON
.
stringify
(
chunkCSS
)}
;`
+
`document.head.appendChild(
${
style
}
);`
if
(
config
.
build
.
sourcemap
)
{
const
s
=
new
MagicString
(
code
)
s
.
prepend
(
injectCode
)
return
{
code
:
s
.
toString
(),
map
:
s
.
generateMap
({
hires
:
true
}),
}
}
else
{
return
{
code
:
injectCode
+
code
}
}
}
}
else
{
// non-split extracted CSS will be minified together
chunkCSS
=
await
processChunkCSS
(
chunkCSS
,
{
inlined
:
false
,
minify
:
false
,
})
outputToExtractedCSSMap
.
set
(
opts
,
(
outputToExtractedCSSMap
.
get
(
opts
)
||
''
)
+
chunkCSS
)
}
return
null
},
async
generateBundle
(
opts
,
bundle
)
{
// remove empty css chunks and their imports
if
(
pureCssChunks
.
size
)
{
const
emptyChunkFiles
=
[...
pureCssChunks
]
.
map
((
file
)
=>
path
.
basename
(
file
))
.
join
(
'
|
'
)
.
replace
(
/
\.
/g
,
'
\\
.
'
)
const
emptyChunkRE
=
new
RegExp
(
opts
.
format
===
'
es
'
?
`\\bimport\\s*"[^"]*(?:
${
emptyChunkFiles
}
)";\n?`
:
`\\brequire\\(\\s*"[^"]*(?:
${
emptyChunkFiles
}
)"\\);\n?`
,
'
g
'
)
for
(
const
file
in
bundle
)
{
const
chunk
=
bundle
[
file
]
if
(
chunk
.
type
===
'
chunk
'
)
{
// remove pure css chunk from other chunk's imports,
// and also register the emitted CSS files under the importer
// chunks instead.
chunk
.
imports
=
chunk
.
imports
.
filter
((
file
)
=>
{
if
(
pureCssChunks
.
has
(
file
))
{
const
css
=
chunkToEmittedCssFileMap
.
get
(
bundle
[
file
]
as
OutputChunk
)
if
(
css
)
{
let
existing
=
chunkToEmittedCssFileMap
.
get
(
chunk
)
if
(
!
existing
)
{
existing
=
new
Set
()
}
css
.
forEach
((
file
)
=>
existing
!
.
add
(
file
))
chunkToEmittedCssFileMap
.
set
(
chunk
,
existing
)
}
return
false
}
return
true
})
chunk
.
code
=
chunk
.
code
.
replace
(
emptyChunkRE
,
// remove css import while preserving source map location
(
m
)
=>
`/* empty css
${
''
.
padEnd
(
m
.
length
-
15
)}
*/`
)
}
}
const
removedPureCssFiles
=
removedPureCssFilesCache
.
get
(
config
)
!
pureCssChunks
.
forEach
((
fileName
)
=>
{
removedPureCssFiles
.
set
(
fileName
,
bundle
[
fileName
]
as
RenderedChunk
)
delete
bundle
[
fileName
]
})
}
let
extractedCss
=
outputToExtractedCSSMap
.
get
(
opts
)
if
(
extractedCss
&&
!
hasEmitted
)
{
hasEmitted
=
true
// minify css
if
(
config
.
build
.
minify
)
{
extractedCss
=
await
minifyCSS
(
extractedCss
,
config
)
}
this
.
emitFile
({
name
:
'
style.css
'
,
type
:
'
asset
'
,
source
:
extractedCss
,
})
}
},
}
}
interface
CSSAtImportResolvers
{
css
:
ResolveFn
sass
:
ResolveFn
less
:
ResolveFn
}
function
createCSSResolvers
(
config
:
ResolvedConfig
):
CSSAtImportResolvers
{
let
cssResolve
:
ResolveFn
|
undefined
let
sassResolve
:
ResolveFn
|
undefined
let
lessResolve
:
ResolveFn
|
undefined
return
{
get
css
()
{
return
(
cssResolve
||
(
cssResolve
=
config
.
createResolver
({
extensions
:
[
'
.css
'
],
mainFields
:
[
'
style
'
],
tryIndex
:
false
,
preferRelative
:
true
,
}))
)
},
get
sass
()
{
return
(
sassResolve
||
(
sassResolve
=
config
.
createResolver
({
extensions
:
[
'
.scss
'
,
'
.sass
'
,
'
.css
'
],
mainFields
:
[
'
sass
'
,
'
style
'
],
tryIndex
:
true
,
tryPrefix
:
'
_
'
,
preferRelative
:
true
,
}))
)
},
get
less
()
{
return
(
lessResolve
||
(
lessResolve
=
config
.
createResolver
({
extensions
:
[
'
.less
'
,
'
.css
'
],
mainFields
:
[
'
less
'
,
'
style
'
],
tryIndex
:
false
,
preferRelative
:
true
,
}))
)
},
}
}
function
getCssResolversKeys
(
resolvers
:
CSSAtImportResolvers
):
Array
<
keyof
CSSAtImportResolvers
>
{
return
Object
.
keys
(
resolvers
)
as
unknown
as
Array
<
keyof
CSSAtImportResolvers
>
}
async
function
compileCSS
(
id
:
string
,
code
:
string
,
config
:
ResolvedConfig
,
urlReplacer
:
CssUrlReplacer
,
atImportResolvers
:
CSSAtImportResolvers
,
server
?:
ViteDevServer
):
Promise
<
{
code
:
string
map
?:
SourceMap
ast
?:
Postcss
.
Result
modules
?:
Record
<
string
,
string
>
deps
?:
Set
<
string
>
}
>
{
const
{
modules
:
modulesOptions
,
preprocessorOptions
}
=
config
.
css
||
{}
const
isModule
=
modulesOptions
!==
false
&&
cssModuleRE
.
test
(
id
)
// although at serve time it can work without processing, we do need to
// crawl them in order to register watch dependencies.
const
needInlineImport
=
code
.
includes
(
'
@import
'
)
const
hasUrl
=
cssUrlRE
.
test
(
code
)
||
cssImageSetRE
.
test
(
code
)
const
postcssConfig
=
await
resolvePostcssConfig
(
config
)
const
lang
=
id
.
match
(
cssLangRE
)?.[
1
]
as
CssLang
|
undefined
// 1. plain css that needs no processing
if
(
lang
===
'
css
'
&&
!
postcssConfig
&&
!
isModule
&&
!
needInlineImport
&&
!
hasUrl
)
{
return
{
code
}
}
let
map
:
SourceMap
|
undefined
let
modules
:
Record
<
string
,
string
>
|
undefined
const
deps
=
new
Set
<
string
>
()
// 2. pre-processors: sass etc.
if
(
isPreProcessor
(
lang
))
{
const
preProcessor
=
preProcessors
[
lang
]
let
opts
=
(
preprocessorOptions
&&
preprocessorOptions
[
lang
])
||
{}
// support @import from node dependencies by default
switch
(
lang
)
{
case
PreprocessLang
.
scss
:
case
PreprocessLang
.
sass
:
opts
=
{
includePaths
:
[
'
node_modules
'
],
alias
:
config
.
resolve
.
alias
,
...
opts
,
}
break
case
PreprocessLang
.
less
:
case
PreprocessLang
.
styl
:
case
PreprocessLang
.
stylus
:
opts
=
{
paths
:
[
'
node_modules
'
],
alias
:
config
.
resolve
.
alias
,
...
opts
,
}
}
// important: set this for relative import resolving
opts
.
filename
=
cleanUrl
(
id
)
const
preprocessResult
=
await
preProcessor
(
code
,
config
.
root
,
opts
,
atImportResolvers
)
if
(
preprocessResult
.
errors
.
length
)
{
throw
preprocessResult
.
errors
[
0
]
}
code
=
preprocessResult
.
code
map
=
preprocessResult
.
map
as
SourceMap
if
(
preprocessResult
.
deps
)
{
preprocessResult
.
deps
.
forEach
((
dep
)
=>
{
// sometimes sass registers the file itself as a dep
if
(
normalizePath
(
dep
)
!==
normalizePath
(
opts
.
filename
))
{
deps
.
add
(
dep
)
}
})
}
}
// 3. postcss
const
postcssOptions
=
(
postcssConfig
&&
postcssConfig
.
options
)
||
{}
const
postcssPlugins
=
postcssConfig
&&
postcssConfig
.
plugins
?
postcssConfig
.
plugins
.
slice
()
:
[]
if
(
needInlineImport
)
{
postcssPlugins
.
unshift
(
(
await
import
(
'
postcss-import
'
)).
default
({
async
resolve
(
id
,
basedir
)
{
const
resolved
=
await
atImportResolvers
.
css
(
id
,
path
.
join
(
basedir
,
'
*
'
)
)
if
(
resolved
)
{
return
path
.
resolve
(
resolved
)
}
return
id
},
})
)
}
postcssPlugins
.
push
(
UrlRewritePostcssPlugin
({
replacer
:
urlReplacer
,
})
as
Postcss
.
Plugin
)
if
(
isModule
)
{
postcssPlugins
.
unshift
(
(
await
import
(
'
postcss-modules
'
)).
default
({
...
modulesOptions
,
getJSON
(
cssFileName
:
string
,
_modules
:
Record
<
string
,
string
>
,
outputFileName
:
string
)
{
modules
=
_modules
if
(
modulesOptions
&&
typeof
modulesOptions
.
getJSON
===
'
function
'
)
{
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
},
})
)
}
if
(
!
postcssPlugins
.
length
)
{
return
{
code
,
map
,
}
}
// postcss is an unbundled dep and should be lazy imported
const
postcssResult
=
await
(
await
import
(
'
postcss
'
))
.
default
(
postcssPlugins
)
.
process
(
code
,
{
...
postcssOptions
,
to
:
id
,
from
:
id
,
map
:
{
inline
:
false
,
annotation
:
false
,
prev
:
map
,
},
})
// record CSS dependencies from @imports
for
(
const
message
of
postcssResult
.
messages
)
{
if
(
message
.
type
===
'
dependency
'
)
{
deps
.
add
(
message
.
file
as
string
)
}
else
if
(
message
.
type
===
'
dir-dependency
'
)
{
// https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#3-dependencies
const
{
dir
,
glob
:
globPattern
=
'
**
'
}
=
message
const
pattern
=
normalizePath
(
path
.
resolve
(
path
.
dirname
(
id
),
dir
))
+
`/`
+
globPattern
const
files
=
glob
.
sync
(
pattern
,
{
ignore
:
[
'
**/node_modules/**
'
],
})
for
(
let
i
=
0
;
i
<
files
.
length
;
i
++
)
{
deps
.
add
(
files
[
i
])
}
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
]
=
{
module
:
server
.
moduleGraph
.
getModuleById
(
id
)
!
,
importGlobs
:
[],
}
}
;(
server
as
any
).
_globImporters
[
id
].
importGlobs
.
push
({
base
:
config
.
root
,
pattern
,
})
}
}
else
if
(
message
.
type
===
'
warning
'
)
{
let
msg
=
`[vite:css]
${
message
.
text
}
`
if
(
message
.
line
&&
message
.
column
)
{
msg
+=
`\n
${
generateCodeFrame
(
code
,
{
line
:
message
.
line
,
column
:
message
.
column
,
})}
`
}
config
.
logger
.
warn
(
chalk
.
yellow
(
msg
))
}
}
return
{
ast
:
postcssResult
,
code
:
postcssResult
.
css
,
map
:
postcssResult
.
map
as
any
,
modules
,
deps
,
}
}
interface
PostCSSConfigResult
{
options
:
Postcss
.
ProcessOptions
plugins
:
Postcss
.
Plugin
[]
}
async
function
resolvePostcssConfig
(
config
:
ResolvedConfig
):
Promise
<
PostCSSConfigResult
|
null
>
{
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
options
=
{
...
inlineOptions
}
delete
options
.
plugins
result
=
{
options
,
plugins
:
inlineOptions
.
plugins
||
[],
}
}
else
{
try
{
const
searchPath
=
typeof
inlineOptions
===
'
string
'
?
inlineOptions
:
config
.
root
// @ts-ignore
result
=
await
postcssrc
({},
searchPath
)
}
catch
(
e
:
any
)
{
if
(
!
/No PostCSS Config found/
.
test
(
e
.
message
))
{
throw
e
}
result
=
null
}
}
postcssConfigCache
.
set
(
config
,
result
)
return
result
}
type
CssUrlReplacer
=
(
url
:
string
,
importer
?:
string
)
=>
string
|
Promise
<
string
>
// https://drafts.csswg.org/css-syntax-3/#identifier-code-point
export
const
cssUrlRE
=
/
(?<
=^|
[^\w\-\u
0080-
\u
ffff
])
url
\(\s
*
(
'
[^
'
]
+'|"
[^
"
]
+"|
[^
'")
]
+
)\s
*
\)
/
const
cssImageSetRE
=
/image-set
\(([^
)
]
+
)\)
/
const
UrlRewritePostcssPlugin
:
Postcss
.
PluginCreator
<
{
replacer
:
CssUrlReplacer
}
>
=
(
opts
)
=>
{
if
(
!
opts
)
{
throw
new
Error
(
'
base or replace is required
'
)
}
return
{
postcssPlugin
:
'
vite-url-rewrite
'
,
Once
(
root
)
{
const
promises
:
Promise
<
void
>
[]
=
[]
root
.
walkDecls
((
declaration
)
=>
{
const
isCssUrl
=
cssUrlRE
.
test
(
declaration
.
value
)
const
isCssImageSet
=
cssImageSetRE
.
test
(
declaration
.
value
)
if
(
isCssUrl
||
isCssImageSet
)
{
const
replacerForDeclaration
=
(
rawUrl
:
string
)
=>
{
const
importer
=
declaration
.
source
?.
input
.
file
return
opts
.
replacer
(
rawUrl
,
importer
)
}
const
rewriterToUse
=
isCssUrl
?
rewriteCssUrls
:
rewriteCssImageSet
promises
.
push
(
rewriterToUse
(
declaration
.
value
,
replacerForDeclaration
).
then
(
(
url
)
=>
{
declaration
.
value
=
url
}
)
)
}
})
if
(
promises
.
length
)
{
return
Promise
.
all
(
promises
)
as
any
}
},
}
}
UrlRewritePostcssPlugin
.
postcss
=
true
function
rewriteCssUrls
(
css
:
string
,
replacer
:
CssUrlReplacer
):
Promise
<
string
>
{
return
asyncReplace
(
css
,
cssUrlRE
,
async
(
match
)
=>
{
const
[
matched
,
rawUrl
]
=
match
return
await
doUrlReplace
(
rawUrl
,
matched
,
replacer
)
})
}
function
rewriteCssImageSet
(
css
:
string
,
replacer
:
CssUrlReplacer
):
Promise
<
string
>
{
return
asyncReplace
(
css
,
cssImageSetRE
,
async
(
match
)
=>
{
const
[
matched
,
rawUrl
]
=
match
const
url
=
await
processSrcSet
(
rawUrl
,
({
url
})
=>
doUrlReplace
(
url
,
matched
,
replacer
)
)
return
`image-set(
${
url
}
)`
})
}
async
function
doUrlReplace
(
rawUrl
:
string
,
matched
:
string
,
replacer
:
CssUrlReplacer
)
{
let
wrap
=
''
const
first
=
rawUrl
[
0
]
if
(
first
===
`"`
||
first
===
`'`
)
{
wrap
=
first
rawUrl
=
rawUrl
.
slice
(
1
,
-
1
)
}
if
(
isExternalUrl
(
rawUrl
)
||
isDataUrl
(
rawUrl
)
||
rawUrl
.
startsWith
(
'
#
'
))
{
return
matched
}
return
`url(
${
wrap
}${
await
replacer
(
rawUrl
)}${
wrap
}
)`
}
async
function
minifyCSS
(
css
:
string
,
config
:
ResolvedConfig
)
{
const
{
code
,
warnings
}
=
await
transform
(
css
,
{
loader
:
'
css
'
,
minify
:
true
,
target
:
config
.
build
.
cssTarget
||
undefined
,
})
if
(
warnings
.
length
)
{
const
msgs
=
await
formatMessages
(
warnings
,
{
kind
:
'
warning
'
})
config
.
logger
.
warn
(
chalk
.
yellow
(
`warnings when minifying css:\n
${
msgs
.
join
(
'
\n
'
)}
`
)
)
}
return
code
}
// #1845
// CSS @import can only appear at top of the file. We need to hoist all @import
// to top when multiple files are concatenated.
async
function
hoistAtImports
(
css
:
string
)
{
const
postcss
=
await
import
(
'
postcss
'
)
return
(
await
postcss
.
default
([
AtImportHoistPlugin
]).
process
(
css
)).
css
}
const
AtImportHoistPlugin
:
Postcss
.
PluginCreator
<
any
>
=
()
=>
{
return
{
postcssPlugin
:
'
vite-hoist-at-imports
'
,
Once
(
root
)
{
const
imports
:
Postcss
.
AtRule
[]
=
[]
root
.
walkAtRules
((
rule
)
=>
{
if
(
rule
.
name
===
'
import
'
)
{
// record in reverse so that can simply prepend to preserve order
imports
.
unshift
(
rule
)
}
})
imports
.
forEach
((
i
)
=>
root
.
prepend
(
i
))
},
}
}
AtImportHoistPlugin
.
postcss
=
true
// Preprocessor support. This logic is largely replicated from @vue/compiler-sfc
type
PreprocessorAdditionalData
=
|
string
|
((
source
:
string
,
filename
:
string
)
=>
string
|
Promise
<
string
>
)
type
StylePreprocessorOptions
=
{
[
key
:
string
]:
any
additionalData
?:
PreprocessorAdditionalData
filename
:
string
alias
:
Alias
[]
}
type
SassStylePreprocessorOptions
=
StylePreprocessorOptions
&
Sass
.
Options
type
StylePreprocessor
=
(
source
:
string
,
root
:
string
,
options
:
StylePreprocessorOptions
,
resolvers
:
CSSAtImportResolvers
)
=>
StylePreprocessorResults
|
Promise
<
StylePreprocessorResults
>
type
SassStylePreprocessor
=
(
source
:
string
,
root
:
string
,
options
:
SassStylePreprocessorOptions
,
resolvers
:
CSSAtImportResolvers
)
=>
StylePreprocessorResults
|
Promise
<
StylePreprocessorResults
>
export
interface
StylePreprocessorResults
{
code
:
string
map
?:
object
errors
:
RollupError
[]
deps
:
string
[]
}
const
loadedPreprocessors
:
Partial
<
Record
<
PreprocessLang
,
any
>>
=
{}
function
loadPreprocessor
(
lang
:
PreprocessLang
.
scss
,
root
:
string
):
typeof
Sass
function
loadPreprocessor
(
lang
:
PreprocessLang
.
sass
,
root
:
string
):
typeof
Sass
function
loadPreprocessor
(
lang
:
PreprocessLang
.
less
,
root
:
string
):
typeof
Less
function
loadPreprocessor
(
lang
:
PreprocessLang
.
stylus
,
root
:
string
):
typeof
Stylus
function
loadPreprocessor
(
lang
:
PreprocessLang
,
root
:
string
):
any
{
if
(
lang
in
loadedPreprocessors
)
{
return
loadedPreprocessors
[
lang
]
}
try
{
// Search for the preprocessor in the root directory first, and fall back
// to the default require paths.
const
fallbackPaths
=
require
.
resolve
.
paths
?.(
lang
)
||
[]
const
resolved
=
require
.
resolve
(
lang
,
{
paths
:
[
root
,
...
fallbackPaths
]
})
return
(
loadedPreprocessors
[
lang
]
=
require
(
resolved
))
}
catch
(
e
)
{
throw
new
Error
(
`Preprocessor dependency "
${
lang
}
" not found. Did you install it?`
)
}
}
// .scss/.sass processor
const
scss
:
SassStylePreprocessor
=
async
(
source
,
root
,
options
,
resolvers
)
=>
{
const
render
=
loadPreprocessor
(
PreprocessLang
.
sass
,
root
).
render
const
internalImporter
:
Sass
.
Importer
=
(
url
,
importer
,
done
)
=>
{
resolvers
.
sass
(
url
,
importer
).
then
((
resolved
)
=>
{
if
(
resolved
)
{
rebaseUrls
(
resolved
,
options
.
filename
,
options
.
alias
)
.
then
((
data
)
=>
done
?.(
data
))
.
catch
((
data
)
=>
done
?.(
data
))
}
else
{
done
?.(
null
)
}
})
}
const
importer
=
[
internalImporter
]
if
(
options
.
importer
)
{
Array
.
isArray
(
options
.
importer
)
?
importer
.
push
(...
options
.
importer
)
:
importer
.
push
(
options
.
importer
)
}
const
finalOptions
:
Sass
.
Options
=
{
...
options
,
data
:
await
getSource
(
source
,
options
.
filename
,
options
.
additionalData
),
file
:
options
.
filename
,
outFile
:
options
.
filename
,
importer
,
}
try
{
const
result
=
await
new
Promise
<
Sass
.
Result
>
((
resolve
,
reject
)
=>
{
render
(
finalOptions
,
(
err
,
res
)
=>
{
if
(
err
)
{
reject
(
err
)
}
else
{
resolve
(
res
)
}
})
})
const
deps
=
result
.
stats
.
includedFiles
return
{
code
:
result
.
css
.
toString
(),
errors
:
[],
deps
,
}
}
catch
(
e
:
any
)
{
// normalize SASS error
e
.
id
=
e
.
file
e
.
frame
=
e
.
formatted
return
{
code
:
''
,
errors
:
[
e
],
deps
:
[]
}
}
}
const
sass
:
SassStylePreprocessor
=
(
source
,
root
,
options
,
aliasResolver
)
=>
scss
(
source
,
root
,
{
...
options
,
indentedSyntax
:
true
,
},
aliasResolver
)
/**
* relative url() inside \@imported sass and less files must be rebased to use
* root file as base.
*/
async
function
rebaseUrls
(
file
:
string
,
rootFile
:
string
,
alias
:
Alias
[]
):
Promise
<
Sass
.
ImporterReturnType
>
{
file
=
path
.
resolve
(
file
)
// ensure os-specific flashes
// fixed by xxxxxx 条件编译
const
contents
=
preprocessCss
(
fs
.
readFileSync
(
file
,
'
utf-8
'
))
// in the same dir, no need to rebase
const
fileDir
=
path
.
dirname
(
file
)
const
rootDir
=
path
.
dirname
(
rootFile
)
if
(
fileDir
===
rootDir
)
{
return
{
file
,
contents
}
}
// no url()
if
(
!
cssUrlRE
.
test
(
contents
))
{
return
{
file
,
contents
}
}
const
rebased
=
await
rewriteCssUrls
(
contents
,
(
url
)
=>
{
if
(
url
.
startsWith
(
'
/
'
))
return
url
// match alias, no need to rewrite
for
(
const
{
find
}
of
alias
)
{
const
matches
=
typeof
find
===
'
string
'
?
url
.
startsWith
(
find
)
:
find
.
test
(
url
)
if
(
matches
)
{
return
url
}
}
const
absolute
=
path
.
resolve
(
fileDir
,
url
)
const
relative
=
path
.
relative
(
rootDir
,
absolute
)
return
normalizePath
(
relative
)
})
return
{
file
,
contents
:
rebased
,
}
}
// .less
const
less
:
StylePreprocessor
=
async
(
source
,
root
,
options
,
resolvers
)
=>
{
const
nodeLess
=
loadPreprocessor
(
PreprocessLang
.
less
,
root
)
const
viteResolverPlugin
=
createViteLessPlugin
(
nodeLess
,
options
.
filename
,
options
.
alias
,
resolvers
)
source
=
await
getSource
(
source
,
options
.
filename
,
options
.
additionalData
)
let
result
:
Less
.
RenderOutput
|
undefined
try
{
result
=
await
nodeLess
.
render
(
source
,
{
...
options
,
plugins
:
[
viteResolverPlugin
,
...(
options
.
plugins
||
[])],
})
}
catch
(
e
)
{
const
error
=
e
as
Less
.
RenderError
// normalize error info
const
normalizedError
:
RollupError
=
new
Error
(
error
.
message
||
error
.
type
)
normalizedError
.
loc
=
{
file
:
error
.
filename
||
options
.
filename
,
line
:
error
.
line
,
column
:
error
.
column
,
}
return
{
code
:
''
,
errors
:
[
normalizedError
],
deps
:
[]
}
}
return
{
code
:
result
.
css
.
toString
(),
deps
:
result
.
imports
,
errors
:
[],
}
}
/**
* Less manager, lazy initialized
*/
let
ViteLessManager
:
any
function
createViteLessPlugin
(
less
:
typeof
Less
,
rootFile
:
string
,
alias
:
Alias
[],
resolvers
:
CSSAtImportResolvers
):
Less
.
Plugin
{
if
(
!
ViteLessManager
)
{
ViteLessManager
=
class
ViteManager
extends
less
.
FileManager
{
resolvers
rootFile
alias
constructor
(
rootFile
:
string
,
resolvers
:
CSSAtImportResolvers
,
alias
:
Alias
[]
)
{
super
()
this
.
rootFile
=
rootFile
this
.
resolvers
=
resolvers
this
.
alias
=
alias
}
override
supports
()
{
return
true
}
override
supportsSync
()
{
return
false
}
override
async
loadFile
(
filename
:
string
,
dir
:
string
,
opts
:
any
,
env
:
any
):
Promise
<
Less
.
FileLoadResult
>
{
const
resolved
=
await
this
.
resolvers
.
less
(
filename
,
path
.
join
(
dir
,
'
*
'
)
)
if
(
resolved
)
{
const
result
=
await
rebaseUrls
(
resolved
,
this
.
rootFile
,
this
.
alias
)
let
contents
:
string
if
(
result
&&
'
contents
'
in
result
)
{
contents
=
result
.
contents
}
else
{
contents
=
fs
.
readFileSync
(
resolved
,
'
utf-8
'
)
}
return
{
filename
:
path
.
resolve
(
resolved
),
contents
,
}
}
else
{
return
super
.
loadFile
(
filename
,
dir
,
opts
,
env
)
}
}
}
}
return
{
install
(
_
,
pluginManager
)
{
pluginManager
.
addFileManager
(
new
ViteLessManager
(
rootFile
,
resolvers
,
alias
)
)
},
minVersion
:
[
3
,
0
,
0
],
}
}
// .styl
const
styl
:
StylePreprocessor
=
async
(
source
,
root
,
options
)
=>
{
const
nodeStylus
=
loadPreprocessor
(
PreprocessLang
.
stylus
,
root
)
// Get source with preprocessor options.additionalData. Make sure a new line separator
// is added to avoid any render error, as added stylus content may not have semi-colon separators
source
=
await
getSource
(
source
,
options
.
filename
,
options
.
additionalData
,
'
\n
'
)
// Get preprocessor options.imports dependencies as stylus
// does not return them with its builtin `.deps()` method
const
importsDeps
=
(
options
.
imports
??
[]).
map
((
dep
:
string
)
=>
path
.
resolve
(
dep
)
)
try
{
const
ref
=
nodeStylus
(
source
,
options
)
// if (map) ref.set('sourcemap', { inline: false, comment: false })
const
result
=
ref
.
render
()
// Concat imports deps with computed deps
const
deps
=
[...
ref
.
deps
(),
...
importsDeps
]
return
{
code
:
result
,
errors
:
[],
deps
}
}
catch
(
e
:
any
)
{
return
{
code
:
''
,
errors
:
[
e
],
deps
:
[]
}
}
}
function
getSource
(
source
:
string
,
filename
:
string
,
additionalData
?:
PreprocessorAdditionalData
,
sep
:
string
=
''
):
string
|
Promise
<
string
>
{
if
(
!
additionalData
)
return
source
if
(
typeof
additionalData
===
'
function
'
)
{
return
additionalData
(
source
,
filename
)
}
return
additionalData
+
sep
+
source
}
const
preProcessors
=
Object
.
freeze
({
[
PreprocessLang
.
less
]:
less
,
[
PreprocessLang
.
sass
]:
sass
,
[
PreprocessLang
.
scss
]:
scss
,
[
PreprocessLang
.
styl
]:
styl
,
[
PreprocessLang
.
stylus
]:
styl
,
})
function
isPreProcessor
(
lang
:
any
):
lang
is
PreprocessLang
{
return
lang
&&
lang
in
preProcessors
}
packages/uni-cli-shared/src/vite/utils/plugin.ts
浏览文件 @
6da01482
import
type
{
Plugin
,
ResolvedConfig
}
from
'
vite
'
import
type
{
Plugin
,
ResolvedConfig
}
from
'
vite
'
import
{
assetPlugin
}
from
'
../plugins/vitejs/plugins/asset
'
import
{
assetPlugin
}
from
'
../plugins/vitejs/plugins/asset
'
import
{
cssPlugin
,
cssPostPlugin
}
from
'
../plugins/vitejs/plugins/css
'
import
{
cssPlugin
,
cssPostPlugin
}
from
'
../plugins/vitejs/plugins/css
'
import
{
assetPlugin
as
h5AssetPlugin
}
from
'
../plugins/vitejs/plugins/h5Asset
'
import
{
cssPlugin
as
h5CssPlugin
,
cssPostPlugin
as
h5CssPostPlugin
,
}
from
'
../plugins/vitejs/plugins/h5Css
'
export
type
CreateUniViteFilterPlugin
=
(
export
type
CreateUniViteFilterPlugin
=
(
opts
:
UniViteFilterPluginOptions
opts
:
UniViteFilterPluginOptions
)
=>
Plugin
)
=>
Plugin
...
@@ -9,6 +14,18 @@ export interface UniViteFilterPluginOptions {
...
@@ -9,6 +14,18 @@ export interface UniViteFilterPluginOptions {
filter
:
(
id
:
string
)
=>
boolean
filter
:
(
id
:
string
)
=>
boolean
}
}
export
function
injectH5AssetPlugin
(
config
:
ResolvedConfig
)
{
replacePlugins
([
h5AssetPlugin
(
config
)],
config
)
}
export
function
injectH5CssPlugin
(
config
:
ResolvedConfig
)
{
replacePlugins
([
h5CssPlugin
(
config
)],
config
)
}
export
function
injectH5CssPostPlugin
(
config
:
ResolvedConfig
)
{
replacePlugins
([
h5CssPostPlugin
(
config
)],
config
)
}
export
function
injectAssetPlugin
(
config
:
ResolvedConfig
)
{
export
function
injectAssetPlugin
(
config
:
ResolvedConfig
)
{
replacePlugins
([
assetPlugin
(
config
)],
config
)
replacePlugins
([
assetPlugin
(
config
)],
config
)
}
}
...
...
packages/uni-h5-vite/src/plugin/index.ts
浏览文件 @
6da01482
import
type
{
ResolvedConfig
}
from
'
vite
'
import
type
{
ResolvedConfig
}
from
'
vite
'
import
{
UniVitePlugin
}
from
'
@dcloudio/uni-cli-shared
'
import
{
injectH5AssetPlugin
,
injectH5CssPlugin
,
injectH5CssPostPlugin
,
UniVitePlugin
,
}
from
'
@dcloudio/uni-cli-shared
'
import
{
createHandleHotUpdate
}
from
'
./handleHotUpdate
'
import
{
createHandleHotUpdate
}
from
'
./handleHotUpdate
'
import
{
createTransformIndexHtml
}
from
'
./transformIndexHtml
'
import
{
createTransformIndexHtml
}
from
'
./transformIndexHtml
'
import
{
createConfigureServer
}
from
'
./configureServer
'
import
{
createConfigureServer
}
from
'
./configureServer
'
...
@@ -21,6 +26,10 @@ export function uniH5PLugin(): UniVitePlugin {
...
@@ -21,6 +26,10 @@ export function uniH5PLugin(): UniVitePlugin {
configOptions
.
resolvedConfig
=
config
configOptions
.
resolvedConfig
=
config
// TODO 禁止 optimizeDeps
// TODO 禁止 optimizeDeps
;(
config
as
any
).
cacheDir
=
''
;(
config
as
any
).
cacheDir
=
''
injectH5AssetPlugin
(
config
)
injectH5CssPlugin
(
config
)
injectH5CssPostPlugin
(
config
)
},
},
configureServer
:
createConfigureServer
(),
configureServer
:
createConfigureServer
(),
handleHotUpdate
:
createHandleHotUpdate
(),
handleHotUpdate
:
createHandleHotUpdate
(),
...
...
packages/uni-h5/dist/uni-h5.cjs.js
浏览文件 @
6da01482
...
@@ -10765,10 +10765,11 @@ function usePageHeadSearchInput({
...
@@ -10765,10 +10765,11 @@ function usePageHeadSearchInput({
};
};
}
}
var
_export_sfc
=
(
sfc
,
props2
)
=>
{
var
_export_sfc
=
(
sfc
,
props2
)
=>
{
const
target
=
sfc
.
__vccOpts
||
sfc
;
for
(
const
[
key
,
val
]
of
props2
)
{
for
(
const
[
key
,
val
]
of
props2
)
{
sfc
[
key
]
=
val
;
target
[
key
]
=
val
;
}
}
return
sfc
;
return
target
;
};
};
const
_sfc_main
=
{
const
_sfc_main
=
{
name
:
"
PageRefresh
"
,
name
:
"
PageRefresh
"
,
...
...
packages/uni-h5/dist/uni-h5.es.js
浏览文件 @
6da01482
...
@@ -21564,10 +21564,11 @@ function usePageHeadSearchInput({
...
@@ -21564,10 +21564,11 @@ function usePageHeadSearchInput({
};
};
}
}
var _export_sfc = (sfc, props2) => {
var _export_sfc = (sfc, props2) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props2) {
for (const [key, val] of props2) {
sfc
[key] = val;
target
[key] = val;
}
}
return
sfc
;
return
target
;
};
};
const _sfc_main = {
const _sfc_main = {
name: "PageRefresh",
name: "PageRefresh",
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录