fix: `processIncludes` no longer swallows errors

**BREAKING CHANGE:**
The previous `<!-- @include: ./path/to/file -->` syntax silently ignored errors when files did not exist. This behavior was originally intended as an escape hatch while documenting includes, but better solutions now exist using Shiki transformers.

For most users, no code changes are required. If you now see errors, it means your includes are broken and were previously not being reported.

Users who intentionally reference non-existent files or want to document includes without resolving them can configure `markdown.codeTransformers` with a `postprocess` hook. See `docs/.vitepress/config.ts` in this repo for an example.
pull/5113/head
Divyansh Singh 7 months ago
parent 5e12ef7f13
commit fb21fdf675

@ -26,10 +26,13 @@ export default defineConfig({
markdown: {
math: true,
codeTransformers: [
// We use `[!!code` in demo to prevent transformation, here we revert it back.
// We use `[!!code` and `@@include` in demo to prevent transformation,
// here we revert it back.
{
postprocess(code) {
return code.replace(/\[\!\!code/g, '[!code')
return code
.replace(/\[\!\!code/g, '[!code')
.replace('@@include', '@include')
}
}
],

@ -793,7 +793,7 @@ For example, you can include a relative markdown file using this:
## Basics
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Part file** (`parts/basics.md`)
@ -829,7 +829,7 @@ It also supports selecting a line range:
## Basics
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Part file** (`parts/basics.md`)
@ -865,8 +865,8 @@ You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/co
## Basics
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**Part file** (`parts/basics.md`)
@ -917,7 +917,7 @@ You can include the `My Base Section` section like this:
```md
## My Extended Section
<!--@include: ./parts/basics.md#my-base-section-->
<!--@@include: ./parts/basics.md#my-base-section-->
```
**Equivalent code**
@ -941,7 +941,7 @@ Here, `my-base-section` is the generated id of the heading element. In case it's
and include it like this:
```md
<!--@include: ./parts/basics.md#custom-id-->
<!--@@include: ./parts/basics.md#custom-id-->
```
## Math Equations

@ -773,7 +773,7 @@ Por ejemplo, puede incluir un archivo markdown relativo usando esto:
## Conceptos Básicos
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Archivo de Parte** (`parts/basics.md`)
@ -809,7 +809,7 @@ También soporta la selección de un intervalo de lineas:
## Conceptos Básicos
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Archivo de Parte** (`parts/basics.md`)

@ -724,7 +724,7 @@ export default config
## مبانی
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**قسمت فایل** (`parts/basics.md`)
@ -760,7 +760,7 @@ export default config
## مبانی
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**قسمت فایل** (`parts/basics.md`)
@ -796,8 +796,8 @@ export default config
## مبانی
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**قسمت فایل** (`parts/basics.md`)

@ -794,7 +794,7 @@ Markdown パスの先頭に `@` を付けることもでき、その場合はソ
## 基本
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**パートファイル**`parts/basics.md`
@ -830,7 +830,7 @@ Markdown パスの先頭に `@` を付けることもでき、その場合はソ
## 基本
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**パートファイル**`parts/basics.md`
@ -866,8 +866,8 @@ Markdown パスの先頭に `@` を付けることもでき、その場合はソ
## 基本
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**パートファイル**`parts/basics.md`
@ -919,7 +919,7 @@ VS Code のリージョンの代わりに、ヘッダーアンカーを使って
```md
## 拡張セクション
<!--@include: ./parts/basics.md#my-base-section-->
<!--@@include: ./parts/basics.md#my-base-section-->
```
**等価なコード**
@ -943,7 +943,7 @@ VS Code のリージョンの代わりに、ヘッダーアンカーを使って
そして次のように取り込みます:
```md
<!--@include: ./parts/basics.md#custom-id-->
<!--@@include: ./parts/basics.md#custom-id-->
```
## 数式 {#math-equations}

@ -771,7 +771,7 @@ export default config
## Basics
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**해당 파일** (`parts/basics.md`)
@ -807,7 +807,7 @@ Can be created using `.foorc.json`.
## Basics
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**해당 파일** (`parts/basics.md`)
@ -843,8 +843,8 @@ Can be created using `.foorc.json`.
## Basics
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**해당 파일** (`parts/basics.md`)

@ -771,7 +771,7 @@ Por exemplo, você pode incluir um arquivo markdown relativo usando isto:
## Conceitos Básicos
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Arquivo da Parte** (`parts/basics.md`)
@ -807,7 +807,7 @@ Também suporta a seleção de um intervalo de linhas:
## Conceitos Básicos
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Arquivo da Parte** (`parts/basics.md`)

@ -795,7 +795,7 @@ export default config
## Основы
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Файл части** (`parts/basics.md`)
@ -831,7 +831,7 @@ export default config
## Основы
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Файл части** (`parts/basics.md`)
@ -867,8 +867,8 @@ export default config
## Основы
<!--@include: ./parts/basics.md#basic-usage{,2}-->
<!--@include: ./parts/basics.md#basic-usage{5,}-->
<!--@@include: ./parts/basics.md#basic-usage{,2}-->
<!--@@include: ./parts/basics.md#basic-usage{5,}-->
```
**Часть файла** (`parts/basics.md`)
@ -919,7 +919,7 @@ export default config
```md
## Мой дополнительный раздел
<!--@include: ./parts/basics.md#мои-основнои-раздел-->
<!--@@include: ./parts/basics.md#мои-основнои-раздел-->
```
**Соответствующий код**
@ -943,7 +943,7 @@ export default config
и включить его следующим образом:
```md
<!--@include: ./parts/basics.md#custom-id-->
<!--@@include: ./parts/basics.md#custom-id-->
```
## Математические уравнения {#math-equations}

@ -771,7 +771,7 @@ export default config
## Basics
<!--@include: ./parts/basics.md-->
<!--@@include: ./parts/basics.md-->
```
**Part file** (`parts/basics.md`)
@ -807,7 +807,7 @@ Can be created using `.foorc.json`.
## Basics
<!--@include: ./parts/basics.md{3,}-->
<!--@@include: ./parts/basics.md{3,}-->
```
**Part file** (`parts/basics.md`)

@ -1,8 +1,7 @@
import fs from 'fs-extra'
import matter from 'gray-matter'
import type { MarkdownItAsync } from 'markdown-it-async'
import fs from 'node:fs'
import path from 'node:path'
import c from 'picocolors'
import { findRegion } from '../markdown/plugins/snippet'
import { slash, type MarkdownEnv } from '../shared'
@ -33,78 +32,70 @@ export function processIncludes(
const atPresent = m1[0] === '@'
try {
const includePath = atPresent
? path.join(srcDir, m1.slice(m1[1] === '/' ? 2 : 1))
: path.join(path.dirname(file), m1)
let content = fs.readFileSync(includePath, 'utf-8')
const includePath = atPresent
? path.join(srcDir, m1.slice(m1[1] === '/' ? 2 : 1))
: path.join(path.dirname(file), m1)
if (region) {
const [regionName] = region
const lines = content.split(/\r?\n/)
let { start, end } = findRegion(lines, regionName.slice(1)) ?? {}
let content = fs.readFileSync(includePath, 'utf-8')
if (start === undefined) {
// region not found, it might be a header
const tokens = md
.parse(content, {
path: includePath,
relativePath: slash(path.relative(srcDir, includePath)),
cleanUrls
} satisfies MarkdownEnv)
.filter((t) => t.type === 'heading_open' && t.map)
const idx = tokens.findIndex(
(t) => t.attrGet('id') === regionName.slice(1)
)
const token = tokens[idx]
if (token) {
start = token.map![1]
const level = parseInt(token.tag.slice(1))
for (let i = idx + 1; i < tokens.length; i++) {
if (parseInt(tokens[i].tag.slice(1)) <= level) {
end = tokens[i].map![0]
break
}
if (region) {
const [regionName] = region
const lines = content.split(/\r?\n/)
let { start, end } = findRegion(lines, regionName.slice(1)) ?? {}
if (start === undefined) {
// region not found, it might be a header
const tokens = md
.parse(content, {
path: includePath,
relativePath: slash(path.relative(srcDir, includePath)),
cleanUrls
} satisfies MarkdownEnv)
.filter((t) => t.type === 'heading_open' && t.map)
const idx = tokens.findIndex(
(t) => t.attrGet('id') === regionName.slice(1)
)
const token = tokens[idx]
if (token) {
start = token.map![1]
const level = parseInt(token.tag.slice(1))
for (let i = idx + 1; i < tokens.length; i++) {
if (parseInt(tokens[i].tag.slice(1)) <= level) {
end = tokens[i].map![0]
break
}
}
}
content = lines.slice(start, end).join('\n')
}
if (range) {
const [, startLine, endLine] = range
const lines = content.split(/\r?\n/)
content = lines
.slice(
startLine ? parseInt(startLine) - 1 : undefined,
endLine ? parseInt(endLine) : undefined
)
.join('\n')
}
content = lines.slice(start, end).join('\n')
}
if (!hasMeta && path.extname(includePath) === '.md') {
content = matter(content).content
}
if (range) {
const [, startLine, endLine] = range
const lines = content.split(/\r?\n/)
content = lines
.slice(
startLine ? parseInt(startLine) - 1 : undefined,
endLine ? parseInt(endLine) : undefined
)
.join('\n')
}
includes.push(slash(includePath))
// recursively process includes in the content
return processIncludes(
md,
srcDir,
content,
includePath,
includes,
cleanUrls
)
if (!hasMeta && path.extname(includePath) === '.md') {
content = matter(content).content
}
//
} catch (error) {
if (process.env.DEBUG) {
process.stderr.write(c.yellow(`\nInclude file not found: ${m1}`))
}
includes.push(slash(includePath))
return m // silently ignore error if file is not present
}
// recursively process includes in the content
return processIncludes(
md,
srcDir,
content,
includePath,
includes,
cleanUrls
)
})
}

Loading…
Cancel
Save