前言
本书定义书稿入口回源逻辑文档。该对象从一个 AsciiDoc book entry 出发,解析受支持的 full-file include directive,构造一份带 line origin 的 in-memory logical document,并通过 asciidoc-abundant-tree 解析器产出可回源的 AbundantDocument。
书稿在逻辑上是一份 AsciiDoc 文档。物理文件系统中,书稿可以由 entry file、frontmatter、chapters、backmatter 和 shared attributes 组成。回源逻辑文档保留这两个事实:解析器看到一份 logical document;每个可回源事实仍指向创建它的原始 source file。
本书定义输入构造对象,不定义下游图、索引或报告的输出形状。下游消费者读取 AbundantDocument 中的 source coordinate,不需要知道 include resolution、line origin 或 logical document 的内部结构。
本书使用中文叙事。book-entry mode、entry file、source set、logical document、line origin、source coordinate、documentRoot、include directive、AbundantDocument 等术语在正文中保留英文形式或中英并列形式,以保持与源码、测试和解析器规约之间的术语连续性。
第一部:对象边界
这一部定义书稿入口回源逻辑文档的对象、消费者、entry file 和 source set。
1. 对象边界
书稿入口回源逻辑文档是一个输入构造对象。它把物理多文件书稿构造成一份带 line origin 的 logical document。
1.1. 本对象
本对象接受一个 book entry file,读取受支持 include directive 指向的 source files,生成 logical text、line origin、source file table 和 diagnostics。
Logical text 交给 asciidoc-abundant-tree 解析器。解析器产出 AbundantDocument。该 AbundantDocument 中可回源事实的 source coordinate 来自 line origin。
Logical document 是一份解析表面,不是多个 parser input。物理 source files 只通过 line origin 进入回源坐标。
1.2. 不属于本对象
本对象不定义下游 graph、resource、relation predicate、复杂属性解释或 query contract。
本对象不替代 asciidoc-abundant-tree parser。它构造 parser input,并要求 parser 支持 origin-aware source recovery。
本对象不生成 HTML、PDF、site navigation 或 build/adoc 发布产物。
本对象不实现完整 AsciiDoc reducer。它只定义本书支持的 full-file include resolution 与 line origin 规则。
1.3. 核心断言
书稿入口回源逻辑文档的核心断言是:logical document 是解析表面,line origin 是回源表面。二者必须同时成立。
只有 logical document 而没有 line origin,会退化为不可回源的聚合文本。只有 source files 而没有 logical document,解析器无法恢复整本书的结构关系。
2. 消费者动作
书稿入口回源逻辑文档的公共表面由消费者动作授权。
2.1. Parser
Parser 消费 logical text。该动作要求 logical text 是一份可被 Asciidoctor 解析的 AsciiDoc document。
Parser 还消费 line origin。该动作要求 parser 在恢复 source layer 时能把 logical line 映射回 origin source file 和 source line。
2.2. 文档作者与修改执行者
文档作者和修改执行者消费 origin source coordinate。该动作要求查询结果、诊断结果或下游投影结果能回到原始章节文件。
回源结果不得指向 build/adoc 聚合产物,除非调用者显式把该聚合产物作为普通 source file 输入。
2.3. 下游投影器
下游投影器消费 AbundantDocument。它读取 section、xref occurrence、target、block 和 source coordinate。
下游投影器不消费 include directive、line origin table 或 source file table。它不需要知道 logical document 如何构造。
RDF12 投影器还承担 source file reconstruction surface。该表面输出 source set 中每个 source file 的 relativePath 和完整原始文本。该表面服务文件级重建,不替代 section、xref occurrence、target 和 block 的 origin source coordinate。
RDF12 source file reconstruction surface 不输出 logical text 作为 source file。Logical text 是 parser surface;逐文件重建对象是 source set 中的原始 source files。
2.4. 测试者与审核者
测试者和审核者消费 diagnostics、logical line count、line origin map 和 origin-aware AbundantDocument。
测试必须证明 logical document 能恢复书稿结构,并且每个可回源事实指向原始 source file。
3. Entry 与 Source Set
Entry file 是 book-entry mode 的显式输入。Source set 是 entry file 通过受支持 include directive 到达的 source files 集合。
3.1. Entry File
Entry file 由调用者显式传入。
parseAbundantTree({
sourcePath: "docs/bookshelf/books/06-rdf12-line-projection/book.adoc",
mode: "book-entry",
documentRoot: "docs/bookshelf",
});
实现不得仅凭文件名、:doctype: book 或 include directive 自动切换输入模式。调用者必须显式设置 book-entry public API mode 或 CLI mode。
:doctype: book 可以作为一致性检查事实。它不是模式切换开关。
3.2. documentRoot
documentRoot 是 relativePath 基准和路径边界。
所有 source file relativePath 均相对于 documentRoot 计算。Include target 解析出的绝对路径不得越出允许边界。
Library API 中,book-entry 调用必须提供 documentRoot。CLI 可以使用当前工作目录作为默认 documentRoot,并允许调用者显式覆盖。
3.3. Source Set
Source set 包含 entry file 和受支持 include directive 指向的 files。
同一绝对路径只读取一次。重复 include 可以在 logical document 中出现多次;source file table 中仍只有一个 file record。
Source set 是输入构造事实,不是下游投影资源集合。下游消费者读取 AbundantDocument 中的 source coordinate,而不是读取 source set。
Book-entry RDF12 输出必须为 source set 提供文件级重建表面。该表面以 documentRoot relativePath 标识 source file,并携带该 source file 的完整原始文本。
Source set reconstruction surface 必须覆盖只贡献 attributes 或其它非结构节点的 source files。没有 section、xref、target 或 block 投影资源的 source file 仍属于 source set,仍必须可从 RDF12 输出重建。
第二部:逻辑文档构造
这一部定义 include resolution、logical document 和 line origin。
4. Include Resolution
Include resolution 读取 source file 中的受支持 include directive,并把目标 source file 的行插入 logical document。
4.1. 支持表面
本对象支持不带转义前缀的 full-file 完整行 include directive:include::chapters/01-projection-object.adoc[]。
Directive 可以带前导空白。Target path 按包含该 directive 的 source file 所在目录解析。
Escaped include 不执行 resolution。Escaped include 的 source line 以反斜杠开头:\include::chapters/01-projection-object.adoc[]。
Escaped include 是普通 source line。它以原文进入 logical document,并保留原始 line origin。
4.2. 递归 include
Included file 可以继续包含 include directive。Resolution 递归执行,并为插入的每一行记录 origin。
递归 resolution 必须检测循环。循环 include 阻止 logical document 成立,并产生错误诊断。
4.3. Unsupported Attrlist
本对象只定义 full-file include。Include attrlist 若包含 tag、lines 或其它改变目标文件行集的语义,resolution 不得静默按整文件 include 处理。
示例 surface 包括 include::chapter.adoc[tag=main] 和 include::chapter.adoc[lines=1..20]。
这些 surface 进入 unsupported include diagnostic。该诊断保护 line origin 的真实性。
4.4. 路径边界
Resolved include path 必须位于允许边界内。允许边界由 documentRoot 或实现定义的 entry root 约束。
越界 include 阻止 logical document 成立。实现不得读取边界外文件后再只发 warning。
4.5. 与 Asciidoctor 语义的关系
AsciiDoc include 是预处理器行为。未转义完整行 include directive 是构造 logical document 的输入事实。
本对象不实现完整 Asciidoctor preprocessor。它只实现本书定义的 full-file include resolution。其它 AsciiDoc 语义由 parser 在 logical document 上解释。
5. Logical Document
Logical document 是 book-entry mode 构造出的 in-memory AsciiDoc 文本。Parser 把它作为一份逻辑 source document 解析。
5.1. Logical Text
Logical text 由 entry file 与 included files 的 source lines 组成。
Include directive line 在 logical text 中被 included file lines 替换。Directive line 仍保留在 entry file 的原始 source text 中,并可作为 entry file heading raw 的一部分被恢复。
Logical text 不写入 build/adoc。它不是发布产物,不是作者编辑对象,也不是 source file。
5.2. Line Preservation
Logical text 中的非 include 行必须原样复制 source line。实现不得改写空白、缩进、Unicode 字符或 inline 内容。
该规则使 logical column 与 origin column 保持一致。Inline occurrence 的 column 可以直接从 logical line 映射到 origin line。
5.3. 示例
本节源码块中的 include line 使用反斜杠阻止本书构建期执行 include。示例 entry file 的 authored surface 是不带反斜杠的 include::chapters/01-projection-object.adoc[]。
Entry file:
= 第一部:投影对象
include::chapters/01-projection-object.adoc[]
Chapter file:
[#projection-object]
== 投影对象
本章定义投影对象的输入事实。
Logical text:
= 第一部:投影对象
[#projection-object]
== 投影对象
本章定义投影对象的输入事实。
Logical text 用于结构解析。= 第一部:投影对象 的 source coordinate 指向 entry file。== 投影对象 的 source coordinate 指向 chapter file。
5.4. 非源文件身份
Logical document 没有独立 source file identity。它可以有 parse identity,但不能作为 raw recovery 的 source。
Raw recovery 必须回到 line origin 指向的 source file。实现不得把 logical text 当成 heading raw、block raw 或 xref sourceSpan 的 source file。
6. Line Origin
Line origin 把 logical line 映射到原始 source file 和 source line。它是 book-entry 回源能力的构成性对象。
6.1. 数据形状
export type LineOrigin = {
logicalLine: number;
absolutePath: string;
relativePath: string;
sourceLine: number;
};
logicalLine 使用 1-based line number。relativePath 相对于 documentRoot。sourceLine 是 origin source file 中的 1-based line number。
每个 logical line 必须有且只有一个 line origin。
Line origin 描述行来源,不描述下游投影资源。可回源资源通过自身 source coordinate 消费 line origin 的结果。
6.2. Source File Record
Source file table 保存参与 logical document 构造的源文件。
export type SourceFileRecord = {
absolutePath: string;
relativePath: string;
text: string;
lineTable: LineTable;
};
Source file table 服务 raw recovery、diagnostics 和 relative path 计算。它不是下游投影资源集合。
6.3. Column 映射
Logical line 文本原样来自 origin line 时,column 保持不变。
logical line 27, column 14
-> origin relativePath "books/example/chapters/01.adoc"
-> origin line 8, column 14
实现不得在 logical text 构造阶段改写行文本。若某种转换需要改写文本,该转换必须同时定义 column map;本对象不包含该转换。
6.4. 不变量
Logical line count 与 line origin count 必须一致。
Line origin 的 absolutePath 必须存在于 source file table。
Line origin 的 sourceLine 必须位于对应 source file lineTable 范围内。
违反这些不变量时,logical document 不成立。
第三部:解析器集成与回源
这一部定义 logical document 如何进入解析器,以及 source coordinate 和 raw 如何从 origin 恢复。
7. Parser Integration
Book-entry pipeline 通过 logical document 调用 asciidoc-abundant-tree parser。它不改变 parser 的对象边界。
7.1. 集成形状
Book-entry pipeline 的内部数据流:
entry file
-> include resolution
-> logical text + line origins + source file table
-> parser core
-> origin-aware AbundantDocument
Parser core 读取 logical text,并通过 line origin 恢复 source layer 坐标。
7.2. Parser 输入
Parser core 需要一个能够接受内存文本的内部入口。该入口服务 book-entry public mode,不改变 single-file public call 的默认行为。
parseLogicalDocument({
logicalText,
lineOrigins,
sourceFiles,
entryPath,
documentRoot,
});
该函数返回 AbundantDocument。返回对象的 sourcePath 表示 entry file,mode 表示 book-entry 输入构造身份。可回源事实的 source coordinate 来自 line origin。
7.3. Asciidoctor 调用
Asciidoctor 应解析 logical text,而不是 entry file。
该调用不得再次展开 include。Include resolution 已由 book-entry pipeline 完成。
Asciidoctor 在该调用中提供 official AST、section tree、block context 和 ref catalog。Raw recovery、xref sourceSpan 和 block source coordinate 不得从 Asciidoctor include sourcemap 推导。
7.4. 与 Parser 规约的关系
Parser 规约定义 AbundantDocument、source layer、official layer 和 single-file parser。Book-entry 规约定义 logical document 如何进入 parser core。
Parser 不需要知道下游投影器。Book-entry pipeline 也不需要知道下游投影器的输出形状。
8. Source Coordinate Recovery
Book-entry source coordinate recovery 把 parser 在 logical document 中观察到的行列位置转换为 origin source coordinate。
8.1. Heading Raw
Section heading 的 logical line 通过 line origin 映射到 origin source file 和 origin heading line。
Heading raw 必须从 origin source file line table 恢复。实现不得从 logical text 直接切 heading raw。
Entry file 中的 heading raw 按 entry file 原文恢复。该 raw 可以包含 include directive,因为 include directive 是 entry file 的原始文本。
Included file 中的 heading raw 按 included file 原文恢复。
Heading raw 终点按 origin source file 内的 heading slice 规则确定。Logical document 只提供结构关系,不提供 raw slice source。
8.2. Xref Occurrence
Inline scanner 在 logical line table 中发现 xref occurrence。
Occurrence 的 logical line 和 logical column 通过 line origin 转换为 origin relativePath、sourceLine 和 column。
Xref sourceSpan 的 relativePath 必须表示 xref 出现的原始 source file。
Column 映射以 logical line 原样复制 origin line 为前提。实现若改写 logical line text,不得继续直接继承 column。
8.3. Block Raw
Listing 或其它 block 的 logical span 可以映射为 origin span。正常 block 的所有 logical lines 应来自同一个 origin source file。
若一个 block span 映射到多个 origin source files,source coordinate recovery 不得输出假 raw。该情况进入 diagnostic。
Block raw 和 content span 从 origin source file line table 恢复。Logical text 中的 block interval 只用于定位对应 logical lines。
8.4. Source Scope 查找
任何通过 line containment 查找 source scope 的算法都必须同时比较 relativePath。
错误规则:
candidate.startLine <= line <= candidate.endLine
正确规则:
candidate.relativePath == occurrence.relativePath
candidate.startLine <= occurrence.line <= candidate.endLine
该规则保护不同 source files 中相同行号的 section 不被错误匹配。
8.5. 下游坐标
AbundantDocument 中的每个可回源事实必须携带 origin source coordinate,或保持 source coordinate 缺失并产生 diagnostic。
下游投影器不得用 entry file path 替代缺失的 resource coordinate。
9. Diagnostics
Book-entry diagnostics 描述 logical document 构造和 origin recovery 中的失败面。
9.1. 构造错误
以下情况阻止 logical document 成立:
-
Entry file 不存在。
-
Include target 不存在。
-
Include path 越出允许边界。
-
Include graph 出现循环。
-
Include directive 使用 unsupported attrlist。
-
Logical line count 与 line origin count 不一致。
-
Line origin 指向 source file table 中不存在的 file。
-
Line origin 的 sourceLine 越出 origin file line table。
这些错误不得被输出为成功 AbundantDocument。
9.2. Recovery Diagnostic
以下情况可以进入 recovery diagnostic:
-
Block span 映射到多个 origin source files。
-
Origin file line table 无法恢复 raw。
-
Parser core 提供的 logical interval 无法映射到 line origin。
Recovery diagnostic 必须阻止假精确坐标输出。无法可靠恢复的 source coordinate 应保持缺失。
9.3. 诊断表面
Diagnostic 必须包含 code、level、message 和可用 source coordinate。
Diagnostic message 应描述对象事实,不写实现者过程。示例:
include.unsupported-attrlist: include directive uses unsupported attrlist 'tag=main'.
该消息说明失败对象和失败原因。它不写“暂时不支持”或“以后实现”。
第四部:公共入口与验收
这一部定义 public API、CLI、contract tests 和完成条件。
10. Public API 与 CLI
Book-entry pipeline 需要显式公共入口。调用者必须声明自己正在解析书稿入口。
10.1. Library API
Library API 使用 parseAbundantTree 的显式 mode:
import { parseAbundantTree } from "asciidoc-abundant-tree";
const document = parseAbundantTree({
sourcePath: "docs/bookshelf/books/06-rdf12-line-projection/book.adoc",
mode: "book-entry",
documentRoot: "docs/bookshelf",
});
该调用返回 AbundantDocument。mode: "book-entry" 表达 book-entry pipeline 的对象身份。
未提供 mode 时,parseAbundantTree 保持 single-file 输入语义。实现不得从 :doctype: book、文件名或 include directive 自动切换模式。
10.2. CLI
CLI 使用显式 mode:
asciidoc-abundant-tree docs/bookshelf/books/06-rdf12-line-projection/book.adoc \
--mode book-entry \
--format json
下游投影格式可以消费该 AbundantDocument。CLI mode 只选择输入构造方式,不改变输出格式的对象定义。
10.3. documentRoot
Library API 要求 documentRoot。
CLI 可以使用当前工作目录作为默认 documentRoot,并允许调用者显式传入:
asciidoc-abundant-tree books/example/book.adoc \
--mode book-entry \
--document-root docs/bookshelf
所有输出 relativePath 均相对于 documentRoot。
10.4. Public Model
Public model 必须让可回源事实携带自己的 source coordinate。Book-entry pipeline 不要求下游消费者读取 line origin table。
Line origin table 是内部 IR。公共 API 可以提供调试表面,但下游投影器的主输入仍是 AbundantDocument。
10.5. RDF12 Source File Reconstruction
RDF12 book-entry 输出必须包含 source file reconstruction surface。该表面为 source set 中每个 source file 输出一个文件级资源。
文件级资源必须包含:
-
relativePath:相对于documentRoot的 source file path。 -
raw:source file 的完整原始文本。
该表面必须覆盖 entry file、frontmatter、chapters、backmatter、shared attributes 和递归 include 到达的 source files。该表面必须保留 include directive 原文;entry file 中的 include directive 不得被 included file 文本替代。
RDF12 source file reconstruction surface 与 resource-level source coordinate 是两个公共表面。前者支持逐文件重建;后者支持 section、xref occurrence、target 和 block 回源。
11. Contract Tests
Book-entry 完成必须由 contract tests 证明。测试对象是 logical document、line origin 和 origin-aware AbundantDocument。
11.1. Logical Source Builder Tests
测试必须覆盖:
-
Simple include 展开。
-
Nested include 展开。
-
Shared attributes include。
-
Escaped include 不展开。
-
Missing include 失败。
-
Circular include 失败。
-
Unsupported attrlist 失败。
-
Path outside documentRoot 失败。
-
Relative path 按包含 directive 的文件目录解析。
-
Logical line count 与 line origin count 一致。
11.2. Parser Integration Tests
测试 fixture 应包含 entry file、两个 chapter files 和一个 nested include file。
断言:
-
Included heading 出现在
AbundantDocumentsection tree 中。 -
Entry file heading 的 source relativePath 指向 entry file。
-
Chapter heading 的 source relativePath 指向 chapter file。
-
Nested include heading 的 source relativePath 指向 nested file。
-
Heading raw 来自 origin file。
-
Heading raw 不从 logical text 直接切取。
-
Xref occurrence 的 sourceSpan 指向 xref 所在 origin file。
-
Source scope 查找不跨 relativePath 匹配。
11.3. Downstream Coordinate Tests
下游 projection 测试只验证它消费到的 source coordinate 正确。测试不得要求 downstream projector 读取 line origin table。
断言:
-
Book-entry 输出不包含
build/adoc聚合路径。 -
同一 logical document 中不同 resources 可以有不同 relativePath。
-
Listing 或 table block 的 raw 和 source span 来自 origin file。
-
跨 origin source files 的 block 不输出假 raw。
11.4. RDF12 Source Reconstruction Tests
Book-entry RDF12 测试必须证明 Turtle 输出可重建 source set 中的每个原始 source file。
断言:
-
RDF12 输出包含 entry file 的完整原始文本。
-
RDF12 输出包含每个 included chapter、frontmatter 和 backmatter file 的完整原始文本。
-
RDF12 输出包含 shared attributes file 的完整原始文本,即使该文件不产生 section、xref、target 或 block resource。
-
Entry file reconstruction 保留 include directive 原文,不使用 expanded logical text 替代 include directive line。
-
RDF12 source reconstruction resources 的
relativePath均相对于documentRoot。
11.5. Regression Tests
single-file mode 必须保持不变。
断言:
-
Single-file parser 不展开 include。
-
Single-file external source location 仍产生 diagnostic。
-
Existing parser tests 和 serializer tests 不回退。
-
:doctype: book不触发隐式 book-entry mode。
11.6. 完成声明
完成声明必须列出实际运行命令和结果。
pnpm test
pnpm typecheck
pnpm build
pnpm test:coverage
pnpm --dir docs/bookshelf test
pnpm --dir docs/bookshelf run build
未运行的命令不能写成通过。
Appendix A: 术语表
- book-entry mode
-
从书稿入口构造 source-mapped logical document 的输入模式。
- entry file
-
调用者显式传入的书稿入口 source file。
- source set
-
Entry file 通过受支持 include directive 到达的 source files 集合。
- logical document
-
Book-entry pipeline 在内存中构造的 AsciiDoc 文本。Parser 将其作为一份逻辑 source document 解析。
- logical line
-
Logical document 中的 1-based line。
- line origin
-
Logical line 到 origin source file 和 origin source line 的映射。
- source file table
-
参与 logical document 构造的 source files 记录集合。
- documentRoot
-
Relative path 基准和 include path 边界。
- include directive
-
AsciiDoc 预处理器表面。本对象只把受支持 full-file include directive 解释为 logical document 构造指令。
- origin source coordinate
-
可回源事实在原始 source file 中的位置。
- origin raw recovery
-
从 origin source file line table 恢复 raw slice 的过程。
参考坐标
-
asciidoc-abundant-tree 解析器规约:本书所消费的 parser 与
AbundantDocument对象。 -
AsciiDoc include directive documentation:include directive 语义坐标。
-
Asciidoctor.js documentation:official parser 语义坐标。