@mintlify/link-rot
Version:
Static checking for broken internal links
85 lines (84 loc) • 4.2 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { generateOpenApiFromDocsConfig, getOpenApiFiles, getOpenApiFilesFromConfig, } from '@mintlify/prebuild';
import { generateOpenApiPagesForDocsConfig } from '@mintlify/scraping';
import { validateDocsConfig } from '@mintlify/validation';
import { readResolvedConfigJson } from './readConfig.js';
const DEFAULT_OUTPUT_DIR = 'api-reference';
/**
* Extract virtual page paths that would be generated from OpenAPI specs.
* These pages don't exist as files but are valid link targets.
*
* For docs.json (v2) configs, this walks the navigation tree so that per-spec
* `directory` overrides and inherited directories are honored — matching the
* slugs production generates. For mint.json (v1) or missing configs, it falls
* back to generating page paths under the default `api-reference` directory.
*
* `$ref` pointers inside the config are resolved first, so navigation split
* across multiple JSON files is walked end-to-end.
*/
export const getOpenApiPagePaths = (baseDir) => __awaiter(void 0, void 0, void 0, function* () {
const pagePaths = [];
try {
const openApiFiles = yield getOpenApiFiles(baseDir);
const docsConfig = yield readDocsConfig(baseDir);
if (docsConfig) {
const urlOpenApiFiles = yield safeGetOpenApiFilesFromConfig(docsConfig);
const allOpenApiFiles = [...openApiFiles, ...urlOpenApiFiles];
const pagesAcc = {};
try {
yield generateOpenApiFromDocsConfig(docsConfig.navigation, allOpenApiFiles, pagesAcc, {
writeFiles: false,
});
}
catch (err) {
console.warn(`Warning: Failed to extract OpenAPI pages from navigation: ${err}`);
}
pagePaths.push(...Object.keys(pagesAcc));
}
// Also enumerate default-directory pages per local spec. This preserves
// the previous behavior for configs that don't wire every OpenAPI file
// into `navigation`, and keeps us permissive (extra virtual nodes can
// only hide broken-link reports, never add false positives).
for (const openApiFile of openApiFiles) {
try {
const { pagesAcc } = yield generateOpenApiPagesForDocsConfig(openApiFile.spec, {
openApiFilePath: openApiFile.originalFileLocation,
writeFiles: false,
outDir: DEFAULT_OUTPUT_DIR,
});
pagePaths.push(...Object.keys(pagesAcc));
}
catch (err) {
console.warn(`Warning: Failed to extract pages from OpenAPI spec ${openApiFile.originalFileLocation}: ${err}`);
}
}
}
catch (err) {
console.warn(`Warning: Failed to categorize files for OpenAPI extraction: ${err}`);
}
return [...new Set(pagePaths)];
});
const readDocsConfig = (baseDir) => __awaiter(void 0, void 0, void 0, function* () {
const resolved = yield readResolvedConfigJson(baseDir);
if (!resolved || resolved.configFile !== 'docs.json')
return null;
const result = validateDocsConfig(resolved.json);
return result.success ? result.data : null;
});
const safeGetOpenApiFilesFromConfig = (docsConfig) => __awaiter(void 0, void 0, void 0, function* () {
try {
return yield getOpenApiFilesFromConfig('docs', docsConfig);
}
catch (err) {
console.warn(`Warning: Failed to fetch URL-based OpenAPI specs: ${err}`);
return [];
}
});