@diplodoc/yfmlint 1.3.5 → 1.6.0

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/build/config.d.ts CHANGED
@@ -15,5 +15,6 @@ declare const _default: {
15
15
  YFM009: LogLevels;
16
16
  YFM010: LogLevels;
17
17
  YFM011: LogLevels;
18
+ YFM018: LogLevels;
18
19
  };
19
20
  export default _default;
package/build/index.js CHANGED
@@ -21337,6 +21337,9 @@ function log(errors, logger) {
21337
21337
  case "warn" /* WARN */:
21338
21338
  logger.warn(message);
21339
21339
  break;
21340
+ case "info" /* INFO */:
21341
+ logger.info(message);
21342
+ break;
21340
21343
  case "disabled" /* DISABLED */:
21341
21344
  default:
21342
21345
  break;
@@ -21367,12 +21370,14 @@ var config_default = {
21367
21370
  // Term used without definition
21368
21371
  YFM008: "warn" /* WARN */,
21369
21372
  // Term inside definition not allowed
21370
- YFM009: "warn" /* WARN */,
21373
+ YFM009: "error" /* ERROR */,
21371
21374
  // Term definition used not at the end of file
21372
21375
  YFM010: "warn" /* WARN */,
21373
21376
  // Autotitle anchor is missed
21374
- YFM011: "warn" /* WARN */
21377
+ YFM011: "warn" /* WARN */,
21375
21378
  // Max svg size
21379
+ YFM018: "info" /* INFO */
21380
+ // Term definition from include
21376
21381
  };
21377
21382
 
21378
21383
  // src/rules/index.ts
@@ -21388,7 +21393,8 @@ __export(rules_exports, {
21388
21393
  yfm008: () => yfm008,
21389
21394
  yfm009: () => yfm009,
21390
21395
  yfm010: () => yfm010,
21391
- yfm011: () => yfm011
21396
+ yfm011: () => yfm011,
21397
+ yfm018: () => yfm018
21392
21398
  });
21393
21399
 
21394
21400
  // src/rules/yfm001.ts
@@ -21490,6 +21496,27 @@ function validateLineNumberAndGetFilePath(params2, rawLineNumber) {
21490
21496
  const filePath = (env == null ? void 0 : env.path) || params2.name;
21491
21497
  return { lineNumber, filePath };
21492
21498
  }
21499
+ function resolveIncludeSource(params2, rawLineNumber, token, parentToken) {
21500
+ var _a, _b, _c, _d;
21501
+ const sourceFile = ((_a = token == null ? void 0 : token.meta) == null ? void 0 : _a.sourceFile) || ((_b = parentToken == null ? void 0 : parentToken.meta) == null ? void 0 : _b.sourceFile);
21502
+ const includeChain = ((_c = token == null ? void 0 : token.meta) == null ? void 0 : _c.includeChain) || ((_d = parentToken == null ? void 0 : parentToken.meta) == null ? void 0 : _d.includeChain);
21503
+ if (!sourceFile || !(includeChain == null ? void 0 : includeChain.length)) {
21504
+ return null;
21505
+ }
21506
+ const mainFileLine = includeChain[0].line;
21507
+ const lineNumber = Math.min(Math.max(1, mainFileLine), params2.lines.length);
21508
+ return {
21509
+ lineNumber,
21510
+ sourceFile,
21511
+ sourceLineNumber: rawLineNumber || 1,
21512
+ includeChain
21513
+ };
21514
+ }
21515
+ function formatIncludeChain(info, brokenTarget) {
21516
+ const parts = info.includeChain.map((entry) => `${entry.file}:${entry.line}`);
21517
+ parts.push(`${info.sourceFile}:${info.sourceLineNumber}`);
21518
+ return `${parts.join(" \u2192 ")} \u219B ${brokenTarget}`;
21519
+ }
21493
21520
  function createContextWithFileInfo(baseContext, filePath, paramsName) {
21494
21521
  const parts = [baseContext];
21495
21522
  if (filePath !== paramsName) {
@@ -21507,19 +21534,28 @@ function findLinksInInlineTokens(params2, ruleName, onError, handler) {
21507
21534
  handler(linkToken, inline);
21508
21535
  } else {
21509
21536
  const rawLineNumber = link.lineNumber || inline.lineNumber;
21510
- const { lineNumber, filePath } = validateLineNumberAndGetFilePath(
21537
+ const includeSource = resolveIncludeSource(
21511
21538
  params2,
21512
- rawLineNumber
21513
- );
21514
- const context = createContextWithFileInfo(
21515
- link.line || inline.line,
21516
- filePath,
21517
- params2.name
21539
+ rawLineNumber,
21540
+ link,
21541
+ inline
21518
21542
  );
21519
- onError({
21520
- lineNumber,
21521
- context
21522
- });
21543
+ if (includeSource) {
21544
+ const href = linkToken.attrGet("href") || "";
21545
+ const context = formatIncludeChain(includeSource, href);
21546
+ onError({ lineNumber: includeSource.lineNumber, context });
21547
+ } else {
21548
+ const { lineNumber, filePath } = validateLineNumberAndGetFilePath(
21549
+ params2,
21550
+ rawLineNumber
21551
+ );
21552
+ const context = createContextWithFileInfo(
21553
+ link.line || inline.line,
21554
+ filePath,
21555
+ params2.name
21556
+ );
21557
+ onError({ lineNumber, context });
21558
+ }
21523
21559
  }
21524
21560
  }
21525
21561
  });
@@ -21535,19 +21571,28 @@ function findImagesInInlineTokens(params2, ruleName, onError, handler) {
21535
21571
  handler(imageToken, inline);
21536
21572
  } else {
21537
21573
  const rawLineNumber = image.lineNumber || inline.lineNumber;
21538
- const { lineNumber, filePath } = validateLineNumberAndGetFilePath(
21574
+ const includeSource = resolveIncludeSource(
21539
21575
  params2,
21540
- rawLineNumber
21541
- );
21542
- const context = createContextWithFileInfo(
21543
- image.line || inline.line,
21544
- filePath,
21545
- params2.name
21576
+ rawLineNumber,
21577
+ image,
21578
+ inline
21546
21579
  );
21547
- onError({
21548
- lineNumber,
21549
- context
21550
- });
21580
+ if (includeSource) {
21581
+ const src = imageToken.attrGet("src") || "";
21582
+ const context = formatIncludeChain(includeSource, src);
21583
+ onError({ lineNumber: includeSource.lineNumber, context });
21584
+ } else {
21585
+ const { lineNumber, filePath } = validateLineNumberAndGetFilePath(
21586
+ params2,
21587
+ rawLineNumber
21588
+ );
21589
+ const context = createContextWithFileInfo(
21590
+ image.line || inline.line,
21591
+ filePath,
21592
+ params2.name
21593
+ );
21594
+ onError({ lineNumber, context });
21595
+ }
21551
21596
  }
21552
21597
  }
21553
21598
  });
@@ -21605,21 +21650,26 @@ var yfm003 = {
21605
21650
  if (reason) {
21606
21651
  const reasonDescription = typeof reason === "string" && REASON_DESCRIPTION[reason] ? `Reason: ${REASON_DESCRIPTION[reason]}` : "";
21607
21652
  const rawLineNumber = linkToken.lineNumber || inline.lineNumber;
21608
- const { lineNumber, filePath } = validateLineNumberAndGetFilePath(
21653
+ const href = linkToken.attrGet("href") || "";
21654
+ const includeSource = resolveIncludeSource(
21609
21655
  params2,
21610
- rawLineNumber
21656
+ rawLineNumber,
21657
+ linkToken,
21658
+ inline
21611
21659
  );
21612
- const baseContext = [
21613
- `Unreachable link: "${linkToken.attrGet("href")}"`,
21614
- reasonDescription,
21615
- `Line: ${rawLineNumber}`
21616
- // Show original line number for debugging
21617
- ].filter(Boolean).join("; ");
21618
- const context = createContextWithFileInfo(baseContext, filePath, params2.name);
21619
- onError({
21620
- lineNumber,
21621
- context
21622
- });
21660
+ if (includeSource) {
21661
+ const chain = formatIncludeChain(includeSource, href);
21662
+ const context = [chain, reasonDescription].filter(Boolean).join("; ");
21663
+ onError({ lineNumber: includeSource.lineNumber, context });
21664
+ } else {
21665
+ const { lineNumber } = validateLineNumberAndGetFilePath(params2, rawLineNumber);
21666
+ const context = [
21667
+ `Unreachable link: "${href}"`,
21668
+ reasonDescription,
21669
+ `Line: ${rawLineNumber}`
21670
+ ].filter(Boolean).join("; ");
21671
+ onError({ lineNumber, context });
21672
+ }
21623
21673
  }
21624
21674
  });
21625
21675
  }
@@ -21722,6 +21772,56 @@ var yfm008 = {
21722
21772
  };
21723
21773
 
21724
21774
  // src/rules/yfm009.ts
21775
+ function isFromInclude(token) {
21776
+ return token.attrGet("from-include") === "true";
21777
+ }
21778
+ function isLintMarker(token) {
21779
+ return token.type === "__yfm_lint";
21780
+ }
21781
+ function findMatchingDfnOpen(tokens, closeIndex) {
21782
+ let depth = 0;
21783
+ for (let j = closeIndex; j >= 0; j--) {
21784
+ if (tokens[j].type === "dfn_close") depth++;
21785
+ if (tokens[j].type === "dfn_open") {
21786
+ depth--;
21787
+ if (depth === 0) return j;
21788
+ }
21789
+ }
21790
+ return -1;
21791
+ }
21792
+ function skipNonContent(tokens, from) {
21793
+ let pos = from;
21794
+ while (pos < tokens.length) {
21795
+ if (isLintMarker(tokens[pos])) {
21796
+ pos++;
21797
+ continue;
21798
+ }
21799
+ if (tokens[pos].type === "dfn_open" && isFromInclude(tokens[pos])) {
21800
+ pos = skipDfnPair(tokens, pos);
21801
+ continue;
21802
+ }
21803
+ break;
21804
+ }
21805
+ return pos;
21806
+ }
21807
+ function skipDfnPair(tokens, from) {
21808
+ let depth = 1;
21809
+ let pos = from + 1;
21810
+ while (pos < tokens.length && depth > 0) {
21811
+ if (tokens[pos].type === "dfn_open") depth++;
21812
+ if (tokens[pos].type === "dfn_close") depth--;
21813
+ pos++;
21814
+ }
21815
+ return pos;
21816
+ }
21817
+ function isLocalDfnClose(tokens, index) {
21818
+ if (tokens[index].type !== "dfn_close") return false;
21819
+ const openIndex = findMatchingDfnOpen(tokens, index);
21820
+ return openIndex < 0 || !isFromInclude(tokens[openIndex]);
21821
+ }
21822
+ function hasLocalDfnOpenAt(tokens, index) {
21823
+ return index < tokens.length && tokens[index].type === "dfn_open" && !isFromInclude(tokens[index]);
21824
+ }
21725
21825
  var yfm009 = {
21726
21826
  names: ["YFM009", "no-term-definition-in-content"],
21727
21827
  description: "Term definition should be placed at the end of file",
@@ -21733,23 +21833,19 @@ var yfm009 = {
21733
21833
  if (!config) {
21734
21834
  return;
21735
21835
  }
21736
- let lastCloseIndex = -1;
21737
21836
  const tokens = params2.parsers.markdownit.tokens;
21738
21837
  const size = tokens.length;
21838
+ let lastLocalCloseIndex = -1;
21739
21839
  for (let i = 0; i < size; i++) {
21740
- if (tokens[i].type === "dfn_close") {
21741
- lastCloseIndex = i;
21742
- }
21743
- if (tokens[i].type !== "dfn_close") {
21840
+ if (!isLocalDfnClose(tokens, i)) {
21744
21841
  continue;
21745
21842
  }
21843
+ lastLocalCloseIndex = i;
21746
21844
  if (i === size - 1) {
21747
21845
  continue;
21748
21846
  }
21749
- if (tokens[i + 1].type === "dfn_open") {
21750
- continue;
21751
- }
21752
- if (i < size - 2 && tokens[i + 2].type === "dfn_open") {
21847
+ const nextReal = skipNonContent(tokens, i + 1);
21848
+ if (nextReal >= size || hasLocalDfnOpenAt(tokens, nextReal)) {
21753
21849
  continue;
21754
21850
  }
21755
21851
  onError({
@@ -21757,12 +21853,13 @@ var yfm009 = {
21757
21853
  detail: "There is a content between term definition. All term defitions should be placed at the end of file."
21758
21854
  });
21759
21855
  }
21760
- if (lastCloseIndex === -1) {
21856
+ if (lastLocalCloseIndex === -1) {
21761
21857
  return;
21762
21858
  }
21763
- if (lastCloseIndex !== size - 1) {
21859
+ const tailPos = skipNonContent(tokens, lastLocalCloseIndex + 1);
21860
+ if (tailPos < size) {
21764
21861
  onError({
21765
- lineNumber: ((_b = tokens[lastCloseIndex + 1]) == null ? void 0 : _b.lineNumber) || tokens[lastCloseIndex].lineNumber || 1,
21862
+ lineNumber: ((_b = tokens[lastLocalCloseIndex + 1]) == null ? void 0 : _b.lineNumber) || tokens[lastLocalCloseIndex].lineNumber || 1,
21766
21863
  detail: "The file must end with term only."
21767
21864
  });
21768
21865
  }
@@ -21781,15 +21878,22 @@ var yfm010 = {
21781
21878
  return;
21782
21879
  }
21783
21880
  findLinksInInlineTokens(params2, "YFM010", onError, (linkToken, inline) => {
21784
- const autotitleAnchorError = `[Unreachable autotitle anchor: "${linkToken.attrGet("href")}"]`;
21881
+ const href = linkToken.attrGet("href") || "";
21785
21882
  const rawLineNumber = linkToken.lineNumber || inline.lineNumber;
21786
- const { lineNumber, filePath } = validateLineNumberAndGetFilePath(params2, rawLineNumber);
21787
- const baseContext = [autotitleAnchorError, linkToken.line || ""].join(" ");
21788
- const context = createContextWithFileInfo(baseContext, filePath, params2.name);
21789
- onError({
21790
- lineNumber,
21791
- context
21792
- });
21883
+ const includeSource = resolveIncludeSource(
21884
+ params2,
21885
+ rawLineNumber,
21886
+ linkToken,
21887
+ inline
21888
+ );
21889
+ if (includeSource) {
21890
+ const context = formatIncludeChain(includeSource, href);
21891
+ onError({ lineNumber: includeSource.lineNumber, context });
21892
+ } else {
21893
+ const { lineNumber } = validateLineNumberAndGetFilePath(params2, rawLineNumber);
21894
+ const context = `Unreachable autotitle anchor: "${href}"; Line: ${rawLineNumber}`;
21895
+ onError({ lineNumber, context });
21896
+ }
21793
21897
  });
21794
21898
  }
21795
21899
  };
@@ -21815,6 +21919,34 @@ var yfm011 = {
21815
21919
  }
21816
21920
  };
21817
21921
 
21922
+ // src/rules/yfm018.ts
21923
+ var yfm018 = {
21924
+ names: ["YFM018", "term-definition-from-include"],
21925
+ description: "Term definition comes from an included file",
21926
+ tags: ["term"],
21927
+ parser: "markdownit",
21928
+ function: function YFM018(params2, onError) {
21929
+ const { config } = params2;
21930
+ if (!config) {
21931
+ return;
21932
+ }
21933
+ const tokens = params2.parsers.markdownit.tokens;
21934
+ const size = tokens.length;
21935
+ for (let i = 0; i < size; i++) {
21936
+ if (tokens[i].type !== "dfn_open") {
21937
+ continue;
21938
+ }
21939
+ if (tokens[i].attrGet("from-include") !== "true") {
21940
+ continue;
21941
+ }
21942
+ onError({
21943
+ lineNumber: tokens[i].lineNumber || 1,
21944
+ detail: "Term definition is provided via include. Consider moving it to the end of the page or defining the term locally."
21945
+ });
21946
+ }
21947
+ }
21948
+ };
21949
+
21818
21950
  // src/index.ts
21819
21951
  async function yfmlint(content3, path3, opts) {
21820
21952
  const { lint } = await Promise.resolve().then(() => (init_exports_promise(), exports_promise_exports));