# @effected/markdown 中文文档

规范地址：https://npmcn.com/package/%40effected/markdown/

版本：0\.29\.0
npm 元数据更新时间：2026-11-10T15:12:48.957Z
中文译文更新时间：2026-11-13T08:59:37.384Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40effected/markdown
原文对照：https://npmcn.com/package/%40effected/markdown/?lang=original
项目主页：https://github.com/spencerbeggs/effected/tree/main/packages/markdown#readme
源码仓库：https://github.com/spencerbeggs/effected

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：622 kB，76 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.11\.0
- 运行依赖：1 个
- peer 依赖：effect ~4\.1\.0，@effected/toml ^0\.20\.0，@effected/yaml ^0\.30\.0，@effected/jsonc ^0\.25\.0
- 首次发布：2026-07-19T19:35:55.990Z
- 最新版本发布：2026-11-10T15:12:44.620Z
- 近 30 天下载量：8942（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：47（近 12 个月 47 个）

## 中文简介

常见马克和GFM 标记下解析、编辑和转换效果计划

## 安装

```sh
npm install @effected/markdown
```

也可以使用：`pnpm add @effected/markdown`、`yarn add @effected/markdown`、`bun add @effected/markdown`

## README

# @ 受影响/ 标记下

[![npm](https://img.shields.io/npm/v/@effected%2Fmarkdown?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/markdown) [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT) [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/) [![TypeScript 7.0](https://img.shields.io/badge/TypeScript-7.0-3178c6.svg)](https://www.typescriptlang.org/)

CommonMark 0.31.2和GFM 解析,编辑和变换,以效果计和纯函数表示. 将标记降为带字节偏移的mdast形状节点,导航标题、分节和链接,计算外科偏移相接编辑,规范标记,将前题解码为经验证的域名图案,为表达式生态系统进行项目往返,并按文档行走 `Stream`。 。 。 .

> **预`1.0.0`。 。 。 .** 此软件包是 `@effected/*` 在稳定基础上建造的工具包 效应v4(`effect` `~4.1.0`) 并仍在 `0.x` 发展。 稳定效果 制作一个工具箱 `1.0.0` 可能,不是自动的。 为了保持你的 `effect` 财务报告和审定财务报表 `@effect/*` 该工具包在线上构建和测试、安装 [`@effected/pnpm-plugin-effected`](https://www.npmjs.com/package/@effected/pnpm-plugin-effected)。 。 。 .
> 
> **稳定:不稳定.** 此软件包的 API 表面尚未考虑 完成并可能改变 `0.x` 释放 定一个确切的版本——甚至 标记软件包 _稳定_ 在此之前 `1.0.0` 可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: [释放策略](https://github.com/spencerbeggs/effected#release-strategy)。 。 。 .

## 为什么 @ 受影响/ markdown

Markdown是文档,更改日志,知识库和AI代理上下文文件实际存在的地方——文件人手编辑并期望能活过一个触摸到它们的工具. 评论生态系统是 JavaScript 默认, 但它的序列器 重构设计: 读出一个文档并写回它, 你得到评论的想法 马克下,而不是你的。 生态系统中没有人会失去科技委员会, 而言论维护者自己则指向位置分层.

这个软件包将这个建议作为架构。 每个节点带字节与非站点行/列位置相抵消,所以编辑是针对原始出处的相接而来,而不是树的再串行化:改变一个标题和空白行,HTML评论和手调相接相接的相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相. `MarkdownEdit` 同字段为 `JsoncEdit`, (中文(简体) ), `YamlEdit` 财务报告和审定财务报表 `TomlEdit`,因此,相同的编辑词汇跨越了工具包中的每一种格式.

该引擎是一个销售商加硬化的普通马克.js端口——由 spec 作者维护的参考解析器——重组为每个模块在方言键入的登记册后建造. 这意味着没有运行时间解析器依赖性,也没有插件表面来解释: `commonmark` 财务报告和审定财务报表 `gfm` 是两种方言, `gfm` 是默认的, 两者都由上游的相容体所支撑。 解析是近乎全面的,因为CommonMark没有语法错误:被打出的错误通道只携带了硬化-护卫出行,从不"malform mark down".

节点的形状是mdast的准确类型名称和字段形状,因此树已经是说出生态系统的形状 `Mdast.toMdast` 剥去这个包的忠心字段, 交给你普通的 spec -valid mdast JSON。 Frontmatter——Mdast完全没有解析的故事——通过独立的每个格式的解码器,在解码器后被捕获,在不拖出三个格式引擎的情况下,将打入一个需要一个格式的捆绑器的灰色物质等同化.

标记到 HTML 是故意超出范围。 此包解析、 编辑并变换下标值; 渲染属于您已经拥有的任何渲染器, 通过 mdast 投影到达 .

## 安装

```bash
npm install @effected/markdown effect
```

```bash
pnpm add @effected/markdown effect
```

需要节点.js~~24.11.0. `effect` v4 是一个对等依赖; 软件包的一次运行依赖是无依赖的 `@effected/source`,其 `LineIndex` 它的再出口.

`@effected/yaml`, (中文(简体) ), `@effected/toml` 财务报告和审定财务报表 `@effected/jsonc` 为: **选项** 等同位素,仅需要您导入的前置编码模块。 一个消费者解码的 yaml 前题安装 `@effected/yaml` 一个从未接触过前置物品的消费者.

```bash
npm install @effected/yaml
```

全体 `@effected/*` 软件包仅为无害环境管理:只公布出口地图 `import` 条件,所以 `require()` 包括解析 CJS 模式的工具 —— 与节点失败 `ERR_PACKAGE_PATH_NOT_EXPORTED` 而不是装入不存在的 CJS 建筑。 从ES模块导入.

## 快开始

`MarkdownDocument.parse` 以一个值给出源、树、定义索引和导航访问器:

```ts
import { MarkdownDocument } from "@effected/markdown";
import { Effect } from "effect";

const source = `# Release notes

## Fixed

- Tables no longer ~~drop~~ trailing cells.
`;

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse(source);
  return doc.headings.map((heading) => `${"#".repeat(heading.depth)} ${heading.text}`);
});

Effect.runPromise(program).then(console.log);
// [ "# Release notes", "## Fixed" ]
```

方言默认为 `gfm`,所以击出 上面的画像 `delete` 没有配置的节点 。 过 `MarkdownParseOptions.make({ dialect: "commonmark" })` 为了严格的普通马克.

每个剖面都有一个同步的双胞胎—— `MarkdownDocument.parseResult` 财务报告和审定财务报表 `Markdown.parseResult` 返回 a `Result` ——所以一个构建脚本,Vite插件或语言服务器勾选可以调用,而不需要效果运行时间.

## 在不重写文档的情况下编辑

`MarkdownFormat.modify` 计算 a `MarkdownEdit` 与已解析的文档相对的阵列; `modifyToString` 应用它一步。 目标是从文档自己的树上取出一个节点,与身份相匹配,而编辑时没有覆盖的一切都活过字节:

```ts
import { MarkdownDocument, MarkdownFormat } from "@effected/markdown";
import { Effect, Option } from "effect";

const source = `# Release notes

See the [changelog](./CHANGELOG.md).
`;

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse(source);
  const label = Option.getOrThrow(doc.find("text"));
  return yield* MarkdownFormat.modifyToString(doc, label, "Release notes (2026)");
});

Effect.runPromise(program).then(console.log);
// # Release notes (2026)
//
// See the [changelog](./CHANGELOG.md).
```

`find` 在文档预排序中行走树并返回文档中自己的节点 `Option` —— 说 `Option.none()` 当没有匹配时——所以匹配的种子 `modify` 以身份证明 替换是字形的字符串或节点片段,两者都通过条形字符串来渲染出,所以一个被修改的文档通过构造可以清晰地分拆出——你不能在原始标记下相接来重开栅栏或打碎桌子.

`MarkdownFormat.format` 处理另一半: 保守的标记正常化, 绝不重写内容 。 它转换了标题样式,弹出字符,强调标记,栅栏字符,主题破解字符和代码块样式,并跳过任何不会安全而非巧妙尝试的转换:

```ts
import { MarkdownFormat, MarkdownFormattingOptions } from "@effected/markdown";

const source = `Setext heading
==============

* one
* two
`;

const options = MarkdownFormattingOptions.make({ headingStyle: "atx", bulletChar: "-" });

console.log(MarkdownFormat.formatToString(source, undefined, options));
// # Setext heading
//
// - one
// - two

console.log(MarkdownFormat.format(source, undefined, options));
// [
//   { offset: 0, length: 29, content: "# Setext heading" },
//   { offset: 31, length: 1, content: "-" },
//   { offset: 37, length: 1, content: "-" }
// ]
```

`format` 纯度和总度,编辑是非模拟数据——将其交给 `MarkdownEdit.applyAll`,或把它们作为文本编辑器的有效载荷发送给编辑器.

`codeBlockStyle` 是一个选项, 可以在您击出默认之前知道 : 没有它, 一个没有语言的代码块会保留它已有的拼写, 一个没有语言的块 `Code` 无节点 `fenceChar` 序列化为 _缩进_ 块. `"fenced"` 将缩成的地块改为围栏, `"indented"` 以相反的方式,结果可以证明是相同的,两个方向仅适用于根级的冲出-左块。 一个有语言的区块没有缩进的拼写,从未被触碰.

## 前题

Frontmatter 抓取是选入, 因为启用它会改变文档打开方式 `---` 剖析: 普通马克读取 `---\ntitle: x\n---` 作为主题课间休息和设定的副标题, 和光谱相符合的读取保持, 除非你要求别的东西。 打开它,并用一个编码器来编曲您的计划,用于打印出灰色物质的等同性:

```ts
import { MarkdownDocument, MarkdownFrontmatter, MarkdownParseOptions, YamlFrontmatter } from "@effected/markdown";
import { Effect, Schema } from "effect";

const source = `---
title: Release notes
draft: false
---

# Release notes
`;

const Post = Schema.Struct({ title: Schema.String, draft: Schema.Boolean });
const decodePost = MarkdownFrontmatter.schema(Post, YamlFrontmatter);

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse(source, MarkdownParseOptions.make({ frontmatter: true }));
  return yield* decodePost(doc);
});

Effect.runPromise(program).then(console.log);
// { title: "Release notes", draft: false }
```

每个阶段都打出并分开:没有抓取是 `FrontmatterMissingError` (抓取可选语义的标记),一个被解码器递错的栅栏是一个 `FrontmatterFormatMismatchError`,不可解析的内容是 `FrontmatterDecodeError` 搭载格式包自身定位的故障结构,而schema-无效数据是一个 `FrontmatterValidationError` 带有结构化的问题树,而不是字符串渲染.

"无前事"有三义 `FrontmatterMissingError.reason` 告诉他们: `"absent"` 当使者没有障碍的时候, `"captureDisabled"` 当它用一个打开,但被解析为切换——其中的修补是一个解析选项,而不是文档编辑——和 `"unclosed"` 当它打开一个没有关闭栅栏的栅栏时 非封闭的栅栏不是前题(语法规则,和评论前题),所以没有什么被抓住,但作者几乎肯定是指一块地. `MarkdownDocument.hasFrontmatterBlock` 财务报告和审定财务报表 `MarkdownDocument.frontmatterFence` (单位:千美元)`"absent"`, (中文(简体) ), `"unclosed"` 或者说 `"closed"`)直接从源头回答同样的问题,因此访问者和错误永远不能有分歧.

要针对整个文件报告YAML、TOML或JSON诊断,请添加解析 `Frontmatter` 节点 `valueStart` 价值开始时,在打开围栏线及其终止者之后,无论它们是什么,均抵消价值内的格式解析报告。 没有自己的栅栏算术:

```ts
import { MarkdownDocument, MarkdownParseOptions } from "@effected/markdown";
import { Yaml } from "@effected/yaml";
import { Option, Result } from "effect";

const source = "---\r\ntitle: [unclosed\r\n---\r\n# Body\r\n";
const doc = Result.getOrThrow(MarkdownDocument.parseResult(source, MarkdownParseOptions.make({ frontmatter: true })));
const node = Option.getOrThrow(doc.frontmatter);
const yaml = Yaml.parseResult(node.value);
if (Result.isFailure(yaml)) {
  const inFile = (node.valueStart ?? 0) + (yaml.failure.diagnostics[0]?.offset ?? 0);
  console.log(doc.lineIndex.positionAt(inFile)); // the diagnostic's line and character in the file
}
```

三个解码器.. `YamlFrontmatter` (单位:千美元)`---`, (中文(简体) ), `TomlFrontmatter` (单位:千美元)`+++`和 `JsonFrontmatter` (单位:千美元)`---json`)——是独立命名的输出,每个模块一个,刻意从不收集到命名空间物体. 命名一个解码器是它的格式引擎的拉动,所以一个JSON-frontmatter的消费者从不支付"活塞"(yaml parser).

Frontmatter 块也可以描述它们自己的计划. `SchemaResolver.classify` 有点 `$schema` 值为标记的工会 `ByUrl`, (中文(简体) ), `ByPath`, (中文(简体) ), `Inline` 财务报告和审定财务报表 `ByName` - 和 `SchemaResolver.fromRegistry` 决定 `ByName` 诸如 `skill@2.1.0` 以你们的计谋盟誓, URL、路径和内入式文档作为数据载出,从未取出:这是一个纯软件包,它不执行IO.

当身体根本不应该通过普通马克引擎时——一个MDX页面,一个模板,一个需要字节精确边界的快照-hash合同—— `FrontmatterSource.split` 使用相同的封闭栅栏语法 在生弦上,没有别的。 总计: 它返回区块格式, 栅栏线之间的精确字节, 以及源代码中的相抵(`valueStart`, (中文(简体) ), `valueEnd`)和遗体残存(`body` 总是如此 `source.slice(bodyOffset)`),不存在可代表的结果而不是错误. `fence` 报告 `"absent"`, (中文(简体) ), `"unclosed"` 或者说 `"closed"`,以及 `fenceFormat` 打开的栅栏所声明的格式,所以未关闭 `---` 与没有任何围栏是不同的. `FrontmatterSource.join` 序列化部件返回,复制原字节用于未修改的回转。 两个边缘正常化而不是复制:一个关闭的栅栏结束文档而无线终止者,则获得一个;而栅栏线与不匹配的终止者(例如: `---\r\n … ---\n`)再用开口栅栏取出. 值和体字节在每种情况下都能够逐字存活,而且从来没有在这个级别解析过——解码会保留代码模块的业务.

## 和树一起工作

`MarkdownDocument` 从树上取出它的导航器,所以他们绝不是反对的. `links` 收集每个包含 URL 的节点并传递 `url` 与书面内容完全一致——捆绑-相亲的外壳从未正常化:

```ts
import { MarkdownDocument } from "@effected/markdown";
import { Effect } from "effect";

const source = `# Release notes

See the [changelog](./CHANGELOG.md) and the [docs](https://example.com/docs).
`;

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse(source);
  return doc.links.map((link) => link.url);
});

Effect.runPromise(program).then(console.log);
// [ "./CHANGELOG.md", "https://example.com/docs" ]
```

`headings` 列出其所坐位置的每个标题,包括块状参数和项目. `headingSlugs` 与它平行: GitHub 风格的锚, 使路条脱落 `github-slugger` 实际数(`dup, dup, dup-1, dup` 给 `dup, dup-1, dup-1-1, dup-2`),以及 `MarkdownDocument.slug(text)` 是一个不重复的原始人。 平等是与 `github-slugger`而不是与GitHub本身. `sections` 仅由根级标题来划分,每个区域的范围会覆盖其分节,因此,编辑层可以在一次编辑中将整个区域相接出. `firstSection` 财务报告和审定财务报表 `sectionByHeading` 找到一个 `DocumentSection` 不扫描 `sections` 自己——在精确修剪后的标题字符串上的第二个匹配,a `RegExp`,或上游,两者均接受 `{ depth }` 限制在一个标题级别。 两个都回 `Option`,就像每一次检查 在包里做的 `frontmatter` 访问器和 `find` 电话:

```ts
import { MarkdownDocument } from "@effected/markdown";
import { Effect, Option } from "effect";

const source = `# Changelog

## 1.2.3

Fixed a bug.

## 1.2.2

Initial release.
`;

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse(source);
  return Option.match(doc.sectionByHeading("1.2.3", { depth: 2 }), {
    onNone: () => "no such release",
    onSome: (section) => section.body.trim(),
  });
});

Effect.runPromise(program).then(console.log);
// Fixed a bug.
```

`DocumentSection.body` 是没有标题的一节,它被刻意地解开——就是字节 `bodyRange` 描述——所以呼叫者写 `.body.trim()` 当他们想要干净的表格.

`unresolvedFootnoteReferences` 列出每个 GFM 脚注标记 - `[^label]` ——没有相匹配的定义,与其原始 `label`,其案件卷 `identifier` 联 合 国 `position`。只有在定义存在时,才使用参考形式,所以未定义的标记保留文字,与邻居合并,以及 `findAll("footnoteReference")` 无法看见它;剖析器在它拒绝的时候,各记录一个。 标出解析器从未读取的——在代码跨度和代码块,自动链接,HTML,背斜后,或形成链接的括号对内——不报告,节点树不变:

```ts
import { MarkdownDocument } from "@effected/markdown";
import { Result } from "effect";

const doc = Result.getOrThrow(MarkdownDocument.parseResult("See [^a] and [^b], not `[^c]`.\n\n[^a]: Defined.\n"));
console.log(doc.unresolvedFootnoteReferences.map((marker) => [marker.label, marker.position.start.offset]));
// [ [ "b", 13 ] ]
```

对于任何进入者没有覆盖, `find` 财务报告和审定财务报表 `findAll` 在文档预排序中将整个树行走。 匹配节点的字符串选择器 `type` 并缩小结果—— `doc.findAll("heading")` 实值 `ReadonlyArray<Heading>`, (中文(简体) ), `doc.find("table")` 实值 `Option<Table>` - 和类型护卫的上方 缩小相同的方式。 节点通过身份回归,所以 `doc.findAll("heading")[1]` 将第二个标题移至 `MarkdownFormat.modify` 没有生孩子索引.

`MarkdownVisitor.visit` 流出与行走相同 `Enter`编号 :`Exit` 带有节点、其子索引路径及其深度的事件:

```ts
import { MarkdownDocument, MarkdownVisitor } from "@effected/markdown";
import { Effect, Stream } from "effect";

const program = Effect.gen(function* () {
  const doc = yield* MarkdownDocument.parse("# Title\n\nA *b* c\n");
  const events = yield* Stream.runCollect(MarkdownVisitor.visit(doc.root));
  return Array.from(events).map((event) => `${event._tag}:${"node" in event ? event.node.type : "?"}`);
});

Effect.runPromise(program).then(console.log);
// [ "Enter:root", "Enter:heading", "Enter:text", "Exit:text", "Exit:heading",
//   "Enter:paragraph", "Enter:text", "Exit:text", "Enter:emphasis", "Enter:text",
//   "Exit:text", "Exit:emphasis", "Enter:text", "Exit:text", "Exit:paragraph", "Exit:root" ]
```

`MarkdownVisitor.walk` 是一个同步而懒惰的行走 `Iterable` - 原始的 - 原始的 `visit` 定义方式是——对于不能被暂停的代码,例如lint规则一个主机同步调用. 每个迭代都从根部重新走出 `for…of` 那个 `break`停止走动:

```ts
import { Markdown, MarkdownVisitor } from "@effected/markdown";
import { Result } from "effect";

const root = Result.getOrThrow(Markdown.parseResult("# Title\n\nA *b* c\n"));
const headings: Array<number> = [];
for (const event of MarkdownVisitor.walk(root)) {
  if (event._tag === "Enter" && event.node.type === "heading") headings.push(event.node.depth);
}
console.log(headings);
// [ 1 ]
```

`MarkdownVisitor.traverse` 开同样的车走过去 `enter`编号 :`exit` 引导它的回调: 返回 `MarkdownVisitor.SKIP` 从 `enter` 使节点的子孙不受影响 `exit` 仍在运行),或 `MarkdownVisitor.EXIT` 停下来。 每个回调也得到节点 `path`, (中文(简体) ), `depth` 财务报告和审定财务报表 `ancestors`。给它一个节点类型,或者一组,只有匹配的节点才能到达回调,按节点类键——行走仍然会通过其他所有方式下降:

```ts
import { Markdown, MarkdownVisitor } from "@effected/markdown";
import { Result } from "effect";

const root = Result.getOrThrow(Markdown.parseResult("# A\n\n> # B\n\n## C\n"));
const depths: Array<number> = [];
MarkdownVisitor.traverse(root, ["heading", "blockquote"], {
  enter: (node) => {
    if (node.type === "blockquote") return MarkdownVisitor.SKIP; // headings quoted inside are not visited
    depths.push(node.depth);
    return undefined;
  },
});
console.log(depths);
// [ 1, 2 ]
```

`traverse` 返回时 `Option.none()`,或 `NestingDepthExceeded` 当一棵人工建造的树筑起后, 深度会结束行走; 一棵被剖开的树永远不会.

## mdast 接口

节点类已经使用 mdast 的类型名称和字段形状 ; `Mdast.toMdast` 剥去此套件所添加的忠义字段——弹出字符、栅栏样式、ATX-对文本拼写(只保留一个) `Text` 节点 `escapeStyle` 设定时,见 [文字文本](https://npmcn.com/package/%40effected/markdown/#readme-literal-text))并表明,该言论的生态系统直接消耗,包括 `mdast-util-to-hast` 如果你想急着:

```ts
import { Markdown, Mdast } from "@effected/markdown";
import { Result } from "effect";

const parsed = Markdown.parseResult("A *b* c\n");
if (Result.isSuccess(parsed)) {
  console.log(JSON.stringify(Mdast.toMdast(parsed.success)).slice(0, 62));
  // {"type":"root","children":[{"type":"paragraph","children":[{"t
}
```

`Mdast.fromMdast` 以另一种方式作为检查的准入边界:它验证一棵外国树,并在一个没有或不完整的地方合成零宽哨位。 树也承认了 树级的工作流程和犬科 `stringify`; 偏移- 相片编辑需要真实位置, 仅需要解析结果 .

## MDX 树

MDX节点词汇 - `MdxJsxFlowElement` 财务报告和审定财务报表 `MdxJsxTextElement` (与 `MdxJsxAttribute`, (中文(简体) ), `MdxJsxExpressionAttribute` 财务报告和审定财务报表 `MdxJsxAttributeValueExpression` (d) 作为其责任人, `MdxFlowExpression`, (中文(简体) ), `MdxTextExpression` 财务报告和审定财务报表 `MdxjsEsm` - 形状完全与 `mdast-util-mdx` 和所有其他节点类一样 解析器不读取 MDX 语法; 这些节点是存在的 **已合成** 树可以携带 JSX 并串行到有效的 MDX 而不是加入字符串 :

```ts
import { Markdown, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxFlowElement, Root } from "@effected/markdown";
import { Result } from "effect";

const page = Root.make({
  children: [
    MdxJsxFlowElement.make({
      name: "ApiSignature",
      attributes: [
        MdxJsxAttribute.make({
          name: "code",
          value: MdxJsxAttributeValueExpression.make({ value: JSON.stringify({ lang: "ts" }) }),
        }),
      ],
      children: [],
    }),
  ],
});

const mdx = Markdown.stringifyResult(page);
if (Result.isSuccess(mdx)) {
  console.log(mdx.success); // <ApiSignature code={{"lang":"ts"}} />
}
```

序列化匹配 `mdxJsxToMarkdown`默认 `"` 引用, 空格自闭 `<a />`, (中文(简体) ), `<></>` 碎片,流出的儿童 缩入两个空间 每个JSX祖先, `{expr}` 表达式、 无害环境管理值逐字—— 和包含任何 MDX 节点的树 `{` mDX 所需的文本中,而一个没有字节的树会按顺序将字节与下表相同。 没有MDX拼写的形状是无法构造的,而不是序列化错误:碎片不能携带属性,属性需要非空名,拒绝在 `make` 在解码处.

## 犬形结构稳定

`Markdown.stringify` 发出一克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克 **更改其中的任何一个是此套件的破解更改而不是补丁**。因此,一个测试可能对这些字节进行断言,一个需要稳定的被转换成标记的管道应该通过这里进行序列化,而不是一个第三方字符串,其默认值可以在发布之间自由移动.

默认为节点的忠义字段( O)`bulletChar`, (中文(简体) ), `headingStyle`, (中文(简体) ), `markerChar`, (中文(简体) ), `fenceChar`, (中文(简体) ), `delimiter`)赢了,所以解析 `* a` 列表或设置ext 标题以写入形式重命名。 放下他们,通过 `{ canonical: true }` 改为 `stringify` 或者说 `stringifyResult`或称纯洁 `MarkdownTransform.canonicalize(tree)` 以获取已清除的树拷贝(输入从未变异)。 既给出相同的输出,又替换 `Mdast.toMdast` 编号 : `Mdast.fromMdastResult` 来回旅行.

对于一个没有忠贞场的节点:

| 构造 | 条形 |
| --- | --- |
| 标题 | ATX (中文(简体) )`## x`在每一个深度 |
| 专题间断 | `***` |
| 符号列表标记 | `-` |
| 排序列表分隔符 | `.`,翻转到 `)` 以分离一个相邻的兄弟姐妹列表 |
| 强调/强 | `*` 编号 : `**` |
| 代码块 | 当节点带有一个 `lang` 或一个 `fenceChar`,因为栅栏已经长出任何内部反向跑道;否则会缩入-除非凹入不会重新作为代码块被分割出,这迫使栅栏 |
| 区块分离 | 完全一个空白行 |
| 文档 | 单行后新行 |

MDX 节点是同一承诺的一部分: [MDX 树](https://npmcn.com/package/%40effected/markdown/#readme-mdx-trees) 被同一种方式钉住,只有真正带MDX节点的树才能额外支付 `{` 逃脱.

**可代表性胜于表.** 单词形式从不发出文本,这些文本会像其他东西一样重新分解,所以一行产生两个冲突的地方. 到达消费者的病例是缩进的代码块:列表被吸收为列表内容后直接缩入的块,所以一个 `Code` 没有节点 `lang` 也没有 `fenceChar` 在那个位置上排出栅栏 并缩入其它地方 因此,对合成代码块的字节级断言取决于前面的兄弟.

使这成为一个非问题的立场——如果建立树木并坚持其字节,则采取的立场是: `fenceChar` 每页 `Code` 既无带节点 `lang` 也没有一个已经,在 后解码行走 到达巢节点。 然后选择让发射器完全离开,任何输出都不要依赖节点的邻居.

须在 **已解码** 树: `Mdast.fromMdast` 承认spec mdast 并剥去它之外的一切,所以 `fenceChar` 入院前被安放在一棵平地上 唯一的例外是 `escapeStyle` 编号 `text` 节点,下一个描述.

带忠贞字段的节点会覆盖相匹配行—— `headingStyle`, (中文(简体) ), `markerChar`, (中文(简体) ), `fenceChar`, (中文(简体) ), `delimiter` —— 解析出的文件如何在作者的拼写中重新序列化. 该表描述了一个被合成的节点,这是关于生成的标记降值的测试所实际坚持的.

### 文字文本

文本逃出默认为空 : `~0.2.1` 在表格单元格中以 `\~0.2.1`A级 `Text` 带节点 `escapeStyle: "literal"` 选择退出——来电者保证该值已经是安全标记了,发送者逐字写出,没有以内线语法为目标的逃出. 该诉讼产生了依赖表等内容,其中: `~0.2.1`, (中文(简体) ), `^1.0.0` 财务报告和审定财务报表 `@scope/pkg` 改为:

```ts
import { Markdown, Mdast } from "@effected/markdown";
import { Result } from "effect";

const cell = (value: string) => ({ type: "tableCell", children: [{ type: "text", value, escapeStyle: "literal" }] });
const tree = Mdast.fromMdastResult({
  type: "root",
  children: [{ type: "table", children: [{ type: "tableRow", children: [cell("Range"), cell("~0.2.1 | ^1.0.0")] }] }],
});
if (Result.isSuccess(tree)) {
  console.log(Result.getOrThrow(Markdown.stringifyResult(tree.success)));
  // | Range | ~0.2.1 \| ^1.0.0 |
  // | --- | --- |
}
```

保护周围的逃亡 **块** 仍然适用,因为放下它们会腐蚀文档,而不是添加格式:

-   在表格单元格中,每个 `|` 还没有反弹- 逃脱变成 `\|`· ;
-   在任何容器中,一个最终值 `\` 当更多的内容跟随它时,它就会加倍,因此它无法逃脱该内容的第一个字符;
-   在表格单元格或标题中,新行变为空格;
-   在一个方向,一个线索 `#` 将读取为关闭序列的运行;
-   在其他地方,将形成一行空白的新行变为: `&#10;`,在一行起步时引导白空间成为字符参考,可以打开块的一行起步被逃脱——a `-`, (中文(简体) ), `+` 或者说 `*` 弹出或命令列表标记,然后是空格、标签或行尾,1至6 `#` 之后是同样的, `>`,3个倒推或平板的运行,一个主题断开或设置ext运行,a `---` 或者说 `+++` 前题栏杆,仅一行 `-`, (中文(简体) ), `:`, (中文(简体) ), `|` 和白空间,保守地说,任何 `<`, (中文(简体) ), `[`, (中文(简体) ), `|` 或者说 `:` - 文本不能,例如: `~0.2.1`, (中文(简体) ), `1.0.0`, (中文(简体) ), `-x` 或者说 `#x`,按原样写;
-   在树上挂着MDX节点, `{` 财务报告和审定财务报表 `<` 继续逃跑.

**以平分表示的字面值不回转**数字 : `*a*` 以逐字记录和重分部分作为重点。 保持无内入语法的值是呼叫者的承诺;发送者不检查. 解析器从未设定 `escapeStyle`,以及 `Mdast.fromMdast` 承认了 `text` 节点——跨越该边界的一个忠心场,因为它是对发件人的指示,而不是源拼音记录——所以一棵平地的mdast树可以直接携带入去. 选出是添加剂:一棵从未将它序列化的树将字节-等同于上面的犬形.

改为: _常态_ 用于不同选择、使用的现有文档 `MarkdownFormat` 与 `MarkdownFormattingOptions`. 这是可布置的地表;这是故意的.

## 在没有解析器的情况下忽略标记

一种仅能建立树木并将其序列化的捆绑——一种将内容渲染成标记的工人,比如说,从 `@effected/markdown/stringify`该入口输出节点类别, `stringify` 财务报告和审定财务报表 `stringifyResult`, (中文(简体) ), `MarkdownStringifyError` 财务报告和审定财务报表 `MarkdownDiagnostic`,而且它没有达到加载块通过,内接通过或HTML实体表,所以一个捆绑器将所有三个放出:

```ts
import { Heading, Root, Text, stringifyResult } from "@effected/markdown/stringify";
import { Result } from "effect";

const tree = Root.make({ children: [Heading.make({ depth: 2, children: [Text.make({ value: "Usage" })] })] });
console.log(Result.getOrThrow(stringifyResult(tree)));
// ## Usage
```

字节正是 `Markdown.stringify`'s——这些静态代表这些函数——根入按身份输出相同的函数和类别,因此任意导入的任意函数树摇. 将解析器放入捆绑的 是通过 `Markdown` 类: 捆绑者不能丢弃未使用的静态成员.

## 特征

-   `Markdown` —— 说 `parse`编号 :`stringify` 作为 `Effect`s 有输入 `MarkdownParseError`编号 :`MarkdownStringifyError` 频道,纯度 `parseResult`编号 :`stringifyResult` 双胞胎 同步呼叫者 `MarkdownFromString` 双向编解码器.
-   `stringify`编号 :`stringifyResult` ——作为自由函数的序列器,在根入和解析器上 `@effected/markdown/stringify` 节点类旁边的入口 .
-   `Markdown.parsePhrasing`编号 :`parsePhrasingResult` ——将散文片段分析为一段落的内行内容,没有完整的文档解析和段落相接:空白行保持内行,引用永远不形成(没有引用上下文),相对于输入字符串,位置是正确的.
-   `MarkdownDocument` 源、树、诊断和链接定义指数,加上衍生的 `headings`, (中文(简体) ), `headingSlugs`, (中文(简体) ), `sections` 财务报告和审定财务报表 `links` 访问者 `firstSection`编号 :`sectionByHeading` 发现者在一个 `DocumentSection` (标题、深度、范围、身体) `find`编号 :`findAll` 对被收缩的选取者进行树质查询, `unresolvedFootnoteReferences` 页:1 `[^label]` 没有定义的标记,以及 `frontmatter` 抓取为 `hasFrontmatterBlock` 财务报告和审定财务报表 `frontmatterFence` 告诉一个没有被抓住或没有关闭的街区.
-   Mdast-形状的节点类——普通马克类型外加GFM `delete`, (中文(简体) ), `table`, (中文(简体) ), `tableRow`, (中文(简体) ), `tableCell`, (中文(简体) ), `footnoteDefinition`, (中文(简体) ), `footnoteReference` 和任务列表 `checked`,每个携带有字节相抵的无主位置 和这个软件包的忠诚域. `position` 默认为零宽的合成哨点,所以一个替换片段在一行中构造—— `Text.make({ value: "shipped" })`。 。 。 .
-   MDX节点词汇 - `MdxJsxFlowElement`编号 :`MdxJsxTextElement` 以他们的配主发誓, `MdxFlowExpression`编号 :`MdxTextExpression` 财务报告和审定财务报表 `MdxjsEsm`,形状为 `mdast-util-mdx` 建筑和系列化合同(解析器读作没有MDX语法).
-   `FrontmatterSource` ——字符串级别前题 `split`编号 :`join` 同一封闭的栅栏语法、字节精确的边界和数值抵消,a `FrontmatterFenceState` 显示没有关闭的栅栏,没有显示价值或身体;对于身体,通用马克引擎不应触碰.
-   `MarkdownEdit` 编号 : `MarkdownRange` (与 `applyAll`) ——非修改文本编辑词汇,字段与 `@effected/jsonc`因为, `@effected/yaml`还有.. `@effected/toml`对.
-   `MarkdownFormat` —— 说 `format`编号 :`formatToString` 计算保守标记-规范化编辑; `modify`编号 :`modifyToString` 以身份取而代之的节点通过犬形字符串.
-   `MarkdownVisitor` ——走一棵被剖开的树作: `Enter`编号 :`Exit` 有儿童索引路径和深度的事件:作为懒惰者同步 `Iterable` (单位:千美元)`walk`),作为a `Stream` (单位:千美元)`visit`),或通过 `enter`编号 :`exit` 调用 `SKIP`编号 :`EXIT` 信号、祖先和受类型限制的选定者`traverse`) (中文(简体) ).
-   `Mdast` —— 说 `toMdast` (一) 向普通JSON项目; `fromMdast`编号 :`fromMdastResult` 承认一棵外国的木头树.
-   `MarkdownFrontmatter.schema` 加减 `YamlFrontmatter`, (中文(简体) ), `TomlFrontmatter` 财务报告和审定财务报表 `JsonFrontmatter` 编解码器——在可选的每个格式的同位素上键入前事解码,有四个可被接取失败模式和一个 `FrontmatterMissingReason` 在缺一个.
-   `SchemaResolver` - 分类前题 `$schema` 声明进入 `ByUrl`编号 :`ByPath`编号 :`Inline`编号 :`ByName` 并且用一个没有独立组织、没有依附关系的登记簿来决定名称.
-   `MarkdownDiagnostic` 结构化诊断`code`, (中文(简体) ), `message`, (中文(简体) ), `offset`, (中文(简体) ), `length`, (中文(简体) ), `line`, (中文(简体) ), `character`每个打出的错误都带有,形状与兄弟姐妹的包相同'.

## 遵约

652 CommonMark 0.31.2 spec 例都用空跳图运行,整体在两种方言下再次运行,并明确宣称有差异列表. GFM扩展子公司从cmark-gfm——光谱扩展段和 `extensions.txt`,唯一正式的脚注-运行完整,27平方 `mdast-util-from-markdown` 程序,它声称AST **财务报告和审定财务报表** 通过 `Mdast` 而不是仅仅匹配输出.

两次独立检查 备份这一点。 差异属性套件对照 `commonmark` npm包横跨实体加数以万计生成的文件,cmark的病理套件将线性时间的保证用校正的预算来钉住——Markdown的DoS向量是四重强调并链接起爆,而分界线-stack算法就是击败它的原因. 递归地表搭载了256个深层筑巢盖,因此筑巢炸弹通过打字错误通道失效而不会溢出堆放. 每个神谕和神职人员都只依靠依赖性;没有一个人到达你的运行时间.

## 许可证

[麻省理工学院](https://npmcn.com/package/%40effected/markdown/LICENSE)

## 常见问题

### @effected/markdown 是什么？

常见马克和GFM 标记下解析、编辑和转换效果计划

### 如何安装 @effected/markdown？

在项目目录运行 npm install @effected/markdown，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：effect（~4\.1\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @effected/markdown 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @effected/markdown 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @effected/markdown 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.11\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @effected/markdown 可以商用吗？

@effected/markdown 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @effected/markdown 的下载量有多少？

根据 npm 官方下载统计，@effected/markdown 在 2026/10/13 – 2026/11/11 这 30 天内被下载 8,942 次，日均约 298 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @effected/markdown 还在维护吗？

最新版本 0\.29\.0 发布于 2026/11/10（2 天前）；至今共发布 47 个版本，其中过去 12 个月发布了 47 个；首次发布于 2026/07/20。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

