/** * extract-naive-styles.mjs * * 在构建时直接调用 NaiveUI 组件的 .cssr.mjs 样式函数的 render() 方法, * 提取所有组件的运行时 JSS 为静态 CSS 文件。 * 彻底解决 WordPress 后台环境中样式丢失问题。 * * 原理: * 每个 NaiveUI 组件都有 src/styles/index.cssr.mjs 文件 * 导出的函数返回 css-render 的 CNode 对象 * 调用 CNode.render({ clsPrefix: 'n' }) 即可获取纯 CSS 字符串 * 无需虚拟 DOM、无需 Vue、无任何浏览器 API 依赖 */ import { writeFile, mkdir } from 'fs/promises' import { dirname, join } from 'path' import { fileURLToPath } from 'url' const __dirname = dirname(fileURLToPath(import.meta.url)) const BUILD_DIR = join(__dirname, 'build') // ============================================================ // 需要提取样式的组件列表(对应各组件的 .cssr.mjs 入口) // 格式:[导入路径在 naive-ui/es 内部的相对路径] // ============================================================ const COMPONENT_STYLES = [ // 内部基础组件 ['_internal/scrollbar/src/styles/index.cssr.mjs'], // 布局 ['layout/src/styles/index.cssr.mjs'], ['layout-sider/src/styles/index.cssr.mjs'], // 导航 ['menu/src/styles/index.cssr.mjs'], ['breadcrumb/src/styles/index.cssr.mjs'], ['tabs/src/styles/index.cssr.mjs'], ['steps/src/styles/index.cssr.mjs'], ['pagination/src/styles/index.cssr.mjs'], // 表单 ['form/src/styles/index.cssr.mjs'], ['form-item/src/styles/index.cssr.mjs'], ['input/src/styles/index.cssr.mjs'], ['input-number/src/styles/index.cssr.mjs'], ['switch/src/styles/index.cssr.mjs'], ['select/src/styles/index.cssr.mjs'], ['radio/src/styles/index.cssr.mjs'], ['checkbox/src/styles/index.cssr.mjs'], ['slider/src/styles/index.cssr.mjs'], ['rate/src/styles/index.cssr.mjs'], ['date-picker/src/styles/indexcode/index.cssr.mjs'], ['time-picker/src/styles/index.cssr.mjs'], // 展示 ['card/src/styles/index.cssr.mjs'], ['tag/src/styles/index.cssr.mjs'], ['alert/src/styles/index.cssr.mjs'], ['data-table/src/styles/index.cssr.mjs'], ['descriptions/src/styles/index.cssr.mjs'], ['empty/src/styles/index.cssr.mjs'], ['result/src/styles/index.cssr.mjs'], ['statistic/src/styles/index.cssr.mjs'], ['list/src/styles/index.cssr.mjs'], ['collapse/src/styles/index.cssr.mjs'], ['timeline/src/styles/index.cssr.mjs'], // 反馈 ['button/src/styles/index.cssr.mjs'], ['modal/src/styles/index.cssr.mjs'], ['dialog/src/styles/index.cssr.mjs'], ['message/src/styles/index.cssr.mjs'], ['notification/src/styles/index.cssr.mjs'], ['popconfirm/src/styles/index.cssr.mjs'], ['popover/src/styles/index.cssr.mjs'], ['tooltip/src/styles/index.cssr.mjs'], ['dropdown/src/styles/index.cssr.mjs'], // 其他 ['icon/src/styles/index.cssr.mjs'], ['icon-wrapper/src/styles/index.cssr.mjs'], ['space/src/styles/index.cssr.mjs'], ['divider/src/styles/index.cssr.mjs'], ['gradient-text/src/styles/index.cssr.mjs'], ['ellipsis/src/styles/index.cssr.mjs'], ['spin/src/styles/index.cssr.mjs'], ['loading-bar/src/styles/index.cssr.mjs'], ['upload/src/styles/index.cssr.mjs'], ['image/src/styles/index.cssr.mjs'], ['carousel/src/styles/index.cssr.mjs'], ['transfer/src/styles/index.cssr.mjs'], ['tree-select/src/styles/index.cssr.mjs'], ['tree/src/styles/index.cssr.mjs'], ['thing/src/styles/index.cssr.mjs'], ['color-picker/src/styles/indexcode/index.cssr.mjs'], ['typography/src/styles/index.cssr.mjs'], ['auto-complete/src/styles/index.cssr.mjs'], ['cascader/src/styles/index.cssr.mjs'], ['dynamic-input/src/styles/index.cssr.mjs'], ['dynamic-tags/src/styles/index.cssr.mjs'], ['input-otp/src/styles/indexcode/index.cssr.mjs'], // 过渡动画 ['_styles/transitions/fade-in-width-expand.cssr.mjs'], ['_styles/transitions/fade-in-height-expand.cssr.mjs'], ['_styles/transitions/icon-switch.cssr.mjs'], ['_styles/transitions/scale-transition.cssr.mjs'], // 全局 ['global-style/src/styles/index.cssr.mjs'], ] async function extractStyles() { console.log('\n[AKSRT] 开始提取 NaiveUI 组件样式...\n') const allCss = [] let successCount = 0 let failCount = 0 for (const [componentPath] of COMPONENT_STYLES) { try { // 动态导入每个组件的 cssr 样式模块 const styleModule = await import( `naive-ui/es/${componentPath}` ) const defaultExport = styleModule.default || styleModule if (typeof defaultExport === 'function') { // 直接调用 render() 提取 CSS const css = defaultExport({ clsPrefix: 'n', // 使用 light 主题变量 }) if (typeof css === 'string' && css.trim()) { allCss.push(`/* ${componentPath} */\n${css}`) successCount++ } } else if (defaultExport && typeof defaultExport.render === 'function') { // 如果是 CNode 对象,调用 render() const css = defaultExport.render() if (typeof css === 'string' && css.trim()) { allCss.push(`/* ${componentPath} */\n${css}`) successCount++ } } } catch (err) { // 某些路径可能不存在或结构不同,跳过但不中断 failCount++ } } const cssOutput = allCss.join('\n\n') if (!cssOutput.trim()) { console.warn('[AKSRT] 警告:未提取到任何 NaiveUI 样式!') return null } // 确保 build 目录存在 await mkdir(BUILD_DIR, { recursive: true }) // 写入 CSS 文件 const cssFile = join(BUILD_DIR, 'naive-ui.css') const fullContent = `/* Auto-extracted by extract-naive-styles.mjs */ /* Naive UI v2 Component Styles - Build-time extraction */ /* DO NOT EDIT MANUALLY */ ${cssOutput}` await writeFile(cssFile, fullContent, 'utf-8') console.log(`[AKSRT] NaiveUI 样式提取完成:`) console.log(` 成功: ${successCount} 个组件`) console.log(` 跳过: ${failCount} 个组件`) console.log(` 输出: ${cssFile}`) console.log(` 大小: ${(Buffer.byteLength(fullContent) / 1024).toFixed(1)} KB\n`) return cssFile } // 执行 extractStyles().catch((err) => { console.error('[AKSRT] 样式提取失败:', err) process.exit(1) })