首页
/ Documentation

Documentation

2026-09-06 12:26:51作者:齐添朝

The following block should be given a js extension on extraction:

console.log("js");

The following example contains the ignore attribute and will be ignored:

const value: Invalid = "ignored";

The following example will trigger the type-checker to fail:

const a: string = 42;

三个块的设计意图与文档注释一一对应:

1. **完整块名 `javascript`**——块注释写明“该块在提取时应被赋予 js 扩展名”。这里验证的核心点是:Deny 不要求代码块必须写短名 `js`,写全名 `javascript` 同样要被正确识别并映射到 JavaScript 媒体类型;
2. **`ignore` 属性**——带 `typescript ignore` 围栏属性的块必须被提取器直接跳过,其中的 `Invalid` 未定义类型不会进入后续类型检查;
3. **故意类型错误的块**——`const a: string = 42;` 会在类型检查阶段报错,验证 `deno test --doc` 会把 doc test 纳入类型检查,且该错误会导致进程以非零码退出。

配套的 [__test__.jsonc](https://gitcode.com/GitHub_Trending/de/deno/blob/e5575e2c0ac3c6ea5281d685bab4c33be8c8b38d/tests/specs/test/markdown_full_block_names/__test__.jsonc?utm_source=gitcode_repo_files) 声明了这条 spec 测试的运行方式:

```jsonc
{
  "args": "test --doc --allow-all main.md",
  "exitCode": 1,
  "output": "main.out"
}

即执行 deno test --doc --allow-all main.md,并断言退出码为 1——这正是由第三个块的类型错误所引发的预期失败;output 字段指向的 main.out 是用于比对 stdout 的黄金文件。值得注意的是目录名 markdown_full_block_names 本身:同一组夹具在 tests/specs/test/markdown/main.mdtests/specs/test/markdown_windows/main.md 中也存在,差异就在于块名写法的完整度,本夹具专门回归“完整块名”这一分支。

提取入口:从 deno test --docextract_doc_tests

--doc 标志的落点在 cli/tools/test/mod.rs。测试收集阶段会调用 get_doc_tests(约 L2747),它遍历目标文件并对每个文件执行:

doc_tests.extend(extract_doc_tests(file)?);

得到的 doc test 随后被 file_fetcher.insert_memory_files(doc_test) 插入为内存中的伪模块(约 L2407-L2411),与真实文件一起进入执行/检查图。extract_doc_tests 的定义在 cli/util/extract.rs:

/// Extracts doc tests from a given file, transforms them into pseudo test
/// files by wrapping the content of the doc tests in a `Deno.test` call,
/// and returns a list of the pseudo test files.
pub fn extract_doc_tests(file: File) -> Result<Vec<File>, AnyError> {
  extract_inner(file, WrapKind::DenoTest)
}

文档注释明确了两点机制:提取出的代码片段会被包装进 Deno.test 调用、形成“伪测试文件”;同时该文件还提供了 extract_snippet_files(extract.rs L49-L51)作为不包装变体——注释说明其差异在于“不包裹在 Deno.test 调用中”,且目前带 dead_code 允许标记,等待原生类型检查器支持 deno check --doc 后启用。这说明 doc test 与 snippet 提取共用同一条管线,仅 WrapKind 不同。

extract_inner 的分流逻辑决定了夹具中每个块走哪条路径(L87-L100):当文件媒体类型是 UnknownMarkdown 时走 extract_files_from_fenced_blocks(围栏块提取);其他源码文件则走 JSDoc 注释提取。本夹具是 .md 文件,因此命中前者。

围栏块解析:完整块名、ignore 与行号元数据

extract_files_from_fenced_blocks(extract.rs L121-L147)先调用 extract_markdown_fenced_blocks 扫描全文,再对每个块调用 media_type_from_fence_attributes 判定媒体类型——判定失败的块(返回 None)被 filter_map 静默丢弃,这就是 ignore 属性生效的位置。

围栏扫描(extract_markdown_fenced_blocks,L157-L227)按行处理源码,几个关键细节:

  • 围栏至少需要 3 个反引号(parse_markdown_fence_openingtick_count < 3 即返回 None,L239-L242);
  • 支持 Markdown 引用块:块行可带 > 前缀,由 strip_markdown_fence_prefix(L257-L267)剥除并记录 is_markdown_blockquote,以便后续清理正文中的引用标记;
  • 每个块记录 attributes(围栏行反引号后的内容)、bodyline_offsetline_count——这些行号元数据用于在错误信息中把问题定位回原 Markdown 的行。

块名到媒体类型的映射media_type_from_attributes(extract.rs L388-L412),它精确实现了夹具三条断言背后的规则:

fn media_type_from_attributes(
  maybe_attributes: Option<Vec<&str>>,
  fallback_media_type: MediaType,
) -> Option<MediaType> {
  let Some(attributes) = maybe_attributes else {
    return Some(fallback_media_type);
  };
  if attributes.contains(&"ignore") {
    return None;
  }

  Some(match attributes.first() {
    Some(&"js") => MediaType::JavaScript,
    Some(&"javascript") => MediaType::JavaScript,
    Some(&"mjs") => MediaType::Mjs,
    Some(&"cjs") => MediaType::Cjs,
    Some(&"jsx") => MediaType::Jsx,
    Some(&"ts") => MediaType::TypeScript,
    Some(&"typescript") => MediaType::TypeScript,
    Some(&"mts") => MediaType::Mts,
    Some(&"cts") => MediaType::Cts,
    Some(&"tsx") => MediaType::Tsx,
    _ => MediaType::Unknown,
  })
}
登录后查看全文
热门项目推荐
相关项目推荐