@mintlify/link-rot 3.0.1038 → 3.0.1040

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
@@ -67,6 +67,7 @@ export declare class Graph {
67
67
  private edges;
68
68
  private fileResolutionMap;
69
69
  private redirectMap;
70
+ private configRedirects;
70
71
  private mintIgnoreGlobs;
71
72
  private virtualNodes;
72
73
  constructor(baseDir: string, mintIgnoreGlobs?: string[]);
@@ -88,6 +89,10 @@ export declare class Graph {
88
89
  getBrokenInternalLinks(options?: {
89
90
  checkAnchors?: boolean;
90
91
  }): MdxPath[];
92
+ getBrokenRedirects(): {
93
+ source: string;
94
+ destination: string;
95
+ }[];
91
96
  getExternalPathsByNode(): Map<string, MdxPath[]>;
92
97
  getAllInternalPaths(): string[];
93
98
  }
package/dist/graph.js CHANGED
@@ -3,7 +3,7 @@ import { getFileListWithDirectories } from '@mintlify/prebuild';
3
3
  import { existsSync } from 'fs';
4
4
  import { parse, join, resolve, relative, dirname, basename } from 'path';
5
5
  import { sep as WINDOWS_SEPARATOR } from 'path/win32';
6
- import { getLinkPaths, normalizePath } from './prebuild.js';
6
+ import { getLinkPaths, normalizePath, removeSelectors } from './prebuild.js';
7
7
  var PathType;
8
8
  (function (PathType) {
9
9
  PathType["INTERNAL"] = "internal";
@@ -216,6 +216,7 @@ export class Graph {
216
216
  this.edges = [];
217
217
  this.fileResolutionMap = new Map();
218
218
  this.redirectMap = new Map();
219
+ this.configRedirects = [];
219
220
  this.mintIgnoreGlobs = [];
220
221
  this.virtualNodes = new Set();
221
222
  this.baseDir = resolve(baseDir);
@@ -239,6 +240,8 @@ export class Graph {
239
240
  }
240
241
  }
241
242
  setRedirects(redirects) {
243
+ this.configRedirects = redirects;
244
+ this.redirectMap.clear();
242
245
  for (const redirect of redirects) {
243
246
  this.redirectMap.set(normalizePath(redirect.source), normalizePath(redirect.destination));
244
247
  }
@@ -404,6 +407,63 @@ export class Graph {
404
407
  }
405
408
  return brokenLinks;
406
409
  }
410
+ getBrokenRedirects() {
411
+ const nodeSet = new Set(Object.values(this.nodes).map((node) => node.toString()));
412
+ const hasWildcard = (pattern) => /[:*]/.test(pattern);
413
+ const resolvesToValidPath = (rawDestination, visited) => {
414
+ if (URL.canParse(rawDestination))
415
+ return true;
416
+ const pathOnly = removeSelectors(rawDestination);
417
+ if (!pathOnly)
418
+ return true;
419
+ // Wildcard path destinations can't be validated as concrete paths — treat as valid,
420
+ // matching the top-level skip semantics. Checked after stripping so `:` or `*` in
421
+ // the fragment/query doesn't cause a false skip.
422
+ if (hasWildcard(pathOnly))
423
+ return true;
424
+ const normalized = normalizePath(pathOnly);
425
+ if (visited.has(normalized))
426
+ return false;
427
+ visited.add(normalized);
428
+ // normalizePath('/') yields '.' — resolve lookups against node labels (which use
429
+ // path.join, so `join('.', 'index.mdx') === 'index.mdx'`) rather than naive
430
+ // string concatenation that would produce './index.mdx' and never match.
431
+ const fileCandidates = normalized === '.'
432
+ ? ['index.mdx', 'index.md']
433
+ : [
434
+ normalized,
435
+ `${normalized}.mdx`,
436
+ `${normalized}.md`,
437
+ join(normalized, 'index.mdx'),
438
+ join(normalized, 'index.md'),
439
+ ];
440
+ if (fileCandidates.some((candidate) => nodeSet.has(candidate)) ||
441
+ this.virtualNodes.has(normalized)) {
442
+ return true;
443
+ }
444
+ const chained = this.redirectMap.get(normalized);
445
+ if (chained !== undefined)
446
+ return resolvesToValidPath(chained, visited);
447
+ for (const [sourcePattern, destinationPattern] of this.redirectMap.entries()) {
448
+ if (hasWildcard(sourcePattern) && this.matchesWildcardPattern(normalized, sourcePattern)) {
449
+ const resolved = this.applyWildcardRedirect(normalized, sourcePattern, destinationPattern);
450
+ return resolvesToValidPath(resolved, visited);
451
+ }
452
+ }
453
+ return false;
454
+ };
455
+ const broken = [];
456
+ for (const { source, destination } of this.configRedirects) {
457
+ if (URL.canParse(destination))
458
+ continue;
459
+ if (hasWildcard(removeSelectors(destination)))
460
+ continue;
461
+ if (!resolvesToValidPath(destination, new Set())) {
462
+ broken.push({ source, destination });
463
+ }
464
+ }
465
+ return broken;
466
+ }
407
467
  getExternalPathsByNode() {
408
468
  const result = new Map();
409
469
  for (const [label, node] of Object.entries(this.nodes)) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { buildGraph, getBrokenInternalLinks } from './static-checking/getBrokenInternalLinks.js';
2
2
  export { getBrokenExternalLinks } from './static-checking/getBrokenExternalLinks.js';
3
3
  export type { ExternalLinkResult } from './static-checking/getBrokenExternalLinks.js';
4
+ export { getBrokenRedirects } from './static-checking/getBrokenRedirects.js';
4
5
  export { renameFilesAndUpdateLinksInContent } from './link-renaming/renameFileAndUpdateLinksInContent.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { buildGraph, getBrokenInternalLinks } from './static-checking/getBrokenInternalLinks.js';
2
2
  export { getBrokenExternalLinks } from './static-checking/getBrokenExternalLinks.js';
3
+ export { getBrokenRedirects } from './static-checking/getBrokenRedirects.js';
3
4
  export { renameFilesAndUpdateLinksInContent } from './link-renaming/renameFileAndUpdateLinksInContent.js';
@@ -0,0 +1,6 @@
1
+ export declare const getBrokenRedirects: (repoPath?: string, options?: {
2
+ checkSnippets?: boolean;
3
+ }) => Promise<{
4
+ source: string;
5
+ destination: string;
6
+ }[]>;
@@ -0,0 +1,16 @@
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 { buildGraph } from './getBrokenInternalLinks.js';
11
+ export const getBrokenRedirects = (repoPath, options) => __awaiter(void 0, void 0, void 0, function* () {
12
+ const graph = yield buildGraph(repoPath, {
13
+ checkSnippets: options === null || options === void 0 ? void 0 : options.checkSnippets,
14
+ });
15
+ return graph.getBrokenRedirects();
16
+ });