@longzai-intelligence-docs/compile-plugin 0.0.0-stage → 0.0.15

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.
@@ -0,0 +1,512 @@
1
+ import "./rolldown-runtime-BqT_7tdF.js";
2
+ import * as z from "zod";
3
+ //#region src/compile.plugin.d.ts
4
+ /**
5
+ * compile 内置插件
6
+ *
7
+ * 文档派生域的一方插件即 items 真源合成与工具面抽取与快照新鲜度校验,
8
+ * 契约承 ADR 0131,对外经 definePlugin 声明四命令,宿主经内置插件列表
9
+ * 装载,外部联邦装载同样可用
10
+ */
11
+ /**
12
+ * compile 内置插件定义
13
+ */
14
+ declare const compilePlugin: import("@longzai-intelligence-docs/core").DocsPlugin;
15
+ //#endregion
16
+ //#region src/compile-config.schema.d.ts
17
+ /**
18
+ * compile 插件配置 schema 即整体缺省
19
+ */
20
+ declare const CompilePluginConfigSchema: z.ZodDefault<z.ZodObject<{
21
+ sourceRoot: z.ZodDefault<z.ZodString>;
22
+ documents: z.ZodDefault<z.ZodArray<z.ZodObject<{
23
+ output: z.ZodString;
24
+ title: z.ZodOptional<z.ZodString>;
25
+ toolFace: z.ZodDefault<z.ZodBoolean>;
26
+ exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
27
+ }, z.core.$strip>>>;
28
+ toolFace: z.ZodDefault<z.ZodObject<{
29
+ bins: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
+ invocations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
31
+ }, z.core.$strip>>;
32
+ }, z.core.$strip>>;
33
+ /**
34
+ * compile 插件配置即解析后形态
35
+ */
36
+ type CompilePluginConfig = z.output<typeof CompilePluginConfigSchema>;
37
+ /**
38
+ * compile 插件配置书写形态
39
+ */
40
+ type CompilePluginConfigInput = z.input<typeof CompilePluginConfigSchema>;
41
+ /**
42
+ * 解析 compile 插件配置
43
+ *
44
+ * @param input - 宿主注入的 plugins.compile.options 节
45
+ * @returns 解析后配置,未书写时节为整体缺省
46
+ * @throws {@link Error} 配置违反 schema 时
47
+ */
48
+ declare const parseCompilePluginConfig: (input: unknown) => CompilePluginConfig;
49
+ //#endregion
50
+ //#region src/check/finding-types.d.ts
51
+ /**
52
+ * 校验与上报共用类型
53
+ */
54
+ /**
55
+ * compile 违规类别
56
+ */
57
+ type CompileFindingKind = 'snapshot-missing' | 'snapshot-stale' | 'anchor-missing' | 'command-not-found';
58
+ /**
59
+ * 单条 compile 违规
60
+ */
61
+ type CompileFinding = {
62
+ /**
63
+ * 违规类别
64
+ */
65
+ kind: CompileFindingKind;
66
+ /**
67
+ * 违规报文,含定位与处置指引
68
+ */
69
+ message: string;
70
+ };
71
+ //#endregion
72
+ //#region src/check/compile-check.core.d.ts
73
+ /**
74
+ * 执行 compile 校验
75
+ *
76
+ * @param root - 仓库根目录绝对路径
77
+ * @param config - 解析后的插件配置
78
+ * @returns 违规清单,空即全绿
79
+ * @throws {@link Error} 扫描或条目元信息非法时由建模上抛
80
+ */
81
+ declare const runCompileCheck: (root: string, config: CompilePluginConfig) => Promise<readonly CompileFinding[]>;
82
+ //#endregion
83
+ //#region src/scan/frontmatter.utils.d.ts
84
+ /**
85
+ * 条目元信息解析后形态
86
+ *
87
+ * 五键均未声明时对应字段为 `null`,零元数据条目完全正常
88
+ */
89
+ type ItemMeta = {
90
+ /**
91
+ * 稳定身份键,未声明时为 `null`
92
+ */
93
+ id: string | null;
94
+ /**
95
+ * 条目类型提示串,未声明时为 `null`
96
+ */
97
+ type: string | null;
98
+ /**
99
+ * 标题提示串,未声明时为 `null`
100
+ */
101
+ title: string | null;
102
+ /**
103
+ * 锚定的源码文件路径即仓库根相对路径,未声明时为 `null`
104
+ */
105
+ anchor: string | null;
106
+ /**
107
+ * 锚定的命令定义名,未声明时为 `null`
108
+ */
109
+ command: string | null;
110
+ };
111
+ //#endregion
112
+ //#region src/scan/items-scan.core.d.ts
113
+ /**
114
+ * 文档内产出条目
115
+ */
116
+ type SourceItem = {
117
+ /**
118
+ * 条目文件名即文档目录内相对路径
119
+ */
120
+ fileName: string;
121
+ /**
122
+ * 定序键即文件名数值前缀
123
+ */
124
+ order: number;
125
+ /**
126
+ * 条目文件解析出的元信息
127
+ */
128
+ meta: ItemMeta;
129
+ /**
130
+ * 正文,已去尾空白
131
+ */
132
+ body: string;
133
+ };
134
+ /**
135
+ * 文档条目解析入参
136
+ */
137
+ type ResolveDocumentItemsInput = {
138
+ /**
139
+ * 文档目录绝对路径
140
+ */
141
+ documentDir: string;
142
+ /**
143
+ * 文档目录名
144
+ */
145
+ documentName: string;
146
+ /**
147
+ * 真源根的仓库根相对显示路径
148
+ */
149
+ sourceRootLabel: string;
150
+ /**
151
+ * 该文档的排除 glob 集合
152
+ */
153
+ exclude: readonly string[];
154
+ };
155
+ /**
156
+ * 解析单文档目录下全部产出条目
157
+ *
158
+ * 文件名数值前缀强制,无前缀即报错,同目录数值重复即报错,正文空白即剔除
159
+ * 暂存,exclude 以 glob 匹配条目文件名整串命中即整条目排除,产出按前缀数值
160
+ * 升序,文档目录不存在时返回空集即零条目文档为正常态
161
+ *
162
+ * @param input - 解析入参
163
+ * @returns 按前缀数值升序排列的产出条目列表
164
+ * @throws {@link Error} 条目文件名无数值前缀、前缀数值重复或元信息非法时
165
+ */
166
+ declare const resolveDocumentItems: (input: ResolveDocumentItemsInput) => Promise<readonly SourceItem[]>;
167
+ //#endregion
168
+ //#region src/scan/manifest-scan.utils.d.ts
169
+ /**
170
+ * manifest 面扫描
171
+ *
172
+ * 读取仓库根 package.json 的清单四字段即 name 与 description 与 version 与
173
+ * bin,作为产物头部身份信息与工具面 bin 缺省来源
174
+ */
175
+ /**
176
+ * manifest 面形态
177
+ */
178
+ type ManifestFace = {
179
+ /**
180
+ * 包名
181
+ */
182
+ name: string | null;
183
+ /**
184
+ * 包描述
185
+ */
186
+ description: string | null;
187
+ /**
188
+ * 包版本
189
+ */
190
+ version: string | null;
191
+ /**
192
+ * bin 名集合,书写形态为单串时归一为单元素集合
193
+ */
194
+ bins: readonly string[];
195
+ };
196
+ //#endregion
197
+ //#region src/scan/tool-face-scan.core.d.ts
198
+ /**
199
+ * 工具面黑盒抽取
200
+ *
201
+ * 逐 bin 运行 `--help` 解析 commander 形态帮助文本为结构化命令清单,逐命令
202
+ * 再取细化帮助,非 commander 形态降级记录原文不中断,调用方式可经配置覆写
203
+ * 以覆盖 dist 未构建场景即以 `bun` 直跑源码入口
204
+ */
205
+ /**
206
+ * 工具面选项条目
207
+ */
208
+ type ToolFaceOption = {
209
+ /**
210
+ * 选项旗标原文,如 `--scope <key>`
211
+ */
212
+ flags: string;
213
+ /**
214
+ * 选项说明
215
+ */
216
+ description: string;
217
+ };
218
+ /**
219
+ * 工具面命令条目
220
+ */
221
+ type ToolFaceCommand = {
222
+ /**
223
+ * 命令名即首词
224
+ */
225
+ name: string;
226
+ /**
227
+ * 用法列原文,已去 ` [options]` 尾缀
228
+ */
229
+ invocation: string;
230
+ /**
231
+ * 顶层帮助中的命令说明
232
+ */
233
+ summary: string;
234
+ /**
235
+ * 细化帮助中的命令描述
236
+ */
237
+ detailDescription: string;
238
+ /**
239
+ * 细化帮助解析出的选项集合
240
+ */
241
+ options: readonly ToolFaceOption[];
242
+ /**
243
+ * 细化帮助是否解析成功
244
+ */
245
+ detailParsed: boolean;
246
+ };
247
+ /**
248
+ * 解析成功的工具面
249
+ */
250
+ type ParsedToolFace = {
251
+ /**
252
+ * bin 名
253
+ */
254
+ bin: string;
255
+ /**
256
+ * 降级标记,恒为 `false`
257
+ */
258
+ degraded: false;
259
+ /**
260
+ * 用法行原文
261
+ */
262
+ usage: string;
263
+ /**
264
+ * bin 级描述
265
+ */
266
+ description: string;
267
+ /**
268
+ * bin 级选项集合
269
+ */
270
+ options: readonly ToolFaceOption[];
271
+ /**
272
+ * 命令集合,保持帮助输出顺序
273
+ */
274
+ commands: readonly ToolFaceCommand[];
275
+ };
276
+ /**
277
+ * 降级工具面
278
+ */
279
+ type DegradedToolFace = {
280
+ /**
281
+ * bin 名
282
+ */
283
+ bin: string;
284
+ /**
285
+ * 降级标记,恒为 `true`
286
+ */
287
+ degraded: true;
288
+ /**
289
+ * 帮助输出原文
290
+ */
291
+ raw: string;
292
+ /**
293
+ * 降级原因
294
+ */
295
+ reason: string;
296
+ };
297
+ /**
298
+ * 工具面联合形态
299
+ */
300
+ type ToolFace = ParsedToolFace | DegradedToolFace;
301
+ /**
302
+ * 帮助文本解析出的命令条目
303
+ */
304
+ type HelpCommandEntry = {
305
+ /**
306
+ * 命令名即首词
307
+ */
308
+ name: string;
309
+ /**
310
+ * 用法列原文,已去 `[options]` 尾缀
311
+ */
312
+ invocation: string;
313
+ /**
314
+ * 命令说明
315
+ */
316
+ summary: string;
317
+ };
318
+ /**
319
+ * 帮助文本解析结果
320
+ */
321
+ type ParsedHelp = {
322
+ /**
323
+ * 用法行原文,非 commander 形态即 `null`
324
+ */
325
+ usage: string | null;
326
+ /**
327
+ * 用法行与首个区块之间的描述
328
+ */
329
+ description: string;
330
+ /**
331
+ * 选项条目集合
332
+ */
333
+ options: ToolFaceOption[];
334
+ /**
335
+ * 命令条目集合
336
+ */
337
+ commands: HelpCommandEntry[];
338
+ };
339
+ /**
340
+ * 解析 commander 形态帮助文本为选项与命令区块
341
+ *
342
+ * 判定口径为存在 `Usage:` 行,缺失即视为非 commander 形态,区块条目按两个
343
+ * 以上连续空白切分左列与右列,左列为命令或旗标、右列为说明,跨列折行的
344
+ * 说明尾行会落入左列,属已知精度边界,此类行以原样保留不丢弃
345
+ *
346
+ * @param text - 帮助输出全文
347
+ * @returns 解析出的用法行、bin 级描述、bin 级选项与命令清单
348
+ */
349
+ declare const parseCommanderHelp: (text: string) => ParsedHelp;
350
+ /**
351
+ * 全量工具面抽取入参
352
+ */
353
+ type ExtractToolFaceInput = {
354
+ /**
355
+ * bin 名集合
356
+ */
357
+ bins: readonly string[];
358
+ /**
359
+ * bin 名到调用方式的覆写映射
360
+ */
361
+ invocations: Readonly<Record<string, readonly string[]>>;
362
+ /**
363
+ * 工作目录绝对路径
364
+ */
365
+ root: string;
366
+ };
367
+ /**
368
+ * 抽取全部 bin 的工具面
369
+ *
370
+ * @param input - 抽取入参
371
+ * @returns 工具面集合,顺序与 bins 声明一致
372
+ */
373
+ declare const extractToolFace: (input: ExtractToolFaceInput) => Promise<readonly ToolFace[]>;
374
+ //#endregion
375
+ //#region src/model/compile-model.core.d.ts
376
+ /**
377
+ * 单个文档的视图
378
+ */
379
+ type DocumentView = {
380
+ /**
381
+ * 文档名即真源目录名,产物 basename 去 `.md`
382
+ */
383
+ document: string;
384
+ /**
385
+ * 产物路径即仓库根相对路径
386
+ */
387
+ output: string;
388
+ /**
389
+ * 文档标题,缺省为文档名
390
+ */
391
+ title: string;
392
+ /**
393
+ * 是否追加工具面命令参考节
394
+ */
395
+ toolFace: boolean;
396
+ /**
397
+ * 该文档产出条目即已定序已过滤
398
+ */
399
+ items: readonly SourceItem[];
400
+ };
401
+ /**
402
+ * 真源视图
403
+ */
404
+ type CompileSourceView = {
405
+ /**
406
+ * 仓库根目录绝对路径
407
+ */
408
+ root: string;
409
+ /**
410
+ * 真源根的仓库根相对路径
411
+ */
412
+ sourceRootLabel: string;
413
+ /**
414
+ * manifest 面
415
+ */
416
+ manifest: ManifestFace;
417
+ /**
418
+ * 工具面集合
419
+ */
420
+ toolFace: readonly ToolFace[];
421
+ /**
422
+ * 各文档视图
423
+ */
424
+ documents: readonly DocumentView[];
425
+ };
426
+ /**
427
+ * 构建真源视图
428
+ *
429
+ * @param root - 仓库根目录绝对路径
430
+ * @param config - 解析后的插件配置
431
+ * @returns 真源视图
432
+ * @throws {@link Error} 任一文档条目文件名或元信息非法时,报错携带条目定位
433
+ */
434
+ declare const buildSourceView: (root: string, config: CompilePluginConfig) => Promise<CompileSourceView>;
435
+ //#endregion
436
+ //#region src/compose/surface-composer.core.d.ts
437
+ /**
438
+ * 合成单个文档产物
439
+ *
440
+ * @param view - 真源视图
441
+ * @param documentView - 该文档视图
442
+ * @returns 产物 markdown 全文,恒以单个换行收尾
443
+ */
444
+ declare const composeSurface: (view: CompileSourceView, documentView: DocumentView) => string;
445
+ /**
446
+ * 合成全部文档产物
447
+ *
448
+ * @param view - 真源视图
449
+ * @returns 文档名到产物路径与产物全文的集合
450
+ */
451
+ declare const composeAllSurfaces: (view: CompileSourceView) => readonly {
452
+ document: string;
453
+ output: string;
454
+ markdown: string;
455
+ }[];
456
+ //#endregion
457
+ //#region src/compile.commands.d.ts
458
+ /**
459
+ * scan 结果形态
460
+ */
461
+ type CompileScanResult = {
462
+ /**
463
+ * manifest 面四字段
464
+ */
465
+ manifest: {
466
+ name: string | null;
467
+ description: string | null;
468
+ version: string | null;
469
+ bins: readonly string[];
470
+ };
471
+ /**
472
+ * 工具面集合
473
+ */
474
+ toolFace: readonly unknown[];
475
+ /**
476
+ * 各文档条目清单
477
+ */
478
+ documents: readonly {
479
+ document: string;
480
+ output: string;
481
+ items: readonly {
482
+ file: string;
483
+ order: number;
484
+ metadata: readonly string[];
485
+ }[];
486
+ }[];
487
+ };
488
+ /**
489
+ * 执行 scan 即扫描真源面出清单
490
+ *
491
+ * @param root - 仓库根目录绝对路径
492
+ * @param config - 解析后的插件配置
493
+ * @returns scan 结果
494
+ * @throws {@link Error} 条目文件名或元信息非法时
495
+ */
496
+ declare const runCompileScan: (root: string, config: CompilePluginConfig) => Promise<CompileScanResult>;
497
+ /**
498
+ * 执行 derive 即建模派生并写盘
499
+ *
500
+ * @param root - 仓库根目录绝对路径
501
+ * @param config - 解析后的插件配置
502
+ * @param dryRun - 只打印不写盘
503
+ * @returns 写盘清单即文档名与产物路径与条目数
504
+ * @throws {@link Error} 未配置文档或条目文件名或元信息非法时
505
+ */
506
+ declare const runCompileDerive: (root: string, config: CompilePluginConfig, dryRun: boolean) => Promise<readonly {
507
+ document: string;
508
+ output: string;
509
+ itemCount: number;
510
+ }[]>;
511
+ //#endregion
512
+ export { type CompilePluginConfig, type CompilePluginConfigInput, CompilePluginConfigSchema, type CompileSourceView, type DocumentView, type SourceItem, type ToolFace, buildSourceView, compilePlugin, composeAllSurfaces, composeSurface, extractToolFace, parseCommanderHelp, parseCompilePluginConfig, resolveDocumentItems, runCompileCheck, runCompileDerive, runCompileScan };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import"./rolldown-runtime-BqT_7tdF.js";import{definePlugin as e,pluginCommand as t}from"@longzai-intelligence-docs/core";import*as n from"zod";import{mkdir as r,stat as i,writeFile as a}from"node:fs/promises";import{dirname as o,join as s}from"node:path";const c=`.lzi/docs/sources`,l=n.object({output:n.string().min(1).refine(e=>!e.startsWith(`/`),`产物路径须为仓库根相对路径`).refine(e=>e.endsWith(`.md`),`产物路径须为 md 文件`),title:n.string().min(1).optional(),toolFace:n.boolean().default(!1),exclude:n.array(n.string().min(1)).default([])}),u=n.object({bins:n.array(n.string().min(1)).optional(),invocations:n.record(n.string().min(1),n.array(n.string().min(1))).default({})}),d=n.object({sourceRoot:n.string().min(1).default(c),documents:n.array(l).default([]),toolFace:u.default({invocations:{}})}).default({sourceRoot:c,documents:[],toolFace:{invocations:{}}}),f=e=>{let t=e.split(`/`).at(-1)??e;return t.endsWith(`.md`)?t.slice(0,-3):t},p=e=>{let t=d.safeParse(e);if(!t.success)throw Error(`compile 插件配置非法:${t.error.issues.map(e=>`${e.path.join(`.`)||`根节`}——${e.message}`).join(`,`)}`);let n=/* @__PURE__ */ new Map;for(let e of t.data.documents){let t=f(e.output),r=n.get(t);if(r!==void 0)throw Error(`compile 插件配置非法:产物 basename '${t}' 跨文档重复,${r} 与 ${e.output},真源目录无法区分归属`);n.set(t,e.output)}return t.data},m=[`<!-- 本文件由 lzi-docs compile 派生维护,禁止手工编辑 -->`,`<!-- 真源为 .lzi/docs/sources 下文档条目与 lzi-docs.config.ts 的 plugins.compile 配置,变更后重新执行 compile-derive 重建 -->`],h=(e,t,n)=>`<!-- compile:source ${e}/${t}/${n} -->`,g=e=>e.replaceAll(`|`,`\\|`).replaceAll(`
2
+ `,` `),_=e=>e.length===0?``:[`| 选项 | 说明 |`,`| --- | --- |`,...e.map(e=>`| \`${g(e.flags)}\` | ${g(e.description)} |`)].join(`
3
+ `),v=e=>{if(e.degraded)return[`### ${e.bin}`,``,`该 bin 的帮助输出未呈可解析形态,以下为原文。`,``,"````",e.raw.trimEnd(),"````"].join(`
4
+ `);let t=[`### ${e.bin}`,``];e.description!==``&&t.push(e.description,``),t.push(`用法:\`${g(e.usage.replace(/^Usage:\s*/,``))}\``);let n=_(e.options);n!==``&&t.push(``,`**全局选项**`,``,n);for(let n of e.commands){let r=n.detailDescription===``?n.summary:n.detailDescription;t.push(``,`#### ${e.bin} ${n.name}`,``),r!==``&&t.push(r,``),t.push(`用法:\`${e.bin} ${g(n.invocation)}\``);let i=_(n.options);i!==``&&t.push(``,`**选项**`,``,i)}return t.join(`
5
+ `)},y=e=>[`## 命令参考`,``,"本节由工具面自 `--help` 输出抽取合成,源为 bin 帮助文案。",``,e.map(e=>v(e)).join(`
6
+
7
+ `)].join(`
8
+ `),b=(e,t)=>{let n=[...m,``,`# ${t.title}`,``];for(let r of t.items)n.push(h(e.sourceRootLabel,t.document,r.fileName),r.body,``);return t.toolFace&&e.toolFace.length>0&&n.push(y(e.toolFace),``),`${n.join(`
9
+ `).trimEnd()}\n`},x=e=>e.documents.map(t=>({document:t.document,output:t.output,markdown:b(e,t)})),S=/* @__PURE__ */ new Set([`id`,`type`,`title`,`anchor`,`command`]),C={id:null,type:null,title:null,anchor:null,command:null},w=e=>{let t=e.split(`
10
+ `);if(t[0]?.trim()!==`---`)return{metaLines:[],body:e};let n=-1;for(let e=1;e<t.length;e+=1)if(t[e]?.trim()===`---`){n=e;break}return n===-1?{metaLines:[],body:e}:{metaLines:t.slice(1,n),body:t.slice(n+1).join(`
11
+ `)}},T=(e,t)=>{let n={...C},r=/* @__PURE__ */ new Set;for(let i of e){let e=i.trim();if(e===``)continue;let a=/^([^\s:]+):\s*(.*)$/.exec(e);if(a===null)throw Error(`条目 ${t} 的 frontmatter 存在无法解析的行 '${e}'`);let o=a[1]??``,s=(a[2]??``).trim();if(o===`order`)throw Error(`条目 ${t} 的 frontmatter 键 order 已废弃,定序由文件名数值前缀唯一承载`);if(!S.has(o))throw Error(`条目 ${t} 的 frontmatter 含未知元数据键 '${o}',仅支持 id 与 type 与 title 与 anchor 与 command`);if(r.has(o))throw Error(`条目 ${t} 的 frontmatter 重复声明 ${o}`);if(r.add(o),s===``)throw Error(`条目 ${t} 的元数据键 ${o} 值为空`);(o===`id`||o===`type`||o===`title`||o===`anchor`||o===`command`)&&(n[o]=s)}return n},ee=/^(\d+)-(.+)$/,E=async e=>{try{return await i(e),!0}catch{return!1}},D=e=>{let t=e.endsWith(`.md`)?e.slice(0,-3):e,n=ee.exec(t);if(n===null)return null;let r=n[1]??``;return{order:Number(r),slug:n[2]??``}},O=async e=>{let t=new Bun.Glob(`*.md`),n=[];for await(let r of t.scan({cwd:e,onlyFiles:!0}))n.push(r);return n.sort()},k=(e,t,n)=>`${e}/${t}/${n}`,A=async e=>{let t=[];if(!await E(e.documentDir))return t;let n=await O(e.documentDir),r=/* @__PURE__ */ new Set,i=e.exclude.map(e=>new Bun.Glob(e));for(let a of n){if(i.some(e=>e.match(a)))continue;let n=D(a);if(n===null)throw Error(`条目 ${k(e.sourceRootLabel,e.documentName,a)} 文件名无数值前缀,形态须为 NNN-slug.md,顺序性由前缀承载`);if(r.has(n.order))throw Error(`文档 ${e.documentName} 内条目前缀数值 ${String(n.order)} 重复,文件 ${a} 与先登记者冲突`);r.add(n.order);let o=await Bun.file(s(e.documentDir,a)).text(),{metaLines:c,body:l}=w(o),u=T(c,k(e.sourceRootLabel,e.documentName,a));l.trim()!==``&&t.push({fileName:a,order:n.order,meta:u,body:l.trimEnd()})}return t.sort((e,t)=>e.order-t.order)},j=e=>typeof e==`object`&&!!e,M=async e=>{let t={name:null,description:null,version:null,bins:[]};try{let n=await Bun.file(`${e}/package.json`).json();if(!j(n))return t;typeof n.name==`string`&&(t.name=n.name),typeof n.description==`string`&&(t.description=n.description),typeof n.version==`string`&&(t.version=n.version);let r=n.bin;typeof r==`string`?t.bins=[r]:j(r)&&(t.bins=Object.keys(r))}catch{return t}return t},N=[{name:`options`,pattern:/^Options:\s*$/},{name:`commands`,pattern:/^Commands:\s*$/}],P=/^\s+\S/,F=e=>{let t=e.split(`
12
+ `),n=t.findIndex(e=>e.startsWith(`Usage:`));if(n===-1)return{usage:null,description:``,options:[],commands:[]};let r=/* @__PURE__ */ new Map;for(let e=0;e<t.length;e+=1)for(let n of N)n.pattern.test(t[e]??``)&&(r.has(n.name)||r.set(n.name,e));let i=r.size===0?t.length:Math.min(...r.values()),a=t.slice(n+1,i).filter(e=>e.trim()!==``),o=e=>{let n=r.get(e);if(n===void 0)return[];let i=[];for(let e=n+1;e<t.length;e+=1){let n=t[e]??``;if(P.test(n))i.push(n);else if(n.trim()!==``)break}return i},s=e=>{let t=e.trim().split(/\s{2,}/);return{left:t[0]??``,right:t.slice(1).join(` `)}},c=o(`options`).map(e=>{let{left:t,right:n}=s(e);return{flags:t,description:n}}),l=o(`commands`).map(e=>{let{left:t,right:n}=s(e);return{name:t.split(/\s+/)[0]??``,invocation:t.replace(/\s*\[options\]/g,``).trim(),summary:n}});return{usage:(t[n]??``).trim(),description:a.join(`
13
+ `),options:c,commands:l}},I=async(e,t)=>{try{let n=Bun.spawn({cmd:[...e],cwd:t,stdout:`pipe`,stderr:`pipe`});return{exitCode:await n.exited,stdout:await new Response(n.stdout).text(),stderr:await new Response(n.stderr).text()}}catch(e){return{exitCode:null,stdout:``,stderr:e instanceof Error?e.message:String(e)}}},L=async e=>{let t=await I([...e.invocation,`--help`],e.root),n=t.stdout===``?t.stderr:t.stdout;if(t.exitCode!==0)return{bin:e.bin,degraded:!0,raw:n,reason:`运行退出码 ${String(t.exitCode)}`};let r=F(n);if(r.usage===null)return{bin:e.bin,degraded:!0,raw:n,reason:`帮助输出未含 Usage 行即非 commander 形态`};let i=[];for(let t of r.commands){let n=await I([...e.invocation,t.name,`--help`],e.root),r=n.stdout===``?n.stderr:n.stdout,a=n.exitCode===0?F(r):null;i.push({name:t.name,invocation:t.invocation,summary:t.summary,detailDescription:a?.description??``,options:a?.options??[],detailParsed:a!==null&&a.usage!==null})}return{bin:e.bin,degraded:!1,usage:r.usage,description:r.description,options:r.options,commands:i}},R=async e=>{let t=[];for(let n of e.bins)t.push(await L({bin:n,invocation:e.invocations[n]??[n],root:e.root}));return t},z=e=>{let t=e.split(`/`).at(-1)??e;return t.endsWith(`.md`)?t.slice(0,-3):t},B=async(e,t)=>{let n=await M(e),r=t.toolFace.bins??n.bins,i=r.length>0?await R({bins:r,invocations:t.toolFace.invocations,root:e}):[],a=[];for(let n of t.documents){let r=z(n.output),i=await A({documentDir:s(e,t.sourceRoot,r),documentName:r,sourceRootLabel:t.sourceRoot,exclude:n.exclude});a.push({document:r,output:n.output,title:n.title??r,toolFace:n.toolFace,items:i})}return{root:e,sourceRootLabel:t.sourceRoot,manifest:n,toolFace:i,documents:a}},V=e=>{let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return[RegExp(`name\\s*[:=]\\s*(['"\`])${t}\\1`),RegExp(`\\.command\\(\\s*(['"\`])${t}\\1`)]},H=async e=>{let t=[];for(let n of e.documents)for(let r of n.items){let i=`${e.sourceRootLabel}/${n.document}/${r.fileName}`;if(r.meta.anchor===null&&r.meta.command===null)continue;if(r.meta.anchor===null){t.push({kind:`anchor-missing`,message:`条目 ${i} 声明了 command 而无 anchor,命令锚定需要 anchor 指向命令定义所在源码文件`});continue}let a=s(e.root,r.meta.anchor);if(!await Bun.file(a).exists()){t.push({kind:`anchor-missing`,message:`条目 ${i} 的 anchor 指向的文件不存在:${r.meta.anchor}`});continue}if(r.meta.command===null)continue;let o=await Bun.file(a).text();V(r.meta.command).some(e=>e.test(o))||t.push({kind:`command-not-found`,message:`条目 ${i} 的 command '${r.meta.command}' 在锚点源码 ${r.meta.anchor} 中未命中命令定义,命令可能已改名`})}return t},U=async(e,t)=>{let n=await B(e,t),r=x(n),i=/* @__PURE__ */ new Map;for(let t of r){let n=s(e,t.output);if(!await Bun.file(n).exists()){i.set(t.document,{status:`missing`,expected:t.markdown});continue}let r=await Bun.file(n).text();i.set(t.document,{status:r===t.markdown?`fresh`:`stale`,expected:t.markdown})}return i},W=async(e,t)=>{let n=await B(e,t),r=[...await H(n)];for(let t of x(n)){let n=s(e,t.output);await Bun.file(n).exists()?await Bun.file(n).text()!==t.markdown&&r.push({kind:`snapshot-stale`,message:`文档 ${t.document} 的产物过期:${t.output} 与真源合成结果不一致,执行 compile-derive 重建`}):r.push({kind:`snapshot-missing`,message:`文档 ${t.document} 的产物缺失:${t.output},执行 compile-derive 生成`})}return r},G=async(e,t)=>{let n=await U(e.root,t),r=0,i=0;for(let t of e.documents)for(let e of t.items)r+=1,(e.meta.id!==null||e.meta.type!==null||e.meta.title!==null||e.meta.anchor!==null||e.meta.command!==null)&&(i+=1);return{repo:e.manifest.name??``,itemCount:r,metadataCoverage:r===0?1:i/r,documents:e.documents.map(e=>({document:e.document,output:e.output,itemCount:e.items.length,freshness:n.get(e.document)?.status??`missing`})),toolFace:e.toolFace.map(e=>({bin:e.bin,degraded:e.degraded,commandCount:e.degraded?0:e.commands.length}))}},K=e=>e===`fresh`?`新鲜`:e===`stale`?`过期`:`缺失`,q=e=>{let t=e.documents.map(e=>`| ${e.document} | \`${e.output}\` | ${e.itemCount} | ${K(e.freshness)} |`),n=e.toolFace.map(e=>`| \`${e.bin}\` | ${e.degraded?`是`:`否`} | ${e.commandCount} |`);return[`# compile inventory:${e.repo===``?`未识别包名`:e.repo}`,``,`- 条目总数:${e.itemCount}`,`- 元数据覆盖率:${(e.metadataCoverage*100).toFixed(0)}%`,``,`## 文档`,``,`| 文档 | 产物 | 条目数 | 新鲜度 |`,`| --- | --- | --- | --- |`,...t.length>0?t:[`| 无 | 无 | 0 | 缺失 |`],``,`## 工具面`,``,`| bin | 降级 | 命令数 |`,`| --- | --- | --- |`,...n.length>0?n:[`| 无 | 是 | 0 |`],``].join(`
14
+ `)},J=e=>e.documents.length===0,Y=async(e,t)=>{let n=await B(e,t);return{manifest:n.manifest,toolFace:n.toolFace,documents:n.documents.map(e=>({document:e.document,output:e.output,items:e.items.map(e=>({file:e.fileName,order:e.order,metadata:[[`id`,e.meta.id],[`type`,e.meta.type],[`title`,e.meta.title],[`anchor`,e.meta.anchor],[`command`,e.meta.command]].filter(([,e])=>e!==null).map(([e])=>e)}))}))}},X=async(e,t,n)=>{if(J(t))throw Error(`plugins.compile.options 未配置任何文档,请在 lzi-docs.config.ts 的 documents 节配置后重试`);let i=await B(e,t),c=x(i);for(let t of c)if(!n){let n=s(e,t.output);await r(o(n),{recursive:!0}),await a(n,t.markdown,`utf-8`)}return c.map(e=>({document:e.document,output:e.output,itemCount:i.documents.find(t=>t.document===e.document)?.items.length??0}))},Z=async(e,t)=>{if(J(t))throw Error(`plugins.compile.options 未配置任何文档,请在 lzi-docs.config.ts 的 documents 节配置后重试`);return{findings:await W(e,t),documentCount:t.documents.length}},Q=async(e,t)=>{let n=await B(e,t),r=await G(n,t);return{markdown:q(r),data:r}},$=n.object({}),te=n.object({dryRun:n.boolean().default(!1)}),ne=n.object({}),re=n.object({json:n.boolean().default(!1)}),ie=e({name:`compile`,version:`0.0.1`,configSchema:d,commands:[e=>ae(e),e=>oe(e),e=>se(e),e=>ce(e)]});function ae(e){return t({name:`compile-scan`,description:`扫描真源面出清单:文档条目、manifest 面与工具面帮助抽取`,schema:$,action:async()=>{try{let t=await Y(process.cwd(),p(e.pluginConfig));process.stdout.write(`${JSON.stringify(t,null,2)}\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}function oe(e){return t({name:`compile-derive`,description:`建模并派生文档产物写盘,同输入逐字节相同输出`,options:[{flags:`--dry-run`,description:`只打印合成结果不写盘`}],schema:te,action:async t=>{try{let n=await X(process.cwd(),p(e.pluginConfig),t.dryRun);for(let e of n)process.stdout.write(`${t.dryRun?`预览`:`已写入`} ${e.output},文档 ${e.document},条目 ${e.itemCount}\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}function se(e){return t({name:`compile-check`,description:`只读校验:快照新鲜度逐字节比对与锚点命令定义命中`,schema:ne,action:async()=>{try{let t=await Z(process.cwd(),p(e.pluginConfig));for(let e of t.findings)process.stderr.write(`[compile:${e.kind}] ${e.message}\n`);t.findings.length>0&&process.exit(1),process.stdout.write(`校验通过,文档 ${t.documentCount} 份全绿\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}function ce(e){return t({name:`compile-report`,description:`单仓 inventory 上报:文档计数、条目数、元数据覆盖率与产物新鲜度`,options:[{flags:`--json`,description:`以 JSON 形态输出`}],schema:re,action:async t=>{try{let n=await Q(process.cwd(),p(e.pluginConfig));process.stdout.write(`${t.json?JSON.stringify(n.data,null,2):n.markdown}\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}export{d as CompilePluginConfigSchema,B as buildSourceView,ie as compilePlugin,x as composeAllSurfaces,b as composeSurface,R as extractToolFace,F as parseCommanderHelp,p as parseCompilePluginConfig,A as resolveDocumentItems,W as runCompileCheck,X as runCompileDerive,Y as runCompileScan};
@@ -0,0 +1 @@
1
+ import"node:module";export{};
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-docs/compile-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.15",
4
+ "description": "Longzai Intelligence Docs compile 插件",
5
+ "keywords": [
6
+ "compile",
7
+ "docs",
8
+ "longzai-intelligence",
9
+ "plugin"
10
+ ],
11
+ "license": "UNLICENSED",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ }
23
+ },
24
+ "scripts": {
25
+ "build": "lzi-builder",
26
+ "build:prod": "NODE_ENV=production bun run build",
27
+ "prepublishOnly": "bun run build:prod",
28
+ "typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
29
+ "typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
30
+ "typecheck:node": "lzi-tsgo typecheck tsconfig/node.json",
31
+ "typecheck:test": "lzi-tsgo typecheck tsconfig/test.json",
32
+ "lint": "oxlint && oxfmt --check",
33
+ "lint:fix": "oxlint --fix && oxfmt",
34
+ "test": "bun test",
35
+ "test:watch": "bun test --watch",
36
+ "test:coverage": "bun test --coverage",
37
+ "clean": "lzi-dev-cli clean"
38
+ },
39
+ "dependencies": {
40
+ "@longzai-intelligence-cli/core": "^0.0.11",
41
+ "@longzai-intelligence-docs/core": "0.0.15",
42
+ "zod": "^4.4.3"
43
+ },
44
+ "devDependencies": {
45
+ "@types/bun": "^1.3.14"
46
+ },
47
+ "packageManager": "bun@1.4.0"
48
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.