Skip to content

Commit 8c89cb2

Browse files
committed
feat: 使 runtimeCssImportRewriteLoader 总是在 postcss-loader 之前执行,而 runtimeClassSetLoader 在其之后执行
1 parent 1379d4b commit 8c89cb2

File tree

8 files changed

+103
-2
lines changed

8 files changed

+103
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"weapp-tailwindcss": patch
3+
---
4+
5+
修正 runtime loader 的插入顺序,使 `runtimeCssImportRewriteLoader` 总是在 `postcss-loader` 之前执行,而 `runtimeClassSetLoader` 在其之后执行;同时新增调试日志和文档,方便排查 loader 链。

apps/taro-webpack-tailwindcss-v4/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"dev:qq": "npm run build:qq -- --watch",
3232
"dev:jd": "npm run build:jd -- --watch",
3333
"dev:harmony-hybrid": "npm run build:harmony-hybrid -- --watch",
34+
"debug:loader-order": "cross-env WEAPP_TW_LOADER_DEBUG=1 taro build --type weapp --watch",
3435
"open": "weapp open -p",
3536
"postinstall": "weapp-tw patch"
3637
},
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Webpack Demo Loader Execution Orders
2+
3+
说明:Webpack 会按照 `module.loaders` / `rule.use` **从后往前**执行 loader。以下顺序均按“执行时的先后”列出,并特别标出 `postcss-loader``runtimeCssImportRewriteLoader``runtimeClassSetLoader` 所处位置。
4+
5+
## native-mina
6+
- 路径:`demo/native-mina/webpack.config.js`
7+
- 执行序:`sass-loader → runtimeCssImportRewriteLoader → postcss-loader → runtimeClassSetLoader → file-loader`
8+
- rewrite 在 PostCSS 之前;class-set 在 PostCSS 之后。
9+
10+
## taro-app / taro-vue3-app / taro-webpack-tailwindcss-v4
11+
- 路径:`demo/taro-app/node_modules/@tarojs/webpack5-runner/dist/webpack/MiniWebpackModule.js`
12+
- 基础执行序:`preprocessor (sass/less/stylus) → runtimeCssImportRewriteLoader → postcss-loader → runtimeClassSetLoader → css-loader → MiniCssExtractPlugin.loader`
13+
- 架构中的 `MiniCssExtractPlugin.loader → css-loader → postcss-loader` 的定义位于 `getCSSLoaders`,rewrite 和 class-set 分别在它们两侧。
14+
15+
## mpx-app / mpx-tailwindcss-v4
16+
- 路径:
17+
- Vue CLI loader 链:`demo/mpx-app/node_modules/@vue/cli-service/lib/config/css.js`
18+
- Mpx 注入:`demo/mpx-app/node_modules/@mpxjs/webpack-plugin/lib/index.js`
19+
- 执行序(含 Mpx 注入后):`preprocessor → runtimeCssImportRewriteLoader → postcss-loader → runtimeClassSetLoader → css-loader (被 wxss-loader 取代) → @mpxjs/wxss/loader → @mpxjs/style-compiler → MiniCssExtractPlugin.loader`
20+
- rewrite 依旧在 `postcss-loader` 之前,class-set 在之后;Mpx 自身的 `wxss/style compiler` 仍保持在 css-loader 之后。
21+
22+
## Uni-app (uni-app / uni-app-webpack5 / uni-app-webpack-tailwindcss-v4)
23+
- 路径:`demo/uni-app/node_modules/@dcloudio/vue-cli-plugin-uni/lib/chain-webpack.js`
24+
- 执行序:`preprocessor → runtimeCssImportRewriteLoader → postcss-loader → runtimeClassSetLoader → uniapp-preprocess (postcss 后再次注入) → css-loader → extract-css-loader / vue-style-loader`
25+
- 在原链路中 `uniapp-preprocess` 会在 `css-loader` 前插入一次;rewrite/class-set 的插入仍然围绕 `postcss-loader` 对称。
26+
27+
## Rax 示例(rax-app)
28+
- 路径:`demo/rax-app/node_modules/.pnpm/node_modules/build-plugin-rax-app/lib/userConfig/atoms/inlineStyle.js`
29+
- MiniApp 目标默认执行序:`preprocessor → runtimeCssImportRewriteLoader → postcss-loader → runtimeClassSetLoader → css-loader → MiniCssExtractPlugin.loader`
30+
-`inlineStyle` 强制开启,则 html/inline rule 会走 `preprocessor → runtimeCssImportRewriteLoader → postcss-loader → stylesheet-loader`(class-set 仍在 PostCSS 后)。
31+
32+
> 备注:`runtimeCssImportRewriteLoader``runtimeClassSetLoader` 是全局注入的 runtime loader,只有在 Tailwind v4 且启用 rewrite 时才会-visible;其位置在所有 demo 中都遵循“rewrite-before-postcss / class-set-after-postcss”这一策略。

demo/webpack-style-loaders.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Webpack Demo Style Loader Orders
2+
3+
以下内容概览 demo 目录中所有基于 Webpack 的子项目,逐一列出样式相关 loader 的执行顺序以及配置来源(按 Webpack 实际执行顺序,即“数组尾部 loader 先执行”)。`
4+
` 表示同一条规则的连续 loader。
5+
6+
## native-mina
7+
- **入口**`demo/native-mina/webpack.config.js:30-52`
8+
- **SCSS**`sass-loader → postcss-loader → file-loader`
9+
- `sass-loader` 先将 `scss` 编译为 CSS。
10+
- `postcss-loader` 接手做 Tailwind 等 PostCSS 处理。
11+
- `file-loader` 最后写出 `.wxss` 文件(通过 `name: '[path][name].wxss'`)。
12+
13+
## Taro 系列(taro-app / taro-vue3-app / taro-webpack-tailwindcss-v4)
14+
- **核心实现**`demo/taro-app/node_modules/@tarojs/webpack5-runner/dist/webpack/MiniWebpackModule.js`
15+
- `getCSSLoaders`(行 101-157)定义基础栈:`MiniCssExtractPlugin.loader → css-loader → postcss-loader`,并在启用 CSS Modules 时换成带模块配置的 css-loader。
16+
- `addCSSLoader`(行 92-99)会克隆上述栈并在尾部追加预处理器:
17+
- `scss`/`sass``… → resolve-url-loader → sass-loader`(行 34-42)。
18+
- `less``… → less-loader`(行 43-46)。
19+
- `stylus``… → stylus-loader`(行 47-50)。
20+
- 结果:任意样式都会先被对应的预处理器处理,再经过 PostCSS,最后交给 `MiniCssExtractPlugin` 输出 `.wxss`
21+
22+
## Mpx 系列(mpx-app / mpx-tailwindcss-v4)
23+
- **基础栈**:继承 Vue CLI(`demo/mpx-app/node_modules/@vue/cli-service/lib/config/css.js:99-194`
24+
- 默认顺序:`MiniCssExtractPlugin.loader`(或 `vue-style-loader``→ css-loader → (cssnano 可选) → postcss-loader → 预处理器`
25+
- **Mpx 注入**`demo/mpx-app/node_modules/@mpxjs/webpack-plugin/lib/index.js:1883-2042`
26+
- `typeLoaderProcessInfo.styles` 定义了 `css-loader → @mpxjs/wxss/loader → @mpxjs/style-compiler` 的串联,并在 `injectStyleStripLoader` 里确保条件编译 loader 紧跟在 `stylus/sass/less/css/wxss` 之后。
27+
-`.mpx` 模块的样式来说,整体顺序为:`MiniCssExtractPlugin.loader → css-loader(被替换为 wxss-loader)→ @mpxjs/wxss/loader → @mpxjs/style-compiler → postcss-loader → 预处理器`
28+
29+
## Uni-app(uni-app / uni-app-webpack5 / uni-app-webpack-tailwindcss-v4)
30+
- **链路**`demo/uni-app/node_modules/@dcloudio/vue-cli-plugin-uni/lib/chain-webpack.js:40-146`
31+
- 每个 `cssLang``oneOf` 均执行 `css-loader → extract-css-loader(非 H5 平台)→ uniapp-preprocess → postcss-loader → 预处理器`
32+
- 若启用了缓存,则在 `css-loader` 之前自动插入 `cache-loader``uniapp-preprocess` 会在 `css-loader`、预处理器前后各执行一次条件编译。
33+
- H5 进一步在 `lib/h5/cssnano-options.js` 中为每种 `rule(oneOf)` 加上 `postcss-loader(cssnano)`,次序保持不变。
34+
35+
## Rax 小程序示例(rax-app)
36+
- **配置来源**`demo/rax-app/node_modules/.pnpm/node_modules/build-plugin-rax-app/lib/userConfig/atoms/inlineStyle.js`
37+
- 该 demo 的 `build.json` 目标是 `wechat-miniprogram`,属于 `miniappStandardList`
38+
- 默认(`inlineStyle: false`):`setCSSRule` 会走 `configPostCssLoader`,即 `MiniCssExtractPlugin.loader → postcss-loader → css-loader`,并在全局样式(`src/global.*`)上额外拉出一条「内联链路」。
39+
- 若设置 `inlineStyle: { forceEnableCSS: true }``css-module` 规则走“抽离链路”,普通 `css` 则拆成 `global` rule(抽离)和余下 rule(`vue-style-loader`/`stylesheet-loader` Inline)。
40+
-`inlineStyle` 显式设为 true,则所有规则都切到 `postcss-loader → stylesheet-loader` 的内联栈。
41+
- 因此本 demo 默认构建顺序是:`MiniCssExtractPlugin.loader → postcss-loader → css-loader`(抽离) + `postcss-loader → stylesheet-loader`(global.* 内联)。
42+
43+
## 说明
44+
- `uni-app-tailwindcss-v4` / `taro-app-vite` 等 Vite 工程不在本文范围。
45+
- 需要查看 loader 以外的 patch(例如 `UnifiedWebpackPluginV5` 注入的 runtime loader)时,可结合 `packages/weapp-tailwindcss/src/bundlers/webpack/BaseUnifiedPlugin`

packages/weapp-tailwindcss/src/bundlers/webpack/BaseUnifiedPlugin/v4.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,18 @@ export class UnifiedWebpackPluginV4 implements IBaseWebpackPlugin {
150150
if (runtimeLoaderRewriteOptions && runtimeCssImportRewriteLoaderExists && cssImportRewriteLoaderOptions) {
151151
const rewriteEntry = createCssImportRewriteLoaderEntry()
152152
if (rewriteEntry) {
153+
// 为了让 rewrite 在执行顺序上先于 postcss-loader,需要把它插到
154+
// postcss-loader 的后方(数组索引更大,执行时更靠前)。
153155
// @ts-ignore
154156
loaderEntries.splice(idx + 1, 0, rewriteEntry)
155157
}
156158
}
157159
if (runtimeClassSetLoaderExists) {
160+
const postcssIndex = loaderEntries.findIndex((entry: any) => entry?.loader?.includes?.('postcss-loader'))
161+
const insertIndex = postcssIndex === -1 ? idx : postcssIndex
162+
// 将 class-set 插在 postcss-loader 的当前位置(数组索引更小),这样在实际执行顺序里它会排在 postcss-loader 之后。
158163
// @ts-ignore
159-
loaderEntries.splice(idx, 0, createRuntimeClassSetLoaderEntry())
164+
loaderEntries.splice(insertIndex, 0, createRuntimeClassSetLoaderEntry())
160165
}
161166
})
162167
})

packages/weapp-tailwindcss/src/bundlers/webpack/BaseUnifiedPlugin/v5.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,15 @@ export class UnifiedWebpackPluginV5 implements IBaseWebpackPlugin {
150150
if (cssImportRewriteLoaderOptions && runtimeCssImportRewriteLoaderExists) {
151151
const rewriteLoaderEntry = createCssImportRewriteLoaderEntry()
152152
if (rewriteLoaderEntry) {
153+
// 让 rewrite 处于 postcss-loader 之后(数组索引更大),这样执行时会排在 postcss-loader 之前。
153154
loaderEntries.splice(idx + 1, 0, rewriteLoaderEntry)
154155
}
155156
}
156157
if (runtimeClassSetLoaderExists) {
157158
const classSetLoaderEntry = createRuntimeClassSetLoaderEntry()
158-
loaderEntries.splice(idx, 0, classSetLoaderEntry)
159+
const postcssIndex = loaderEntries.findIndex(entry => entry?.loader?.includes?.('postcss-loader'))
160+
const insertIndex = postcssIndex === -1 ? idx : postcssIndex
161+
loaderEntries.splice(insertIndex, 0, classSetLoaderEntry)
159162
}
160163
})
161164

packages/weapp-tailwindcss/src/bundlers/webpack/loaders/weapp-tw-css-import-rewrite-loader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-nocheck
22
import type { Buffer } from 'node:buffer'
33
import type webpack from 'webpack'
4+
import process from 'node:process'
45
import loaderUtils from 'loader-utils'
56
import { rewriteTailwindcssImportsInCode } from '@/bundlers/shared/css-imports'
67

@@ -44,6 +45,10 @@ const WeappTwCssImportRewriteLoader: webpack.LoaderDefinitionFunction<CssImportR
4445
this: webpack.LoaderContext<any>,
4546
source: string | Buffer,
4647
) {
48+
if (process.env.WEAPP_TW_LOADER_DEBUG) {
49+
// eslint-disable-next-line no-console
50+
console.log('[weapp-tw-css-import-rewrite-loader] executing for', this.resourcePath)
51+
}
4752
const opt = loaderUtils.getOptions(this)
4853
return transformSource(source, opt)
4954
}

packages/weapp-tailwindcss/src/bundlers/webpack/loaders/weapp-tw-runtime-classset-loader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-nocheck
22
import type { Buffer } from 'node:buffer'
33
import type webpack from 'webpack'
4+
import process from 'node:process'
45
import loaderUtils from 'loader-utils'
56

67
interface RuntimeClassSetLoaderOptions {
@@ -11,6 +12,10 @@ const WeappTwRuntimeClassSetLoader: webpack.LoaderDefinitionFunction<RuntimeClas
1112
this: webpack.LoaderContext<any>,
1213
source: string | Buffer,
1314
) {
15+
if (process.env.WEAPP_TW_LOADER_DEBUG) {
16+
// eslint-disable-next-line no-console
17+
console.log('[weapp-tw-runtime-classset-loader] executing for', this.resourcePath)
18+
}
1419
const opt = loaderUtils.getOptions(this)
1520
const maybePromise = opt?.getClassSet?.()
1621
if (maybePromise && typeof (maybePromise as PromiseLike<void>).then === 'function') {

0 commit comments

Comments
 (0)