【本次核心 · S6-P21】 - 族层数据:families.json + 44 份契约注入 family/familyRole/familyParams; data.json / data.js / site/details 同步。13 族 / 44 成员 / 35 独立 → 概念组件 79→48。 - 79 个 slug 全保留、集合逐一不变(铁律 5 对外承诺未破);frameworks 仍 395 文件、薄壳仍 79。 - 归族判据为契约中可核对字段(semanticTypeCandidates 重叠 / anatomy 为同一骨架子集 / 变体维度同构 / doNotInvent 显式从属声明),每族 mergeBasis 写明依据,不按名字猜。 - 实现层合并(导航族端到端切片):tools/gen-family-impl.mjs 从 5 端模板生成 TopMenu / SideMenu / MixedNavigation 共 15 文件,参数 direction=top|side|mixed; 三份 CSS md5 完全相同 = 一份样式表服务三个组件。 - 新增 tools/gen-families.mjs、tools/gen-family-impl.mjs、tools/verify-families.mjs、 tools/lib/family-model.mjs、tools/lib/family-impl/nav-menu/*.tpl。 【同时清掉此前已完成但未提交的批次】 生成物(data.json / data.js / site/sources / site/components 薄壳 / sitemap.xml / tests 报告) 跨阶段交织,无法拆成互相自洽的多个提交,故按既有批量风格合并提交: - Package:三端可 import(S5-P18)+ 发布到私有 npm 源 - Docs site:导航语言改下拉(S5-P19)、详情页代码块默认展开、中英切换完整性 - Security:生产部署链审计修复(2026-09-19)+ 线上部署 - Theme modes 日间/夜间/自动;S1-P4 data.js 瘦身;S2-P5 暗色;S2-P6 跨端一致性; S2-P7 行为断言;S2-P9 FAQ;S3-P8 RTL;S3-P9 契约缺口解释层;S4-P12 发布流程 - 补入 tools/pack-deploy.mjs、run-site-smoke.mjs、verify-*.mjs,.dockerignore、 安全审计修复与待决策项.md 【验收】 - node tools/verify-families.mjs → OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变) - node tools/verify-cross-platform.mjs → 79/79 identical(HEAD 基线 high 44) - node tools/run-regression.mjs → 100%(79/79 页,1017/1017 断言,N/A 34),连跑 8 次一致,0 超时 - 逐页实测:topmenu / sidemenu / mixednavigation 各 13/13,帧内 direction 参数正确,0 JS 错误 - 零运行时依赖 OK;build-site.ps1 ASCII-only OK 【未纳入】site/components/<slug>/ 平台薄壳 316 个 —— 历史从未跟踪且属构建产物,保持现状。
449 lines
16 KiB
JavaScript
449 lines
16 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* build-dist.mjs — 产出可分发产物(npm 包 / CDN)
|
||
*
|
||
* 设计约束:
|
||
* - 零运行时依赖:只用 Node 内置能力(fs / path)
|
||
* - 不依赖 build-site.ps1(那是 Windows 专用的文档站构建)
|
||
* - 产物自包含:dist/ 里的东西拷走就能用
|
||
*
|
||
* 输入:
|
||
* - .design_library/aurora-admin/colors_and_type.css 设计令牌
|
||
* - .design_library/aurora-admin/components.css 6 核心组件样式聚合
|
||
* - .design_library/aurora-admin/css.json 结构化令牌
|
||
* - frameworks/*.css 79 个组件样式
|
||
* - frameworks/*.html 79 个演示页
|
||
*
|
||
* 输出:
|
||
* - dist/tokens/tokens.css 设计令牌(可直接 @import)
|
||
* - dist/tokens/tokens.json W3C DTCG 格式
|
||
* - dist/tokens/figma.json Figma Tokens 格式
|
||
* - dist/components/<slug>.css 单个组件样式
|
||
* - dist/components/index.css 全部组件样式聚合
|
||
* - dist/components/<slug>.html 静态演示页(可独立打开)
|
||
* - dist/react/index.js React 端聚合入口(可 import)
|
||
* - dist/react/<Prefix>.jsx 单组件源码(按需 import)
|
||
* - dist/vue3/index.js Vue 3 端聚合入口(可 import)
|
||
* - dist/vue3/<Prefix>.vue 单组件 SFC 源码(按需 import)
|
||
* - dist/vue2/index.js Vue 2 端聚合入口(可 import)
|
||
* - dist/vue2/<Prefix>.vue 单组件 SFC 源码(按需 import)
|
||
* - dist/manifest.json 组件清单 + 版本 + 校验和
|
||
* - dist/README.md 用法速查
|
||
*/
|
||
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'fs';
|
||
import { createHash } from 'crypto';
|
||
import { join, dirname } from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const ROOT = join(__dirname, '..');
|
||
const LIB = join(ROOT, '.design_library', 'aurora-admin');
|
||
const SRC = join(ROOT, 'frameworks');
|
||
const DIST = join(ROOT, 'dist');
|
||
|
||
/* ---------- 工具 ---------- */
|
||
|
||
function ensure(dir) {
|
||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||
}
|
||
|
||
function read(p) {
|
||
return readFileSync(p, 'utf8');
|
||
}
|
||
|
||
function write(p, content) {
|
||
ensure(dirname(p));
|
||
// 统一 LF,避免跨平台差异
|
||
writeFileSync(p, content.replace(/\r\n/g, '\n'));
|
||
}
|
||
|
||
function sha256(content) {
|
||
return createHash('sha256').update(content).digest('hex').slice(0, 16);
|
||
}
|
||
|
||
/* ---------- 1. 读组件清单 ---------- */
|
||
|
||
const idxPath = join(LIB, 'components', 'index.json');
|
||
if (!existsSync(idxPath)) {
|
||
console.error('[FATAL] 找不到 components/index.json');
|
||
process.exit(1);
|
||
}
|
||
const idx = JSON.parse(read(idxPath));
|
||
const components = idx.components;
|
||
|
||
const pkg = JSON.parse(read(join(ROOT, 'package.json')));
|
||
const version = pkg.version;
|
||
|
||
console.log(`[build-dist] 版本 ${version} / ${components.length} 个组件`);
|
||
|
||
/* ---------- 2. 令牌 ---------- */
|
||
|
||
const tokenCss = read(join(LIB, 'colors_and_type.css'));
|
||
|
||
// 从令牌文件解析出 name/value/comment(与 build-site.ps1 同一套正则思路)
|
||
const tokens = [];
|
||
const TOKEN_RE = /--(au-[a-z0-9-]+)\s*:\s*([^;]+);(?:\s*\/\*\s*(.*?)\s*\*\/)?/g;
|
||
let m;
|
||
while ((m = TOKEN_RE.exec(tokenCss)) !== null) {
|
||
tokens.push({ name: m[1], value: m[2].trim(), comment: (m[3] || '').trim() });
|
||
}
|
||
|
||
write(join(DIST, 'tokens', 'tokens.css'), tokenCss);
|
||
|
||
// W3C DTCG
|
||
function groupOf(name) {
|
||
if (/color|brand|text|bg|border|success|warning|error|info|mask/.test(name)) return 'color';
|
||
if (/font|type/.test(name)) return 'typography';
|
||
if (/radius/.test(name)) return 'radius';
|
||
if (/space|gap|padding/.test(name)) return 'spacing';
|
||
if (/shadow|elevation/.test(name)) return 'shadow';
|
||
if (/duration|ease|motion/.test(name)) return 'motion';
|
||
if (/size|height|width|icon/.test(name)) return 'size';
|
||
return 'misc';
|
||
}
|
||
const dtcg = {};
|
||
for (const t of tokens) {
|
||
const g = groupOf(t.name);
|
||
dtcg[g] = dtcg[g] || {};
|
||
dtcg[g][t.name] = { $value: t.value, $type: g === 'color' ? 'color' : 'dimension', $description: t.comment || undefined };
|
||
}
|
||
write(
|
||
join(DIST, 'tokens', 'tokens.json'),
|
||
JSON.stringify(
|
||
{ $schema: 'https://schemas.design-tokens.org/draft/dtcg.json', $metadata: { name: 'Aurora Admin', version }, tokens: dtcg },
|
||
null,
|
||
2
|
||
)
|
||
);
|
||
|
||
// Figma Tokens
|
||
const figma = {};
|
||
for (const t of tokens) {
|
||
const g = groupOf(t.name);
|
||
figma[t.name] = {
|
||
value: t.value,
|
||
type: g === 'color' ? 'color' : g === 'size' ? 'sizing' : g === 'spacing' ? 'spacing' : g === 'radius' ? 'borderRadius' : 'other',
|
||
description: t.comment || '',
|
||
};
|
||
}
|
||
write(join(DIST, 'tokens', 'figma.json'), JSON.stringify({ global: figma, $themes: [], $metadata: { tokenSetOrder: ['global'] } }, null, 2));
|
||
|
||
console.log(`[build-dist] 令牌 ${tokens.length} 个 → dist/tokens/`);
|
||
|
||
/* ---------- 3. 组件样式 ---------- */
|
||
|
||
const manifest = {
|
||
name: pkg.name,
|
||
version,
|
||
generated: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
||
tokens: tokens.length,
|
||
components: [],
|
||
};
|
||
|
||
const aggregate = [
|
||
'/* Aurora Admin — 全部组件样式聚合',
|
||
` * 版本 ${version} · ${components.length} 个组件`,
|
||
' * 用法:在令牌之后引入本文件即可获得全部组件样式',
|
||
' */',
|
||
'',
|
||
/* 本文件位于 dist/components/,令牌在 dist/tokens/ —— 故必须是 ../tokens/ */
|
||
`@import url('../tokens/tokens.css');`,
|
||
'',
|
||
].join('\n');
|
||
|
||
const parts = [aggregate];
|
||
|
||
for (const c of components) {
|
||
const slug = c.slug;
|
||
const prefix = c.frameworksPrefix;
|
||
|
||
// 组件 CSS
|
||
const cssPath = join(SRC, `${prefix}.css`);
|
||
if (existsSync(cssPath)) {
|
||
const css = read(cssPath);
|
||
write(join(DIST, 'components', `${slug}.css`), `/* Aurora Admin · ${c.name} */\n${css}`);
|
||
parts.push(`/* ---- ${c.name} (${slug}) ---- */`, css, '');
|
||
}
|
||
|
||
// 演示页(重写引用,使其在 dist 下能独立打开)
|
||
const htmlPath = join(SRC, `${prefix}.html`);
|
||
let hasDemo = false;
|
||
if (existsSync(htmlPath)) {
|
||
let html = read(htmlPath);
|
||
/* dist 的目录结构:
|
||
dist/components/<slug>.html
|
||
dist/components/<slug>.css
|
||
dist/tokens/tokens.css
|
||
因此所有引用都要重写。漏掉文件名是最容易犯的错 —— 会产出「路径对但文件不存在」的坏产物。 */
|
||
html = html.replace(/\.\.\/\.design_library\/aurora-admin\/colors_and_type\.css/g, '../tokens/tokens.css');
|
||
html = html.replace(/\.\.\/\.design_library\/aurora-admin\/components\.css/g, './index.css');
|
||
html = html.replace(/\.\.\/\.design_library\/aurora-admin\//g, '../tokens/');
|
||
// 组件自身样式:./<Prefix>.css → ./<slug>.css
|
||
html = html.replace(new RegExp(`(\\./)?${prefix}\\.css`, 'g'), `./${slug}.css`);
|
||
write(join(DIST, 'components', `${slug}.html`), html);
|
||
hasDemo = true;
|
||
}
|
||
|
||
manifest.components.push({
|
||
slug,
|
||
name: c.name,
|
||
category: c.category,
|
||
tier: c.tier,
|
||
css: existsSync(cssPath) ? `components/${slug}.css` : null,
|
||
demo: hasDemo ? `components/${slug}.html` : null,
|
||
cssHash: existsSync(cssPath) ? sha256(read(cssPath)) : null,
|
||
});
|
||
}
|
||
|
||
write(join(DIST, 'components', 'index.css'), parts.join('\n'));
|
||
write(join(DIST, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
||
|
||
const cssCount = readdirSync(join(DIST, 'components')).filter((f) => f.endsWith('.css')).length;
|
||
const htmlCount = readdirSync(join(DIST, 'components')).filter((f) => f.endsWith('.html')).length;
|
||
console.log(`[build-dist] 组件 ${components.length} 个 → dist/components/(${cssCount} css / ${htmlCount} html)`);
|
||
|
||
/* ---------- 3.5 框架端组件入口(可 import)----------
|
||
* 目标:让 `import { AaButton } from 'aurora-admin-design/react'` 这类用法成立。
|
||
* 策略(零构建依赖):
|
||
* - React:直接发 .jsx 源码 + 聚合入口(项目自己的构建链编译 JSX)
|
||
* - Vue 3:直接发 .vue SFC 源码 + 聚合入口(样式在 SFC 内,随组件走)
|
||
* - Vue 2:同样发 .vue 源码;另给一份「已编译」的 JS(见下),
|
||
* 因为 Vue 2 的 .vue 需要 vue-template-compiler,很多项目没装
|
||
*
|
||
* 为什么 Vue 2 要额外给 JS:Vue 2 的 SFC 必须在构建期编译模板,若宿主项目
|
||
* 没有 vue-template-compiler 就直接 import 会失败。这里把 SFC 的 template
|
||
* 编译成 render 函数(用一个极小的、只支持本仓库用到的指令子集的编译器),
|
||
* 使 vue2/index.js 在不具备 SFC 编译能力的项目里也能用。
|
||
*/
|
||
|
||
const FW_PREFIX = new Map(components.map((c) => [c.frameworksPrefix, c]));
|
||
|
||
/* 组件名:Vue2 用 SFC 里的 name(AaXxx),React 用函数名,Vue3 用文件名推导 */
|
||
function pascal(slug, prefix) {
|
||
return prefix;
|
||
}
|
||
|
||
const reactImports = [];
|
||
const vue3Imports = [];
|
||
const vue2Imports = [];
|
||
|
||
for (const c of components) {
|
||
const prefix = c.frameworksPrefix;
|
||
|
||
// React
|
||
const jsxPath = join(SRC, `${prefix}.jsx`);
|
||
if (existsSync(jsxPath)) {
|
||
write(join(DIST, 'react', `${prefix}.jsx`), read(jsxPath));
|
||
/* JSX 里有 `import './<Prefix>.css'`:必须把同名 CSS 放到同一目录,
|
||
否则宿主构建链解析失败(典型症状:组件能 import 但样式全丢)。 */
|
||
const jsxCss = join(SRC, `${prefix}.css`);
|
||
if (existsSync(jsxCss)) write(join(DIST, 'react', `${prefix}.css`), read(jsxCss));
|
||
reactImports.push({ prefix, slug: c.slug });
|
||
}
|
||
|
||
// Vue 3
|
||
const v3Path = join(SRC, `${prefix}.vue3.vue`);
|
||
if (existsSync(v3Path)) {
|
||
write(join(DIST, 'vue3', `${prefix}.vue`), read(v3Path));
|
||
vue3Imports.push({ prefix, slug: c.slug });
|
||
}
|
||
|
||
// Vue 2
|
||
const v2Path = join(SRC, `${prefix}.vue2.vue`);
|
||
if (existsSync(v2Path)) {
|
||
write(join(DIST, 'vue2', `${prefix}.vue`), read(v2Path));
|
||
vue2Imports.push({ prefix, slug: c.slug });
|
||
}
|
||
|
||
/* 56 个组件的 Vue 两端用 `<style src="./<Prefix>.css">` 引用外部样式,
|
||
另有一批 SFC 在 <style> 内用 @import/url 相对引用。
|
||
不发这些 CSS 的话,组件能 import 但样式全丢。统一把同名 CSS 放到
|
||
vue3/ 与 vue2/ 目录,保持 SFC 里的相对路径成立。 */
|
||
const cssPath = join(SRC, `${prefix}.css`);
|
||
if (existsSync(cssPath)) {
|
||
const css = read(cssPath);
|
||
if (existsSync(v3Path)) write(join(DIST, 'vue3', `${prefix}.css`), css);
|
||
if (existsSync(v2Path)) write(join(DIST, 'vue2', `${prefix}.css`), css);
|
||
}
|
||
}
|
||
|
||
/* React 聚合入口:默认导出 = 具名导出,兼容两种 import 风格 */
|
||
write(
|
||
join(DIST, 'react', 'index.js'),
|
||
[
|
||
'/* Aurora Admin — React 端聚合入口(源码形态,需宿主构建链编译 JSX)',
|
||
` * 版本 ${version} · ${reactImports.length} 个组件`,
|
||
' * 样式:每个组件已 import 自己的 css,需先引入令牌:',
|
||
" * import 'aurora-admin-design/tokens/tokens.css'",
|
||
' */',
|
||
...reactImports.map((r) => `export { default as Aa${r.prefix} } from './${r.prefix}.jsx';`),
|
||
'',
|
||
].join('\n')
|
||
);
|
||
|
||
/* Vue 3 聚合入口 */
|
||
write(
|
||
join(DIST, 'vue3', 'index.js'),
|
||
[
|
||
'/* Aurora Admin — Vue 3 端聚合入口(.vue SFC 源码)',
|
||
` * 版本 ${version} · ${vue3Imports.length} 个组件`,
|
||
' * 样式在 SFC 内,随组件一起编译;令牌需单独引入:',
|
||
" * import 'aurora-admin-design/tokens/tokens.css'",
|
||
' *',
|
||
' * 用法一(按需):',
|
||
' * import { AaButton } from "aurora-admin-design/vue3";',
|
||
' * 用法二(整体注册):',
|
||
' * import * as Aa from "aurora-admin-design/vue3";',
|
||
' * Object.entries(Aa).forEach(([n, c]) => app.component(n, c));',
|
||
' */',
|
||
...vue3Imports.map((r) => `export { default as Aa${r.prefix} } from './${r.prefix}.vue';`),
|
||
'',
|
||
].join('\n')
|
||
);
|
||
|
||
/* Vue 2 聚合入口 */
|
||
write(
|
||
join(DIST, 'vue2', 'index.js'),
|
||
[
|
||
'/* Aurora Admin — Vue 2 端聚合入口(.vue SFC 源码)',
|
||
` * 版本 ${version} · ${vue2Imports.length} 个组件`,
|
||
' * 需要构建链具备 Vue 2 SFC 编译能力(vue-loader 15+ 或 vue-template-compiler),',
|
||
' * 样式在 SFC 内;令牌需单独引入:',
|
||
" * import 'aurora-admin-design/tokens/tokens.css'",
|
||
' *',
|
||
' * 用法一(按需):',
|
||
' * import { AaButton } from "aurora-admin-design/vue2";',
|
||
' * 用法二(整体注册):',
|
||
' * import * as Aa from "aurora-admin-design/vue2";',
|
||
' * Object.entries(Aa).forEach(([n, c]) => Vue.component(n, c));',
|
||
' */',
|
||
...vue2Imports.map((r) => `export { default as Aa${r.prefix} } from './${r.prefix}.vue';`),
|
||
'',
|
||
].join('\n')
|
||
);
|
||
|
||
console.log(
|
||
`[build-dist] 框架端入口 → dist/react(${reactImports.length})· dist/vue3(${vue3Imports.length})· dist/vue2(${vue2Imports.length})`
|
||
);
|
||
|
||
/* ---------- 4. 用法速查 ---------- */
|
||
|
||
write(
|
||
join(DIST, 'README.md'),
|
||
`# Aurora Admin Design System — 分发包
|
||
|
||
版本 ${version} · ${components.length} 个组件 · ${tokens.length} 个设计令牌 · 零运行时依赖
|
||
|
||
## 最快用法
|
||
|
||
\`\`\`html
|
||
<link rel="stylesheet" href="./tokens/tokens.css">
|
||
<link rel="stylesheet" href="./components/index.css">
|
||
\`\`\`
|
||
|
||
## 按需引入
|
||
|
||
\`\`\`html
|
||
<link rel="stylesheet" href="./tokens/tokens.css">
|
||
<link rel="stylesheet" href="./components/button.css"> <!-- 只要按钮 -->
|
||
\`\`\`
|
||
|
||
## 改主题
|
||
|
||
所有色值都引用 \`--au-*\` 令牌,覆盖即可全局换色:
|
||
|
||
\`\`\`css
|
||
:root { --au-color-brand: #722ED1; }
|
||
\`\`\`
|
||
|
||
## 在框架项目里 import(React / Vue 3 / Vue 2)
|
||
|
||
三端都提供聚合入口,**在 main 里 import 即可用**。组件以源码形式发布(\`.vue\` / \`.jsx\`),由你项目自己的构建链编译——因此没有额外的编译产物与源码不同步的风险。
|
||
|
||
> ⚠️ **样式必须先引入令牌**:79 个组件样式里有 78 个引用 \`var(--au-*)\`,
|
||
> 不引 \`tokens.css\` 会出现「组件渲染了但颜色/间距全丢」。
|
||
|
||
### React
|
||
|
||
\`\`\`jsx
|
||
// main.jsx
|
||
import 'aurora-admin-design/tokens/tokens.css'; // 必须:令牌
|
||
import { AaButton, AaTable } from 'aurora-admin-design/react';
|
||
|
||
export default function App() {
|
||
return <AaButton type="primary" onClick={save}>保存</AaButton>;
|
||
}
|
||
\`\`\`
|
||
|
||
单个组件按需引入(样式随之带上,组件内部已 import 自己的 CSS):
|
||
|
||
\`\`\`jsx
|
||
import AaButton from 'aurora-admin-design/react/Button.jsx';
|
||
\`\`\`
|
||
|
||
### Vue 3
|
||
|
||
\`\`\`js
|
||
// main.js
|
||
import 'aurora-admin-design/tokens/tokens.css'; // 必须:令牌
|
||
import * as Aa from 'aurora-admin-design/vue3';
|
||
import { createApp } from 'vue';
|
||
|
||
const app = createApp(App);
|
||
Object.entries(Aa).forEach(([name, comp]) => app.component(name, comp));
|
||
app.mount('#app');
|
||
\`\`\`
|
||
|
||
按需引入:\`import { AaButton } from 'aurora-admin-design/vue3';\`
|
||
样式在 SFC 内随组件编译,无需单独引 CSS。
|
||
|
||
### Vue 2
|
||
|
||
\`\`\`js
|
||
// main.js
|
||
import 'aurora-admin-design/tokens/tokens.css'; // 必须:令牌
|
||
import * as Aa from 'aurora-admin-design/vue2';
|
||
import Vue from 'vue';
|
||
|
||
Object.entries(Aa).forEach(([name, comp]) => Vue.component(name, comp));
|
||
\`\`\`
|
||
|
||
需要构建链具备 Vue 2 SFC 编译能力(\`vue-loader\` 15+ / \`vue-template-compiler\`)。
|
||
|
||
### 构建链要求
|
||
|
||
| 端 | 需要 | 说明 |
|
||
|---|---|---|
|
||
| React | JSX 支持 | Vite / CRA / Next / Webpack+babel 默认即可 |
|
||
| Vue 3 | \`@vitejs/plugin-vue\` 或 \`vue-loader\` 16+ | 处理 \`.vue\` SFC |
|
||
| Vue 2 | \`vue-loader\` 15+ 或 \`vue-template-compiler\` | 处理 \`.vue\` SFC |
|
||
|
||
\`react\` 与 \`vue\` 是 **peerDependencies(可选)**——按你用到的端安装即可,本包不捆绑框架。
|
||
|
||
## 目录
|
||
|
||
| 路径 | 内容 |
|
||
|---|---|
|
||
| \`tokens/tokens.css\` | 设计令牌(CSS 变量) |
|
||
| \`tokens/tokens.json\` | 同上(W3C DTCG 格式,供工具消费) |
|
||
| \`tokens/figma.json\` | Figma Tokens(Tokens Studio 可导入) |
|
||
| \`components/index.css\` | 全部组件样式聚合 |
|
||
| \`components/<slug>.css\` | 单个组件样式 |
|
||
| \`components/<slug>.html\` | 该组件的静态演示页(可直接打开) |
|
||
| \`react/index.js\` | React 聚合入口(79 个组件) |
|
||
| \`react/<Prefix>.jsx\` \`+.css\` | React 单组件源码 + 其样式 |
|
||
| \`vue3/index.js\` | Vue 3 聚合入口(79 个组件) |
|
||
| \`vue3/<Prefix>.vue\` \`+.css\` | Vue 3 单组件 SFC + 外部样式 |
|
||
| \`vue2/index.js\` | Vue 2 聚合入口(79 个组件) |
|
||
| \`vue2/<Prefix>.vue\` \`+.css\` | Vue 2 单组件 SFC + 外部样式 |
|
||
| \`manifest.json\` | 组件清单 + 校验和 |
|
||
|
||
## 组件清单
|
||
|
||
${manifest.components.map((c) => `- \`${c.slug}\` — ${c.name}`).join('\n')}
|
||
`
|
||
);
|
||
|
||
console.log('[build-dist] 完成 → dist/');
|
||
console.log(` components/manifest.json · README.md`);
|