@doccov/sdk 0.3.2 → 0.3.4

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/index.js CHANGED
@@ -669,22 +669,31 @@ function levenshtein(a, b) {
669
669
  return matrix[b.length][a.length];
670
670
  }
671
671
  function detectExampleDrift(entry, exportRegistry) {
672
- if (!exportRegistry || !entry.examples || entry.examples.length === 0) {
672
+ if (!exportRegistry || !entry.examples?.length)
673
673
  return [];
674
- }
675
674
  const drifts = [];
676
- const identifierPattern = /\b([A-Z][a-zA-Z0-9]*)\b/g;
677
675
  for (const example of entry.examples) {
678
- if (typeof example !== "string") {
676
+ let visit = function(node) {
677
+ if (ts.isIdentifier(node) && isPascalCase(node.text)) {
678
+ if (isLocalDeclaration(node)) {
679
+ localDeclarations.add(node.text);
680
+ } else if (isIdentifierReference(node) && !isBuiltInIdentifier(node.text)) {
681
+ referencedIdentifiers.add(node.text);
682
+ }
683
+ }
684
+ ts.forEachChild(node, visit);
685
+ };
686
+ if (typeof example !== "string")
679
687
  continue;
680
- }
681
- const matches = example.matchAll(identifierPattern);
688
+ const codeContent = example.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
689
+ if (!codeContent)
690
+ continue;
691
+ const sourceFile = ts.createSourceFile("example.ts", codeContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
692
+ const localDeclarations = new Set;
682
693
  const referencedIdentifiers = new Set;
683
- for (const match of matches) {
684
- const identifier = match[1];
685
- if (identifier && !isBuiltInIdentifier(identifier)) {
686
- referencedIdentifiers.add(identifier);
687
- }
694
+ visit(sourceFile);
695
+ for (const local of localDeclarations) {
696
+ referencedIdentifiers.delete(local);
688
697
  }
689
698
  for (const identifier of referencedIdentifiers) {
690
699
  if (!exportRegistry.has(identifier)) {
@@ -757,6 +766,37 @@ function isBuiltInIdentifier(identifier) {
757
766
  ]);
758
767
  return builtIns.has(identifier);
759
768
  }
769
+ function isPascalCase(text) {
770
+ return /^[A-Z][a-zA-Z0-9]*$/.test(text);
771
+ }
772
+ function isLocalDeclaration(node) {
773
+ const parent = node.parent;
774
+ if (!parent)
775
+ return false;
776
+ if (ts.isClassDeclaration(parent) && parent.name === node)
777
+ return true;
778
+ if (ts.isFunctionDeclaration(parent) && parent.name === node)
779
+ return true;
780
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
781
+ return true;
782
+ return false;
783
+ }
784
+ function isIdentifierReference(node) {
785
+ const parent = node.parent;
786
+ if (!parent)
787
+ return false;
788
+ if (ts.isClassDeclaration(parent) && parent.name === node)
789
+ return false;
790
+ if (ts.isFunctionDeclaration(parent) && parent.name === node)
791
+ return false;
792
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
793
+ return false;
794
+ if (ts.isMethodDeclaration(parent) && parent.name === node)
795
+ return false;
796
+ if (ts.isPropertyDeclaration(parent) && parent.name === node)
797
+ return false;
798
+ return true;
799
+ }
760
800
  function detectBrokenLinks(entry, exportRegistry) {
761
801
  if (!exportRegistry) {
762
802
  return [];
@@ -983,6 +1023,267 @@ function detectExampleAssertionFailures(entry, runtimeResults) {
983
1023
  }
984
1024
  return drifts;
985
1025
  }
1026
+ // src/markdown/parser.ts
1027
+ import remarkMdx from "remark-mdx";
1028
+ import remarkParse from "remark-parse";
1029
+ import { unified } from "unified";
1030
+ import { visit } from "unist-util-visit";
1031
+ var EXECUTABLE_LANGS = new Set([
1032
+ "ts",
1033
+ "typescript",
1034
+ "js",
1035
+ "javascript",
1036
+ "tsx",
1037
+ "jsx"
1038
+ ]);
1039
+ function isExecutableLang(lang) {
1040
+ if (!lang)
1041
+ return false;
1042
+ return EXECUTABLE_LANGS.has(lang.toLowerCase());
1043
+ }
1044
+ function parseMarkdownFile(content, filePath) {
1045
+ const processor = unified().use(remarkParse).use(remarkMdx);
1046
+ const tree = processor.parse(content);
1047
+ const codeBlocks = [];
1048
+ visit(tree, "code", (node) => {
1049
+ if (isExecutableLang(node.lang)) {
1050
+ codeBlocks.push({
1051
+ lang: node.lang ?? "ts",
1052
+ code: node.value,
1053
+ meta: node.meta ?? undefined,
1054
+ lineStart: node.position?.start.line ?? 0,
1055
+ lineEnd: node.position?.end.line ?? 0
1056
+ });
1057
+ }
1058
+ });
1059
+ return { path: filePath, codeBlocks };
1060
+ }
1061
+ function parseMarkdownFiles(files) {
1062
+ return files.map((f) => parseMarkdownFile(f.content, f.path));
1063
+ }
1064
+ function extractImports(code) {
1065
+ const imports = [];
1066
+ const importRegex = /import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
1067
+ let match;
1068
+ while ((match = importRegex.exec(code)) !== null) {
1069
+ const names = match[1];
1070
+ const from = match[2];
1071
+ const namedImports = names.split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
1072
+ for (const name of namedImports) {
1073
+ if (name) {
1074
+ imports.push({ name, from });
1075
+ }
1076
+ }
1077
+ }
1078
+ return imports;
1079
+ }
1080
+ function extractFunctionCalls(code) {
1081
+ const calls = new Set;
1082
+ const callRegex = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[(<]/g;
1083
+ const keywords = new Set([
1084
+ "if",
1085
+ "for",
1086
+ "while",
1087
+ "switch",
1088
+ "catch",
1089
+ "function",
1090
+ "class",
1091
+ "interface",
1092
+ "type",
1093
+ "import",
1094
+ "export",
1095
+ "return",
1096
+ "throw",
1097
+ "new",
1098
+ "typeof",
1099
+ "instanceof"
1100
+ ]);
1101
+ let match;
1102
+ while ((match = callRegex.exec(code)) !== null) {
1103
+ const name = match[1];
1104
+ if (!keywords.has(name)) {
1105
+ calls.add(name);
1106
+ }
1107
+ }
1108
+ return Array.from(calls);
1109
+ }
1110
+ function findExportReferences(files, exportNames) {
1111
+ const references = [];
1112
+ const exportSet = new Set(exportNames);
1113
+ for (const file of files) {
1114
+ for (let blockIndex = 0;blockIndex < file.codeBlocks.length; blockIndex++) {
1115
+ const block = file.codeBlocks[blockIndex];
1116
+ const imports = extractImports(block.code);
1117
+ for (const imp of imports) {
1118
+ if (exportSet.has(imp.name)) {
1119
+ references.push({
1120
+ exportName: imp.name,
1121
+ file: file.path,
1122
+ line: block.lineStart,
1123
+ context: getContextFromCode(block.code, imp.name),
1124
+ inCodeBlock: true,
1125
+ blockIndex
1126
+ });
1127
+ }
1128
+ }
1129
+ const calls = extractFunctionCalls(block.code);
1130
+ for (const call of calls) {
1131
+ if (exportSet.has(call)) {
1132
+ const alreadyFound = references.some((r) => r.exportName === call && r.file === file.path && r.blockIndex === blockIndex);
1133
+ if (!alreadyFound) {
1134
+ references.push({
1135
+ exportName: call,
1136
+ file: file.path,
1137
+ line: block.lineStart,
1138
+ context: getContextFromCode(block.code, call),
1139
+ inCodeBlock: true,
1140
+ blockIndex
1141
+ });
1142
+ }
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ return references;
1148
+ }
1149
+ function getContextFromCode(code, name) {
1150
+ const lines = code.split(`
1151
+ `);
1152
+ for (let i = 0;i < lines.length; i++) {
1153
+ if (lines[i].includes(name)) {
1154
+ const start = Math.max(0, i - 1);
1155
+ const end = Math.min(lines.length, i + 2);
1156
+ return lines.slice(start, end).join(`
1157
+ `);
1158
+ }
1159
+ }
1160
+ return code.slice(0, 100);
1161
+ }
1162
+ function blockReferencesExport(block, exportName) {
1163
+ const imports = extractImports(block.code);
1164
+ if (imports.some((i) => i.name === exportName)) {
1165
+ return true;
1166
+ }
1167
+ const calls = extractFunctionCalls(block.code);
1168
+ return calls.includes(exportName);
1169
+ }
1170
+ // src/markdown/analyzer.ts
1171
+ function getChangeType(exportName, diff) {
1172
+ if (diff.breaking.includes(exportName)) {
1173
+ return "signature-changed";
1174
+ }
1175
+ return null;
1176
+ }
1177
+ function analyzeDocsImpact(diff, markdownFiles, newExportNames = []) {
1178
+ const changedExports = [
1179
+ ...diff.breaking
1180
+ ];
1181
+ const references = findExportReferences(markdownFiles, changedExports);
1182
+ const impactByFile = new Map;
1183
+ for (const ref of references) {
1184
+ const changeType = getChangeType(ref.exportName, diff);
1185
+ if (!changeType)
1186
+ continue;
1187
+ let impact = impactByFile.get(ref.file);
1188
+ if (!impact) {
1189
+ impact = { file: ref.file, references: [] };
1190
+ impactByFile.set(ref.file, impact);
1191
+ }
1192
+ impact.references.push({
1193
+ exportName: ref.exportName,
1194
+ line: ref.line,
1195
+ changeType,
1196
+ context: ref.context
1197
+ });
1198
+ }
1199
+ const documentedExports = new Set;
1200
+ for (const file of markdownFiles) {
1201
+ for (const block of file.codeBlocks) {
1202
+ for (const exportName of newExportNames) {
1203
+ if (block.code.includes(exportName)) {
1204
+ documentedExports.add(exportName);
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+ const missingDocs = diff.nonBreaking.filter((name) => !documentedExports.has(name));
1210
+ const totalCodeBlocks = markdownFiles.reduce((sum, f) => sum + f.codeBlocks.length, 0);
1211
+ const allReferences = findExportReferences(markdownFiles, [
1212
+ ...changedExports,
1213
+ ...diff.nonBreaking
1214
+ ]);
1215
+ return {
1216
+ impactedFiles: Array.from(impactByFile.values()),
1217
+ missingDocs,
1218
+ stats: {
1219
+ filesScanned: markdownFiles.length,
1220
+ codeBlocksFound: totalCodeBlocks,
1221
+ referencesFound: allReferences.length,
1222
+ impactedReferences: references.length
1223
+ }
1224
+ };
1225
+ }
1226
+ function findDeprecatedReferences(markdownFiles, deprecatedExports) {
1227
+ return findExportReferences(markdownFiles, deprecatedExports);
1228
+ }
1229
+ function findRemovedReferences(markdownFiles, removedExports) {
1230
+ return findExportReferences(markdownFiles, removedExports);
1231
+ }
1232
+ function hasDocsForExport(markdownFiles, exportName) {
1233
+ const refs = findExportReferences(markdownFiles, [exportName]);
1234
+ return refs.length > 0;
1235
+ }
1236
+ function getDocumentedExports(markdownFiles, exportNames) {
1237
+ const documented = [];
1238
+ for (const name of exportNames) {
1239
+ if (hasDocsForExport(markdownFiles, name)) {
1240
+ documented.push(name);
1241
+ }
1242
+ }
1243
+ return documented;
1244
+ }
1245
+ function getUndocumentedExports(markdownFiles, exportNames) {
1246
+ const documented = new Set(getDocumentedExports(markdownFiles, exportNames));
1247
+ return exportNames.filter((name) => !documented.has(name));
1248
+ }
1249
+ // src/markdown/diff-with-docs.ts
1250
+ import { diffSpec } from "@openpkg-ts/spec";
1251
+ function diffSpecWithDocs(oldSpec, newSpec, options = {}) {
1252
+ const baseDiff = diffSpec(oldSpec, newSpec);
1253
+ if (!options.markdownFiles?.length) {
1254
+ return baseDiff;
1255
+ }
1256
+ const newExportNames = newSpec.exports?.map((e) => e.name) ?? [];
1257
+ const docsImpact = analyzeDocsImpact(baseDiff, options.markdownFiles, newExportNames);
1258
+ return {
1259
+ ...baseDiff,
1260
+ docsImpact
1261
+ };
1262
+ }
1263
+ function hasDocsImpact(diff) {
1264
+ if (!diff.docsImpact)
1265
+ return false;
1266
+ return diff.docsImpact.impactedFiles.length > 0 || diff.docsImpact.missingDocs.length > 0;
1267
+ }
1268
+ function getDocsImpactSummary(diff) {
1269
+ if (!diff.docsImpact) {
1270
+ return {
1271
+ impactedFileCount: 0,
1272
+ impactedReferenceCount: 0,
1273
+ missingDocsCount: 0,
1274
+ totalIssues: 0
1275
+ };
1276
+ }
1277
+ const impactedFileCount = diff.docsImpact.impactedFiles.length;
1278
+ const impactedReferenceCount = diff.docsImpact.impactedFiles.reduce((sum, f) => sum + f.references.length, 0);
1279
+ const missingDocsCount = diff.docsImpact.missingDocs.length;
1280
+ return {
1281
+ impactedFileCount,
1282
+ impactedReferenceCount,
1283
+ missingDocsCount,
1284
+ totalIssues: impactedReferenceCount + missingDocsCount
1285
+ };
1286
+ }
986
1287
  // src/analysis/run-analysis.ts
987
1288
  import * as fs2 from "node:fs";
988
1289
  import * as path4 from "node:path";
@@ -1317,6 +1618,20 @@ function withDescription(schema, description) {
1317
1618
  description
1318
1619
  };
1319
1620
  }
1621
+ function getPropertyType(prop, parentType, typeChecker) {
1622
+ if (prop.valueDeclaration) {
1623
+ return typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
1624
+ }
1625
+ const propType = typeChecker.getTypeOfPropertyOfType(parentType, prop.getName());
1626
+ if (propType) {
1627
+ return propType;
1628
+ }
1629
+ const decl = prop.declarations?.[0];
1630
+ if (decl) {
1631
+ return typeChecker.getTypeOfSymbolAtLocation(prop, decl);
1632
+ }
1633
+ return typeChecker.getAnyType();
1634
+ }
1320
1635
  function propertiesToSchema(properties, description) {
1321
1636
  const schema = {
1322
1637
  type: "object",
@@ -1657,7 +1972,7 @@ function formatTypeReference(type, typeChecker, typeRefs, referencedTypes, visit
1657
1972
  };
1658
1973
  const required = [];
1659
1974
  for (const prop of properties) {
1660
- const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
1975
+ const propType = getPropertyType(prop, type, typeChecker);
1661
1976
  const propName = prop.getName();
1662
1977
  objSchema.properties[propName] = formatTypeReference(propType, typeChecker, typeRefs, referencedTypes, visited);
1663
1978
  if (!(prop.flags & ts2.SymbolFlags.Optional)) {
@@ -1742,7 +2057,7 @@ function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs,
1742
2057
  const isAnonymousObject = isObjectLiteralType(subType) || symbol2?.getName().startsWith("__");
1743
2058
  if (isAnonymousObject) {
1744
2059
  for (const prop of subType.getProperties()) {
1745
- const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
2060
+ const propType = getPropertyType(prop, subType, typeChecker);
1746
2061
  let description = "";
1747
2062
  if (functionDoc) {
1748
2063
  const propName = prop.getName();
@@ -1762,7 +2077,7 @@ function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs,
1762
2077
  const _symbolName = symbol2.getName();
1763
2078
  if (!isBuiltInType(_symbolName)) {
1764
2079
  for (const prop of subType.getProperties()) {
1765
- const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
2080
+ const propType = getPropertyType(prop, subType, typeChecker);
1766
2081
  properties.push({
1767
2082
  name: prop.getName(),
1768
2083
  type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
@@ -1793,7 +2108,7 @@ function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs,
1793
2108
  if (isObjectLiteralType(subType) || symbol2?.getName().startsWith("__")) {
1794
2109
  const properties = [];
1795
2110
  for (const prop of subType.getProperties()) {
1796
- const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
2111
+ const propType = getPropertyType(prop, subType, typeChecker);
1797
2112
  properties.push({
1798
2113
  name: prop.getName(),
1799
2114
  type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
@@ -1827,7 +2142,7 @@ function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs,
1827
2142
  if ((symbol?.getName().startsWith("__") || isObjectLiteralType(paramType)) && paramType.getProperties().length > 0) {
1828
2143
  const properties = [];
1829
2144
  for (const prop of paramType.getProperties()) {
1830
- const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
2145
+ const propType = getPropertyType(prop, paramType, typeChecker);
1831
2146
  properties.push({
1832
2147
  name: prop.getName(),
1833
2148
  type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
@@ -2524,7 +2839,8 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
2524
2839
  return signatures.map((signature) => {
2525
2840
  const parameters = signature.getParameters().map((param) => {
2526
2841
  const paramDecl = param.declarations?.find(ts2.isParameter);
2527
- const paramType = paramDecl ? paramDecl.type != null ? checker.getTypeFromTypeNode(paramDecl.type) : checker.getTypeAtLocation(paramDecl) : checker.getTypeOfSymbolAtLocation(param, symbol?.declarations?.[0] ?? signature.declaration ?? param.declarations?.[0] ?? param.valueDeclaration);
2842
+ const location = symbol?.declarations?.[0] ?? signature.declaration ?? param.declarations?.[0] ?? param.valueDeclaration;
2843
+ const paramType = paramDecl ? paramDecl.type != null ? checker.getTypeFromTypeNode(paramDecl.type) : checker.getTypeAtLocation(paramDecl) : location ? checker.getTypeOfSymbolAtLocation(param, location) : checker.getAnyType();
2528
2844
  collectReferencedTypes(paramType, checker, referencedTypes);
2529
2845
  if (paramDecl?.type) {
2530
2846
  collectReferencedTypesFromNode(paramDecl.type, checker, referencedTypes);
@@ -4083,7 +4399,7 @@ function findJSDocLocation(sourceFile, symbolName, approximateLine) {
4083
4399
  };
4084
4400
  }
4085
4401
  }
4086
- function visit(node) {
4402
+ function visit2(node) {
4087
4403
  if (ts2.isFunctionDeclaration(node) && node.name) {
4088
4404
  processNode(node, node.name.getText(sourceFile));
4089
4405
  }
@@ -4106,9 +4422,9 @@ function findJSDocLocation(sourceFile, symbolName, approximateLine) {
4106
4422
  if (ts2.isMethodDeclaration(node) && node.name) {
4107
4423
  processNode(node, node.name.getText(sourceFile));
4108
4424
  }
4109
- ts2.forEachChild(node, visit);
4425
+ ts2.forEachChild(node, visit2);
4110
4426
  }
4111
- visit(sourceFile);
4427
+ visit2(sourceFile);
4112
4428
  return result;
4113
4429
  }
4114
4430
  async function applyEdits(edits) {
@@ -4660,28 +4976,530 @@ async function runExamplesWithPackage(examples, options) {
4660
4976
  } catch {}
4661
4977
  }
4662
4978
  }
4979
+ // src/detect/filesystem.ts
4980
+ import * as fs6 from "node:fs";
4981
+ import * as nodePath from "node:path";
4982
+ import { Writable } from "node:stream";
4983
+
4984
+ class NodeFileSystem {
4985
+ basePath;
4986
+ constructor(basePath) {
4987
+ this.basePath = basePath;
4988
+ }
4989
+ resolve(relativePath) {
4990
+ return nodePath.join(this.basePath, relativePath);
4991
+ }
4992
+ async exists(relativePath) {
4993
+ return fs6.existsSync(this.resolve(relativePath));
4994
+ }
4995
+ async readFile(relativePath) {
4996
+ return fs6.readFileSync(this.resolve(relativePath), "utf-8");
4997
+ }
4998
+ async readDir(relativePath) {
4999
+ return fs6.readdirSync(this.resolve(relativePath));
5000
+ }
5001
+ async isDirectory(relativePath) {
5002
+ const fullPath = this.resolve(relativePath);
5003
+ if (!fs6.existsSync(fullPath))
5004
+ return false;
5005
+ return fs6.statSync(fullPath).isDirectory();
5006
+ }
5007
+ }
5008
+ function createCaptureStream() {
5009
+ let output = "";
5010
+ const stream = new Writable({
5011
+ write(chunk, _encoding, callback) {
5012
+ output += chunk.toString();
5013
+ callback();
5014
+ }
5015
+ });
5016
+ return { stream, getOutput: () => output };
5017
+ }
5018
+
5019
+ class SandboxFileSystem {
5020
+ sandbox;
5021
+ constructor(sandbox) {
5022
+ this.sandbox = sandbox;
5023
+ }
5024
+ async exists(path8) {
5025
+ const result = await this.sandbox.runCommand({
5026
+ cmd: "test",
5027
+ args: ["-e", path8]
5028
+ });
5029
+ return result.exitCode === 0;
5030
+ }
5031
+ async readFile(path8) {
5032
+ const capture = createCaptureStream();
5033
+ await this.sandbox.runCommand({
5034
+ cmd: "cat",
5035
+ args: [path8],
5036
+ stdout: capture.stream
5037
+ });
5038
+ return capture.getOutput();
5039
+ }
5040
+ async readDir(path8) {
5041
+ const capture = createCaptureStream();
5042
+ await this.sandbox.runCommand({
5043
+ cmd: "ls",
5044
+ args: ["-1", path8],
5045
+ stdout: capture.stream
5046
+ });
5047
+ return capture.getOutput().split(`
5048
+ `).filter(Boolean);
5049
+ }
5050
+ async isDirectory(path8) {
5051
+ const result = await this.sandbox.runCommand({
5052
+ cmd: "test",
5053
+ args: ["-d", path8]
5054
+ });
5055
+ return result.exitCode === 0;
5056
+ }
5057
+ }
5058
+ // src/detect/package-manager.ts
5059
+ var PM_CONFIGS = [
5060
+ {
5061
+ lockfile: "pnpm-lock.yaml",
5062
+ info: {
5063
+ name: "pnpm",
5064
+ lockfile: "pnpm-lock.yaml",
5065
+ installArgs: ["install", "--frozen-lockfile"],
5066
+ runPrefix: ["pnpm"]
5067
+ }
5068
+ },
5069
+ {
5070
+ lockfile: "bun.lock",
5071
+ info: {
5072
+ name: "bun",
5073
+ lockfile: "bun.lock",
5074
+ installArgs: ["install", "--frozen-lockfile"],
5075
+ runPrefix: ["bun"]
5076
+ }
5077
+ },
5078
+ {
5079
+ lockfile: "bun.lockb",
5080
+ info: {
5081
+ name: "bun",
5082
+ lockfile: "bun.lockb",
5083
+ installArgs: ["install", "--frozen-lockfile"],
5084
+ runPrefix: ["bun"]
5085
+ }
5086
+ },
5087
+ {
5088
+ lockfile: "yarn.lock",
5089
+ info: {
5090
+ name: "yarn",
5091
+ lockfile: "yarn.lock",
5092
+ installArgs: ["install", "--frozen-lockfile"],
5093
+ runPrefix: ["yarn"]
5094
+ }
5095
+ },
5096
+ {
5097
+ lockfile: "package-lock.json",
5098
+ info: {
5099
+ name: "npm",
5100
+ lockfile: "package-lock.json",
5101
+ installArgs: ["install", "--legacy-peer-deps"],
5102
+ runPrefix: ["npm", "run"]
5103
+ }
5104
+ }
5105
+ ];
5106
+ var DEFAULT_PM = {
5107
+ name: "npm",
5108
+ lockfile: null,
5109
+ installArgs: ["install", "--legacy-peer-deps"],
5110
+ runPrefix: ["npm", "run"]
5111
+ };
5112
+ async function detectPackageManager2(fs7) {
5113
+ for (const { lockfile, info } of PM_CONFIGS) {
5114
+ if (await fs7.exists(lockfile)) {
5115
+ return info;
5116
+ }
5117
+ }
5118
+ return DEFAULT_PM;
5119
+ }
5120
+ function getInstallCommand2(pm) {
5121
+ return [pm.name, ...pm.installArgs];
5122
+ }
5123
+ function getRunCommand(pm, script) {
5124
+ return [...pm.runPrefix, script];
5125
+ }
5126
+ // src/detect/utils.ts
5127
+ async function safeParseJson(fs7, path8) {
5128
+ try {
5129
+ if (!await fs7.exists(path8))
5130
+ return null;
5131
+ const content = await fs7.readFile(path8);
5132
+ return JSON.parse(content);
5133
+ } catch {
5134
+ return null;
5135
+ }
5136
+ }
5137
+ async function readPackageJson(fs7, dir) {
5138
+ const path8 = dir === "." ? "package.json" : `${dir}/package.json`;
5139
+ return safeParseJson(fs7, path8);
5140
+ }
5141
+
5142
+ // src/detect/monorepo.ts
5143
+ async function detectMonorepo(fs7) {
5144
+ const pkgJson = await readPackageJson(fs7, ".");
5145
+ if (pkgJson?.workspaces) {
5146
+ const patterns = extractWorkspacePatterns(pkgJson.workspaces);
5147
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson.name, pkgJson.private);
5148
+ return {
5149
+ isMonorepo: packages.length > 0,
5150
+ type: "npm-workspaces",
5151
+ patterns,
5152
+ packages
5153
+ };
5154
+ }
5155
+ if (await fs7.exists("pnpm-workspace.yaml")) {
5156
+ const content = await fs7.readFile("pnpm-workspace.yaml");
5157
+ const patterns = parsePnpmWorkspace(content);
5158
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson?.name, pkgJson?.private);
5159
+ return {
5160
+ isMonorepo: packages.length > 0,
5161
+ type: "pnpm-workspaces",
5162
+ patterns,
5163
+ packages
5164
+ };
5165
+ }
5166
+ if (await fs7.exists("lerna.json")) {
5167
+ const lerna = await safeParseJson(fs7, "lerna.json");
5168
+ const patterns = lerna?.packages ?? ["packages/*"];
5169
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson?.name, pkgJson?.private);
5170
+ return {
5171
+ isMonorepo: packages.length > 0,
5172
+ type: "lerna",
5173
+ patterns,
5174
+ packages
5175
+ };
5176
+ }
5177
+ return { isMonorepo: false, type: "none", patterns: [], packages: [] };
5178
+ }
5179
+ function extractWorkspacePatterns(workspaces) {
5180
+ if (Array.isArray(workspaces)) {
5181
+ return workspaces.filter((w) => typeof w === "string");
5182
+ }
5183
+ if (typeof workspaces === "object" && workspaces !== null) {
5184
+ if (Array.isArray(workspaces.packages)) {
5185
+ return workspaces.packages.filter((w) => typeof w === "string");
5186
+ }
5187
+ }
5188
+ return [];
5189
+ }
5190
+ function parsePnpmWorkspace(content) {
5191
+ const lines = content.split(`
5192
+ `);
5193
+ const patterns = [];
5194
+ let inPackages = false;
5195
+ for (const line of lines) {
5196
+ if (line.match(/^packages:/i)) {
5197
+ inPackages = true;
5198
+ continue;
5199
+ }
5200
+ if (inPackages) {
5201
+ if (line.match(/^\w+:/) && !line.startsWith(" ") && !line.startsWith("\t")) {
5202
+ break;
5203
+ }
5204
+ const match = line.match(/^\s*-\s*['"]?([^'"]+)['"]?\s*$/);
5205
+ if (match) {
5206
+ patterns.push(match[1].trim());
5207
+ }
5208
+ }
5209
+ }
5210
+ return patterns.length > 0 ? patterns : ["packages/*"];
5211
+ }
5212
+ async function resolveWorkspacePackages(fs7, patterns, rootPackageName, rootIsPrivate) {
5213
+ const packages = [];
5214
+ const seen = new Set;
5215
+ if (rootPackageName && !rootIsPrivate && rootPackageName !== "root") {
5216
+ packages.push({
5217
+ name: rootPackageName,
5218
+ path: ".",
5219
+ private: false
5220
+ });
5221
+ seen.add(rootPackageName);
5222
+ }
5223
+ const dirsToScan = new Set;
5224
+ for (const pattern of patterns) {
5225
+ if (pattern.startsWith("!"))
5226
+ continue;
5227
+ const dir = pattern.replace(/\/?\*\*?$/, "");
5228
+ if (dir && !dir.includes("*")) {
5229
+ dirsToScan.add(dir);
5230
+ }
5231
+ }
5232
+ dirsToScan.add("packages");
5233
+ for (const dir of dirsToScan) {
5234
+ if (!await fs7.exists(dir))
5235
+ continue;
5236
+ if (!await fs7.isDirectory(dir))
5237
+ continue;
5238
+ const subdirs = await fs7.readDir(dir);
5239
+ for (const subdir of subdirs) {
5240
+ const pkgPath = `${dir}/${subdir}`;
5241
+ const pkgJsonPath = `${pkgPath}/package.json`;
5242
+ if (!await fs7.exists(pkgJsonPath))
5243
+ continue;
5244
+ try {
5245
+ const content = await fs7.readFile(pkgJsonPath);
5246
+ if (content.includes("No such file"))
5247
+ continue;
5248
+ const pkg = JSON.parse(content);
5249
+ if (pkg.name && !seen.has(pkg.name)) {
5250
+ seen.add(pkg.name);
5251
+ packages.push({
5252
+ name: pkg.name,
5253
+ path: pkgPath,
5254
+ private: pkg.private ?? false
5255
+ });
5256
+ }
5257
+ } catch {}
5258
+ }
5259
+ }
5260
+ return packages.sort((a, b) => a.name.localeCompare(b.name));
5261
+ }
5262
+ function findPackageByName(packages, nameOrPath) {
5263
+ return packages.find((p) => p.name === nameOrPath || p.path === nameOrPath);
5264
+ }
5265
+ function formatPackageList(packages, limit = 10) {
5266
+ const publicPackages = packages.filter((p) => !p.private);
5267
+ const lines = publicPackages.slice(0, limit).map((pkg) => ` --package ${pkg.name}`);
5268
+ if (publicPackages.length > limit) {
5269
+ lines.push(` ... and ${publicPackages.length - limit} more`);
5270
+ }
5271
+ return lines.join(`
5272
+ `);
5273
+ }
5274
+ // src/detect/entry-point.ts
5275
+ async function detectEntryPoint(fs7, packagePath = ".") {
5276
+ const pkgJson = await readPackageJson(fs7, packagePath);
5277
+ if (!pkgJson) {
5278
+ throw new Error("No package.json found - not a valid npm package");
5279
+ }
5280
+ const typesField = pkgJson.types || pkgJson.typings;
5281
+ if (typesField && typeof typesField === "string") {
5282
+ const resolved = await resolveToSource(fs7, packagePath, typesField);
5283
+ if (resolved) {
5284
+ return { ...resolved, source: "types" };
5285
+ }
5286
+ }
5287
+ if (pkgJson.exports && typeof pkgJson.exports === "object") {
5288
+ const dotExport = pkgJson.exports["."];
5289
+ if (dotExport && typeof dotExport === "object" && "types" in dotExport) {
5290
+ const typesPath = dotExport.types;
5291
+ if (typesPath && typeof typesPath === "string") {
5292
+ const resolved = await resolveToSource(fs7, packagePath, typesPath);
5293
+ if (resolved) {
5294
+ return { ...resolved, source: "exports" };
5295
+ }
5296
+ }
5297
+ }
5298
+ }
5299
+ if (pkgJson.main && typeof pkgJson.main === "string") {
5300
+ const resolved = await resolveToSource(fs7, packagePath, pkgJson.main);
5301
+ if (resolved) {
5302
+ return { ...resolved, source: "main" };
5303
+ }
5304
+ }
5305
+ if (pkgJson.module && typeof pkgJson.module === "string") {
5306
+ const resolved = await resolveToSource(fs7, packagePath, pkgJson.module);
5307
+ if (resolved) {
5308
+ return { ...resolved, source: "module" };
5309
+ }
5310
+ }
5311
+ const fallbacks = [
5312
+ "src/index.ts",
5313
+ "src/index.tsx",
5314
+ "src/main.ts",
5315
+ "index.ts",
5316
+ "lib/index.ts",
5317
+ "source/index.ts"
5318
+ ];
5319
+ for (const fallback of fallbacks) {
5320
+ const fullPath = packagePath === "." ? fallback : `${packagePath}/${fallback}`;
5321
+ if (await fs7.exists(fullPath)) {
5322
+ return { path: fullPath, source: "fallback", isDeclarationOnly: false };
5323
+ }
5324
+ }
5325
+ throw new Error("Could not detect TypeScript entry point. No types field in package.json and no common entry paths found.");
5326
+ }
5327
+ async function resolveToSource(fs7, basePath, filePath) {
5328
+ const normalized = filePath.replace(/^\.\//, "");
5329
+ const fullPath = (p) => basePath === "." ? p : `${basePath}/${p}`;
5330
+ const isSourceTs = normalized.endsWith(".ts") && !normalized.endsWith(".d.ts") || normalized.endsWith(".tsx");
5331
+ if (isSourceTs) {
5332
+ const path8 = fullPath(normalized);
5333
+ if (await fs7.exists(path8)) {
5334
+ return { path: path8, isDeclarationOnly: false };
5335
+ }
5336
+ }
5337
+ const candidates = [];
5338
+ if (normalized.startsWith("dist/")) {
5339
+ const srcPath = normalized.replace(/^dist\//, "src/");
5340
+ candidates.push(srcPath.replace(/\.js$/, ".ts"));
5341
+ candidates.push(srcPath.replace(/\.d\.ts$/, ".ts"));
5342
+ candidates.push(srcPath.replace(/\.js$/, ".tsx"));
5343
+ }
5344
+ if (normalized.startsWith("build/")) {
5345
+ const srcPath = normalized.replace(/^build\//, "src/");
5346
+ candidates.push(srcPath.replace(/\.js$/, ".ts"));
5347
+ candidates.push(srcPath.replace(/\.d\.ts$/, ".ts"));
5348
+ }
5349
+ if (normalized.startsWith("lib/")) {
5350
+ const srcPath = normalized.replace(/^lib\//, "src/");
5351
+ candidates.push(srcPath.replace(/\.js$/, ".ts"));
5352
+ candidates.push(srcPath.replace(/\.d\.ts$/, ".ts"));
5353
+ }
5354
+ candidates.push(normalized.replace(/\.js$/, ".ts"));
5355
+ candidates.push(normalized.replace(/\.d\.ts$/, ".ts"));
5356
+ candidates.push(normalized.replace(/\.js$/, ".tsx"));
5357
+ if (normalized.endsWith(".d.ts")) {
5358
+ const baseName = normalized.replace(/\.d\.ts$/, "").split("/").pop();
5359
+ if (baseName) {
5360
+ candidates.push(`src/${baseName}.ts`);
5361
+ }
5362
+ }
5363
+ for (const candidate of candidates) {
5364
+ if (candidate.endsWith(".d.ts"))
5365
+ continue;
5366
+ const path8 = fullPath(candidate);
5367
+ if (await fs7.exists(path8)) {
5368
+ return { path: path8, isDeclarationOnly: false };
5369
+ }
5370
+ }
5371
+ if (normalized.endsWith(".d.ts")) {
5372
+ const path8 = fullPath(normalized);
5373
+ if (await fs7.exists(path8)) {
5374
+ return { path: path8, isDeclarationOnly: true };
5375
+ }
5376
+ }
5377
+ return null;
5378
+ }
5379
+ // src/detect/build.ts
5380
+ async function detectBuildInfo(fs7, packagePath = ".") {
5381
+ const pkgJson = await readPackageJson(fs7, packagePath);
5382
+ const scripts = pkgJson?.scripts ?? {};
5383
+ const scriptNames = Object.keys(scripts);
5384
+ const buildScripts = scriptNames.filter((name) => name === "build" || name === "compile" || name === "tsc" || name.startsWith("build:") || name.startsWith("compile:"));
5385
+ const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
5386
+ const hasTsConfig = await fs7.exists(tsconfigPath);
5387
+ const hasTsDep = pkgJson?.devDependencies?.typescript !== undefined || pkgJson?.dependencies?.typescript !== undefined;
5388
+ const hasTypeScript = hasTsConfig || hasTsDep;
5389
+ const wasm = await detectWasmProject(fs7, packagePath, scripts);
5390
+ const napi = detectNapiProject(pkgJson);
5391
+ return {
5392
+ scripts: buildScripts,
5393
+ hasBuildScript: buildScripts.length > 0,
5394
+ hasTypeScript,
5395
+ exoticIndicators: { wasm, napi }
5396
+ };
5397
+ }
5398
+ async function detectWasmProject(fs7, packagePath, scripts) {
5399
+ const pkgCargoPath = packagePath === "." ? "Cargo.toml" : `${packagePath}/Cargo.toml`;
5400
+ if (await fs7.exists(pkgCargoPath))
5401
+ return true;
5402
+ if (packagePath !== "." && await fs7.exists("Cargo.toml"))
5403
+ return true;
5404
+ const allScripts = Object.values(scripts).join(" ");
5405
+ return allScripts.includes("wasm-pack") || allScripts.includes("wasm");
5406
+ }
5407
+ function detectNapiProject(pkgJson) {
5408
+ if (!pkgJson)
5409
+ return false;
5410
+ const deps = {
5411
+ ...pkgJson.dependencies ?? {},
5412
+ ...pkgJson.devDependencies ?? {}
5413
+ };
5414
+ return Object.keys(deps).some((dep) => dep.includes("napi"));
5415
+ }
5416
+ function getPrimaryBuildScript(buildInfo) {
5417
+ if (buildInfo.scripts.includes("build"))
5418
+ return "build";
5419
+ if (buildInfo.scripts.includes("compile"))
5420
+ return "compile";
5421
+ if (buildInfo.scripts.includes("tsc"))
5422
+ return "tsc";
5423
+ return buildInfo.scripts[0] ?? null;
5424
+ }
5425
+ // src/detect/index.ts
5426
+ async function analyzeProject(fs7, options = {}) {
5427
+ const [packageManager, monorepo] = await Promise.all([
5428
+ detectPackageManager2(fs7),
5429
+ detectMonorepo(fs7)
5430
+ ]);
5431
+ let targetPath = ".";
5432
+ if (monorepo.isMonorepo) {
5433
+ if (!options.targetPackage) {
5434
+ const publicPackages = monorepo.packages.filter((p) => !p.private);
5435
+ const packageNames = publicPackages.map((p) => p.name).join(", ");
5436
+ throw new Error(`Monorepo detected with ${publicPackages.length} packages. ` + `Specify target with --package. Available: ${packageNames}`);
5437
+ }
5438
+ const pkg = findPackageByName(monorepo.packages, options.targetPackage);
5439
+ if (!pkg) {
5440
+ const available = monorepo.packages.map((p) => p.name).join(", ");
5441
+ throw new Error(`Package not found: ${options.targetPackage}. Available packages: ${available}`);
5442
+ }
5443
+ targetPath = pkg.path;
5444
+ }
5445
+ const [entryPoint, build] = await Promise.all([
5446
+ detectEntryPoint(fs7, targetPath),
5447
+ detectBuildInfo(fs7, targetPath)
5448
+ ]);
5449
+ return { packageManager, monorepo, entryPoint, build };
5450
+ }
4663
5451
  export {
4664
5452
  serializeJSDoc,
5453
+ safeParseJson,
4665
5454
  runExamplesWithPackage,
4666
5455
  runExamples,
4667
5456
  runExample,
5457
+ readPackageJson,
5458
+ parseMarkdownFiles,
5459
+ parseMarkdownFile,
4668
5460
  parseJSDocToPatch,
4669
5461
  parseAssertions,
4670
5462
  mergeFixes,
4671
5463
  isFixableDrift,
5464
+ isExecutableLang,
4672
5465
  hasNonAssertionComments,
5466
+ hasDocsImpact,
5467
+ hasDocsForExport,
5468
+ getUndocumentedExports,
5469
+ getRunCommand,
5470
+ getPrimaryBuildScript,
5471
+ getInstallCommand2 as getInstallCommand,
5472
+ getDocumentedExports,
5473
+ getDocsImpactSummary,
4673
5474
  generateFixesForExport,
4674
5475
  generateFix,
5476
+ formatPackageList,
5477
+ findRemovedReferences,
5478
+ findPackageByName,
4675
5479
  findJSDocLocation,
5480
+ findExportReferences,
5481
+ findDeprecatedReferences,
4676
5482
  extractPackageSpec,
5483
+ extractImports,
5484
+ extractFunctionCalls,
5485
+ diffSpecWithDocs,
5486
+ detectPackageManager2 as detectPackageManager,
5487
+ detectMonorepo,
4677
5488
  detectExampleRuntimeErrors,
4678
5489
  detectExampleAssertionFailures,
5490
+ detectEntryPoint,
5491
+ detectBuildInfo,
4679
5492
  createSourceFile,
4680
5493
  categorizeDrifts,
5494
+ blockReferencesExport,
4681
5495
  applyPatchToJSDoc,
4682
5496
  applyEdits,
5497
+ analyzeProject,
4683
5498
  analyzeFile,
5499
+ analyzeDocsImpact,
4684
5500
  analyze,
5501
+ SandboxFileSystem,
4685
5502
  OpenPkg,
5503
+ NodeFileSystem,
4686
5504
  DocCov
4687
5505
  };