@longzai-intelligence-templates/template-engine 0.0.5
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/.turbo/turbo-build$colon$prod.log +5 -0
- package/.turbo/turbo-build.log +33 -0
- package/.turbo/turbo-lint.log +11 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +43 -0
- package/README.md +15 -0
- package/dist/index.d.ts +271 -0
- package/dist/index.js +1 -0
- package/docs/overview.md +23 -0
- package/lzi-builder.config.ts +6 -0
- package/lzi-dev.config.ts +10 -0
- package/oxlint.config.ts +6 -0
- package/package.json +32 -0
- package/src/engine.utils.ts +190 -0
- package/src/generator.service.ts +150 -0
- package/src/index.ts +39 -0
- package/src/loader.service.ts +185 -0
- package/src/template.schema.ts +194 -0
- package/tsconfig/.cache/build.tsbuildinfo +1 -0
- package/tsconfig/.cache/node.tsbuildinfo +1 -0
- package/tsconfig/.cache/test.tsbuildinfo +1 -0
- package/tsconfig/app.json +13 -0
- package/tsconfig/build.json +15 -0
- package/tsconfig/node.json +12 -0
- package/tsconfig/test.json +15 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$ lzi-builder
|
|
2
|
+
ℹ lzi-builder v0.2.13 powered by rolldown v1.2.2
|
|
3
|
+
ℹ @longzai-intelligence-templates/template-engine tsconfig: tsconfig/build.json
|
|
4
|
+
TypeScript 7.0 尚未提供稳定的 API,仍处于实验阶段。部分选项将不可用。
|
|
5
|
+
|
|
6
|
+
ERROR Error: Build failed with 2 errors:
|
|
7
|
+
|
|
8
|
+
[31m[MISSING_EXPORT] [0m"TemplateCategory" is not exported by "src/template.schema.ts".
|
|
9
|
+
[38;5;246m╭[0m[38;5;246m─[0m[38;5;246m[[0m src/index.ts:9:3 [38;5;246m][0m
|
|
10
|
+
[38;5;246m│[0m
|
|
11
|
+
[38;5;246m9 │[0m [38;5;249m [0m[38;5;249m [0mTemplateCategory[38;5;249m,[0m
|
|
12
|
+
[38;5;240m │[0m ────────┬───────
|
|
13
|
+
[38;5;240m │[0m ╰───────── Missing export
|
|
14
|
+
[38;5;240m │[0m
|
|
15
|
+
[38;5;240m │[0m [38;5;115mNote[0m: If you meant to import a type rather than a value, make sure to add the `type` modifier (e.g. `import { type Foo } from './template.schema'`).
|
|
16
|
+
[38;5;246m───╯[0m
|
|
17
|
+
|
|
18
|
+
[31m[MISSING_EXPORT] [0m"VariableType" is not exported by "src/template.schema.ts".
|
|
19
|
+
[38;5;246m╭[0m[38;5;246m─[0m[38;5;246m[[0m src/index.ts:10:3 [38;5;246m][0m
|
|
20
|
+
[38;5;246m│[0m
|
|
21
|
+
[38;5;246m10 │[0m [38;5;249m [0m[38;5;249m [0mVariableType[38;5;249m,[0m
|
|
22
|
+
[38;5;240m │[0m ──────┬─────
|
|
23
|
+
[38;5;240m │[0m ╰─────── Missing export
|
|
24
|
+
[38;5;240m │[0m
|
|
25
|
+
[38;5;240m │[0m [38;5;115mNote[0m: If you meant to import a type rather than a value, make sure to add the `type` modifier (e.g. `import { type Foo } from './template.schema'`).
|
|
26
|
+
[38;5;246m────╯[0m
|
|
27
|
+
|
|
28
|
+
at aggregateBindingErrorsIntoJsError (/Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence-m5-edition/.lzi/bun/store/links/rolldown@1.2.2-8934269c46bb2da1/node_modules/rolldown/dist/shared/error-nLggAzpQ.mjs:48:22)
|
|
29
|
+
at unwrapBindingResult (/Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence-m5-edition/.lzi/bun/store/links/rolldown@1.2.2-8934269c46bb2da1/node_modules/rolldown/dist/shared/error-nLggAzpQ.mjs:18:128)
|
|
30
|
+
at #build (/Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence-m5-edition/.lzi/bun/store/links/rolldown@1.2.2-8934269c46bb2da1/node_modules/rolldown/dist/shared/rolldown-D23fYgSS.mjs:132:34)
|
|
31
|
+
at processTicksAndRejections (native:7:39)
|
|
32
|
+
|
|
33
|
+
error: script "build" exited with code 1
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
$ oxlint && oxfmt --check
|
|
2
|
+
Found 0 warnings and 0 errors.
|
|
3
|
+
Finished in 792ms on 8 files with 186 rules using 10 threads.
|
|
4
|
+
(node:45992) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence-m5-edition/longzai-intelligence-templates/oxfmt.config.ts?cache=1790354605913 is not specified and it doesn't parse as CommonJS.
|
|
5
|
+
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
|
|
6
|
+
To eliminate this warning, add "type": "module" to /Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence-m5-edition/longzai-intelligence-templates/package.json.
|
|
7
|
+
(Use `node --trace-warnings ...` to show where the warning was created)
|
|
8
|
+
Checking formatting...
|
|
9
|
+
|
|
10
|
+
All matched files use the correct format.
|
|
11
|
+
Finished in 601ms on 16 files using 10 threads.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
本项目的所有重要更改都将记录在此文件中。
|
|
4
|
+
|
|
5
|
+
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
6
|
+
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
7
|
+
|
|
8
|
+
## [0.0.5] - 2026-10-04
|
|
9
|
+
|
|
10
|
+
### 变更
|
|
11
|
+
|
|
12
|
+
- 升级 @longzai-intelligence-templates/template-engine 至 0.0.5
|
|
13
|
+
|
|
14
|
+
## [0.0.4] - 2026-10-04
|
|
15
|
+
|
|
16
|
+
### 修复
|
|
17
|
+
|
|
18
|
+
- 修复模板引擎入口将纯类型按值导出导致的模块加载错误
|
|
19
|
+
- 修复模板加载在分类目录缺失时的崩溃,缺失分类改为静默跳过
|
|
20
|
+
|
|
21
|
+
## [0.0.3] - 2026-10-03
|
|
22
|
+
|
|
23
|
+
### 变更
|
|
24
|
+
|
|
25
|
+
- 升级 @longzai-intelligence-templates/template-engine 至 0.0.3
|
|
26
|
+
|
|
27
|
+
## [0.0.2] - 2026-10-03
|
|
28
|
+
|
|
29
|
+
### 变更
|
|
30
|
+
|
|
31
|
+
- 升级 @longzai-intelligence-templates/template-engine 至 0.0.2
|
|
32
|
+
|
|
33
|
+
## [0.0.1] - 2026-10-02
|
|
34
|
+
|
|
35
|
+
### 新增
|
|
36
|
+
|
|
37
|
+
- 新增模板引擎核心包,提供变量替换、文件生成与模板加载能力
|
|
38
|
+
|
|
39
|
+
### 变更
|
|
40
|
+
|
|
41
|
+
- 重构模板引擎模块结构与导出组织
|
|
42
|
+
- 模板元数据校验失败时抛出携带路径与校验详情的错误
|
|
43
|
+
- 批量加载模板时跳过校验未通过的模板
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @longzai-intelligence-templates/template-engine
|
|
2
|
+
|
|
3
|
+
Longzai Intelligence Templates 的模板引擎核心包,为模板浏览与生成应用提供变量替换、文件生成与模板加载能力。
|
|
4
|
+
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
7
|
+
治理模板生成中的变量处理与模板加载问题:预置模板中的占位变量需经统一解析、校验与合并后方可生成目标文件,模板元数据、模板分类与模板文件需要统一的 Schema 与类型约束,生成目标路径需要从模板路径统一解析——本包作为模板产品的核心引擎层承载这些约束,供模板浏览与生成应用直接引用。
|
|
8
|
+
|
|
9
|
+
## 能力
|
|
10
|
+
|
|
11
|
+
- 模板变量替换:变量解析、变量校验、内置变量构建与变量合并
|
|
12
|
+
- 目标路径解析:模板文件路径到生成目标路径的统一映射
|
|
13
|
+
- 模板加载:模板元数据加载、全量模板加载、模板文件读取、分类过滤、搜索与按 ID 查找
|
|
14
|
+
- 预览与文件生成:生成前预览与目标文件生成
|
|
15
|
+
- Schema 与类型定义:模板元数据、模板分类、变量类型、模板变量与模板文件
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
//#region src/template.schema.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* 模板分类枚举 Schema
|
|
5
|
+
*/
|
|
6
|
+
declare const TemplateCategorySchema: z.ZodEnum<{
|
|
7
|
+
adapter: "adapter";
|
|
8
|
+
app: "app";
|
|
9
|
+
config: "config";
|
|
10
|
+
docs: "docs";
|
|
11
|
+
domain: "domain";
|
|
12
|
+
package: "package";
|
|
13
|
+
project: "project";
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* 模板分类类型
|
|
17
|
+
*/
|
|
18
|
+
type TemplateCategory = z.infer<typeof TemplateCategorySchema>;
|
|
19
|
+
/**
|
|
20
|
+
* 模板变量类型枚举 Schema
|
|
21
|
+
*/
|
|
22
|
+
declare const VariableTypeSchema: z.ZodEnum<{
|
|
23
|
+
boolean: "boolean";
|
|
24
|
+
select: "select";
|
|
25
|
+
string: "string";
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* 模板变量类型
|
|
29
|
+
*/
|
|
30
|
+
type VariableType = z.infer<typeof VariableTypeSchema>;
|
|
31
|
+
/**
|
|
32
|
+
* 模板变量定义 Schema
|
|
33
|
+
*/
|
|
34
|
+
declare const TemplateVariableSchema: z.ZodObject<{
|
|
35
|
+
name: z.ZodString;
|
|
36
|
+
type: z.ZodEnum<{
|
|
37
|
+
boolean: "boolean";
|
|
38
|
+
select: "select";
|
|
39
|
+
string: "string";
|
|
40
|
+
}>;
|
|
41
|
+
description: z.ZodString;
|
|
42
|
+
defaultValue: z.ZodOptional<z.ZodString>;
|
|
43
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
44
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
45
|
+
label: z.ZodString;
|
|
46
|
+
value: z.ZodString;
|
|
47
|
+
}, z.core.$strip>>>;
|
|
48
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
49
|
+
patternMessage: z.ZodOptional<z.ZodString>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
/**
|
|
52
|
+
* 模板变量定义类型
|
|
53
|
+
*/
|
|
54
|
+
type TemplateVariable = z.infer<typeof TemplateVariableSchema>;
|
|
55
|
+
/**
|
|
56
|
+
* 模板文件定义 Schema
|
|
57
|
+
*/
|
|
58
|
+
declare const TemplateFileSchema: z.ZodObject<{
|
|
59
|
+
source: z.ZodString;
|
|
60
|
+
destination: z.ZodString;
|
|
61
|
+
executable: z.ZodDefault<z.ZodBoolean>;
|
|
62
|
+
condition: z.ZodOptional<z.ZodString>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
/**
|
|
65
|
+
* 模板文件定义类型
|
|
66
|
+
*/
|
|
67
|
+
type TemplateFile = z.infer<typeof TemplateFileSchema>;
|
|
68
|
+
/**
|
|
69
|
+
* 模板元数据 Schema
|
|
70
|
+
*/
|
|
71
|
+
declare const TemplateMetaSchema: z.ZodObject<{
|
|
72
|
+
id: z.ZodString;
|
|
73
|
+
name: z.ZodString;
|
|
74
|
+
description: z.ZodString;
|
|
75
|
+
category: z.ZodEnum<{
|
|
76
|
+
adapter: "adapter";
|
|
77
|
+
app: "app";
|
|
78
|
+
config: "config";
|
|
79
|
+
docs: "docs";
|
|
80
|
+
domain: "domain";
|
|
81
|
+
package: "package";
|
|
82
|
+
project: "project";
|
|
83
|
+
}>;
|
|
84
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
85
|
+
variables: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
86
|
+
name: z.ZodString;
|
|
87
|
+
type: z.ZodEnum<{
|
|
88
|
+
boolean: "boolean";
|
|
89
|
+
select: "select";
|
|
90
|
+
string: "string";
|
|
91
|
+
}>;
|
|
92
|
+
description: z.ZodString;
|
|
93
|
+
defaultValue: z.ZodOptional<z.ZodString>;
|
|
94
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
95
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
96
|
+
label: z.ZodString;
|
|
97
|
+
value: z.ZodString;
|
|
98
|
+
}, z.core.$strip>>>;
|
|
99
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
100
|
+
patternMessage: z.ZodOptional<z.ZodString>;
|
|
101
|
+
}, z.core.$strip>>>;
|
|
102
|
+
files: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
103
|
+
source: z.ZodString;
|
|
104
|
+
destination: z.ZodString;
|
|
105
|
+
executable: z.ZodDefault<z.ZodBoolean>;
|
|
106
|
+
condition: z.ZodOptional<z.ZodString>;
|
|
107
|
+
}, z.core.$strip>>>;
|
|
108
|
+
dependencies: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
109
|
+
version: z.ZodDefault<z.ZodString>;
|
|
110
|
+
}, z.core.$strip>;
|
|
111
|
+
/**
|
|
112
|
+
* 模板元数据类型
|
|
113
|
+
*/
|
|
114
|
+
type TemplateMeta = z.infer<typeof TemplateMetaSchema>;
|
|
115
|
+
/**
|
|
116
|
+
* 内置变量名称常量
|
|
117
|
+
*/
|
|
118
|
+
declare const BUILTIN_VARIABLES: {
|
|
119
|
+
readonly projectName: 'projectName';
|
|
120
|
+
readonly projectScope: 'projectScope';
|
|
121
|
+
readonly domainName: 'domainName';
|
|
122
|
+
readonly packageName: 'packageName';
|
|
123
|
+
readonly author: 'author';
|
|
124
|
+
readonly year: 'year';
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* 内置变量类型
|
|
128
|
+
*/
|
|
129
|
+
type BuiltinVariable = keyof typeof BUILTIN_VARIABLES;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/engine.utils.d.ts
|
|
132
|
+
/**
|
|
133
|
+
* 模板变量值映射
|
|
134
|
+
*/
|
|
135
|
+
type VariableValues = Record<string, string | boolean>;
|
|
136
|
+
/**
|
|
137
|
+
* 替换模板内容中的变量占位符
|
|
138
|
+
*
|
|
139
|
+
* @param content - 模板内容
|
|
140
|
+
* @param variables - 变量值映射
|
|
141
|
+
* @returns 替换后的内容
|
|
142
|
+
*/
|
|
143
|
+
declare const replaceVariables: (content: string, variables: VariableValues) => string;
|
|
144
|
+
/**
|
|
145
|
+
* 解析变量输入值
|
|
146
|
+
*
|
|
147
|
+
* @param variable - 变量定义
|
|
148
|
+
* @param rawValue - 原始输入值
|
|
149
|
+
* @returns 解析后的值
|
|
150
|
+
*/
|
|
151
|
+
declare const parseVariableValue: (variable: TemplateVariable, rawValue: string) => string | boolean;
|
|
152
|
+
/**
|
|
153
|
+
* 验证变量值
|
|
154
|
+
*
|
|
155
|
+
* @param variable - 变量定义
|
|
156
|
+
* @param value - 变量值
|
|
157
|
+
* @returns 验证错误信息,无错误返回 null
|
|
158
|
+
*/
|
|
159
|
+
declare const validateVariable: (variable: TemplateVariable, value: string | boolean | undefined) => string | null;
|
|
160
|
+
/**
|
|
161
|
+
* 构建内置变量
|
|
162
|
+
*
|
|
163
|
+
* @param projectName - 项目名称
|
|
164
|
+
* @returns 内置变量映射
|
|
165
|
+
*/
|
|
166
|
+
declare const buildBuiltinVariables: (projectName: string) => VariableValues;
|
|
167
|
+
/**
|
|
168
|
+
* 合并模板变量默认值与用户输入值
|
|
169
|
+
*
|
|
170
|
+
* @param meta - 模板元数据
|
|
171
|
+
* @param userValues - 用户输入值
|
|
172
|
+
* @returns 合并后的变量值映射
|
|
173
|
+
*/
|
|
174
|
+
declare const mergeVariableValues: (meta: TemplateMeta, userValues: VariableValues) => VariableValues;
|
|
175
|
+
/**
|
|
176
|
+
* 生成目标文件路径
|
|
177
|
+
*
|
|
178
|
+
* @param destinationTemplate - 目标路径模板
|
|
179
|
+
* @param variables - 变量值映射
|
|
180
|
+
* @returns 生成后的目标路径
|
|
181
|
+
*/
|
|
182
|
+
declare const resolveDestinationPath: (destinationTemplate: string, variables: VariableValues) => string;
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/loader.service.d.ts
|
|
185
|
+
/**
|
|
186
|
+
* 从目录加载模板元数据
|
|
187
|
+
*
|
|
188
|
+
* @param templateDir - 模板目录路径
|
|
189
|
+
* @returns 模板元数据
|
|
190
|
+
* @throws {@link Error} 元数据文件内容未通过 Schema 校验时抛出(含校验错误详情)
|
|
191
|
+
*/
|
|
192
|
+
declare const loadTemplateMeta: (templateDir: string) => Promise<TemplateMeta>;
|
|
193
|
+
/**
|
|
194
|
+
* 加载目录下所有模板的元数据
|
|
195
|
+
*
|
|
196
|
+
* @param templatesRoot - 模板根目录路径
|
|
197
|
+
* @returns 模板元数据数组
|
|
198
|
+
*/
|
|
199
|
+
declare const loadAllTemplates: (templatesRoot: string) => Promise<TemplateMeta[]>;
|
|
200
|
+
/**
|
|
201
|
+
* 读取模板文件内容
|
|
202
|
+
*
|
|
203
|
+
* @param templateDir - 模板目录路径
|
|
204
|
+
* @param sourcePath - 源文件路径(相对于模板目录)
|
|
205
|
+
* @returns 文件内容字符串
|
|
206
|
+
*/
|
|
207
|
+
declare const readTemplateFile: (templateDir: string, sourcePath: string) => Promise<string>;
|
|
208
|
+
/**
|
|
209
|
+
* 按分类过滤模板
|
|
210
|
+
*
|
|
211
|
+
* @param templates - 模板列表
|
|
212
|
+
* @param category - 分类名称
|
|
213
|
+
* @returns 过滤后的模板列表
|
|
214
|
+
*/
|
|
215
|
+
declare const filterByCategory: (templates: TemplateMeta[], category?: string) => TemplateMeta[];
|
|
216
|
+
/**
|
|
217
|
+
* 按关键词搜索模板
|
|
218
|
+
*
|
|
219
|
+
* @param templates - 模板列表
|
|
220
|
+
* @param keyword - 搜索关键词
|
|
221
|
+
* @returns 匹配的模板列表
|
|
222
|
+
*/
|
|
223
|
+
declare const searchTemplates: (templates: TemplateMeta[], keyword: string) => TemplateMeta[];
|
|
224
|
+
/**
|
|
225
|
+
* 根据 ID 查找模板
|
|
226
|
+
*
|
|
227
|
+
* @param templates - 模板列表
|
|
228
|
+
* @param id - 模板 ID
|
|
229
|
+
* @returns 模板元数据或 undefined
|
|
230
|
+
*/
|
|
231
|
+
declare const findTemplateById: (templates: TemplateMeta[], id: string) => TemplateMeta | undefined;
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/generator.service.d.ts
|
|
234
|
+
/**
|
|
235
|
+
* 生成结果项
|
|
236
|
+
*/
|
|
237
|
+
type GeneratedFile = {
|
|
238
|
+
/**
|
|
239
|
+
* 目标文件路径
|
|
240
|
+
*/
|
|
241
|
+
destination: string;
|
|
242
|
+
/**
|
|
243
|
+
* 是否跳过(条件不满足)
|
|
244
|
+
*/
|
|
245
|
+
skipped: boolean;
|
|
246
|
+
/**
|
|
247
|
+
* 是否为可执行文件
|
|
248
|
+
*/
|
|
249
|
+
executable: boolean;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* 预览将要生成的文件列表
|
|
253
|
+
*
|
|
254
|
+
* @param meta - 模板元数据
|
|
255
|
+
* @param variables - 变量值映射
|
|
256
|
+
* @returns 生成结果预览
|
|
257
|
+
*/
|
|
258
|
+
declare const previewGeneration: (meta: TemplateMeta, variables: VariableValues) => GeneratedFile[];
|
|
259
|
+
/**
|
|
260
|
+
* 生成模板文件到目标目录
|
|
261
|
+
*
|
|
262
|
+
* @param templateDir - 模板目录路径
|
|
263
|
+
* @param meta - 模板元数据
|
|
264
|
+
* @param variables - 变量值映射
|
|
265
|
+
* @param outputDir - 输出目录路径
|
|
266
|
+
* @param dryRun - 是否仅预览
|
|
267
|
+
* @returns 生成的文件列表
|
|
268
|
+
*/
|
|
269
|
+
declare const generateFiles: (templateDir: string, meta: TemplateMeta, variables: VariableValues, outputDir: string, dryRun?: boolean) => Promise<GeneratedFile[]>;
|
|
270
|
+
//#endregion
|
|
271
|
+
export { BUILTIN_VARIABLES, type BuiltinVariable, type GeneratedFile, type TemplateCategory, type TemplateFile, TemplateFileSchema, type TemplateMeta, TemplateMetaSchema, type TemplateVariable, TemplateVariableSchema, type VariableType, type VariableValues, buildBuiltinVariables, filterByCategory, findTemplateById, generateFiles, loadAllTemplates, loadTemplateMeta, mergeVariableValues, parseVariableValue, previewGeneration, readTemplateFile, replaceVariables, resolveDestinationPath, searchTemplates, validateVariable };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"zod";import{existsSync as t}from"node:fs";const n=e.enum([`project`,`app`,`package`,`domain`,`config`,`docs`,`adapter`]),r=e.enum([`string`,`boolean`,`select`]),i=e.object({name:e.string(),type:r,description:e.string(),defaultValue:e.string().optional(),required:e.boolean().default(!0),options:e.array(e.object({label:e.string(),value:e.string()})).optional(),pattern:e.string().optional(),patternMessage:e.string().optional()}),a=e.object({source:e.string(),destination:e.string(),executable:e.boolean().default(!1),condition:e.string().optional()}),o=e.object({id:e.string(),name:e.string(),description:e.string(),category:n,tags:e.array(e.string()).default([]),variables:e.array(i).default([]),files:e.array(a).default([]),dependencies:e.array(e.string()).default([]),version:e.string().default(`0.1.0`)}),s={projectName:`projectName`,projectScope:`projectScope`,domainName:`domainName`,packageName:`packageName`,author:`author`,year:`year`},c=(e,t)=>{let n=e;return n=l(n,t),n=n.replace(/\{\{(\w+)\}\}/g,(e,n)=>{let r=t[n];return r===void 0?e:String(r)}),n},l=(e,t)=>e.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g,(e,n,r)=>{let i=t[n];return typeof i==`boolean`?i?r:``:typeof i==`string`&&i&&i!==`false`?r:``}),u=(e,t)=>e.type===`boolean`?t===`true`||t===`1`:t,d=(e,t)=>e.required&&(t===void 0||t===``)?`${e.name} 是必填项`:e.pattern&&typeof t==`string`&&!new RegExp(e.pattern).test(t)?e.patternMessage??`${e.name} 格式不正确`:null,f=e=>{let t=e.replace(/^longzai-intelligence-/,``);return{projectName:e,projectScope:t,domainName:t,packageName:t,author:`Longzai Intelligence`,year:String((/* @__PURE__ */ new Date()).getFullYear())}},p=(e,t)=>{let n={};for(let r of e.variables){let e=t[r.name];e===void 0?r.defaultValue!==void 0&&(n[r.name]=r.defaultValue):n[r.name]=e}return n},m=(e,t)=>c(e,t),h=async e=>{let t=`${e}/template.json`,n=await Bun.file(t).json(),r=o.safeParse(n);if(!r.success)throw Error(`模板元数据校验失败(${t}): ${r.error.message}`);return r.data},g=async e=>{let n=[`projects`,`apps`,`packages`,`domains`,`configs`,`docs`,`adapters`],r=[];for(let i of n){let n=`${e}/${i}`;if(!t(n))continue;let a=new Bun.Glob(`*`);for await(let e of a.scan({cwd:n,onlyFiles:!1})){let t=`${n}/${e}/template.json`;try{let e=Bun.file(t);if(await e.exists()){let t=await e.json(),n=o.safeParse(t);n.success&&r.push(n.data)}}catch{}}}return r},_=async(e,t)=>{let n=`${e}/${t}`;return await Bun.file(n).text()},v=(e,t)=>t?e.filter(e=>e.category===t):e,y=(e,t)=>{let n=t.toLowerCase();return e.filter(e=>e.name.toLowerCase().includes(n)||e.description.toLowerCase().includes(n)||e.tags.some(e=>e.toLowerCase().includes(n)))},b=(e,t)=>e.find(e=>e.id===t),x=(e,t)=>e.files.map(e=>{let n=S(e,t);return{destination:m(e.destination,t),skipped:!n,executable:e.executable}}),S=(e,t)=>{if(!e.condition)return!0;let n=t[e.condition];return typeof n==`boolean`?n:typeof n==`string`&&n!==``&&n!==`false`},C=async(e,t,n,r,i=!1)=>{let a=[];for(let o of t.files){let t=S(o,n),s=m(o.destination,n);if(!t){a.push({destination:s,skipped:!0,executable:o.executable});continue}if(i){a.push({destination:s,skipped:!1,executable:o.executable});continue}let l=await _(e,o.source),u=c(l,n),d=`${r}/${s}`;await Bun.write(d,u),a.push({destination:s,skipped:!1,executable:o.executable})}return a};export{s as BUILTIN_VARIABLES,a as TemplateFileSchema,o as TemplateMetaSchema,i as TemplateVariableSchema,f as buildBuiltinVariables,v as filterByCategory,b as findTemplateById,C as generateFiles,g as loadAllTemplates,h as loadTemplateMeta,p as mergeVariableValues,u as parseVariableValue,x as previewGeneration,_ as readTemplateFile,c as replaceVariables,m as resolveDestinationPath,y as searchTemplates,d as validateVariable};
|
package/docs/overview.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# template-engine 概览
|
|
2
|
+
|
|
3
|
+
> 承接自 README 的介绍细节(README 发布口径精简迁移);本文件为仓内内部文档,不随包发布,仓内互链合法。
|
|
4
|
+
|
|
5
|
+
## 原四段叙述(承接)
|
|
6
|
+
|
|
7
|
+
以下为原 README 的意图/定位/职能/实现情况叙述。原节结构(意图/定位/职能/实现情况)与全仓统一四段形态(品牌首句 + 简介 + 能力)不一致,且「实现情况」属状态/成熟度类字段(发布 README 不写),按 README 发布口径迁此承载:
|
|
8
|
+
|
|
9
|
+
### 意图
|
|
10
|
+
|
|
11
|
+
为模板产品提供模板引擎核心能力,支撑模板变量替换、文件生成与模板加载,作为模板浏览与生成应用的基础设施。
|
|
12
|
+
|
|
13
|
+
### 定位
|
|
14
|
+
|
|
15
|
+
属于模板产品的核心引擎层包,位于 packages/template-engine 目录下,不依赖任何业务框架,被 Web 应用引用以驱动模板管理与文件生成流程。
|
|
16
|
+
|
|
17
|
+
### 职能
|
|
18
|
+
|
|
19
|
+
导出模板元数据、模板分类、变量类型、模板变量、模板文件等 Schema 与类型定义;提供变量替换、变量解析、变量校验、内置变量构建、变量合并、目标路径解析等引擎能力;提供模板元数据加载、全量模板加载、模板文件读取、分类过滤、搜索、按 ID 查找等加载器能力;提供预览生成与文件生成器能力。
|
|
20
|
+
|
|
21
|
+
### 实现情况
|
|
22
|
+
|
|
23
|
+
已完整组织 Schema、引擎、加载器、生成器四大模块(src 下 template.schema.ts / engine.utils.ts / loader.service.ts / generator.service.ts)的导出,作为模板产品的核心引擎稳定交付。
|
package/oxlint.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-templates/template-engine",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "lzi-builder",
|
|
15
|
+
"build:prod": "NODE_ENV=production bun run build",
|
|
16
|
+
"prepublishOnly": "bun run build:prod",
|
|
17
|
+
"typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
|
|
18
|
+
"typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
|
|
19
|
+
"typecheck:node": "lzi-tsgo typecheck tsconfig/node.json",
|
|
20
|
+
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json",
|
|
21
|
+
"lint": "oxlint && oxfmt --check",
|
|
22
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
23
|
+
"test": "bun test",
|
|
24
|
+
"test:watch": "bun test --watch",
|
|
25
|
+
"test:coverage": "bun test --coverage",
|
|
26
|
+
"clean": "lzi-dev-cli clean"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"zod": "^4.6.5"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {}
|
|
32
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模板引擎核心
|
|
3
|
+
*
|
|
4
|
+
* 提供模板变量替换、条件块处理和文件生成功能
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TemplateMeta, TemplateVariable } from './template.schema';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 模板变量值映射
|
|
11
|
+
*/
|
|
12
|
+
export type VariableValues = Record<string, string | boolean>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 替换模板内容中的变量占位符
|
|
16
|
+
*
|
|
17
|
+
* @param content - 模板内容
|
|
18
|
+
* @param variables - 变量值映射
|
|
19
|
+
* @returns 替换后的内容
|
|
20
|
+
*/
|
|
21
|
+
export const replaceVariables = (content: string, variables: VariableValues): string => {
|
|
22
|
+
/**
|
|
23
|
+
* 逐轮替换的中间结果,先承载原始内容再逐步收敛
|
|
24
|
+
*/
|
|
25
|
+
let result = content;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 先处理 {{#if variableName}}...{{/if}} 条件块,避免变量替换破坏块语法
|
|
29
|
+
*/
|
|
30
|
+
result = processConditionalBlocks(result, variables);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 再处理 {{variableName}} 变量替换
|
|
34
|
+
*/
|
|
35
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (match, varName: string) => {
|
|
36
|
+
/**
|
|
37
|
+
* 当前占位符对应的变量值,未提供时保留原占位符
|
|
38
|
+
*/
|
|
39
|
+
const value = variables[varName];
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
return match;
|
|
42
|
+
}
|
|
43
|
+
return String(value);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 处理条件块 {{#if variableName}}...{{/if}}
|
|
51
|
+
*
|
|
52
|
+
* @param content - 模板内容
|
|
53
|
+
* @param variables - 变量值映射
|
|
54
|
+
* @returns 处理后的内容
|
|
55
|
+
*/
|
|
56
|
+
const processConditionalBlocks = (content: string, variables: VariableValues): string => {
|
|
57
|
+
/**
|
|
58
|
+
* 匹配 {{#if var}}...{{/if}} 模式,支持嵌套空白
|
|
59
|
+
*/
|
|
60
|
+
const pattern = /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
|
|
61
|
+
|
|
62
|
+
return content.replace(pattern, (_match, varName: string, innerContent: string) => {
|
|
63
|
+
/**
|
|
64
|
+
* 条件变量对应的值,决定保留还是剔除块内容
|
|
65
|
+
*/
|
|
66
|
+
const value = variables[varName];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 布尔值直接判断,字符串判断是否为真值
|
|
70
|
+
*/
|
|
71
|
+
if (typeof value === 'boolean') {
|
|
72
|
+
return value ? innerContent : '';
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === 'string') {
|
|
75
|
+
return value && value !== 'false' ? innerContent : '';
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 解析变量输入值
|
|
83
|
+
*
|
|
84
|
+
* @param variable - 变量定义
|
|
85
|
+
* @param rawValue - 原始输入值
|
|
86
|
+
* @returns 解析后的值
|
|
87
|
+
*/
|
|
88
|
+
export const parseVariableValue = (
|
|
89
|
+
variable: TemplateVariable,
|
|
90
|
+
rawValue: string,
|
|
91
|
+
): string | boolean => {
|
|
92
|
+
if (variable.type === 'boolean') {
|
|
93
|
+
return rawValue === 'true' || rawValue === '1';
|
|
94
|
+
}
|
|
95
|
+
return rawValue;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 验证变量值
|
|
100
|
+
*
|
|
101
|
+
* @param variable - 变量定义
|
|
102
|
+
* @param value - 变量值
|
|
103
|
+
* @returns 验证错误信息,无错误返回 null
|
|
104
|
+
*/
|
|
105
|
+
export const validateVariable = (
|
|
106
|
+
variable: TemplateVariable,
|
|
107
|
+
value: string | boolean | undefined,
|
|
108
|
+
): string | null => {
|
|
109
|
+
if (variable.required && (value === undefined || value === '')) {
|
|
110
|
+
return `${variable.name} 是必填项`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (variable.pattern && typeof value === 'string') {
|
|
114
|
+
/**
|
|
115
|
+
* 按变量定义的正则校验输入格式
|
|
116
|
+
*/
|
|
117
|
+
const regex = new RegExp(variable.pattern);
|
|
118
|
+
if (!regex.test(value)) {
|
|
119
|
+
return variable.patternMessage ?? `${variable.name} 格式不正确`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return null;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 构建内置变量
|
|
128
|
+
*
|
|
129
|
+
* @param projectName - 项目名称
|
|
130
|
+
* @returns 内置变量映射
|
|
131
|
+
*/
|
|
132
|
+
export const buildBuiltinVariables = (projectName: string): VariableValues => {
|
|
133
|
+
/**
|
|
134
|
+
* 从项目名称剥离产品群前缀后得到的作用域名
|
|
135
|
+
*/
|
|
136
|
+
const scope = projectName.replace(/^longzai-intelligence-/, '');
|
|
137
|
+
return {
|
|
138
|
+
projectName,
|
|
139
|
+
projectScope: scope,
|
|
140
|
+
domainName: scope,
|
|
141
|
+
packageName: scope,
|
|
142
|
+
author: 'Longzai Intelligence',
|
|
143
|
+
year: String(new Date().getFullYear()),
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 合并模板变量默认值与用户输入值
|
|
149
|
+
*
|
|
150
|
+
* @param meta - 模板元数据
|
|
151
|
+
* @param userValues - 用户输入值
|
|
152
|
+
* @returns 合并后的变量值映射
|
|
153
|
+
*/
|
|
154
|
+
export const mergeVariableValues = (
|
|
155
|
+
meta: TemplateMeta,
|
|
156
|
+
userValues: VariableValues,
|
|
157
|
+
): VariableValues => {
|
|
158
|
+
/**
|
|
159
|
+
* 用户输入优先、默认值兜底的合并结果
|
|
160
|
+
*/
|
|
161
|
+
const result: VariableValues = {};
|
|
162
|
+
|
|
163
|
+
for (const variable of meta.variables) {
|
|
164
|
+
/**
|
|
165
|
+
* 用户为该变量提供的输入值,优先于默认值生效
|
|
166
|
+
*/
|
|
167
|
+
const userValue = userValues[variable.name];
|
|
168
|
+
if (userValue !== undefined) {
|
|
169
|
+
result[variable.name] = userValue;
|
|
170
|
+
} else if (variable.defaultValue !== undefined) {
|
|
171
|
+
result[variable.name] = variable.defaultValue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return result;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 生成目标文件路径
|
|
180
|
+
*
|
|
181
|
+
* @param destinationTemplate - 目标路径模板
|
|
182
|
+
* @param variables - 变量值映射
|
|
183
|
+
* @returns 生成后的目标路径
|
|
184
|
+
*/
|
|
185
|
+
export const resolveDestinationPath = (
|
|
186
|
+
destinationTemplate: string,
|
|
187
|
+
variables: VariableValues,
|
|
188
|
+
): string => {
|
|
189
|
+
return replaceVariables(destinationTemplate, variables);
|
|
190
|
+
};
|