Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .cursor/skills/nutui-taro-weapp-wxss/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
name: nutui-taro-weapp-wxss
description: >-
NutUI React Taro 小程序 WXSS 编译超时修复:nutui-taro-demo 样式 bundle 合并、
删除无效 renderingMode、app-origin.wxss 体积治理。Use when 微信小程序报
[ WXSS 文件编译错误] something must be wrong、wcsc timeout、app-origin.wxss
过大、predev:taro 重写 app.config、或修改 generate-nutui-taro.js /
nutui.react.scss.taro.bundle.scss。
---

# NutUI Taro 小程序 WXSS 编译治理

## 1. 背景与根因

**现象**:`[ WXSS 文件编译错误] something must be wrong` + `Error: timeout`;偶发 `无效的 app.json ["renderingMode"]`。

**根因**:`app.ts` → `nutui.react.scss.taro.ts` 曾 **100+ 条**独立 `import` 组件 scss;Taro `sass.resource` 向**每个**文件注入 `variables.scss` + `theme-default.scss`(含 base64 `@font-face`)。

| 指标 | 修复前 | 修复后(bundle) |
| --- | --- | --- |
| `app-origin.wxss` | ~4MB | ~0.3–0.9MB |
| `@font-face` 重复 | ~216 | ~2–10 |

Comment on lines +22 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

验收口径需要统一(“2–10”与“个位数”冲突)。

Line 22-23 写的是修复后 @font-face2–10,但 Line 85 期望写“个位数”。建议统一成同一阈值(例如“<=10”或“1–9”),避免验证时标准不一致。

Also applies to: 85-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/skills/nutui-taro-weapp-wxss/SKILL.md around lines 22 - 23, Summary:
The acceptance criteria for duplicate `@font-face` counts are inconsistent
between the table (“2–10”) and line 85 (“个位数”); make them consistent. Fix:
choose a single threshold phrasing (e.g., "<=10" or "1–9") and update both the
table row containing "`@font-face` 重复 | ~216 | ~2–10 |" and the sentence on line
85 that currently says “个位数” so they match exactly; ensure the chosen
representation is used consistently elsewhere in the document.

**不是**单个组件(如 `switch.scss`)的 bug,而是 **demo 全量样式打包方式** 导致。

**数据流(修复后)**:

```text
app.ts → nutui.react.scss.taro.ts → nutui.react.scss.taro.bundle.scss
├─ variables + theme(各 1 次)
└─ 全部组件 scss → dist/weapp/app-origin.wxss
```

---

## 2. 最小改动清单

| 文件 | 改动 |
| --- | --- |
| `scripts/taro/generate-nutui-taro.js` | 新增 `bundleScssStr`;遍历组件拼 `@import`;`nutui.react.scss.taro.ts` 改为单 import bundle;新增写 `nutui.react.scss.taro.bundle.scss` |
| `src/packages/nutui.react.scss.taro.bundle.scss` | variables/theme + 全部组件 `@import`(**自动生成,勿手改**) |
| `scripts/build-taro.mjs` | `buildCSS` / `buildHarmonyCSS` 的 glob 排除 `nutui.react.scss.taro.bundle.scss`,避免打进 npm 按组件发包 |
| `scripts/harmony/update-taro-entry.js` | 删除 `renderingMode: 'mixed'` |
| `scripts/taro/generate-taro-route.js` | 同上 |

- `nutui.react.scss.ts`(H5)**不变**,`generate-nutui.js` 仍生成 100+ 条 JS import
- **勿只改** `app.config.ts`:`predev:taro` 会跑 `update-taro-entry.js` 覆盖
- **不在最小集**:`config/index.js` minimize、各组件 scss calc 优化、`package.json` dev:weapp

### 边界说明(H5 / 发包)

| 场景 | 是否走 bundle | 说明 |
| --- | --- | --- |
| H5 文档站 `sites/mobile` + `nutui.react.scss.ts` | 否 | Vite `additionalData` 只注入 variables,theme 在 `main.tsx` 单独 import 一次,无 wcsc 超时问题 |
| Taro demo `dev:taro`(weapp / h5) | 是 | `app.ts` → `nutui.react.scss.taro.ts` → bundle |
| Taro 发包 `build:taro` | 否 | bundle 被 glob exclude;构建结束会删除 `nutui.react.scss.js`;业务方仍 `import '@nutui/nutui-react-taro/dist/es/packages/xxx/style'` |

---

## 3. 解决 / 好处 / 影响

**解决**:WXSS 超时;无效 `renderingMode` 警告;demo 可预览(需登录开发者工具 + 清缓存)。

**好处**:样式单入口;不改 `build:taro` 发包;新增组件仍由 `generate-nutui-taro.js` 写入 bundle。

| 命令 | 影响 |
| --- | --- |
| `dev:taro weapp` | 有 |
| `build:taro` / `build:taro:demo` | 无(bundle 已在 `build-taro.mjs` glob 中排除) |
| `prepare` / `generate:file:taro` | 有(再生 bundle) |
| `VITE_APP_PROJECT_ID=jmapp` | bundle 写死 default 主题,需另行验证 |

**已知限制**:`build:weapp` 生产压缩时 postcss-calc 可能破坏嵌套 `var+calc`(dev 无此问题);游客模式 `webapi_getwxaasyncsecinfo:fail` 为环境问题。

---

## 4. 验证

```bash
pnpm run dev:taro weapp
wc -c packages/nutui-taro-demo/dist/weapp/app-origin.wxss
grep -c '@font-face' packages/nutui-taro-demo/dist/weapp/app-origin.wxss
```

期望:体积远小于 4MB;`@font-face` 为个位数。

---

## 5. 未采纳方案(备查)

| 方案 | 未采纳原因 |
| --- | --- |
| 各组件 scss 局部 CSS 变量 | 非主因,扩大 diff |
| `config/index.js` minimize(false) | dev 不需要 |
| `webpackImporter: false` | 引发 Module not found |
| sass.resource 置空 | 嵌套 @import(popup→overlay)缺变量 |
| 手改 app.config.ts | predev:taro 会覆盖 |

---

## 6. Agent 自检

- [ ] 只改生成脚本 + bundle + `build-taro.mjs` exclude,未扩大组件 scss diff?
- [ ] `build-taro.mjs` 已排除 `nutui.react.scss.taro.bundle.scss`?
- [ ] 两个生成脚本已去掉 `renderingMode`?
- [ ] 仍超时:查 `app-origin.wxss` 体积、游客模式、是否未重新 dev
11 changes: 9 additions & 2 deletions scripts/build-taro.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,11 @@ async function copyStyles() {
// 构建样式
async function buildCSS(themeName = '') {
const componentScssFiles = await glob(['src/packages/**/*.scss'], {
ignore: ['src/packages/**/demo.scss'],
ignore: [
'src/packages/**/demo.scss',
// demo 专用全量 bundle,不参与 npm 按组件发包
'src/packages/nutui.react.scss.taro.bundle.scss',
],
})

const variables = await readFile(
Expand Down Expand Up @@ -438,7 +442,10 @@ async function buildCSS(themeName = '') {
// 构建样式
async function buildHarmonyCSS(themeName = '') {
const componentScssFiles = await glob(['src/packages/**/*.scss'], {
ignore: ['src/packages/**/demo.scss'],
ignore: [
'src/packages/**/demo.scss',
'src/packages/nutui.react.scss.taro.bundle.scss',
],
})

const variables = await readFile(
Expand Down
6 changes: 5 additions & 1 deletion scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,11 @@ async function copyStyles() {
// 构建样式
async function buildCSS(themeName = '') {
const componentScssFiles = await glob(['src/packages/**/*.scss'], {
ignore: ['src/packages/**/demo.scss'],
ignore: [
'src/packages/**/demo.scss',
// Taro demo 专用全量 bundle,不参与 H5 npm 按组件发包
'src/packages/nutui.react.scss.taro.bundle.scss',
],
})

const variables = await readFile(
Expand Down
1 change: 0 additions & 1 deletion scripts/harmony/update-taro-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ export default defineAppConfig({
navigationBarTitleText: 'NutUI-React',
navigationBarTextStyle: 'black'
},
renderingMode: 'mixed',
components: ['pages/index/index', ...(subPackages.map(subPackage => {
return subPackage.pages.map(page => \`\${subPackage.root}/\${page}\`)
}).flat())]
Expand Down
14 changes: 12 additions & 2 deletions scripts/taro/generate-nutui-taro.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let importStr = ``
let importRNStr = ``
let importMarkdownStr = ``
let importScssStr = `\n`
let bundleScssStr = `@import '../styles/variables.scss';\n@import '../styles/theme-default.scss';\n`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

生成的 bundle @import 与当前 stylelint 规则不兼容,会持续产出 lint 错误。

现在脚本会生成带 .scss 后缀的 @import,与 scss/load-partial-extension 规则冲突(在产物文件中已触发整文件报错)。建议在生成阶段去掉后缀,并回刷 nutui.react.scss.taro.bundle.scss

建议修复
-let bundleScssStr = `@import '../styles/variables.scss';\n@import '../styles/theme-default.scss';\n`
+let bundleScssStr = `@import '../styles/variables';\n@import '../styles/theme-default';\n`

...
-    bundleScssStr += `@import './${name.toLowerCase()}/${name.toLowerCase()}.scss';\n`
+    bundleScssStr += `@import './${name.toLowerCase()}/${name.toLowerCase()}';\n`

Also applies to: 25-25, 115-122

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/taro/generate-nutui-taro.js` at line 10, The generated bundleScssStr
currently emits `@import` with “.scss” suffix (variable bundleScssStr in
scripts/taro/generate-nutui-taro.js), which violates the
scss/load-partial-extension lint rule; update the generator to emit imports
without the “.scss” extension (e.g. "`@import` '../styles/variables';" and
"`@import` '../styles/theme-default';") and apply the same change to the other
generator occurrences noted around the file (the other bundle string
constructions). After changing the generator, regenerate and commit the produced
nutui.react.scss.taro.bundle.scss artifact so the repo no longer contains
lint-failing imports.

Source: Linters/SAST tools

const packages = []
const mds = []
const raws = []
Expand All @@ -21,6 +22,7 @@ config.nav.map((item) => {
importRNStr += `import ${name} from '@/packages/${name.toLowerCase()}/index.${rn?'rn':'taro'}'\n`
importRNStr += `export * from '@/packages/${name.toLowerCase()}/index.${rn?'rn':'taro'}'\n`
importScssStr += `import '@/packages/${name.toLowerCase()}/${name.toLowerCase()}.scss'\n`
bundleScssStr += `@import './${name.toLowerCase()}/${name.toLowerCase()}.scss';\n`
packages.push(name)

glob
Expand Down Expand Up @@ -99,8 +101,7 @@ fs.outputFile(
// }
// )

let taroScssfileStr = `
${importScssStr}
let taroScssfileStr = `import './nutui.react.scss.taro.bundle.scss'
export default { "NutUI":"NutUI-Taro" };`
fs.outputFile(
path.resolve(__dirname, '../../src/packages/nutui.react.scss.taro.ts'),
Expand All @@ -111,6 +112,15 @@ fs.outputFile(
}
)

fs.outputFile(
path.resolve(__dirname, '../../src/packages/nutui.react.scss.taro.bundle.scss'),
bundleScssStr,
'utf8',
(error) => {
if (error) throw error
}
)

fs.outputFile(
path.resolve(__dirname, '../../src/packages/nutui.react.scss.ts'),
importScssStr,
Expand Down
1 change: 0 additions & 1 deletion scripts/taro/generate-taro-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export default defineAppConfig ({
navigationBarTitleText: 'NutUI-React',
navigationBarTextStyle: 'black'
},
renderingMode: 'mixed',
components: ['pages/index/index', ...(subPackages.map(subPackage => {
return subPackage.pages.map(page => \`\${subPackage.root}/\${page}\`)
}).flat())]
Expand Down
107 changes: 107 additions & 0 deletions src/packages/nutui.react.scss.taro.bundle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
@import '../styles/variables.scss';
@import '../styles/theme-default.scss';
@import './button/button.scss';
@import './cell/cell.scss';
@import './cellgroup/cellgroup.scss';
@import './configprovider/configprovider.scss';
@import './image/image.scss';
@import './overlay/overlay.scss';
@import './divider/divider.scss';
@import './grid/grid.scss';
@import './griditem/griditem.scss';
@import './layout/layout.scss';
@import './col/col.scss';
@import './row/row.scss';
@import './safearea/safearea.scss';
@import './space/space.scss';
@import './sticky/sticky.scss';
@import './backtop/backtop.scss';
@import './elevator/elevator.scss';
@import './fixednav/fixednav.scss';
@import './hoverbutton/hoverbutton.scss';
@import './hoverbuttonitem/hoverbuttonitem.scss';
@import './navbar/navbar.scss';
@import './sidebar/sidebar.scss';
@import './sidebaritem/sidebaritem.scss';
@import './tabbar/tabbar.scss';
@import './tabbaritem/tabbaritem.scss';
@import './tabpane/tabpane.scss';
@import './tabs/tabs.scss';
@import './address/address.scss';
@import './calendar/calendar.scss';
@import './calendaritem/calendaritem.scss';
@import './calendarcard/calendarcard.scss';
@import './cascader/cascader.scss';
@import './checkbox/checkbox.scss';
@import './checkboxgroup/checkboxgroup.scss';
@import './datepicker/datepicker.scss';
@import './datepickerview/datepickerview.scss';
@import './form/form.scss';
@import './formitem/formitem.scss';
@import './input/input.scss';
@import './inputnumber/inputnumber.scss';
@import './menu/menu.scss';
@import './menuitem/menuitem.scss';
@import './numberkeyboard/numberkeyboard.scss';
@import './picker/picker.scss';
@import './pickerview/pickerview.scss';
@import './radio/radio.scss';
@import './radiogroup/radiogroup.scss';
@import './range/range.scss';
@import './rate/rate.scss';
@import './searchbar/searchbar.scss';
@import './shortpassword/shortpassword.scss';
@import './signature/signature.scss';
@import './switch/switch.scss';
@import './textarea/textarea.scss';
@import './uploader/uploader.scss';
@import './actionsheet/actionsheet.scss';
@import './badge/badge.scss';
@import './dialog/dialog.scss';
@import './drag/drag.scss';
@import './empty/empty.scss';
@import './infiniteloading/infiniteloading.scss';
@import './loading/loading.scss';
@import './noticebar/noticebar.scss';
@import './notify/notify.scss';
@import './popover/popover.scss';
@import './popup/popup.scss';
@import './pulltorefresh/pulltorefresh.scss';
@import './resultpage/resultpage.scss';
@import './skeleton/skeleton.scss';
@import './swipe/swipe.scss';
@import './toast/toast.scss';
@import './animate/animate.scss';
@import './animatingnumbers/animatingnumbers.scss';
@import './audio/audio.scss';
@import './avatar/avatar.scss';
@import './avatargroup/avatargroup.scss';
@import './circleprogress/circleprogress.scss';
@import './collapse/collapse.scss';
@import './collapseitem/collapseitem.scss';
@import './countdown/countdown.scss';
@import './ellipsis/ellipsis.scss';
@import './imagepreview/imagepreview.scss';
@import './indicator/indicator.scss';
@import './lottie/lottie.scss';
@import './pagination/pagination.scss';
@import './price/price.scss';
@import './progress/progress.scss';
@import './segmented/segmented.scss';
@import './step/step.scss';
@import './steps/steps.scss';
@import './swiper/swiper.scss';
@import './swiperitem/swiperitem.scss';
@import './table/table.scss';
@import './tag/tag.scss';
@import './tour/tour.scss';
@import './video/video.scss';
@import './virtuallist/virtuallist.scss';
@import './quickenter/quickenter.scss';
@import './avatarcropper/avatarcropper.scss';
@import './barrage/barrage.scss';
@import './card/card.scss';
@import './timedetail/timedetail.scss';
@import './timeselect/timeselect.scss';
@import './trendarrow/trendarrow.scss';
@import './watermark/watermark.scss';
Loading