@longzai-intelligence-docs/compile-plugin 0.0.14 → 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.
package/dist/index.d.ts CHANGED
@@ -18,8 +18,8 @@ declare const compilePlugin: import("@longzai-intelligence-docs/core").DocsPlugi
18
18
  * compile 插件配置 schema 即整体缺省
19
19
  */
20
20
  declare const CompilePluginConfigSchema: z.ZodDefault<z.ZodObject<{
21
- itemsRoot: z.ZodDefault<z.ZodString>;
22
- surfaces: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
21
+ sourceRoot: z.ZodDefault<z.ZodString>;
22
+ documents: z.ZodDefault<z.ZodArray<z.ZodObject<{
23
23
  output: z.ZodString;
24
24
  title: z.ZodOptional<z.ZodString>;
25
25
  toolFace: z.ZodDefault<z.ZodBoolean>;
@@ -84,7 +84,7 @@ declare const runCompileCheck: (root: string, config: CompilePluginConfig) => Pr
84
84
  /**
85
85
  * 条目元信息解析后形态
86
86
  *
87
- * 六键均未声明时对应字段为 `null`,零元数据条目完全正常
87
+ * 五键均未声明时对应字段为 `null`,零元数据条目完全正常
88
88
  */
89
89
  type ItemMeta = {
90
90
  /**
@@ -99,10 +99,6 @@ type ItemMeta = {
99
99
  * 标题提示串,未声明时为 `null`
100
100
  */
101
101
  title: string | null;
102
- /**
103
- * 定序键,须为有限数值,未声明时为 `null`
104
- */
105
- order: number | null;
106
102
  /**
107
103
  * 锚定的源码文件路径即仓库根相对路径,未声明时为 `null`
108
104
  */
@@ -115,30 +111,59 @@ type ItemMeta = {
115
111
  //#endregion
116
112
  //#region src/scan/items-scan.core.d.ts
117
113
  /**
118
- * 面内产出条目
114
+ * 文档内产出条目
119
115
  */
120
- type SurfaceItem = {
116
+ type SourceItem = {
121
117
  /**
122
- * 条目 base 相对路径,不含扩展名
118
+ * 条目文件名即文档目录内相对路径
123
119
  */
124
- baseRelPath: string;
120
+ fileName: string;
125
121
  /**
126
- * 该面选用的源文件相对路径即 items 根内相对路径,含扩展名
122
+ * 定序键即文件名数值前缀
127
123
  */
128
- chosenRelPath: string;
124
+ order: number;
129
125
  /**
130
- * 选用文件解析出的元信息
126
+ * 条目文件解析出的元信息
131
127
  */
132
128
  meta: ItemMeta;
133
129
  /**
134
130
  * 正文,已去尾空白
135
131
  */
136
132
  body: string;
133
+ };
134
+ /**
135
+ * 文档条目解析入参
136
+ */
137
+ type ResolveDocumentItemsInput = {
138
+ /**
139
+ * 文档目录绝对路径
140
+ */
141
+ documentDir: string;
137
142
  /**
138
- * 定序键,未声明为 `null`
143
+ * 文档目录名
139
144
  */
140
- order: number | null;
145
+ documentName: string;
146
+ /**
147
+ * 真源根的仓库根相对显示路径
148
+ */
149
+ sourceRootLabel: string;
150
+ /**
151
+ * 该文档的排除 glob 集合
152
+ */
153
+ exclude: readonly string[];
141
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[]>;
142
167
  //#endregion
143
168
  //#region src/scan/manifest-scan.utils.d.ts
144
169
  /**
@@ -349,19 +374,19 @@ declare const extractToolFace: (input: ExtractToolFaceInput) => Promise<readonly
349
374
  //#endregion
350
375
  //#region src/model/compile-model.core.d.ts
351
376
  /**
352
- * 单个公开面的视图
377
+ * 单个文档的视图
353
378
  */
354
- type SurfaceView = {
379
+ type DocumentView = {
355
380
  /**
356
- * 公开面名
381
+ * 文档名即真源目录名,产物 basename 去 `.md`
357
382
  */
358
- surface: string;
383
+ document: string;
359
384
  /**
360
385
  * 产物路径即仓库根相对路径
361
386
  */
362
387
  output: string;
363
388
  /**
364
- * 面标题,缺省为面名
389
+ * 文档标题,缺省为文档名
365
390
  */
366
391
  title: string;
367
392
  /**
@@ -369,9 +394,9 @@ type SurfaceView = {
369
394
  */
370
395
  toolFace: boolean;
371
396
  /**
372
- * 该面产出条目即已定序已过滤
397
+ * 该文档产出条目即已定序已过滤
373
398
  */
374
- items: readonly SurfaceItem[];
399
+ items: readonly SourceItem[];
375
400
  };
376
401
  /**
377
402
  * 真源视图
@@ -382,9 +407,9 @@ type CompileSourceView = {
382
407
  */
383
408
  root: string;
384
409
  /**
385
- * items 根的仓库根相对路径
410
+ * 真源根的仓库根相对路径
386
411
  */
387
- itemsRootLabel: string;
412
+ sourceRootLabel: string;
388
413
  /**
389
414
  * manifest 面
390
415
  */
@@ -394,9 +419,9 @@ type CompileSourceView = {
394
419
  */
395
420
  toolFace: readonly ToolFace[];
396
421
  /**
397
- * 各公开面视图
422
+ * 各文档视图
398
423
  */
399
- surfaces: readonly SurfaceView[];
424
+ documents: readonly DocumentView[];
400
425
  };
401
426
  /**
402
427
  * 构建真源视图
@@ -404,27 +429,27 @@ type CompileSourceView = {
404
429
  * @param root - 仓库根目录绝对路径
405
430
  * @param config - 解析后的插件配置
406
431
  * @returns 真源视图
407
- * @throws {@link Error} 任一面条目元信息非法时,报错携带条目定位
432
+ * @throws {@link Error} 任一文档条目文件名或元信息非法时,报错携带条目定位
408
433
  */
409
434
  declare const buildSourceView: (root: string, config: CompilePluginConfig) => Promise<CompileSourceView>;
410
435
  //#endregion
411
436
  //#region src/compose/surface-composer.core.d.ts
412
437
  /**
413
- * 合成单个公开面产物
438
+ * 合成单个文档产物
414
439
  *
415
440
  * @param view - 真源视图
416
- * @param surfaceView - 该面视图
441
+ * @param documentView - 该文档视图
417
442
  * @returns 产物 markdown 全文,恒以单个换行收尾
418
443
  */
419
- declare const composeSurface: (view: CompileSourceView, surfaceView: SurfaceView) => string;
444
+ declare const composeSurface: (view: CompileSourceView, documentView: DocumentView) => string;
420
445
  /**
421
- * 合成全部公开面产物
446
+ * 合成全部文档产物
422
447
  *
423
448
  * @param view - 真源视图
424
- * @returns 面名到产物路径与产物全文的集合
449
+ * @returns 文档名到产物路径与产物全文的集合
425
450
  */
426
451
  declare const composeAllSurfaces: (view: CompileSourceView) => readonly {
427
- surface: string;
452
+ document: string;
428
453
  output: string;
429
454
  markdown: string;
430
455
  }[];
@@ -448,15 +473,14 @@ type CompileScanResult = {
448
473
  */
449
474
  toolFace: readonly unknown[];
450
475
  /**
451
- * 各面条目清单
476
+ * 各文档条目清单
452
477
  */
453
- surfaces: readonly {
454
- surface: string;
478
+ documents: readonly {
479
+ document: string;
455
480
  output: string;
456
481
  items: readonly {
457
- base: string;
458
- source: string;
459
- order: number | null;
482
+ file: string;
483
+ order: number;
460
484
  metadata: readonly string[];
461
485
  }[];
462
486
  }[];
@@ -467,7 +491,7 @@ type CompileScanResult = {
467
491
  * @param root - 仓库根目录绝对路径
468
492
  * @param config - 解析后的插件配置
469
493
  * @returns scan 结果
470
- * @throws {@link Error} 条目元信息非法时
494
+ * @throws {@link Error} 条目文件名或元信息非法时
471
495
  */
472
496
  declare const runCompileScan: (root: string, config: CompilePluginConfig) => Promise<CompileScanResult>;
473
497
  /**
@@ -476,13 +500,13 @@ declare const runCompileScan: (root: string, config: CompilePluginConfig) => Pro
476
500
  * @param root - 仓库根目录绝对路径
477
501
  * @param config - 解析后的插件配置
478
502
  * @param dryRun - 只打印不写盘
479
- * @returns 写盘清单即面名与产物路径与条目数
480
- * @throws {@link Error} 未配置公开面或条目元信息非法时
503
+ * @returns 写盘清单即文档名与产物路径与条目数
504
+ * @throws {@link Error} 未配置文档或条目文件名或元信息非法时
481
505
  */
482
506
  declare const runCompileDerive: (root: string, config: CompilePluginConfig, dryRun: boolean) => Promise<readonly {
483
- surface: string;
507
+ document: string;
484
508
  output: string;
485
509
  itemCount: number;
486
510
  }[]>;
487
511
  //#endregion
488
- export { type CompilePluginConfig, type CompilePluginConfigInput, CompilePluginConfigSchema, type CompileSourceView, type SurfaceView, type ToolFace, buildSourceView, compilePlugin, composeAllSurfaces, composeSurface, extractToolFace, parseCommanderHelp, parseCompilePluginConfig, runCompileCheck, runCompileDerive, runCompileScan };
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 CHANGED
@@ -1,14 +1,14 @@
1
- import"./rolldown-runtime-BqT_7tdF.js";import{IDENTIFIER_KEBAB_PATTERN as e,definePlugin as t,pluginCommand as n}from"@longzai-intelligence-docs/core";import*as r from"zod";import{mkdir as i,stat as a,writeFile as o}from"node:fs/promises";import{dirname as s,join as c}from"node:path";const l=`docs/items`,u=r.object({output:r.string().min(1).refine(e=>!e.startsWith(`/`),`产物路径须为仓库根相对路径`),title:r.string().min(1).optional(),toolFace:r.boolean().default(!1),exclude:r.array(r.string().min(1)).default([])}),d=r.object({bins:r.array(r.string().min(1)).optional(),invocations:r.record(r.string().min(1),r.array(r.string().min(1))).default({})}),f=r.object({itemsRoot:r.string().min(1).default(l),surfaces:r.record(r.string().regex(e),u).default({}),toolFace:d.default({invocations:{}})}).default({itemsRoot:l,surfaces:{},toolFace:{invocations:{}}}),p=e=>{let t=f.safeParse(e);if(!t.success)throw Error(`compile 插件配置非法:${t.error.issues.map(e=>`${e.path.join(`.`)||`根节`}——${e.message}`).join(`,`)}`);return t.data},ee=[`<!-- 本文件由 lzi-docs compile 派生维护,禁止手工编辑 -->`,`<!-- 真源为 items 条目与 lzi-docs.config.ts 的 plugins.compile 配置,变更后重新执行 compile-derive 重建 -->`],m=(e,t)=>`<!-- compile:source ${e}/${t} -->`,h=e=>e.replaceAll(`|`,`\\|`).replaceAll(`
2
- `,` `),g=e=>e.length===0?``:[`| 选项 | 说明 |`,`| --- | --- |`,...e.map(e=>`| \`${h(e.flags)}\` | ${h(e.description)} |`)].join(`
3
- `),_=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(`用法:\`${h(e.usage.replace(/^Usage:\s*/,``))}\``);let n=g(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} ${h(n.invocation)}\``);let i=g(n.options);i!==``&&t.push(``,`**选项**`,``,i)}return t.join(`
5
- `)},v=e=>[`## 命令参考`,``,"本节由工具面自 `--help` 输出抽取合成,源为 bin 帮助文案。",``,e.map(e=>_(e)).join(`
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
6
 
7
7
  `)].join(`
8
- `),y=(e,t)=>{let n=[...ee,``,`# ${t.title}`,``];for(let r of t.items)n.push(m(e.itemsRootLabel,r.chosenRelPath),r.body,``);return t.toolFace&&e.toolFace.length>0&&n.push(v(e.toolFace),``),`${n.join(`
9
- `).trimEnd()}\n`},b=e=>e.surfaces.map(t=>({surface:t.surface,output:t.output,markdown:y(e,t)})),x=/* @__PURE__ */ new Set([`id`,`type`,`title`,`order`,`anchor`,`command`]),S={id:null,type:null,title:null,order:null,anchor:null,command:null},C=e=>{let t=e.split(`
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
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
- `)}},w=(e,t)=>{let n={...S},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(!x.has(o))throw Error(`条目 ${t} 的 frontmatter 含未知元数据键 '${o}',仅支持 id 与 type 与 title 与 order 与 anchor 与 command`);if(r.has(o))throw Error(`条目 ${t} 的 frontmatter 重复声明 ${o}`);if(r.add(o),s===``)throw Error(`条目 ${t} 的元数据键 ${o} 值为空`);if(o===`order`){let e=Number(s);if(!Number.isFinite(e))throw Error(`条目 ${t} 的 order 值 '${s}' 不是有限数值`);n.order=e}else(o===`id`||o===`type`||o===`title`||o===`anchor`||o===`command`)&&(n[o]=s)}return n},T=async e=>{try{return await a(e),!0}catch{return!1}},E=async e=>Bun.file(e).text(),D=(e,t)=>{let n=e.endsWith(`.md`)?e.slice(0,-3):e,r=n.lastIndexOf(`.`);if(r===-1)return{baseRelPath:n,surface:null};let i=n.slice(r+1);return t.includes(i)?{baseRelPath:n.slice(0,r),surface:i}:{baseRelPath:n,surface:null}},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=async(e,t)=>{let n=/* @__PURE__ */ new Map;if(!await T(e))return n;let r=await O(e);for(let e of r){let{baseRelPath:r,surface:i}=D(e,t);n.has(r)||n.set(r,{common:null,variants:/* @__PURE__ */ new Map});let a=n.get(r);a!==void 0&&(i===null?a.common===null&&(a.common=e):a.variants.has(i)||a.variants.set(i,e))}return n},A=(e,t)=>{let n=e.order??1/0,r=t.order??1/0;return n===r?e.baseRelPath===t.baseRelPath?0:e.baseRelPath<t.baseRelPath?-1:1:n-r},j=(e,t)=>`${e}/${t}`,M=async e=>{let t=await k(e.itemsRoot,e.surfaceNames),n=[],r=e.exclude.map(e=>new Bun.Glob(e));for(let[i,a]of t){if(r.some(e=>e.match(i)))continue;let t=a.variants.get(e.surface)??null,o=t??a.common;if(o===null)continue;let s=await E(c(e.itemsRoot,o)),{metaLines:l,body:u}=C(s),d=w(l,j(e.itemsRootLabel,o)),f=d.order;if(t!==null&&a.common!==null){if(d.order!==null)throw Error(`条目 ${j(e.itemsRootLabel,i+`.md`)} 的变体 ${t} 不得声明 order,条目定位由共同版唯一决定`);let n=await E(c(e.itemsRoot,a.common)),{metaLines:r}=C(n);f=w(r,j(e.itemsRootLabel,a.common)).order}u.trim()!==``&&n.push({baseRelPath:i,chosenRelPath:o,meta:d,body:u.trimEnd(),order:f})}return n.sort(A)},N=e=>typeof e==`object`&&!!e,P=async e=>{let t={name:null,description:null,version:null,bins:[]};try{let n=await Bun.file(`${e}/package.json`).json();if(!N(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]:N(r)&&(t.bins=Object.keys(r))}catch{return t}return t},F=[{name:`options`,pattern:/^Options:\s*$/},{name:`commands`,pattern:/^Commands:\s*$/}],I=/^\s+\S/,L=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 F)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(I.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}},R=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)}}},z=async e=>{let t=await R([...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=L(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 R([...e.invocation,t.name,`--help`],e.root),r=n.stdout===``?n.stderr:n.stdout,a=n.exitCode===0?L(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}},B=async e=>{let t=[];for(let n of e.bins)t.push(await z({bin:n,invocation:e.invocations[n]??[n],root:e.root}));return t},V=async(e,t)=>{let n=await P(e),r=Object.keys(t.surfaces),i=t.toolFace.bins??n.bins,a=i.length>0?await B({bins:i,invocations:t.toolFace.invocations,root:e}):[],o=[];for(let[n,i]of Object.entries(t.surfaces)){let a=await M({itemsRoot:c(e,t.itemsRoot),itemsRootLabel:t.itemsRoot,surface:n,surfaceNames:r,exclude:i.exclude});o.push({surface:n,output:i.output,title:i.title??n,toolFace:i.toolFace,items:a})}return{root:e,itemsRootLabel:t.itemsRoot,manifest:n,toolFace:a,surfaces:o}},H=e=>{let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return[RegExp(`name\\s*[:=]\\s*(['"\`])${t}\\1`),RegExp(`\\.command\\(\\s*(['"\`])${t}\\1`)]},U=async e=>{let t=[];for(let n of e.surfaces)for(let r of n.items){let n=`${e.itemsRootLabel}/${r.chosenRelPath}`;if(r.meta.anchor===null&&r.meta.command===null)continue;if(r.meta.anchor===null){t.push({kind:`anchor-missing`,message:`条目 ${n} 声明了 command 而无 anchor,命令锚定需要 anchor 指向命令定义所在源码文件`});continue}let i=c(e.root,r.meta.anchor);if(!await Bun.file(i).exists()){t.push({kind:`anchor-missing`,message:`条目 ${n} 的 anchor 指向的文件不存在:${r.meta.anchor}`});continue}if(r.meta.command===null)continue;let a=await Bun.file(i).text();H(r.meta.command).some(e=>e.test(a))||t.push({kind:`command-not-found`,message:`条目 ${n} 的 command '${r.meta.command}' 在锚点源码 ${r.meta.anchor} 中未命中命令定义,命令可能已改名`})}return t},W=async(e,t)=>{let n=await V(e,t),r=b(n),i=/* @__PURE__ */ new Map;for(let t of r){let n=c(e,t.output);if(!await Bun.file(n).exists()){i.set(t.surface,{status:`missing`,expected:t.markdown});continue}let r=await Bun.file(n).text();i.set(t.surface,{status:r===t.markdown?`fresh`:`stale`,expected:t.markdown})}return i},G=async(e,t)=>{let n=await V(e,t),r=[...await U(n)];for(let t of b(n)){let n=c(e,t.output);await Bun.file(n).exists()?await Bun.file(n).text()!==t.markdown&&r.push({kind:`snapshot-stale`,message:`公开面 ${t.surface} 的产物过期:${t.output} 与真源合成结果不一致,执行 compile-derive 重建`}):r.push({kind:`snapshot-missing`,message:`公开面 ${t.surface} 的产物缺失:${t.output},执行 compile-derive 生成`})}return r},K=async(e,t)=>{let n=await W(e.root,t),r=/* @__PURE__ */ new Set,i=0,a=0;for(let t of e.surfaces)for(let e of t.items)r.add(e.baseRelPath),i+=1,(e.meta.id!==null||e.meta.type!==null||e.meta.title!==null||e.meta.order!==null||e.meta.anchor!==null||e.meta.command!==null)&&(a+=1);return{repo:e.manifest.name??``,itemBases:r.size,metadataCoverage:i===0?1:a/i,surfaces:e.surfaces.map(e=>({surface:e.surface,output:e.output,itemCount:e.items.length,freshness:n.get(e.surface)?.status??`missing`})),toolFace:e.toolFace.map(e=>({bin:e.bin,degraded:e.degraded,commandCount:e.degraded?0:e.commands.length}))}},q=e=>e===`fresh`?`新鲜`:e===`stale`?`过期`:`缺失`,J=e=>{let t=e.surfaces.map(e=>`| ${e.surface} | \`${e.output}\` | ${e.itemCount} | ${q(e.freshness)} |`),n=e.toolFace.map(e=>`| \`${e.bin}\` | ${e.degraded?`是`:`否`} | ${e.commandCount} |`);return[`# compile inventory:${e.repo===``?`未识别包名`:e.repo}`,``,`- 条目 base 总数:${e.itemBases}`,`- 元数据覆盖率:${(e.metadataCoverage*100).toFixed(0)}%`,``,`## 公开面`,``,`| 面 | 产物 | 条目数 | 新鲜度 |`,`| --- | --- | --- | --- |`,...t.length>0?t:[`| 无 | 无 | 0 | 缺失 |`],``,`## 工具面`,``,`| bin | 降级 | 命令数 |`,`| --- | --- | --- |`,...n.length>0?n:[`| 无 | 是 | 0 |`],``].join(`
14
- `)},Y=e=>Object.keys(e.surfaces).length===0,X=async(e,t)=>{let n=await V(e,t);return{manifest:n.manifest,toolFace:n.toolFace,surfaces:n.surfaces.map(e=>({surface:e.surface,output:e.output,items:e.items.map(e=>({base:e.baseRelPath,source:`${n.itemsRootLabel}/${e.chosenRelPath}`,order:e.order,metadata:[[`id`,e.meta.id],[`type`,e.meta.type],[`title`,e.meta.title],[`order`,e.meta.order],[`anchor`,e.meta.anchor],[`command`,e.meta.command]].filter(([,e])=>e!==null).map(([e])=>e)}))}))}},Z=async(e,t,n)=>{if(Y(t))throw Error(`plugins.compile.options 未配置任何公开面,请在 lzi-docs.config.ts 的 surfaces 节配置后重试`);let r=await V(e,t),a=b(r);for(let t of a)if(!n){let n=c(e,t.output);await i(s(n),{recursive:!0}),await o(n,t.markdown,`utf-8`)}return a.map(e=>({surface:e.surface,output:e.output,itemCount:r.surfaces.find(t=>t.surface===e.surface)?.items.length??0}))},Q=async(e,t)=>{if(Y(t))throw Error(`plugins.compile.options 未配置任何公开面,请在 lzi-docs.config.ts 的 surfaces 节配置后重试`);return{findings:await G(e,t),surfaceCount:Object.keys(t.surfaces).length}},te=async(e,t)=>{let n=await V(e,t),r=await K(n,t);return{markdown:J(r),data:r}},$=r.object({}),ne=r.object({dryRun:r.boolean().default(!1)}),re=r.object({}),ie=r.object({json:r.boolean().default(!1)}),ae=t({name:`compile`,version:`0.0.1`,configSchema:f,commands:[e=>oe(e),e=>se(e),e=>ce(e),e=>le(e)]});function oe(e){return n({name:`compile-scan`,description:`扫描真源面出清单:items 条目、manifest 面与工具面帮助抽取`,schema:$,action:async()=>{try{let t=await X(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 se(e){return n({name:`compile-derive`,description:`建模并派生公开面产物写盘,同输入逐字节相同输出`,options:[{flags:`--dry-run`,description:`只打印合成结果不写盘`}],schema:ne,action:async t=>{try{let n=await Z(process.cwd(),p(e.pluginConfig),t.dryRun);for(let e of n)process.stdout.write(`${t.dryRun?`预览`:`已写入`} ${e.output},公开面 ${e.surface},条目 ${e.itemCount}\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}function ce(e){return n({name:`compile-check`,description:`只读校验:快照新鲜度逐字节比对与锚点命令定义命中`,schema:re,action:async()=>{try{let t=await Q(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.surfaceCount} 面全绿\n`)}catch(e){process.stderr.write(`错误: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}})}function le(e){return n({name:`compile-report`,description:`单仓 inventory 上报:面计数、条目数、元数据覆盖率与产物新鲜度`,options:[{flags:`--json`,description:`以 JSON 形态输出`}],schema:ie,action:async t=>{try{let n=await te(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{f as CompilePluginConfigSchema,V as buildSourceView,ae as compilePlugin,b as composeAllSurfaces,y as composeSurface,B as extractToolFace,L as parseCommanderHelp,p as parseCompilePluginConfig,G as runCompileCheck,Z as runCompileDerive,X as runCompileScan};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-docs/compile-plugin",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "Longzai Intelligence Docs compile 插件",
5
5
  "keywords": [
6
6
  "compile",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@longzai-intelligence-cli/core": "^0.0.11",
41
- "@longzai-intelligence-docs/core": "0.0.14",
41
+ "@longzai-intelligence-docs/core": "0.0.15",
42
42
  "zod": "^4.4.3"
43
43
  },
44
44
  "devDependencies": {