charbi-font 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/bin/charbi.mjs +4 -0
- package/dist/chunk-Bmb41Sf3.cjs +1 -0
- package/dist/cli.cjs +32 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +32 -0
- package/dist/config/index.cjs +1 -0
- package/dist/config/index.d.cts +2 -0
- package/dist/config/index.d.mts +2 -0
- package/dist/config/index.mjs +1 -0
- package/dist/config/loader.cjs +1 -0
- package/dist/config/loader.d.cts +10 -0
- package/dist/config/loader.d.mts +10 -0
- package/dist/config/loader.mjs +1 -0
- package/dist/schema-B8yH_G4r.d.cts +69 -0
- package/dist/schema-BuMY-k7u.mjs +1 -0
- package/dist/schema-ClqJnGnv.cjs +1 -0
- package/dist/schema-bUBG41yN.d.mts +69 -0
- package/dist/vite/index.cjs +1 -0
- package/dist/vite/index.d.cts +9 -0
- package/dist/vite/index.d.mts +9 -0
- package/dist/vite/index.mjs +1 -0
- package/package.json +100 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 imba97 <https://github.com/imba97>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# charbi-font
|
|
2
|
+
|
|
3
|
+
中文字体子集化工具,扫描代码提取字符,生成精简字体包。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- ✂️ **字体子集化** — 仅包含代码中实际使用的字符
|
|
8
|
+
- 📁 **代码扫描** — 递归扫描源码文件,提取字符使用情况
|
|
9
|
+
- 📦 **多字重合并** — 同一字体的多个字重合并到一个文件
|
|
10
|
+
- ☁️ **CDN 上传** — 可上传到 CDN(如腾讯云 COS)
|
|
11
|
+
- ⚡ **本地缓存** — 字体文件本地缓存,避免重复下载
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install charbi-font
|
|
17
|
+
# 或
|
|
18
|
+
pnpm add charbi-font
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
如需 CDN 上传功能,需安装 `cos-nodejs-sdk-v5`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install cos-nodejs-sdk-v5
|
|
25
|
+
# 或
|
|
26
|
+
pnpm add cos-nodejs-sdk-v5
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
### 1. 创建配置文件
|
|
32
|
+
|
|
33
|
+
在项目根目录创建 `fonts.config.ts`:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { defineConfig } from "charbi-font/config";
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
build: {
|
|
40
|
+
scan: {
|
|
41
|
+
srcDir: ["src"],
|
|
42
|
+
extensions: ["vue", "ts", "js", "scss", "css"]
|
|
43
|
+
},
|
|
44
|
+
fonts: [
|
|
45
|
+
{
|
|
46
|
+
family: "AlibabaPuHuiTi",
|
|
47
|
+
name: "Regular",
|
|
48
|
+
weight: 400,
|
|
49
|
+
url: "https://your-cos.com/path/to/AlibabaPuHuiTi-3.ttf"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
family: "AlibabaPuHuiTi",
|
|
53
|
+
name: "Bold",
|
|
54
|
+
weight: 700,
|
|
55
|
+
url: "https://your-cos.com/path/to/AlibabaPuHuiTi-3-bold.ttf"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
output: {
|
|
59
|
+
cssDir: "src/styles",
|
|
60
|
+
format: "woff"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
cos: {
|
|
64
|
+
bucket: "your-bucket",
|
|
65
|
+
region: "ap-guangzhou",
|
|
66
|
+
basePath: "static/fonts/{version}",
|
|
67
|
+
cdnUrl: "https://your-cos.com"
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. 执行构建
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
charbi-font
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 3. 引入字体
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// main.ts 或 App.vue
|
|
82
|
+
import "@/styles/fonts";
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```css
|
|
86
|
+
.my-text {
|
|
87
|
+
font-family: "AlibabaPuHuiTi", sans-serif;
|
|
88
|
+
font-weight: 400;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 构建流程
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
1. 下载字体 → 从 COS 下载字体源文件并缓存
|
|
96
|
+
2. 扫描代码 → 递归扫描 src/ 目录
|
|
97
|
+
3. 提取字符 → 提取中文、英文、数字、常用符号
|
|
98
|
+
4. 生成子集 → 使用 fontmin 压缩并转换为 WOFF
|
|
99
|
+
5. 生成 CSS → 输出 font-*.scss 和 fonts.scss
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 输出结构
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
src/styles/
|
|
106
|
+
├── font-assets/
|
|
107
|
+
│ ├── alibaba-pu-hui-ti.scss # 阿里普惠体(多字重)
|
|
108
|
+
│ └── fonts.scss # 汇总引入文件
|
|
109
|
+
└── fonts.scss # 入口文件
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 配置说明
|
|
113
|
+
|
|
114
|
+
### `build.scan`
|
|
115
|
+
|
|
116
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
117
|
+
| ------------ | -------------------- | --------- | -------------------------- |
|
|
118
|
+
| `srcDir` | `string[]` | `['src']` | 扫描的目录 |
|
|
119
|
+
| `extensions` | `string[]` | 上方列表 | 扫描的文件类型 |
|
|
120
|
+
| `extraText` | `string \| string[]` | — | 额外包含的字符(所有字体) |
|
|
121
|
+
|
|
122
|
+
### `build.fonts[]`
|
|
123
|
+
|
|
124
|
+
| 配置项 | 类型 | 必填 | 说明 |
|
|
125
|
+
| ----------- | ---------------------------- | ---- | ---------------------------- |
|
|
126
|
+
| `family` | `string` | 是 | 字体系列名(用于分组) |
|
|
127
|
+
| `name` | `string` | 是 | 显示名称 |
|
|
128
|
+
| `weight` | `number` | 是 | 字重 |
|
|
129
|
+
| `url` | `string` | 是 | 字体源地址 |
|
|
130
|
+
| `style` | `'normal' \| 'italic'` | 否 | 字体样式 |
|
|
131
|
+
| `format` | `'woff' \| 'woff2' \| 'ttf'` | 否 | 覆盖全局格式 |
|
|
132
|
+
| `extraText` | `string \| string[]` | 否 | 额外包含的字符(仅当前字体) |
|
|
133
|
+
|
|
134
|
+
### `build.output`
|
|
135
|
+
|
|
136
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
137
|
+
| ------------- | ---------------------------- | -------------- | ------------ |
|
|
138
|
+
| `cssDir` | `string` | `'src/styles'` | CSS 输出目录 |
|
|
139
|
+
| `format` | `'woff' \| 'woff2' \| 'ttf'` | `'woff'` | 输出格式 |
|
|
140
|
+
| `styleFormat` | `'scss' \| 'css'` | `'scss'` | 样式文件格式 |
|
|
141
|
+
|
|
142
|
+
### `cacheDir`
|
|
143
|
+
|
|
144
|
+
自定义缓存目录(默认:`node_modules/charbi-font/.cache/fonts`):
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export default defineConfig({
|
|
148
|
+
cacheDir: ".cache/fonts", // 相对于项目根目录
|
|
149
|
+
// 或绝对路径
|
|
150
|
+
cacheDir: "/path/to/cache/fonts"
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 生成结果
|
|
155
|
+
|
|
156
|
+
生成的 CSS 使用 `font-display: swap`:
|
|
157
|
+
|
|
158
|
+
```scss
|
|
159
|
+
@font-face {
|
|
160
|
+
font-family: "AlibabaPuHuiTi";
|
|
161
|
+
src: url("https://your-cos.com/static/fonts/1.0.0/AlibabaPuHuiTi-400.woff") format("woff");
|
|
162
|
+
font-weight: 400;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 使用命令
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
charbi-font # 执行构建
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT
|
package/bin/charbi.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return s}});
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const e=require(`./chunk-Bmb41Sf3.cjs`),t=require(`./schema-ClqJnGnv.cjs`),n=require(`./config/loader.cjs`);let r=require(`node:fs`);r=e.t(r);let i=require(`node:path`);i=e.t(i);let a=require(`node:process`);a=e.t(a);let o=require(`node:url`),s=require(`cac`),c=require(`node:http`);c=e.t(c);let l=require(`node:https`);l=e.t(l);let u=require(`consola`);u=e.t(u);let d=require(`fast-glob`);d=e.t(d);let f=require(`fontmin`);f=e.t(f);let p=require(`p-limit`);p=e.t(p);let m=require(`enquirer`);m=e.t(m);async function h(e,t){return new Promise((n,i)=>{let a=e.startsWith(`https`)?l.default:c.default,o=r.default.createWriteStream(t);a.get(e,e=>{if(e.statusCode!==200){o.close(),r.default.unlink(t,()=>{}),i(Error(`下载失败: ${e.statusCode}`));return}e.pipe(o),o.on(`finish`,()=>{if(o.close(),r.default.statSync(t).size===0){r.default.unlink(t,()=>{}),i(Error(`下载的文件为空`));return}n()})}).on(`error`,e=>{o.close(),r.default.unlink(t,()=>{}),i(e)})})}async function g(e,t,n=!1){u.default.info(`下载字体文件...`),r.default.mkdirSync(e,{recursive:!0});let a=new Map;for(let o of t){let t=`${o.family}-${o.weight}.ttf`,s=i.default.join(e,t);if(n&&r.default.existsSync(s)&&r.default.unlinkSync(s),r.default.existsSync(s)){let e=r.default.statSync(s);if(e.size===0)r.default.unlinkSync(s);else{let t=(e.size/1024).toFixed(2);u.default.info(` ✓ ${o.family} ${o.name} (缓存, ${t} KB)`),a.set(`${o.family}-${o.weight}`,s);continue}}u.default.info(` ↓ ${o.family} ${o.name}...`);try{await h(o.url,s);let e=(r.default.statSync(s).size/1024).toFixed(2);u.default.success(` 完成 ${t} (${e} KB)`),a.set(`${o.family}-${o.weight}`,s)}catch(e){u.default.error(` 失败 ${o.family} ${o.name} 下载失败:`,e.message),r.default.existsSync(s)&&r.default.unlinkSync(s)}}return a}function _(e){return e.replace(/\s+/g,``)}function v(e,t,n,r){let{cos:i}=e;if(i.cdnUrl){if(!i.basePath)throw Error(`COS 配置缺少 basePath,请在 fonts.config.ts 中设置 cos.basePath`);let e=`${_(t.family)}-${t.weight}.${r}`,a=i.basePath.replace(`{version}`,n),o=a.startsWith(`/`)?a.slice(1):a;return`${i.cdnUrl}/${o}/${e}`.replace(/\/+/g,`/`)}return`./fonts/${_(t.family)}-${t.weight}.${r}`}function y(e,t,n,r,i=`normal`){return`@font-face {
|
|
2
|
+
font-family: '${e}';
|
|
3
|
+
src: url('${n}') format('${r}');
|
|
4
|
+
font-display: swap;
|
|
5
|
+
font-weight: ${t};
|
|
6
|
+
font-style: ${i};
|
|
7
|
+
}`}function b(e){return e===`scss`?`scss`:`css`}function x(e,t){return t===`scss`?`@use './${e}' as *;`:`@import './${e}';`}async function S(e,n,a,o){u.default.info(`生成字体样式文件...`);let s=n.output.styleFormat,c=b(s),l=i.default.join(n.root,n.output.cssDir);r.default.mkdirSync(l,{recursive:!0});let d=i.default.join(l,t.t);r.default.mkdirSync(d,{recursive:!0});let f=[],p=0;for(let[l,m]of e){let e=`${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}.${c}`,h=i.default.join(d,e),g=n.output.cssDir.replace(/^src\//,``),_=`/**
|
|
8
|
+
* ${l} 字体
|
|
9
|
+
* 由 charbi 自动生成
|
|
10
|
+
*
|
|
11
|
+
* 使用方式:
|
|
12
|
+
* ${s===`scss`?`@use '@/${g}/${t.t}/${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}' as *;`:`import '@/${g}/${t.t}/${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}';`}
|
|
13
|
+
*
|
|
14
|
+
* CSS 中使用:
|
|
15
|
+
* font-family: '${l}', sans-serif;
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
`;for(let e of m){let{config:t,size:r}=e,i=v(n,t,a,o);_+=y(l,t.weight,i,n.output.format,t.style||`normal`),_+=`
|
|
19
|
+
|
|
20
|
+
`;let s=(r/1024).toFixed(2);u.default.success(` ${l} ${t.name} (${t.weight}): ${s} KB`)}r.default.writeFileSync(h,_),f.push(e);let b=(_.length/1024).toFixed(2);p+=_.length,u.default.info(` 生成: ${e} (${b} KB)`)}let m=n.output.cssDir.replace(/^src\//,``),h=`fonts.${c}`,g=`/**
|
|
21
|
+
* 字体汇总文件
|
|
22
|
+
* 由 charbi 自动生成
|
|
23
|
+
*
|
|
24
|
+
* 此文件包含所有字体的 @font-face 声明
|
|
25
|
+
*
|
|
26
|
+
* 使用方式:
|
|
27
|
+
* ${s===`scss`?`@use '@/${m}/fonts' as *;`:`import '@/${m}/fonts';`}
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
${f.map(e=>x(`${t.t}/${e.replace(/\.(scss|css)$/,``)}`,n.output.styleFormat)).join(`
|
|
31
|
+
`)}
|
|
32
|
+
`,_=i.default.join(l,h);r.default.writeFileSync(_,g);let S=(p/1024).toFixed(2);u.default.success(`生成汇总文件: ${h} (${S} KB)`)}function C(e){return e.replace(/\/\/.*$/gm,``).replace(/\/\*[\s\S]*?\*\//g,``).replace(/\/\*\*[\s\S]*?\*\//g,``)}function w(e){return e.replace(/<!--[\s\S]*?-->/g,``)}function T(e){return e.replace(/\/\*[\s\S]*?\*\//g,``)}function E(e){let t=e;return t=t.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi,(e,t)=>`<template>${w(t)}</template>`),t=t.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,(e,t)=>`<script>${C(t)}<\/script>`),t=t.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi,(e,t)=>`<style>${T(t)}</style>`),t}function D(e,t){switch(t){case`vue`:return E(e);case`tsx`:case`ts`:case`jsx`:case`js`:return C(e);case`scss`:case`css`:return T(e);default:return e}}function O(e,t){(e.match(/[\u4E00-\u9FA5]/g)||[]).forEach(e=>t.add(e)),(e.match(/\d/g)||[]).forEach(e=>t.add(e)),(e.match(/[a-z]/gi)||[]).forEach(e=>t.add(e)),(e.match(/[·.,;:!?@#$%^&*()_+\-=[\]{}|\\/"'<>,。;:!?、【】《》「」『』()]/g)||[]).forEach(e=>t.add(e))}function k(e){return e?Array.isArray(e)?e:[e]:[]}function A(e,t){for(let n of e)for(let e of n)e.trim()&&t.add(e)}async function j(e){u.default.info(`扫描项目文件...`);let t=await(0,d.default)(e.scan.srcDir.flatMap(t=>e.scan.extensions.map(e=>`${t}/**/*.${e}`)),{cwd:e.root,absolute:!1,onlyFiles:!0});u.default.info(` 找到 ${t.length} 个文件`);let n=new Set;for(let a of t)O(D(r.default.readFileSync(i.default.join(e.root,a),`utf-8`),a.split(`.`).pop()||``),n);A(k(e.scan.extraText),n),u.default.info(` 收集到 ${n.size} 个唯一字符`);for(let e of`0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.;:!?'",。;:!?`)n.add(e);return u.default.info(` 添加默认字符集后共 ${n.size} 个字符`),n}function M(e){return e?Array.isArray(e)?e:[e]:[]}function N(e,t){let n=new Set(e);for(let e of M(t))for(let t of e)t.trim()&&n.add(t);return Array.from(n).join(``)}async function P(e,t,n,a,o=`woff`){u.default.info(`生成字体子集...`);let s=new Map;for(let c of a){let a=e.get(`${c.family}-${c.weight}`);if(!a){u.default.warn(` 跳过 ${c.family} ${c.name} (未下载)`);continue}u.default.info(` 处理 ${c.family} ${c.name}...`);let l=c.format||o,d=N(n,c.extraText);try{let e=await new Promise((e,n)=>{let o=(0,f.default)().src(a).use(f.default.glyph({text:d,hinting:!1}));l===`woff2`?o.use(f.default.ttf2woff2()):l===`woff`&&o.use(f.default.ttf2woff()),o.dest(t).run((a,o)=>{if(a)n(a);else if(o&&o[0]){let n=o[0],a=l===`woff2`?`woff2`:l,s=`${_(c.family)}-${c.weight}.${a}`,u=i.default.join(t,s);n.path&&r.default.existsSync(n.path)&&r.default.renameSync(n.path,u),e({outputPath:u,size:r.default.statSync(u).size})}else n(Error(`No output file generated`))})}),n=(e.size/1024).toFixed(2);u.default.success(` 完成 ${c.family} ${c.name} (${n} KB)`),s.has(c.family)||s.set(c.family,[]),s.get(c.family).push({config:c,filePath:e.outputPath,size:e.size,format:l})}catch(e){u.default.error(` 失败 ${c.family} ${c.name}: ${e.message}`)}}return s}async function F(e,t){if(!t.basePath)throw Error(`COS 配置缺少 basePath,请在 fonts.config.ts 中设置 cos.basePath`);if(!t.bucket)throw Error(`COS 配置缺少 bucket,请在 fonts.config.ts 中设置 cos.bucket`);if(!t.region)throw Error(`COS 配置缺少 region,请在 fonts.config.ts 中设置 cos.region`);if(!t.cdnUrl)throw Error(`COS 配置缺少 cdnUrl,请在 fonts.config.ts 中设置 cos.cdnUrl`);let n=t.basePath.replace(`{version}`,e),r=[];r.push({type:`input`,name:`secretId`,message:`请输入腾讯云 SecretId:`,validate:e=>e.length>0||`SecretId 不能为空`}),r.push({type:`password`,name:`secretKey`,message:`请输入腾讯云 SecretKey:`,validate:e=>e.length>0||`SecretKey 不能为空`}),t.overwrite===void 0&&r.push({type:`confirm`,name:`overwrite`,message:`如果文件已存在,是否覆盖?`,initial:!1});let i=await m.default.prompt(r);return{secretId:i.secretId,secretKey:i.secretKey,bucket:t.bucket,region:t.region,uploadPath:n,cdnUrl:t.cdnUrl,overwrite:t.overwrite??i.overwrite??!1}}async function I(){try{let e=await import(`cos-nodejs-sdk-v5`);return e.default||e}catch{throw Error(`缺少依赖 cos-nodejs-sdk-v5,请在使用项目中安装该包后重试`)}}var L=class{validateConfig(e){if(!e.cos.basePath||!e.cos.bucket||!e.cos.region||!e.cos.cdnUrl)throw Error(`COS 上传配置不完整,请检查 fonts.config.ts 中的 cos 配置`)}async create(e,t){this.cosConfig=await F(e,t.cos),this.cos=new(await(I()))({SecretId:this.cosConfig.secretId,SecretKey:this.cosConfig.secretKey}),u.default.info(`开始上传到 COS...`),u.default.info(` Bucket: ${this.cosConfig.bucket}`),u.default.info(` 区域: ${this.cosConfig.region}`),u.default.info(` 路径: ${this.cosConfig.uploadPath}`)}async uploadFile(e){let t=`${this.cosConfig.uploadPath}/${e.fileName}`.replace(/\/+/g,`/`);if(!this.cosConfig.overwrite)try{return await this.cos.headObject({Bucket:this.cosConfig.bucket,Region:this.cosConfig.region,Key:t}),{status:`skipped`}}catch{}return await this.cos.putObject({Bucket:this.cosConfig.bucket,Region:this.cosConfig.region,Key:t,StorageClass:`STANDARD`,Body:r.default.createReadStream(e.filePath)}),{status:`uploaded`}}finalize(){u.default.success(`上传完成!`),u.default.info(` CDN 地址: ${this.cosConfig.cdnUrl}${this.cosConfig.uploadPath}/`)}};function R(e){switch(e.upload.provider||`cos`){case`cos`:return new L;default:throw Error(`不支持的上传 provider: ${e.upload.provider}`)}}async function z(e,t,n){let a=R(n);a.validateConfig(n);let o=e.filter(e=>r.default.existsSync(e)?r.default.statSync(e).size>0:!1).map(e=>{let t=r.default.statSync(e);return{filePath:e,fileName:i.default.basename(e),sizeKB:(t.size/1024).toFixed(2)}});if(o.length===0){u.default.error(`没有有效的文件可上传`);return}await a.create(t,n);let s=(0,p.default)(n.upload.concurrency??5);await Promise.all(o.map(e=>s(async()=>{try{(await a.uploadFile(e)).status===`skipped`?u.default.info(` 跳过 ${e.fileName} (远端已存在)`):u.default.success(` 完成 ${e.fileName} (${e.sizeKB} KB)`)}catch(t){u.default.error(` 失败 ${e.fileName} 上传失败:`,t.message)}}))),a.finalize()}const B=i.default.dirname((0,o.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),V=JSON.parse(r.default.readFileSync(i.default.resolve(B,`../package.json`),`utf-8`)),H=new s.CAC(`charbi`);H.version(V.version),H.option(`--mode <mode>`,`development 或 production`),H.command(``).action(U),H.command(`build`).option(`--no-cache`,`强制重新下载字体文件`).action(U),H.command(`upload`).action(W);async function U(e,t){let o=t.mode||`development`,s=await n.loadConfig(o),c=n.getVersion(s.version);u.default.info(`charbi`),u.default.info(` 模式: ${o}`),u.default.info(` 版本基线: ${c}`),u.default.info(` 格式: ${s.output.format}`);let l=i.default.join(s.cacheDir,`subsets`);r.default.mkdirSync(l,{recursive:!0});let d=await g(s.cacheDir,s.fonts,!e.cache);d.size===0&&(u.default.error(`没有可用的字体文件,构建失败`),a.default.exit(1));let f=await P(d,l,await j(s),s.fonts,s.output.format),p=0,m=0;for(let e of f.values())for(let t of e)p+=t.size,m++;let h=(p/1024).toFixed(2);u.default.success(`字体子集生成完成!`),u.default.info(` 生成文件: ${m} 个`),u.default.info(` 总大小: ${h} KB`);let _=c;u.default.info(` 字体版本: ${_}`),await S(f,s,_,s.output.format);let v=[];for(let e of f.values())for(let t of e)v.push(t.filePath);await z(v,_,s);try{r.default.rmSync(l,{recursive:!0,force:!0})}catch{}}async function W(e,t){let o=await n.loadConfig(t.mode||`development`),s=n.getVersion(o.version);u.default.info(`charbi upload`),u.default.info(` 版本: ${s}`);let c=i.default.join(o.cacheDir,`subsets`),l=[];for(let e of r.default.readdirSync(c)){let t=i.default.join(c,e);r.default.statSync(t).isFile()&&l.push(t)}l.length===0&&(u.default.error(`没有找到字体文件,请先执行 build`),a.default.exit(1)),await z(l,s,o)}H.help(),H.parse();
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import{t as e}from"./schema-BuMY-k7u.mjs";import{getVersion as t,loadConfig as n}from"./config/loader.mjs";import r from"node:fs";import i from"node:path";import a from"node:process";import{fileURLToPath as o}from"node:url";import{CAC as s}from"cac";import c from"node:http";import l from"node:https";import u from"consola";import d from"fast-glob";import f from"fontmin";import p from"p-limit";import m from"enquirer";async function h(e,t){return new Promise((n,i)=>{let a=e.startsWith(`https`)?l:c,o=r.createWriteStream(t);a.get(e,e=>{if(e.statusCode!==200){o.close(),r.unlink(t,()=>{}),i(Error(`下载失败: ${e.statusCode}`));return}e.pipe(o),o.on(`finish`,()=>{if(o.close(),r.statSync(t).size===0){r.unlink(t,()=>{}),i(Error(`下载的文件为空`));return}n()})}).on(`error`,e=>{o.close(),r.unlink(t,()=>{}),i(e)})})}async function g(e,t,n=!1){u.info(`下载字体文件...`),r.mkdirSync(e,{recursive:!0});let a=new Map;for(let o of t){let t=`${o.family}-${o.weight}.ttf`,s=i.join(e,t);if(n&&r.existsSync(s)&&r.unlinkSync(s),r.existsSync(s)){let e=r.statSync(s);if(e.size===0)r.unlinkSync(s);else{let t=(e.size/1024).toFixed(2);u.info(` ✓ ${o.family} ${o.name} (缓存, ${t} KB)`),a.set(`${o.family}-${o.weight}`,s);continue}}u.info(` ↓ ${o.family} ${o.name}...`);try{await h(o.url,s);let e=(r.statSync(s).size/1024).toFixed(2);u.success(` 完成 ${t} (${e} KB)`),a.set(`${o.family}-${o.weight}`,s)}catch(e){u.error(` 失败 ${o.family} ${o.name} 下载失败:`,e.message),r.existsSync(s)&&r.unlinkSync(s)}}return a}function _(e){return e.replace(/\s+/g,``)}function v(e,t,n,r){let{cos:i}=e;if(i.cdnUrl){if(!i.basePath)throw Error(`COS 配置缺少 basePath,请在 fonts.config.ts 中设置 cos.basePath`);let e=`${_(t.family)}-${t.weight}.${r}`,a=i.basePath.replace(`{version}`,n),o=a.startsWith(`/`)?a.slice(1):a;return`${i.cdnUrl}/${o}/${e}`.replace(/\/+/g,`/`)}return`./fonts/${_(t.family)}-${t.weight}.${r}`}function y(e,t,n,r,i=`normal`){return`@font-face {
|
|
2
|
+
font-family: '${e}';
|
|
3
|
+
src: url('${n}') format('${r}');
|
|
4
|
+
font-display: swap;
|
|
5
|
+
font-weight: ${t};
|
|
6
|
+
font-style: ${i};
|
|
7
|
+
}`}function b(e){return e===`scss`?`scss`:`css`}function x(e,t){return t===`scss`?`@use './${e}' as *;`:`@import './${e}';`}async function S(t,n,a,o){u.info(`生成字体样式文件...`);let s=n.output.styleFormat,c=b(s),l=i.join(n.root,n.output.cssDir);r.mkdirSync(l,{recursive:!0});let d=i.join(l,e);r.mkdirSync(d,{recursive:!0});let f=[],p=0;for(let[l,m]of t){let t=`${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}.${c}`,h=i.join(d,t),g=n.output.cssDir.replace(/^src\//,``),_=`/**
|
|
8
|
+
* ${l} 字体
|
|
9
|
+
* 由 charbi 自动生成
|
|
10
|
+
*
|
|
11
|
+
* 使用方式:
|
|
12
|
+
* ${s===`scss`?`@use '@/${g}/${e}/${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}' as *;`:`import '@/${g}/${e}/${l.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}';`}
|
|
13
|
+
*
|
|
14
|
+
* CSS 中使用:
|
|
15
|
+
* font-family: '${l}', sans-serif;
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
`;for(let e of m){let{config:t,size:r}=e,i=v(n,t,a,o);_+=y(l,t.weight,i,n.output.format,t.style||`normal`),_+=`
|
|
19
|
+
|
|
20
|
+
`;let s=(r/1024).toFixed(2);u.success(` ${l} ${t.name} (${t.weight}): ${s} KB`)}r.writeFileSync(h,_),f.push(t);let b=(_.length/1024).toFixed(2);p+=_.length,u.info(` 生成: ${t} (${b} KB)`)}let m=n.output.cssDir.replace(/^src\//,``),h=`fonts.${c}`,g=`/**
|
|
21
|
+
* 字体汇总文件
|
|
22
|
+
* 由 charbi 自动生成
|
|
23
|
+
*
|
|
24
|
+
* 此文件包含所有字体的 @font-face 声明
|
|
25
|
+
*
|
|
26
|
+
* 使用方式:
|
|
27
|
+
* ${s===`scss`?`@use '@/${m}/fonts' as *;`:`import '@/${m}/fonts';`}
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
${f.map(t=>x(`${e}/${t.replace(/\.(scss|css)$/,``)}`,n.output.styleFormat)).join(`
|
|
31
|
+
`)}
|
|
32
|
+
`,_=i.join(l,h);r.writeFileSync(_,g);let S=(p/1024).toFixed(2);u.success(`生成汇总文件: ${h} (${S} KB)`)}function C(e){return e.replace(/\/\/.*$/gm,``).replace(/\/\*[\s\S]*?\*\//g,``).replace(/\/\*\*[\s\S]*?\*\//g,``)}function w(e){return e.replace(/<!--[\s\S]*?-->/g,``)}function T(e){return e.replace(/\/\*[\s\S]*?\*\//g,``)}function E(e){let t=e;return t=t.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi,(e,t)=>`<template>${w(t)}</template>`),t=t.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,(e,t)=>`<script>${C(t)}<\/script>`),t=t.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi,(e,t)=>`<style>${T(t)}</style>`),t}function D(e,t){switch(t){case`vue`:return E(e);case`tsx`:case`ts`:case`jsx`:case`js`:return C(e);case`scss`:case`css`:return T(e);default:return e}}function O(e,t){(e.match(/[\u4E00-\u9FA5]/g)||[]).forEach(e=>t.add(e)),(e.match(/\d/g)||[]).forEach(e=>t.add(e)),(e.match(/[a-z]/gi)||[]).forEach(e=>t.add(e)),(e.match(/[·.,;:!?@#$%^&*()_+\-=[\]{}|\\/"'<>,。;:!?、【】《》「」『』()]/g)||[]).forEach(e=>t.add(e))}function k(e){return e?Array.isArray(e)?e:[e]:[]}function A(e,t){for(let n of e)for(let e of n)e.trim()&&t.add(e)}async function j(e){u.info(`扫描项目文件...`);let t=await d(e.scan.srcDir.flatMap(t=>e.scan.extensions.map(e=>`${t}/**/*.${e}`)),{cwd:e.root,absolute:!1,onlyFiles:!0});u.info(` 找到 ${t.length} 个文件`);let n=new Set;for(let a of t)O(D(r.readFileSync(i.join(e.root,a),`utf-8`),a.split(`.`).pop()||``),n);A(k(e.scan.extraText),n),u.info(` 收集到 ${n.size} 个唯一字符`);for(let e of`0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.;:!?'",。;:!?`)n.add(e);return u.info(` 添加默认字符集后共 ${n.size} 个字符`),n}function M(e){return e?Array.isArray(e)?e:[e]:[]}function N(e,t){let n=new Set(e);for(let e of M(t))for(let t of e)t.trim()&&n.add(t);return Array.from(n).join(``)}async function P(e,t,n,a,o=`woff`){u.info(`生成字体子集...`);let s=new Map;for(let c of a){let a=e.get(`${c.family}-${c.weight}`);if(!a){u.warn(` 跳过 ${c.family} ${c.name} (未下载)`);continue}u.info(` 处理 ${c.family} ${c.name}...`);let l=c.format||o,d=N(n,c.extraText);try{let e=await new Promise((e,n)=>{let o=f().src(a).use(f.glyph({text:d,hinting:!1}));l===`woff2`?o.use(f.ttf2woff2()):l===`woff`&&o.use(f.ttf2woff()),o.dest(t).run((a,o)=>{if(a)n(a);else if(o&&o[0]){let n=o[0],a=l===`woff2`?`woff2`:l,s=`${_(c.family)}-${c.weight}.${a}`,u=i.join(t,s);n.path&&r.existsSync(n.path)&&r.renameSync(n.path,u),e({outputPath:u,size:r.statSync(u).size})}else n(Error(`No output file generated`))})}),n=(e.size/1024).toFixed(2);u.success(` 完成 ${c.family} ${c.name} (${n} KB)`),s.has(c.family)||s.set(c.family,[]),s.get(c.family).push({config:c,filePath:e.outputPath,size:e.size,format:l})}catch(e){u.error(` 失败 ${c.family} ${c.name}: ${e.message}`)}}return s}async function F(e,t){if(!t.basePath)throw Error(`COS 配置缺少 basePath,请在 fonts.config.ts 中设置 cos.basePath`);if(!t.bucket)throw Error(`COS 配置缺少 bucket,请在 fonts.config.ts 中设置 cos.bucket`);if(!t.region)throw Error(`COS 配置缺少 region,请在 fonts.config.ts 中设置 cos.region`);if(!t.cdnUrl)throw Error(`COS 配置缺少 cdnUrl,请在 fonts.config.ts 中设置 cos.cdnUrl`);let n=t.basePath.replace(`{version}`,e),r=[];r.push({type:`input`,name:`secretId`,message:`请输入腾讯云 SecretId:`,validate:e=>e.length>0||`SecretId 不能为空`}),r.push({type:`password`,name:`secretKey`,message:`请输入腾讯云 SecretKey:`,validate:e=>e.length>0||`SecretKey 不能为空`}),t.overwrite===void 0&&r.push({type:`confirm`,name:`overwrite`,message:`如果文件已存在,是否覆盖?`,initial:!1});let i=await m.prompt(r);return{secretId:i.secretId,secretKey:i.secretKey,bucket:t.bucket,region:t.region,uploadPath:n,cdnUrl:t.cdnUrl,overwrite:t.overwrite??i.overwrite??!1}}async function I(){try{let e=await import(`cos-nodejs-sdk-v5`);return e.default||e}catch{throw Error(`缺少依赖 cos-nodejs-sdk-v5,请在使用项目中安装该包后重试`)}}var L=class{validateConfig(e){if(!e.cos.basePath||!e.cos.bucket||!e.cos.region||!e.cos.cdnUrl)throw Error(`COS 上传配置不完整,请检查 fonts.config.ts 中的 cos 配置`)}async create(e,t){this.cosConfig=await F(e,t.cos),this.cos=new(await(I()))({SecretId:this.cosConfig.secretId,SecretKey:this.cosConfig.secretKey}),u.info(`开始上传到 COS...`),u.info(` Bucket: ${this.cosConfig.bucket}`),u.info(` 区域: ${this.cosConfig.region}`),u.info(` 路径: ${this.cosConfig.uploadPath}`)}async uploadFile(e){let t=`${this.cosConfig.uploadPath}/${e.fileName}`.replace(/\/+/g,`/`);if(!this.cosConfig.overwrite)try{return await this.cos.headObject({Bucket:this.cosConfig.bucket,Region:this.cosConfig.region,Key:t}),{status:`skipped`}}catch{}return await this.cos.putObject({Bucket:this.cosConfig.bucket,Region:this.cosConfig.region,Key:t,StorageClass:`STANDARD`,Body:r.createReadStream(e.filePath)}),{status:`uploaded`}}finalize(){u.success(`上传完成!`),u.info(` CDN 地址: ${this.cosConfig.cdnUrl}${this.cosConfig.uploadPath}/`)}};function R(e){switch(e.upload.provider||`cos`){case`cos`:return new L;default:throw Error(`不支持的上传 provider: ${e.upload.provider}`)}}async function z(e,t,n){let a=R(n);a.validateConfig(n);let o=e.filter(e=>r.existsSync(e)?r.statSync(e).size>0:!1).map(e=>{let t=r.statSync(e);return{filePath:e,fileName:i.basename(e),sizeKB:(t.size/1024).toFixed(2)}});if(o.length===0){u.error(`没有有效的文件可上传`);return}await a.create(t,n);let s=p(n.upload.concurrency??5);await Promise.all(o.map(e=>s(async()=>{try{(await a.uploadFile(e)).status===`skipped`?u.info(` 跳过 ${e.fileName} (远端已存在)`):u.success(` 完成 ${e.fileName} (${e.sizeKB} KB)`)}catch(t){u.error(` 失败 ${e.fileName} 上传失败:`,t.message)}}))),a.finalize()}const B=i.dirname(o(import.meta.url)),V=JSON.parse(r.readFileSync(i.resolve(B,`../package.json`),`utf-8`)),H=new s(`charbi`);H.version(V.version),H.option(`--mode <mode>`,`development 或 production`),H.command(``).action(U),H.command(`build`).option(`--no-cache`,`强制重新下载字体文件`).action(U),H.command(`upload`).action(W);async function U(e,o){let s=o.mode||`development`,c=await n(s),l=t(c.version);u.info(`charbi`),u.info(` 模式: ${s}`),u.info(` 版本基线: ${l}`),u.info(` 格式: ${c.output.format}`);let d=i.join(c.cacheDir,`subsets`);r.mkdirSync(d,{recursive:!0});let f=await g(c.cacheDir,c.fonts,!e.cache);f.size===0&&(u.error(`没有可用的字体文件,构建失败`),a.exit(1));let p=await P(f,d,await j(c),c.fonts,c.output.format),m=0,h=0;for(let e of p.values())for(let t of e)m+=t.size,h++;let _=(m/1024).toFixed(2);u.success(`字体子集生成完成!`),u.info(` 生成文件: ${h} 个`),u.info(` 总大小: ${_} KB`);let v=l;u.info(` 字体版本: ${v}`),await S(p,c,v,c.output.format);let y=[];for(let e of p.values())for(let t of e)y.push(t.filePath);await z(y,v,c);try{r.rmSync(d,{recursive:!0,force:!0})}catch{}}async function W(e,o){let s=await n(o.mode||`development`),c=t(s.version);u.info(`charbi upload`),u.info(` 版本: ${c}`);let l=i.join(s.cacheDir,`subsets`),d=[];for(let e of r.readdirSync(l)){let t=i.join(l,e);r.statSync(t).isFile()&&d.push(t)}d.length===0&&(u.error(`没有找到字体文件,请先执行 build`),a.exit(1)),await z(d,c,s)}H.help(),H.parse();export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../schema-ClqJnGnv.cjs`);exports.FONT_ASSETS_DIR=e.t,exports.defaultConfig=e.n,exports.defineConfig=e.r;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as FONT_ASSETS_DIR, c as OutputConfig, d as StyleFormat, f as UploadConfig, g as defineConfig, h as defaultConfig, i as ExtraText, l as ResolvedConfig, m as UserConfig, n as COSConfig, o as FontConfig, p as UploadProviderType, r as EnvConfig, s as FontFormat, t as BuildConfig, u as ScanConfig } from "../schema-B8yH_G4r.cjs";
|
|
2
|
+
export { BuildConfig, COSConfig, EnvConfig, ExtraText, FONT_ASSETS_DIR, FontConfig, FontFormat, OutputConfig, ResolvedConfig, ScanConfig, StyleFormat, UploadConfig, UploadProviderType, UserConfig, defaultConfig, defineConfig };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as FONT_ASSETS_DIR, c as OutputConfig, d as StyleFormat, f as UploadConfig, g as defineConfig, h as defaultConfig, i as ExtraText, l as ResolvedConfig, m as UserConfig, n as COSConfig, o as FontConfig, p as UploadProviderType, r as EnvConfig, s as FontFormat, t as BuildConfig, u as ScanConfig } from "../schema-bUBG41yN.mjs";
|
|
2
|
+
export { BuildConfig, COSConfig, EnvConfig, ExtraText, FONT_ASSETS_DIR, FontConfig, FontFormat, OutputConfig, ResolvedConfig, ScanConfig, StyleFormat, UploadConfig, UploadProviderType, UserConfig, defaultConfig, defineConfig };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t,t as n}from"../schema-BuMY-k7u.mjs";export{n as FONT_ASSETS_DIR,e as defaultConfig,t as defineConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`../schema-ClqJnGnv.cjs`);let n=require(`node:fs`);n=e.t(n);let r=require(`node:path`);r=e.t(r);let i=require(`node:process`);i=e.t(i);let a=require(`node:url`),o=require(`defu`),s=require(`unconfig`),c=require(`node:util`);const l=(0,o.createDefu)((e,t,n)=>{if(Array.isArray(e[t])&&Array.isArray(n))return e[t]=n,!0}),u=r.default.dirname((0,a.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));function d(){return i.default.cwd()}function f(e){if(e)return e;let t=r.default.join(d(),`package.json`);return JSON.parse(n.default.readFileSync(t,`utf-8`)).version||`0.0.1`}function p(e){return e?r.default.isAbsolute(e)?e:r.default.join(d(),e):r.default.join(u,`../../.cache/fonts`)}function m(e,t){let a=t?.[e]||{development:`.env.development`,production:`.env.production`}[e],o=r.default.join(d(),a);if(n.default.existsSync(o)){let e=(0,c.parseEnv)(n.default.readFileSync(o,`utf-8`));for(let[t,n]of Object.entries(e))i.default.env[t]=n}}async function h(e=`development`){let n=(await(0,s.loadConfig)({sources:[{files:`fonts.config`,extensions:[`ts`,`mts`,`cts`,`js`,`mjs`,`cjs`,`json`,`json5`]}],cwd:d(),defaults:{}})).config||{};m(e,n.build?.env);let r=l(n.build||{},t.n);if(!r.fonts||r.fonts.length===0)throw Error(`未配置字体,请在 fonts.config.ts 的 build.fonts 中添加字体配置`);return{scan:r.scan,fonts:r.fonts,output:r.output,upload:{provider:n.upload?.provider||`cos`,concurrency:n.upload?.concurrency??5},cos:n.cos||{},root:d(),cacheDir:p(n.cacheDir),version:r.version,env:r.env||{},mode:e}}exports.getCacheDir=p,exports.getProjectRoot=d,exports.getVersion=f,exports.loadConfig=h,exports.loadEnvFile=m;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { l as ResolvedConfig, t as BuildConfig } from "../schema-B8yH_G4r.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/config/loader.d.ts
|
|
4
|
+
declare function getProjectRoot(): string;
|
|
5
|
+
declare function getVersion(userVersion?: string): string;
|
|
6
|
+
declare function getCacheDir(userCacheDir?: string): string;
|
|
7
|
+
declare function loadEnvFile(mode: "development" | "production", userEnv?: BuildConfig["env"]): void;
|
|
8
|
+
declare function loadConfig(mode?: "development" | "production"): Promise<ResolvedConfig>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getCacheDir, getProjectRoot, getVersion, loadConfig, loadEnvFile };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { l as ResolvedConfig, t as BuildConfig } from "../schema-bUBG41yN.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/config/loader.d.ts
|
|
4
|
+
declare function getProjectRoot(): string;
|
|
5
|
+
declare function getVersion(userVersion?: string): string;
|
|
6
|
+
declare function getCacheDir(userCacheDir?: string): string;
|
|
7
|
+
declare function loadEnvFile(mode: "development" | "production", userEnv?: BuildConfig["env"]): void;
|
|
8
|
+
declare function loadConfig(mode?: "development" | "production"): Promise<ResolvedConfig>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getCacheDir, getProjectRoot, getVersion, loadConfig, loadEnvFile };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"../schema-BuMY-k7u.mjs";import t from"node:fs";import n from"node:path";import r from"node:process";import{fileURLToPath as i}from"node:url";import{createDefu as a}from"defu";import{loadConfig as o}from"unconfig";import{parseEnv as s}from"node:util";const c=a((e,t,n)=>{if(Array.isArray(e[t])&&Array.isArray(n))return e[t]=n,!0}),l=n.dirname(i(import.meta.url));function u(){return r.cwd()}function d(e){if(e)return e;let r=n.join(u(),`package.json`);return JSON.parse(t.readFileSync(r,`utf-8`)).version||`0.0.1`}function f(e){return e?n.isAbsolute(e)?e:n.join(u(),e):n.join(l,`../../.cache/fonts`)}function p(e,i){let a=i?.[e]||{development:`.env.development`,production:`.env.production`}[e],o=n.join(u(),a);if(t.existsSync(o)){let e=s(t.readFileSync(o,`utf-8`));for(let[t,n]of Object.entries(e))r.env[t]=n}}async function m(t=`development`){let n=(await o({sources:[{files:`fonts.config`,extensions:[`ts`,`mts`,`cts`,`js`,`mjs`,`cjs`,`json`,`json5`]}],cwd:u(),defaults:{}})).config||{};p(t,n.build?.env);let r=c(n.build||{},e);if(!r.fonts||r.fonts.length===0)throw Error(`未配置字体,请在 fonts.config.ts 的 build.fonts 中添加字体配置`);return{scan:r.scan,fonts:r.fonts,output:r.output,upload:{provider:n.upload?.provider||`cos`,concurrency:n.upload?.concurrency??5},cos:n.cos||{},root:u(),cacheDir:f(n.cacheDir),version:r.version,env:r.env||{},mode:t}}export{f as getCacheDir,u as getProjectRoot,d as getVersion,m as loadConfig,p as loadEnvFile};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/config/schema.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 字体子集化构建工具配置类型定义
|
|
4
|
+
*/
|
|
5
|
+
type FontFormat = "woff" | "woff2" | "ttf";
|
|
6
|
+
type ExtraText = string | string[];
|
|
7
|
+
interface FontConfig {
|
|
8
|
+
name: string;
|
|
9
|
+
family: string;
|
|
10
|
+
weight: number;
|
|
11
|
+
url: string;
|
|
12
|
+
style?: "normal" | "italic";
|
|
13
|
+
format?: FontFormat;
|
|
14
|
+
extraText?: ExtraText;
|
|
15
|
+
}
|
|
16
|
+
interface ScanConfig {
|
|
17
|
+
srcDir: string[];
|
|
18
|
+
extensions: string[];
|
|
19
|
+
extraText?: ExtraText;
|
|
20
|
+
}
|
|
21
|
+
interface COSConfig {
|
|
22
|
+
bucket?: string;
|
|
23
|
+
region?: string;
|
|
24
|
+
basePath?: string;
|
|
25
|
+
cdnUrl?: string;
|
|
26
|
+
overwrite?: boolean;
|
|
27
|
+
}
|
|
28
|
+
type UploadProviderType = "cos";
|
|
29
|
+
interface UploadConfig {
|
|
30
|
+
provider?: UploadProviderType;
|
|
31
|
+
concurrency?: number;
|
|
32
|
+
}
|
|
33
|
+
type StyleFormat = "scss" | "css";
|
|
34
|
+
interface OutputConfig {
|
|
35
|
+
cssDir: string;
|
|
36
|
+
format: FontFormat;
|
|
37
|
+
styleFormat: StyleFormat;
|
|
38
|
+
}
|
|
39
|
+
interface EnvConfig {
|
|
40
|
+
development?: string;
|
|
41
|
+
production?: string;
|
|
42
|
+
}
|
|
43
|
+
interface BuildConfig {
|
|
44
|
+
scan: ScanConfig;
|
|
45
|
+
fonts: FontConfig[];
|
|
46
|
+
output: OutputConfig;
|
|
47
|
+
version?: string;
|
|
48
|
+
env?: EnvConfig;
|
|
49
|
+
}
|
|
50
|
+
interface UserConfig {
|
|
51
|
+
build?: Partial<BuildConfig>;
|
|
52
|
+
upload?: UploadConfig;
|
|
53
|
+
cos?: COSConfig;
|
|
54
|
+
cacheDir?: string;
|
|
55
|
+
}
|
|
56
|
+
interface ResolvedConfig extends Required<Omit<BuildConfig, "version" | "env">> {
|
|
57
|
+
upload: UploadConfig;
|
|
58
|
+
cos: COSConfig;
|
|
59
|
+
root: string;
|
|
60
|
+
cacheDir: string;
|
|
61
|
+
version?: string;
|
|
62
|
+
env: EnvConfig;
|
|
63
|
+
mode: "development" | "production";
|
|
64
|
+
}
|
|
65
|
+
declare const defaultConfig: Omit<BuildConfig, "version">;
|
|
66
|
+
declare const FONT_ASSETS_DIR = "font-assets";
|
|
67
|
+
declare function defineConfig(config: UserConfig): UserConfig;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { FONT_ASSETS_DIR as a, OutputConfig as c, StyleFormat as d, UploadConfig as f, defineConfig as g, defaultConfig as h, ExtraText as i, ResolvedConfig as l, UserConfig as m, COSConfig as n, FontConfig as o, UploadProviderType as p, EnvConfig as r, FontFormat as s, BuildConfig as t, ScanConfig as u };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={scan:{srcDir:[`src`],extensions:[`vue`,`ts`,`tsx`,`js`,`jsx`,`scss`,`css`],extraText:``},fonts:[],output:{cssDir:`src/styles`,format:`woff`,styleFormat:`scss`}},t=`font-assets`;function n(e){return e}export{e as n,n as r,t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={scan:{srcDir:[`src`],extensions:[`vue`,`ts`,`tsx`,`js`,`jsx`,`scss`,`css`],extraText:``},fonts:[],output:{cssDir:`src/styles`,format:`woff`,styleFormat:`scss`}};function t(e){return e}Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return`font-assets`}});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/config/schema.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 字体子集化构建工具配置类型定义
|
|
4
|
+
*/
|
|
5
|
+
type FontFormat = "woff" | "woff2" | "ttf";
|
|
6
|
+
type ExtraText = string | string[];
|
|
7
|
+
interface FontConfig {
|
|
8
|
+
name: string;
|
|
9
|
+
family: string;
|
|
10
|
+
weight: number;
|
|
11
|
+
url: string;
|
|
12
|
+
style?: "normal" | "italic";
|
|
13
|
+
format?: FontFormat;
|
|
14
|
+
extraText?: ExtraText;
|
|
15
|
+
}
|
|
16
|
+
interface ScanConfig {
|
|
17
|
+
srcDir: string[];
|
|
18
|
+
extensions: string[];
|
|
19
|
+
extraText?: ExtraText;
|
|
20
|
+
}
|
|
21
|
+
interface COSConfig {
|
|
22
|
+
bucket?: string;
|
|
23
|
+
region?: string;
|
|
24
|
+
basePath?: string;
|
|
25
|
+
cdnUrl?: string;
|
|
26
|
+
overwrite?: boolean;
|
|
27
|
+
}
|
|
28
|
+
type UploadProviderType = "cos";
|
|
29
|
+
interface UploadConfig {
|
|
30
|
+
provider?: UploadProviderType;
|
|
31
|
+
concurrency?: number;
|
|
32
|
+
}
|
|
33
|
+
type StyleFormat = "scss" | "css";
|
|
34
|
+
interface OutputConfig {
|
|
35
|
+
cssDir: string;
|
|
36
|
+
format: FontFormat;
|
|
37
|
+
styleFormat: StyleFormat;
|
|
38
|
+
}
|
|
39
|
+
interface EnvConfig {
|
|
40
|
+
development?: string;
|
|
41
|
+
production?: string;
|
|
42
|
+
}
|
|
43
|
+
interface BuildConfig {
|
|
44
|
+
scan: ScanConfig;
|
|
45
|
+
fonts: FontConfig[];
|
|
46
|
+
output: OutputConfig;
|
|
47
|
+
version?: string;
|
|
48
|
+
env?: EnvConfig;
|
|
49
|
+
}
|
|
50
|
+
interface UserConfig {
|
|
51
|
+
build?: Partial<BuildConfig>;
|
|
52
|
+
upload?: UploadConfig;
|
|
53
|
+
cos?: COSConfig;
|
|
54
|
+
cacheDir?: string;
|
|
55
|
+
}
|
|
56
|
+
interface ResolvedConfig extends Required<Omit<BuildConfig, "version" | "env">> {
|
|
57
|
+
upload: UploadConfig;
|
|
58
|
+
cos: COSConfig;
|
|
59
|
+
root: string;
|
|
60
|
+
cacheDir: string;
|
|
61
|
+
version?: string;
|
|
62
|
+
env: EnvConfig;
|
|
63
|
+
mode: "development" | "production";
|
|
64
|
+
}
|
|
65
|
+
declare const defaultConfig: Omit<BuildConfig, "version">;
|
|
66
|
+
declare const FONT_ASSETS_DIR = "font-assets";
|
|
67
|
+
declare function defineConfig(config: UserConfig): UserConfig;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { FONT_ASSETS_DIR as a, OutputConfig as c, StyleFormat as d, UploadConfig as f, defineConfig as g, defaultConfig as h, ExtraText as i, ResolvedConfig as l, UserConfig as m, COSConfig as n, FontConfig as o, UploadProviderType as p, EnvConfig as r, FontFormat as s, BuildConfig as t, ScanConfig as u };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require(`../chunk-Bmb41Sf3.cjs`);let t=require(`node:fs`);t=e.t(t);let n=require(`node:path`);n=e.t(n);let r=require(`node:process`);r=e.t(r);function i(){let e=n.default.join(r.default.cwd(),`package.json`);if(t.default.existsSync(e))try{let n=JSON.parse(t.default.readFileSync(e,`utf-8`));if(typeof n?.version==`string`&&n.version.length>0)return n.version}catch{}}function a(){return r.default.env.VITE_FONT_BUILD_VERSION||i()||r.default.env.npm_package_version||`0.0.1`}function o(e=a()){return{name:`virtual-charbi`,resolveId(e){return e===`virtual:charbi`?`\0virtual:charbi`:null},load(t){return t===`\0virtual:charbi`?`export const FONT_BUILD_VERSION = ${JSON.stringify(e)}`:null}}}module.exports=o;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/vite/index.d.ts
|
|
2
|
+
interface VitePluginLike {
|
|
3
|
+
name: string;
|
|
4
|
+
resolveId?: (id: string) => string | null;
|
|
5
|
+
load?: (id: string) => string | null;
|
|
6
|
+
}
|
|
7
|
+
declare function UniBuildFont(buildFontVersion?: string): VitePluginLike;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { VitePluginLike, UniBuildFont as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/vite/index.d.ts
|
|
2
|
+
interface VitePluginLike {
|
|
3
|
+
name: string;
|
|
4
|
+
resolveId?: (id: string) => string | null;
|
|
5
|
+
load?: (id: string) => string | null;
|
|
6
|
+
}
|
|
7
|
+
declare function UniBuildFont(buildFontVersion?: string): VitePluginLike;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { VitePluginLike, UniBuildFont as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"node:fs";import t from"node:path";import n from"node:process";function r(){let r=t.join(n.cwd(),`package.json`);if(e.existsSync(r))try{let t=JSON.parse(e.readFileSync(r,`utf-8`));if(typeof t?.version==`string`&&t.version.length>0)return t.version}catch{}}function i(){return n.env.VITE_FONT_BUILD_VERSION||r()||n.env.npm_package_version||`0.0.1`}function a(e=i()){return{name:`virtual-charbi`,resolveId(e){return e===`virtual:charbi`?`\0virtual:charbi`:null},load(t){return t===`\0virtual:charbi`?`export const FONT_BUILD_VERSION = ${JSON.stringify(e)}`:null}}}export{a as default};
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "charbi-font",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "中文字体子集化工具,扫描代码提取字符,生成精简字体包",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"builder",
|
|
7
|
+
"cli",
|
|
8
|
+
"font",
|
|
9
|
+
"subset"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/imba97/charbi-font#readme",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "imba97",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git@github.com:imba97/charbi-font.git"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"charbi": "./bin/charbi.mjs"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"typesVersions": {
|
|
26
|
+
"*": {
|
|
27
|
+
"loader": [
|
|
28
|
+
"./dist/config/loader.d.mts"
|
|
29
|
+
],
|
|
30
|
+
"config": [
|
|
31
|
+
"./dist/config/index.d.mts"
|
|
32
|
+
],
|
|
33
|
+
"vite": [
|
|
34
|
+
"./dist/vite/index.d.mts"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"exports": {
|
|
39
|
+
"./cli": {
|
|
40
|
+
"import": "./dist/cli.mjs",
|
|
41
|
+
"require": "./dist/cli.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./config": {
|
|
44
|
+
"import": "./dist/config/index.mjs",
|
|
45
|
+
"require": "./dist/config/index.cjs"
|
|
46
|
+
},
|
|
47
|
+
"./config/loader": {
|
|
48
|
+
"import": "./dist/config/loader.mjs",
|
|
49
|
+
"require": "./dist/config/loader.cjs"
|
|
50
|
+
},
|
|
51
|
+
"./vite": {
|
|
52
|
+
"import": "./dist/vite/index.mjs",
|
|
53
|
+
"require": "./dist/vite/index.cjs"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "vp pack",
|
|
58
|
+
"dev": "vp pack --watch",
|
|
59
|
+
"charbi": "node dist/cli.mjs",
|
|
60
|
+
"test": "vp test run",
|
|
61
|
+
"test:watch": "vp test",
|
|
62
|
+
"check": "vp check",
|
|
63
|
+
"check:fix": "vp check --fix",
|
|
64
|
+
"lint": "vp lint",
|
|
65
|
+
"lint:fix": "vp lint --fix",
|
|
66
|
+
"fmt": "vp fmt",
|
|
67
|
+
"fmt:fix": "vp fmt --write",
|
|
68
|
+
"prepare": "simple-git-hooks"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"cac": "^7.0.0",
|
|
72
|
+
"consola": "^3.4.2",
|
|
73
|
+
"defu": "^6.1.7",
|
|
74
|
+
"enquirer": "^2.4.1",
|
|
75
|
+
"fast-glob": "^3.3.3",
|
|
76
|
+
"fontmin": "^1.1.1",
|
|
77
|
+
"p-limit": "^7.3.0",
|
|
78
|
+
"unconfig": "^7.5.0"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/node": "^25.6.0",
|
|
82
|
+
"cos-nodejs-sdk-v5": "^2.15.4",
|
|
83
|
+
"nano-staged": "^1.0.2",
|
|
84
|
+
"simple-git-hooks": "^2.13.1",
|
|
85
|
+
"typescript": "^6.0.2",
|
|
86
|
+
"vite-plus": "^0.1.16"
|
|
87
|
+
},
|
|
88
|
+
"peerDependencies": {
|
|
89
|
+
"cos-nodejs-sdk-v5": "*"
|
|
90
|
+
},
|
|
91
|
+
"simple-git-hooks": {
|
|
92
|
+
"pre-commit": "pnpm nano-staged"
|
|
93
|
+
},
|
|
94
|
+
"nano-staged": {
|
|
95
|
+
"src/**/*.{ts,js,mjs,cjs}": "vp check --fix",
|
|
96
|
+
"vite.config.ts": "vp check --fix",
|
|
97
|
+
"package.json": "vp check --fix",
|
|
98
|
+
"README.md": "vp check --fix"
|
|
99
|
+
}
|
|
100
|
+
}
|