Skip to content

types: expose positioned AST types for parseMd#39

Merged
luojiyin1987 merged 6 commits into
masterfrom
types/positioned-ast
Jul 2, 2026
Merged

types: expose positioned AST types for parseMd#39
luojiyin1987 merged 6 commits into
masterfrom
types/positioned-ast

Conversation

@luojiyin1987

Copy link
Copy Markdown
Contributor

背景

@lint-md/core 从 parser 0.0.14 升级到 0.1.1 后,MarkdownNode 直接采用 mdast/unist 的节点类型。该类型将 node.positionposition.start/end.offset 声明为可选,而 parseMd 运行时实际生成的节点总是带有完整的 position 与 offset。

由于公开类型未表达这一契约,下游 @lint-md/corestrictNullChecks 下必须反复检查 node.position / start / end 并对 offset 使用非空断言。

改动

新增 positioned AST 类型,并让 parseMd 返回专门的 positioned 根类型,在 API 边界表达"所有解析节点都带 positionstart/end.offset"的运行时契约。

新公开类型 (src/types.ts):

  • ParsedPointline / column / offset 全部必填
  • ParsedPositionstart / end 必填
  • Positioned<T> — 递归重写 mdast 节点,使 position 与子节点的 position 都为必填
  • PositionedMarkdownRoot = Positioned<MarkdownRoot>
  • PositionedMarkdownNode = Positioned<MarkdownNode>

parseMd 返回值 (src/parse-md.ts):

  • (md: string) => PositionedMarkdownRoot(用 as 断言表达运行时契约,运行时不变)
  • JSDoc 明确"解析后的 AST 根节点,递归带 position(含 start/end.offset)"
  • revertMdAstNode 仍接受宽类型 MarkdownRoot,不破坏下游传入自构 AST 的场景

不改动:

  • MarkdownRoot / MarkdownNode / MarkdownDirectiveFields 等现有类型保持原样,避免污染所有 AST 上下文
  • 不全局 declare module 'mdast' / 'unist' 收紧原类型

测试

  • 新增 __tests__/position.spec.ts — 递归遍历整棵树,断言 parseMd 输出中每个节点的 position.start.{line,column,offset}position.end.{line,column,offset} 都是 number;覆盖 frontmatter / heading / paragraph / list / table / task list / strikethrough / code / link / autolink / inline math / container directive
  • __tests__/types/package-exports.mts / .cts — 在 ESM 与 CJS 入口验证 parseMd('# …').position.start.offset 编译为 number,并能赋值给 PositionedMarkdownRoot / PositionedMarkdownNode
  • pnpm run lint / build / test / test:types / test:package 全过;API Extractor 自动重生成 etc/parser.api.md

下游影响

@lint-md/core 升级后可直接:

const ast = parseMd('# title');
ast.position.start.offset;            // number
ast.children[0].position.end.offset;  // number

无需 if (!node.position)! 非空断言。

验证

pnpm install --frozen-lockfile
pnpm run lint
pnpm run build
pnpm run test

Closes #38

@luojiyin1987

Copy link
Copy Markdown
Contributor Author

review 反馈已处理(新增 2 个 commit)

843645c docs(types,parse-md,readme): document position contract and TSDoc new types
098b738 test(position): cover every supported node with parameterized corpus

主要修改

  1. 测试覆盖完整化(review 第 2 点)

    • __tests__/position.spec.ts 改为 test.each 参数化 corpus,24 个独立用例覆盖:
      frontmatter / heading / paragraph / thematicBreak / blockquote / ordered/unordered/task list / table / strikethrough / inline code / code block / hard break / autolink / explicit link / image / reference link / definition / html block / inline html / inline math / block math / container / leaf / text directive
    • 增加 start.offset <= end.offset 断言,及 1 个 composite fixture
    • 单 fixture 失败时直接定位到具体节点类型
    • 总测试数:48 → 72(position.spec.ts 自身:1 → 25)
  2. as 断言处加契约来源注释(review 第 1 点)

    • src/parse-md.ts 列出三个保证:GFM workaround / processor 单例 / position.spec.ts 回归测试
    • 给出 remarkjs/remark-gfm#16 链接,便于未来 reader 理解为什么这个 cast 是安全的
  3. TSDoc + README 文档化(review 第 4 点)

    • ParsedPoint / ParsedPosition / Positioned / PositionedMarkdownRoot / PositionedMarkdownNode 全部加 TSDoc 描述
    • etc/parser.api.md 中本 PR 引入的 5 个新条目 (undocumented) 标记全部移除(原有 MarkdownMath 等的不动)
    • README 新增"位置契约"小节,说明:契约示例代码、三个保证的来源、MarkdownRoot/MarkdownNode 仍可选 position 的设计理由
  4. TS 4.8 兼容性本地手测(review 第 3 点,按你要求未改 CI)

    • pnpm add -D typescript@4.8.4 --save-exact 后:
      • tsc --noEmit 源码层 → exit 0,源码本身在 TS 4.8 下编译通过
      • tsc -p __tests__/types/tsconfig.json --skipLibCheckexit 0,类型测试 fixture 在 TS 4.8 下编译通过
    • 默认配置(skipLibCheck: false)下报错来自 @types/node@22.20.0(需要 TS 5.x),与本 PR 无关——这是项目既有环境约束,不是新引入的兼容性问题
    • 已恢复 package.json / pnpm-lock.yaml 到 TS 5.8

最终验证(TS 5.8 默认环境)

pnpm run lint    → exit 0
pnpm run build   → API Extractor 成功,report 自动重生成
pnpm run test    → 7 suites / 72 tests / 2 snapshots 全部 PASS
pnpm run test:types  → exit 0
pnpm run test:package → exit 0

@luojiyin1987 luojiyin1987 merged commit a7a1b57 into master Jul 2, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

types: expose positioned AST types for parseMd results

1 participant