@riebeckite/plugin-diagnostics 0.0.17 → 0.0.18

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/README.md CHANGED
@@ -159,4 +159,4 @@ assertNoErrors(report);
159
159
 
160
160
  ## See also
161
161
 
162
- - [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
162
+ - [Plugin guide](../../../docs/docs/reference/plugin-api.en.md)
package/README_ja.md CHANGED
@@ -117,4 +117,4 @@ assertNoErrors(report);
117
117
 
118
118
  ## 関連資料
119
119
 
120
- - [プラグインシステム](../../../docs/ja/docs/reference/plugin-api.md)
120
+ - [プラグインシステム](../../../docs/docs/reference/plugin-api.md)
package/dist/bin.js CHANGED
@@ -258,7 +258,11 @@ async function scanVault(config) {
258
258
  const note = {
259
259
  relativePath: relative,
260
260
  slug,
261
- language: resolveNoteLanguage(relative, config.languages),
261
+ language: resolveNoteLanguage(
262
+ relative,
263
+ fm.values.lang,
264
+ config.languages
265
+ ),
262
266
  markdown,
263
267
  fm,
264
268
  headings: extractHeadings(markdown),
@@ -417,11 +421,23 @@ function fragmentExists(note, fragment) {
417
421
  function normalizeMarkdown(markdown) {
418
422
  return markdown.replace(/\r\n/g, "\n");
419
423
  }
420
- function resolveNoteLanguage(relativePath, languages) {
424
+ function resolveNoteLanguage(relativePath, frontmatterLang, languages) {
421
425
  if (!languages || languages.length === 0) return void 0;
422
- const segment = relativePath.split("/")[0]?.toLowerCase();
423
- if (!segment) return void 0;
424
- return languages.find((language) => language.toLowerCase() === segment);
426
+ if (typeof frontmatterLang === "string" && frontmatterLang.trim()) {
427
+ const value = frontmatterLang.trim().toLowerCase();
428
+ const match = languages.find(
429
+ (language) => language.toLowerCase() === value
430
+ );
431
+ if (match) return match;
432
+ }
433
+ const filename = relativePath.split("/").at(-1)?.replace(/\.md$/i, "") ?? "";
434
+ const lower = filename.toLowerCase();
435
+ for (const language of languages) {
436
+ const tag = language.toLowerCase();
437
+ if (lower.endsWith(`.${tag}`) || lower.endsWith(`-${tag}`) || lower.endsWith(`_${tag}`))
438
+ return language;
439
+ }
440
+ return void 0;
425
441
  }
426
442
  function getExtension(filePath) {
427
443
  const lastSegment = filePath.split("/").pop() ?? filePath;
package/dist/index.js CHANGED
@@ -254,7 +254,11 @@ async function scanVault(config) {
254
254
  const note = {
255
255
  relativePath: relative,
256
256
  slug,
257
- language: resolveNoteLanguage(relative, config.languages),
257
+ language: resolveNoteLanguage(
258
+ relative,
259
+ fm.values.lang,
260
+ config.languages
261
+ ),
258
262
  markdown,
259
263
  fm,
260
264
  headings: extractHeadings(markdown),
@@ -413,11 +417,23 @@ function fragmentExists(note, fragment) {
413
417
  function normalizeMarkdown(markdown) {
414
418
  return markdown.replace(/\r\n/g, "\n");
415
419
  }
416
- function resolveNoteLanguage(relativePath, languages) {
420
+ function resolveNoteLanguage(relativePath, frontmatterLang, languages) {
417
421
  if (!languages || languages.length === 0) return void 0;
418
- const segment = relativePath.split("/")[0]?.toLowerCase();
419
- if (!segment) return void 0;
420
- return languages.find((language) => language.toLowerCase() === segment);
422
+ if (typeof frontmatterLang === "string" && frontmatterLang.trim()) {
423
+ const value = frontmatterLang.trim().toLowerCase();
424
+ const match = languages.find(
425
+ (language) => language.toLowerCase() === value
426
+ );
427
+ if (match) return match;
428
+ }
429
+ const filename = relativePath.split("/").at(-1)?.replace(/\.md$/i, "") ?? "";
430
+ const lower = filename.toLowerCase();
431
+ for (const language of languages) {
432
+ const tag = language.toLowerCase();
433
+ if (lower.endsWith(`.${tag}`) || lower.endsWith(`-${tag}`) || lower.endsWith(`_${tag}`))
434
+ return language;
435
+ }
436
+ return void 0;
421
437
  }
422
438
  function getExtension(filePath) {
423
439
  const lastSegment = filePath.split("/").pop() ?? filePath;
@@ -991,6 +1007,7 @@ var PLUGIN_NAME2 = "diagnostics";
991
1007
  var DEFAULT_SEVERITY2 = {
992
1008
  "content-integrity:broken-link": "warning",
993
1009
  "content-integrity:unresolved-wikilink": "warning",
1010
+ "content-integrity:ambiguous-wikilink": "warning",
994
1011
  "content-integrity:broken-asset": "warning",
995
1012
  "content-integrity:duplicate-public-location": "error",
996
1013
  "content-integrity:redirect-target-missing": "warning",
@@ -1006,8 +1023,18 @@ function checkSiteIntegrity(manifest, options = {}) {
1006
1023
  const routeOwners = buildRouteOwners(manifest, diagnostics2, options);
1007
1024
  const publicRoutes = new Set(routeOwners.keys());
1008
1025
  const assetPaths = buildAssetPaths(manifest);
1026
+ const noteSlugs = new Set(manifest.entries.map((entry) => entry.slug));
1027
+ const ambiguities = manifest.contentIndexAmbiguities ?? /* @__PURE__ */ new Map();
1009
1028
  for (const entry of manifest.publicEntries) {
1010
- checkResolvedLinks(entry, publicSlugs, assetPaths, diagnostics2, options);
1029
+ checkResolvedLinks(
1030
+ entry,
1031
+ publicSlugs,
1032
+ assetPaths,
1033
+ noteSlugs,
1034
+ ambiguities,
1035
+ diagnostics2,
1036
+ options
1037
+ );
1011
1038
  checkHtmlReferences(entry, publicRoutes, assetPaths, diagnostics2, options);
1012
1039
  }
1013
1040
  checkRedirectTargets(manifest, diagnostics2, options);
@@ -1070,9 +1097,24 @@ function reserveRoute(owners, diagnostics2, options, next) {
1070
1097
  })
1071
1098
  );
1072
1099
  }
1073
- function checkResolvedLinks(entry, publicSlugs, assetPaths, diagnostics2, options) {
1100
+ function checkResolvedLinks(entry, publicSlugs, assetPaths, noteSlugs, ambiguities, diagnostics2, options) {
1074
1101
  for (const link of entry.links) {
1075
1102
  if (link.kind === "unresolved") {
1103
+ const ambiguity = ambiguities.get(link.raw.toLowerCase());
1104
+ if (ambiguity && ambiguity.length > 0) {
1105
+ diagnostics2.push(
1106
+ createAmbiguousWikilinkDiagnostic(
1107
+ entry,
1108
+ link.raw,
1109
+ link.embed,
1110
+ ambiguity,
1111
+ noteSlugs,
1112
+ publicSlugs,
1113
+ options
1114
+ )
1115
+ );
1116
+ continue;
1117
+ }
1076
1118
  diagnostics2.push(
1077
1119
  diagnostic(options, {
1078
1120
  code: "content-integrity:unresolved-wikilink",
@@ -1118,6 +1160,37 @@ function checkResolvedLinks(entry, publicSlugs, assetPaths, diagnostics2, option
1118
1160
  }
1119
1161
  }
1120
1162
  }
1163
+ function createAmbiguousWikilinkDiagnostic(entry, raw, embed, candidates, noteSlugs, publicSlugs, options) {
1164
+ const visible = [];
1165
+ let unpublishedCount = 0;
1166
+ for (const candidate of candidates) {
1167
+ if (!noteSlugs.has(candidate)) {
1168
+ visible.push(candidate);
1169
+ continue;
1170
+ }
1171
+ if (publicSlugs.has(candidate)) visible.push(candidate);
1172
+ else unpublishedCount += 1;
1173
+ }
1174
+ const described = [...visible];
1175
+ if (unpublishedCount > 0)
1176
+ described.push(`${unpublishedCount} unpublished candidate(s)`);
1177
+ const first = visible[0];
1178
+ const suggestion = first ? `Use an explicit path such as [[${first}]].` : "Rename one of the duplicate targets or reference it by a unique path.";
1179
+ return diagnostic(options, {
1180
+ code: "content-integrity:ambiguous-wikilink",
1181
+ slug: entry.slug,
1182
+ filePath: `${entry.slug}.md`,
1183
+ target: raw,
1184
+ message: `WikiLink target "${raw}" matches multiple candidates: ${described.join(", ")}.`,
1185
+ suggestion,
1186
+ meta: {
1187
+ raw,
1188
+ embed,
1189
+ candidates: visible,
1190
+ ...unpublishedCount > 0 ? { unpublishedCandidateCount: unpublishedCount } : {}
1191
+ }
1192
+ });
1193
+ }
1121
1194
  function checkHtmlReferences(entry, publicRoutes, assetPaths, diagnostics2, options) {
1122
1195
  for (const href of extractAttributes(entry.html, "href")) {
1123
1196
  if (!isSiteLocalReference(href)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-diagnostics",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "remark-parse": "^11.0.0",
21
21
  "unified": "^11.0.5",
22
22
  "unist-util-visit": "^5.1.0",
23
- "@riebeckite/core": "0.0.17"
23
+ "@riebeckite/core": "0.0.18"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/mdast": "^4.0.4",