@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.d.ts +466 -2
- package/dist/index.js +838 -20
- package/package.json +8 -2
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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,
|
|
4425
|
+
ts2.forEachChild(node, visit2);
|
|
4110
4426
|
}
|
|
4111
|
-
|
|
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
|
};
|