@mintlify/link-rot 3.0.892 → 3.0.894

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/dist/graph.d.ts CHANGED
@@ -43,6 +43,7 @@ export declare class Node {
43
43
  relativeDir: string;
44
44
  filename: string;
45
45
  edges: Edge[];
46
+ headingSlugs: Set<string> | null;
46
47
  constructor(label: string, paths?: MdxPath[]);
47
48
  toString(): string;
48
49
  equals(other: Node): boolean;
@@ -83,6 +84,7 @@ export declare class Graph {
83
84
  private addEdge;
84
85
  addEdgesBetweenNodes(): void;
85
86
  precomputeFileResolutions(): void;
87
+ private getHeadingSlugsForPath;
86
88
  getBrokenInternalLinks(): MdxPath[];
87
89
  getAllInternalPaths(): string[];
88
90
  }
package/dist/graph.js CHANGED
@@ -150,6 +150,7 @@ export class Node {
150
150
  this.label = label;
151
151
  this.paths = paths;
152
152
  this.edges = [];
153
+ this.headingSlugs = null;
153
154
  this.relativeDir = normalizePath(dirname(label));
154
155
  this.filename = basename(label);
155
156
  this.label = join(this.relativeDir, this.filename);
@@ -339,6 +340,26 @@ export class Graph {
339
340
  });
340
341
  });
341
342
  }
343
+ getHeadingSlugsForPath(pathString, nodeSet) {
344
+ var _a;
345
+ const resolvedFiles = this.fileResolutionMap.get(pathString);
346
+ if (resolvedFiles) {
347
+ for (const file of resolvedFiles) {
348
+ if (nodeSet.has(file)) {
349
+ const node = this.nodes[file];
350
+ if (node && node.headingSlugs !== null)
351
+ return node.headingSlugs;
352
+ }
353
+ }
354
+ }
355
+ const directNode = this.nodes[pathString];
356
+ if (directNode && directNode.headingSlugs !== null)
357
+ return directNode.headingSlugs;
358
+ const indexNode = (_a = this.nodes[`${pathString}/index.mdx`]) !== null && _a !== void 0 ? _a : this.nodes[`${pathString}/index.md`];
359
+ if (indexNode && indexNode.headingSlugs !== null)
360
+ return indexNode.headingSlugs;
361
+ return undefined;
362
+ }
342
363
  getBrokenInternalLinks() {
343
364
  if (this.fileResolutionMap.size === 0) {
344
365
  this.precomputeFileResolutions();
@@ -369,6 +390,13 @@ export class Graph {
369
390
  if (!hasExistingFile && !hasValidRedirect) {
370
391
  brokenLinks.push(path);
371
392
  }
393
+ else if (path.anchorLink) {
394
+ const anchor = path.anchorLink.slice(1);
395
+ const headingSlugs = this.getHeadingSlugsForPath(pathString, nodeSet);
396
+ if (headingSlugs && !headingSlugs.has(anchor)) {
397
+ brokenLinks.push(path);
398
+ }
399
+ }
372
400
  }
373
401
  }
374
402
  }
@@ -1,4 +1,6 @@
1
+ import type { TableOfContentsSectionType } from '@mintlify/common';
1
2
  import { Node } from '../graph.js';
3
+ export declare const flattenTableOfContentsSlugs: (sections: TableOfContentsSectionType[]) => Set<string>;
2
4
  export declare const decorateGraphNodeFromPageContent: (graphNode: Node, content: string) => Promise<void>;
3
5
  /**
4
6
  * Get all broken internal links used in the site
@@ -7,16 +7,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { coreRemark, isMintIgnored } from '@mintlify/common';
10
+ import { coreRemark, isMintIgnored, remarkComponentIds, remarkExtractTableOfContents, } from '@mintlify/common';
11
11
  import { getMintIgnore } from '@mintlify/prebuild';
12
12
  import fs from 'fs-extra';
13
13
  import path from 'path';
14
14
  import { visit } from 'unist-util-visit';
15
15
  import { Graph, Wrapper } from '../graph.js';
16
16
  import { getLinkPaths, getPagePaths } from '../prebuild.js';
17
+ import { getNavigationHrefs } from './getNavigationHrefs.js';
17
18
  import { getOpenApiPagePaths } from './getOpenApiPagePaths.js';
18
19
  import { getRedirects } from './getRedirects.js';
20
+ export const flattenTableOfContentsSlugs = (sections) => {
21
+ const slugs = new Set();
22
+ for (const section of sections) {
23
+ slugs.add(section.slug);
24
+ for (const slug of flattenTableOfContentsSlugs(section.children)) {
25
+ slugs.add(slug);
26
+ }
27
+ }
28
+ return slugs;
29
+ };
19
30
  export const decorateGraphNodeFromPageContent = (graphNode, content) => __awaiter(void 0, void 0, void 0, function* () {
31
+ const mdxExtracts = {};
20
32
  const visitLinks = () => {
21
33
  return (tree) => {
22
34
  visit(tree, (node) => {
@@ -49,8 +61,15 @@ export const decorateGraphNodeFromPageContent = (graphNode, content) => __awaite
49
61
  return tree;
50
62
  };
51
63
  };
52
- yield coreRemark().use(visitLinks).process(content);
53
- return;
64
+ const processor = coreRemark()
65
+ .use(visitLinks)
66
+ .use(remarkComponentIds)
67
+ .use(remarkExtractTableOfContents, mdxExtracts);
68
+ const tree = processor.parse(content);
69
+ yield processor.run(tree);
70
+ graphNode.headingSlugs = mdxExtracts.tableOfContents
71
+ ? flattenTableOfContentsSlugs(mdxExtracts.tableOfContents)
72
+ : new Set();
54
73
  });
55
74
  /**
56
75
  * Get all broken internal links used in the site
@@ -78,6 +97,14 @@ export const getBrokenInternalLinks = (repoPath) => __awaiter(void 0, void 0, vo
78
97
  catch (err) {
79
98
  console.warn(`Warning: Failed to extract OpenAPI page paths: ${err}`);
80
99
  }
100
+ const navResult = yield getNavigationHrefs(baseDir);
101
+ if (navResult) {
102
+ const configNode = graph.addNode(navResult.configFile);
103
+ for (const href of navResult.hrefs) {
104
+ const normalized = href === '/' || href === '' ? '/index' : href.replace(/^\/#/, '/index#');
105
+ configNode.addPath(normalized);
106
+ }
107
+ }
81
108
  const allSitePages = getPagePaths(baseDir);
82
109
  const sitePages = allSitePages.filter((file) => !isMintIgnored(file, mintIgnoreGlobs));
83
110
  yield Promise.all(sitePages.map((filePath) => __awaiter(void 0, void 0, void 0, function* () {
@@ -0,0 +1,4 @@
1
+ export declare const getNavigationHrefs: (baseDir: string) => Promise<{
2
+ hrefs: string[];
3
+ configFile: string;
4
+ } | undefined>;
@@ -0,0 +1,41 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from 'fs-extra';
11
+ import path from 'path';
12
+ const collectHrefs = (obj) => {
13
+ if (typeof obj !== 'object' || obj === null)
14
+ return [];
15
+ if (Array.isArray(obj))
16
+ return obj.flatMap(collectHrefs);
17
+ const record = obj;
18
+ const hrefs = [];
19
+ if (typeof record.href === 'string') {
20
+ hrefs.push(record.href);
21
+ }
22
+ for (const value of Object.values(record)) {
23
+ hrefs.push(...collectHrefs(value));
24
+ }
25
+ return hrefs;
26
+ };
27
+ export const getNavigationHrefs = (baseDir) => __awaiter(void 0, void 0, void 0, function* () {
28
+ let configJson;
29
+ let configFile;
30
+ if (fs.existsSync(path.join(baseDir, 'docs.json'))) {
31
+ configJson = yield fs.readJSON(path.join(baseDir, 'docs.json'));
32
+ configFile = 'docs.json';
33
+ }
34
+ else if (fs.existsSync(path.join(baseDir, 'mint.json'))) {
35
+ configJson = yield fs.readJSON(path.join(baseDir, 'mint.json'));
36
+ configFile = 'mint.json';
37
+ }
38
+ if (!(configJson === null || configJson === void 0 ? void 0 : configJson.navigation) || !configFile)
39
+ return undefined;
40
+ return { hrefs: collectHrefs(configJson.navigation), configFile };
41
+ });