vitepress-allyouneed 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +8 -2
- package/dist/index.cjs +75 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -20
- package/dist/index.js.map +1 -1
- package/dist/markdown-it.cjs +67 -16
- package/dist/markdown-it.cjs.map +1 -1
- package/dist/markdown-it.js +67 -16
- package/dist/markdown-it.js.map +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.js +1 -1
- package/dist/vitepress.cjs +75 -20
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.js +75 -20
- package/dist/vitepress.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [0.5.2] - 2026-06-20
|
|
6
|
+
|
|
7
|
+
实战(90 页 DE+ZH 物理知识库,重 KaTeX / Wikilink / GFM 表格)发现的三个 build-blocker / 死链 bug,纯插件侧可修,无需改任何内容。
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **致命崩溃(同 0.5.1 那类 `state.pos`,但这次出在 `#tag` inline rule):`vitepress build` 在含 `#tag` 的 link label / 嵌套方括号 / GFM 表格单元格上整站构建失败**,报 `inline rule didn't increment state.pos`。
|
|
11
|
+
- 根因:`modules/tags/rule.ts` 的 `#tag` inline rule 注册在 `link` 之前;markdown-it 的 link 核心规则用 `parseLinkLabel → skipToken` 以 **silent 模式**扫描 `[...]` label,本规则在 silent 下 `return true` 却**没推进 `state.pos`**,触发 markdown-it 的安全检查并经 `parseLinkLabel` 对未消费的 `[` 递归。0.5.1 已修了 wikilink rule 的同款问题,但 tag rule 漏改 —— 复现文件正是报告里的 `themen/Astronomie und Exobiologie.md`(`[[#Biomarker|…]]` 被表格 `|` 拆成 `[[#Biomarker`,其中 `#Biomarker` 命中 tag rule)。
|
|
12
|
+
- 修复:silent 模式下也把 `state.pos` 推过匹配长度再 `return true`,token 产出仍门控在 `!silent`(对齐 markdown-it 自带规则)。
|
|
13
|
+
- **页内跳转锚点 `[[#heading]]` 即使 slug 精确匹配也被判死链 / 失去 `href`**(报告:仅德语主题就 74 处)。
|
|
14
|
+
- 根因:自引用 wikilink(只有 `#anchor`、无文件部分)解析时 `target` 被切成空串 → `lookupEntry('')`/`resolveSimple('')` 返回空 → 整条 wikilink 误判 dead。
|
|
15
|
+
- 修复:`core/resolver.ts` 与 `core/scan-wikilinks.ts` 双路径统一处理 —— 有 heading 部分且无文件部分时解析到**当前文件本身**,后续 heading 匹配(exact slug,各 `anchorMatch` 模式通吃)正常命中;`scan-wikilinks` 不再把自引用锚点计入死链汇总。
|
|
16
|
+
- **缺失的 embed 资源(`![[foo.gif]]` / 音视频 / PDF 指向不存在文件)把整个 Vite/Rollup 构建硬中断**,报 `Rollup failed to resolve import "/foo.gif"`。
|
|
17
|
+
- 根因:`modules/embeds/image.ts` 与 `modules/embeds/media.ts` 在 asset 解析不到时回退成绝对路径 `/{basename}`,该 URL 被 VitePress 的 Vite 插件当模块 import,Rollup 解析不到即 throw。
|
|
18
|
+
- 修复:缺失资源不再产出会被 Vite 解析的 `src`,改为按 `deadLink` 策略告警(`silent`/`warn`/`error`,`error` 推 `index.warnings` 让构建以非零退出但不中断渲染)+ 渲染一个**不触发 Vite 解析**的占位 `<span class="ayn-embed ayn-embed--missing">`,与死链行为一致,绝不 throw。
|
|
19
|
+
|
|
20
|
+
### Tests
|
|
21
|
+
- 新增 `tests/v052-bugfixes.test.ts`(Bug1 silent-pos / Bug2 自引用锚点 + scanWikilinks / Bug3 image+media 缺失占位 + `deadLink='error'` 告警)。
|
|
22
|
+
- 更新 `tests/render.test.ts`、`tests/v03-phase2.test.ts`:缺失 image/media 现在断言占位 `<span>` 而非旧的会崩 build 的 `<img>/<audio>/<iframe src="/…">`;media player 用例改指向 `fixtures/vault/media/` 下的真实占位资源。
|
|
23
|
+
- 真实 `vitepress build` 端到端验证(复现 Astronomie 嵌套链接 + 自引用锚点 + 缺失 gif/mp4/pdf)全部通过:无 `state.pos` 崩溃、零死链上报、缺失 embed 降级告警、`build complete` 退出码 0。
|
|
24
|
+
|
|
25
|
+
### Files touched
|
|
26
|
+
- `src/modules/tags/rule.ts`(silent 推进 `state.pos`)
|
|
27
|
+
- `src/core/resolver.ts`、`src/core/scan-wikilinks.ts`(自引用锚点 → 当前文件)
|
|
28
|
+
- `src/modules/embeds/image.ts`、`src/modules/embeds/media.ts`(缺失资源 → 占位 + `deadLink` 策略,不 throw)
|
|
29
|
+
|
|
30
|
+
## [0.5.1] - 2026-06-20
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **致命崩溃:wikilink 在 markdown link label / 嵌套方括号 / GFM 表格单元格里会让整个 `vitepress build`/`dev` 中止**,报错 `inline rule didn't increment state.pos`。
|
|
34
|
+
- 根因:wikilink 的 inline rule 在 **silent 模式**(markdown-it 的 `parseLinkLabel → skipToken` 会以 silent 模式逐个跑 inline rule 来扫描 `[...]` label)下直接 `return true` 而**没有推进 `state.pos`**,违反 markdown-it 契约(`parser_inline.skipToken` 在规则返回 true 但 pos 未前进时抛此错)。`[[` 的第一个 `[` 触发 link 核心规则的 label 扫描时即命中。
|
|
35
|
+
- 修复:silent 模式下也把 `state.pos` 推过闭合 `]]` 再返回 true(对齐 markdown-it 自带规则如 backtick 的做法:silent 也设 pos,仅把 token 产出门控在 `!silent`)。`![[embed]]` 同路径一并修复。
|
|
36
|
+
- 影响面:任何把 `[[...]]` 或 `![[...]]` 放进 markdown link label、嵌套 `[ ... ]`、或 GFM 表格单元格的文档,此前会整站构建失败;现在正常渲染。
|
|
37
|
+
- 新增 7 个回归测试锁定该行为。
|
|
38
|
+
|
|
5
39
|
## [0.5.0] - 2026-06-20
|
|
6
40
|
|
|
7
41
|
0.5.0 正式版。收口此前 4 个 beta(主题底层化 + `@layer`),并叠加图谱体验、UI 健壮性与文档站重做。
|
package/README.md
CHANGED
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
Forked from [`actuallysomecat/markdown-it-wikilinks-plus`](https://github.com/actuallysomecat/markdown-it-wikilinks-plus) (MIT) 并针对 VitePress 大幅重写。
|
|
6
6
|
|
|
7
|
-
## 当前版本:0.5.
|
|
7
|
+
## 当前版本:0.5.2(2026-06-20)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**实战 build-blocker 修复**(详见 [CHANGELOG](./CHANGELOG.md#052---2026-06-20)):
|
|
10
|
+
|
|
11
|
+
- `#tag` 出现在 link label / 嵌套方括号 / GFM 表格单元格时不再让 `vitepress build` 整站崩溃(`inline rule didn't increment state.pos`)。
|
|
12
|
+
- 页内自引用锚点 `[[#heading]]` 不再被误判死链 —— slug 精确匹配现在正常生成可点击 `href`。
|
|
13
|
+
- 缺失的 embed 资源(`![[foo.gif]]` / 音视频 / PDF)不再硬中断 Vite 构建,改为按 `deadLink` 策略告警 + 渲染占位标记。
|
|
14
|
+
|
|
15
|
+
### 0.5.1:主题集成大改 —— 目标:让第三方 VitePress 主题作者不需要知道本插件存在
|
|
10
16
|
|
|
11
17
|
### CSS 自动让用户赢(`@layer`)
|
|
12
18
|
|
package/dist/index.cjs
CHANGED
|
@@ -680,13 +680,11 @@ function resolveWikilink(rawTarget, index, options, kind = "page", currentSource
|
|
|
680
680
|
const wasFolderForm = /\/$/.test(target);
|
|
681
681
|
if (wasFolderForm) target = target.replace(/\/+$/, "");
|
|
682
682
|
target = stripMarkdownExt(target);
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
index
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
wasFolderForm
|
|
689
|
-
);
|
|
683
|
+
let selfEntry;
|
|
684
|
+
if (!target && headingPart && currentSourcePath) {
|
|
685
|
+
selfEntry = index.files.get(currentSourcePath);
|
|
686
|
+
}
|
|
687
|
+
const entry = selfEntry ?? lookupEntry(target, index, options, currentSourcePath, wasFolderForm);
|
|
690
688
|
if (!entry) {
|
|
691
689
|
return {
|
|
692
690
|
url: buildDeadUrl(rawTarget, options),
|
|
@@ -1038,14 +1036,14 @@ function renderImageHtml(rawTarget, aliasParts, env) {
|
|
|
1038
1036
|
const { altText, dim } = parseAltAndDim(aliasParts);
|
|
1039
1037
|
const processedTarget = options.embeds.postProcessImageTarget(rawTarget);
|
|
1040
1038
|
const { asset } = resolveAsset(processedTarget, index, options, env.currentPath);
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
env.referencedAssets?.add(asset);
|
|
1045
|
-
src = buildPlaceholderUrl(asset, options) + options.embeds.uriSuffix;
|
|
1046
|
-
} else {
|
|
1047
|
-
src = options.base + encodeURIComponent(basename(processedTarget)) + options.embeds.uriSuffix;
|
|
1039
|
+
if (!asset) {
|
|
1040
|
+
handleMissingAsset(env, processedTarget);
|
|
1041
|
+
return renderMissingImageHtml(processedTarget, options);
|
|
1048
1042
|
}
|
|
1043
|
+
let src;
|
|
1044
|
+
asset.referencedBy.add(env.currentPath ?? "<unknown>");
|
|
1045
|
+
env.referencedAssets?.add(asset);
|
|
1046
|
+
src = buildPlaceholderUrl(asset, options) + options.embeds.uriSuffix;
|
|
1049
1047
|
const finalAlt = determineAlt(altText, processedTarget, options);
|
|
1050
1048
|
const attrs = {
|
|
1051
1049
|
src,
|
|
@@ -1068,6 +1066,24 @@ function renderImageHtml(rawTarget, aliasParts, env) {
|
|
|
1068
1066
|
}
|
|
1069
1067
|
return "<img " + Object.entries(attrs).map(([k, v]) => `${escapeAttrName(k)}="${escapeHtml(v)}"`).join(" ") + " />";
|
|
1070
1068
|
}
|
|
1069
|
+
function handleMissingAsset(env, target) {
|
|
1070
|
+
const { options } = env;
|
|
1071
|
+
if (options.deadLink === "silent") return;
|
|
1072
|
+
const msg = `vitepress-allyouneed: missing image embed ![[${target}]]` + (env.currentPath ? ` (in ${env.currentPath})` : "");
|
|
1073
|
+
if (options.deadLink === "warn") {
|
|
1074
|
+
console.warn(msg);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
env.index.warnings.push({
|
|
1078
|
+
kind: "unknown",
|
|
1079
|
+
message: msg,
|
|
1080
|
+
affected: env.currentPath ? [env.currentPath] : []
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
function renderMissingImageHtml(target, _options) {
|
|
1084
|
+
const bn = basename(target);
|
|
1085
|
+
return `<span class="ayn-embed ayn-embed--missing" data-missing-src="${escapeHtml(bn)}" title="Missing image: ${escapeHtml(target)}">\u26A0 ${escapeHtml(bn)}</span>`;
|
|
1086
|
+
}
|
|
1071
1087
|
function handleImageEmbed(state, rawTarget, aliasParts, env) {
|
|
1072
1088
|
const html = renderImageHtml(rawTarget, aliasParts, env);
|
|
1073
1089
|
const token = state.push("html_inline", "", 0);
|
|
@@ -1292,14 +1308,42 @@ function resolveSrc(rawTarget, env) {
|
|
|
1292
1308
|
env.referencedAssets?.add(asset);
|
|
1293
1309
|
return buildPlaceholderUrl(asset, options) + options.embeds.uriSuffix;
|
|
1294
1310
|
}
|
|
1295
|
-
return
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
function handleMissingMedia(env, kind, rawTarget) {
|
|
1314
|
+
const { options } = env;
|
|
1315
|
+
const processedTarget = options.embeds.postProcessImageTarget(rawTarget);
|
|
1316
|
+
if (options.deadLink === "silent") return;
|
|
1317
|
+
const msg = `vitepress-allyouneed: missing ${kind} embed ![[${processedTarget}]]` + (env.currentPath ? ` (in ${env.currentPath})` : "");
|
|
1318
|
+
if (options.deadLink === "warn") {
|
|
1319
|
+
console.warn(msg);
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
env.index.warnings.push({
|
|
1323
|
+
kind: "unknown",
|
|
1324
|
+
message: msg,
|
|
1325
|
+
affected: env.currentPath ? [env.currentPath] : []
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
function renderMissingMediaHtml(rawTarget, env) {
|
|
1329
|
+
const processedTarget = env.options.embeds.postProcessImageTarget(rawTarget);
|
|
1330
|
+
const bn = basename(processedTarget);
|
|
1331
|
+
return `<span class="ayn-embed ayn-embed--missing" data-missing-src="${escapeHtml(bn)}" title="Missing media: ${escapeHtml(processedTarget)}">\u26A0 ${escapeHtml(bn)}</span>`;
|
|
1296
1332
|
}
|
|
1297
1333
|
function renderAudioHtml(rawTarget, _aliasParts, env) {
|
|
1298
1334
|
const src = resolveSrc(rawTarget, env);
|
|
1335
|
+
if (src === null) {
|
|
1336
|
+
handleMissingMedia(env, "audio", rawTarget);
|
|
1337
|
+
return renderMissingMediaHtml(rawTarget, env);
|
|
1338
|
+
}
|
|
1299
1339
|
return `<audio class="ayn-embed ayn-embed--audio" controls preload="metadata" src="${escapeHtml(src)}">Your browser does not support the audio element.</audio>`;
|
|
1300
1340
|
}
|
|
1301
1341
|
function renderVideoHtml(rawTarget, aliasParts, env) {
|
|
1302
1342
|
const src = resolveSrc(rawTarget, env);
|
|
1343
|
+
if (src === null) {
|
|
1344
|
+
handleMissingMedia(env, "video", rawTarget);
|
|
1345
|
+
return renderMissingMediaHtml(rawTarget, env);
|
|
1346
|
+
}
|
|
1303
1347
|
const dim = parseAliasDim(aliasParts);
|
|
1304
1348
|
const attrs = [
|
|
1305
1349
|
`class="ayn-embed ayn-embed--video"`,
|
|
@@ -1313,6 +1357,10 @@ function renderVideoHtml(rawTarget, aliasParts, env) {
|
|
|
1313
1357
|
}
|
|
1314
1358
|
function renderPdfHtml(rawTarget, aliasParts, env) {
|
|
1315
1359
|
const src = resolveSrc(rawTarget, env);
|
|
1360
|
+
if (src === null) {
|
|
1361
|
+
handleMissingMedia(env, "pdf", rawTarget);
|
|
1362
|
+
return renderMissingMediaHtml(rawTarget, env);
|
|
1363
|
+
}
|
|
1316
1364
|
const dim = parseAliasDim(aliasParts);
|
|
1317
1365
|
const width = dim.width !== void 0 ? `${dim.width}px` : "100%";
|
|
1318
1366
|
const height = dim.height !== void 0 ? `${dim.height}px` : "600px";
|
|
@@ -1355,7 +1403,10 @@ function makeWikilinkRule(scope) {
|
|
|
1355
1403
|
inner = src.slice(innerStart, closeIdx);
|
|
1356
1404
|
if (inner.includes("\n")) return false;
|
|
1357
1405
|
if (!inner.trim()) return false;
|
|
1358
|
-
if (silent)
|
|
1406
|
+
if (silent) {
|
|
1407
|
+
state.pos = closeIdx + 2;
|
|
1408
|
+
return true;
|
|
1409
|
+
}
|
|
1359
1410
|
const env = state.env;
|
|
1360
1411
|
if (!env || !env.index || !env.options) {
|
|
1361
1412
|
return false;
|
|
@@ -2284,7 +2335,7 @@ function renderTemplate(v) {
|
|
|
2284
2335
|
// src/core/views/generate-data.ts
|
|
2285
2336
|
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
2286
2337
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
2287
|
-
var PLUGIN_VERSION = true ? "0.5.
|
|
2338
|
+
var PLUGIN_VERSION = true ? "0.5.2" : "0.0.0-dev";
|
|
2288
2339
|
var WIKILINK_RE = /(!?)\[\[([^\]\n]+)\]\]/g;
|
|
2289
2340
|
var DEFAULT_BODY_TAG_RE = /(?:^|[\s([{,;。,;])#([\p{L}_][\p{L}\p{N}_/-]*)/gu;
|
|
2290
2341
|
function buildBodyTagRe(userPattern) {
|
|
@@ -2643,7 +2694,10 @@ function makeTagRule() {
|
|
|
2643
2694
|
const m = pattern.exec(slice);
|
|
2644
2695
|
if (!m) return false;
|
|
2645
2696
|
const tag = m[1];
|
|
2646
|
-
if (silent)
|
|
2697
|
+
if (silent) {
|
|
2698
|
+
state.pos = start + m[0].length;
|
|
2699
|
+
return true;
|
|
2700
|
+
}
|
|
2647
2701
|
if (!env.referencedTags) env.referencedTags = /* @__PURE__ */ new Set();
|
|
2648
2702
|
env.referencedTags.add(tag);
|
|
2649
2703
|
const tagsViewName = env.options?.views?.names?.tags ?? "tags";
|
|
@@ -3789,7 +3843,8 @@ function scanWikilinks(index, options) {
|
|
|
3789
3843
|
if (isAsset) continue;
|
|
3790
3844
|
}
|
|
3791
3845
|
}
|
|
3792
|
-
const
|
|
3846
|
+
const isSelfAnchor = !rawTarget && !!headingPart;
|
|
3847
|
+
const found = isSelfAnchor || resolveSimple(rawTarget, index, options, f.relativePath);
|
|
3793
3848
|
if (!found) {
|
|
3794
3849
|
dead.push({
|
|
3795
3850
|
source: f.relativePath,
|
|
@@ -3799,7 +3854,7 @@ function scanWikilinks(index, options) {
|
|
|
3799
3854
|
continue;
|
|
3800
3855
|
}
|
|
3801
3856
|
if (scanAmbig && headingPart) {
|
|
3802
|
-
const targetEntry = resolveSimpleEntry(rawTarget, index, options, f.relativePath);
|
|
3857
|
+
const targetEntry = isSelfAnchor ? f : resolveSimpleEntry(rawTarget, index, options, f.relativePath);
|
|
3803
3858
|
if (!targetEntry) continue;
|
|
3804
3859
|
const matches2 = findAmbiguousLeadingNumberMatches(targetEntry, headingPart);
|
|
3805
3860
|
if (matches2.length > 1) {
|