@doccov/sdk 0.6.0 → 0.7.0
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 +633 -8
- package/dist/index.js +564 -41
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = createRequire(import.meta.url);
|
|
19
|
+
|
|
1
20
|
// src/analysis/docs-coverage.ts
|
|
2
21
|
import ts from "typescript";
|
|
3
22
|
|
|
@@ -2042,7 +2061,7 @@ function createAnalysisContext({
|
|
|
2042
2061
|
// src/analysis/spec-builder.ts
|
|
2043
2062
|
import * as fs2 from "node:fs";
|
|
2044
2063
|
import * as path3 from "node:path";
|
|
2045
|
-
import { SCHEMA_URL } from "@openpkg-ts/spec";
|
|
2064
|
+
import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
|
|
2046
2065
|
|
|
2047
2066
|
// src/utils/parameter-utils.ts
|
|
2048
2067
|
var BUILTIN_TYPE_SCHEMAS = {
|
|
@@ -3141,14 +3160,23 @@ function processTagContent(result, tag, content) {
|
|
|
3141
3160
|
result.rawParamNames.push(parsed.name);
|
|
3142
3161
|
result.params.push(parsed);
|
|
3143
3162
|
}
|
|
3144
|
-
result.tags.push({
|
|
3163
|
+
result.tags.push({
|
|
3164
|
+
name: "param",
|
|
3165
|
+
text: trimmedContent,
|
|
3166
|
+
paramName: parsed?.name,
|
|
3167
|
+
typeAnnotation: parsed?.type
|
|
3168
|
+
});
|
|
3145
3169
|
break;
|
|
3146
3170
|
}
|
|
3147
3171
|
case "returns":
|
|
3148
3172
|
case "return": {
|
|
3149
3173
|
const parsed = parseReturnContent(trimmedContent);
|
|
3150
3174
|
result.returns = parsed;
|
|
3151
|
-
result.tags.push({
|
|
3175
|
+
result.tags.push({
|
|
3176
|
+
name: "returns",
|
|
3177
|
+
text: trimmedContent,
|
|
3178
|
+
typeAnnotation: parsed.type
|
|
3179
|
+
});
|
|
3152
3180
|
break;
|
|
3153
3181
|
}
|
|
3154
3182
|
case "example": {
|
|
@@ -3156,24 +3184,68 @@ function processTagContent(result, tag, content) {
|
|
|
3156
3184
|
if (example) {
|
|
3157
3185
|
result.examples.push(example);
|
|
3158
3186
|
}
|
|
3187
|
+
const langMatch = trimmedContent.match(/^```(\w+)/);
|
|
3188
|
+
result.tags.push({
|
|
3189
|
+
name: "example",
|
|
3190
|
+
text: example,
|
|
3191
|
+
language: langMatch?.[1]
|
|
3192
|
+
});
|
|
3159
3193
|
break;
|
|
3160
3194
|
}
|
|
3161
3195
|
case "see": {
|
|
3162
3196
|
const linkTargets = extractAllLinkTargets(trimmedContent);
|
|
3163
3197
|
for (const target of linkTargets) {
|
|
3164
|
-
result.tags.push({ name: "link", text: target });
|
|
3165
|
-
result.tags.push({ name: "see", text: target });
|
|
3198
|
+
result.tags.push({ name: "link", text: target, reference: target });
|
|
3199
|
+
result.tags.push({ name: "see", text: target, reference: target });
|
|
3166
3200
|
}
|
|
3167
3201
|
if (linkTargets.length === 0) {
|
|
3168
|
-
result.tags.push({ name: "see", text: trimmedContent });
|
|
3202
|
+
result.tags.push({ name: "see", text: trimmedContent, reference: trimmedContent });
|
|
3169
3203
|
}
|
|
3170
3204
|
break;
|
|
3171
3205
|
}
|
|
3172
3206
|
case "link": {
|
|
3173
3207
|
const target = extractLinkTarget(trimmedContent);
|
|
3174
3208
|
if (target) {
|
|
3175
|
-
result.tags.push({ name: "link", text: target });
|
|
3209
|
+
result.tags.push({ name: "link", text: target, reference: target });
|
|
3210
|
+
}
|
|
3211
|
+
break;
|
|
3212
|
+
}
|
|
3213
|
+
case "since": {
|
|
3214
|
+
result.tags.push({ name: "since", text: trimmedContent, version: trimmedContent });
|
|
3215
|
+
break;
|
|
3216
|
+
}
|
|
3217
|
+
case "deprecated": {
|
|
3218
|
+
const versionMatch = trimmedContent.match(/^(\d+\.\d+(?:\.\d+)?)\s*(.*)?$/);
|
|
3219
|
+
if (versionMatch) {
|
|
3220
|
+
result.tags.push({
|
|
3221
|
+
name: "deprecated",
|
|
3222
|
+
text: trimmedContent,
|
|
3223
|
+
version: versionMatch[1],
|
|
3224
|
+
reason: versionMatch[2]?.trim() || undefined
|
|
3225
|
+
});
|
|
3226
|
+
} else {
|
|
3227
|
+
result.tags.push({
|
|
3228
|
+
name: "deprecated",
|
|
3229
|
+
text: trimmedContent,
|
|
3230
|
+
reason: trimmedContent || undefined
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
break;
|
|
3234
|
+
}
|
|
3235
|
+
case "type":
|
|
3236
|
+
case "typedef": {
|
|
3237
|
+
let typeAnnotation;
|
|
3238
|
+
if (trimmedContent.startsWith("{")) {
|
|
3239
|
+
const typeEnd = findMatchingBrace(trimmedContent, 0);
|
|
3240
|
+
if (typeEnd > 0) {
|
|
3241
|
+
typeAnnotation = trimmedContent.slice(1, typeEnd).trim();
|
|
3242
|
+
}
|
|
3176
3243
|
}
|
|
3244
|
+
result.tags.push({
|
|
3245
|
+
name: tag,
|
|
3246
|
+
text: trimmedContent,
|
|
3247
|
+
typeAnnotation
|
|
3248
|
+
});
|
|
3177
3249
|
break;
|
|
3178
3250
|
}
|
|
3179
3251
|
case "inheritdoc": {
|
|
@@ -3515,10 +3587,12 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
|
|
|
3515
3587
|
const memberName = member.name?.getText() ?? "method";
|
|
3516
3588
|
const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
|
|
3517
3589
|
const methodDoc = memberSymbol ? parseJSDocComment(memberSymbol, checker) : null;
|
|
3518
|
-
const
|
|
3519
|
-
const
|
|
3520
|
-
|
|
3521
|
-
|
|
3590
|
+
const methodType = memberSymbol ? checker.getTypeOfSymbolAtLocation(memberSymbol, member) : checker.getTypeAtLocation(member);
|
|
3591
|
+
const callSignatures = methodType.getCallSignatures();
|
|
3592
|
+
const signatures = callSignatures.length > 0 ? callSignatures.map((sig, index) => ({
|
|
3593
|
+
...serializeSignature(sig, checker, typeRefs, referencedTypes, methodDoc, memberSymbol),
|
|
3594
|
+
overloadIndex: callSignatures.length > 1 ? index : undefined
|
|
3595
|
+
})) : undefined;
|
|
3522
3596
|
members.push({
|
|
3523
3597
|
id: memberName,
|
|
3524
3598
|
name: memberName,
|
|
@@ -3534,8 +3608,12 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
|
|
|
3534
3608
|
if (ts2.isConstructorDeclaration(member)) {
|
|
3535
3609
|
const ctorSymbol = checker.getSymbolAtLocation(member);
|
|
3536
3610
|
const ctorDoc = ctorSymbol ? parseJSDocComment(ctorSymbol, checker) : null;
|
|
3537
|
-
const
|
|
3538
|
-
const
|
|
3611
|
+
const classType = checker.getTypeAtLocation(declaration);
|
|
3612
|
+
const constructSignatures = classType.getConstructSignatures();
|
|
3613
|
+
const signatures = constructSignatures.length > 0 ? constructSignatures.map((sig, index) => ({
|
|
3614
|
+
...serializeSignature(sig, checker, typeRefs, referencedTypes, ctorDoc, ctorSymbol),
|
|
3615
|
+
overloadIndex: constructSignatures.length > 1 ? index : undefined
|
|
3616
|
+
})) : undefined;
|
|
3539
3617
|
members.push({
|
|
3540
3618
|
id: "constructor",
|
|
3541
3619
|
name: "constructor",
|
|
@@ -3677,7 +3755,7 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
|
|
|
3677
3755
|
const typeRefs = typeRegistry.getTypeRefs();
|
|
3678
3756
|
const referencedTypes = typeRegistry.getReferencedTypes();
|
|
3679
3757
|
const functionDoc = parsedDoc ?? (symbol ? parseJSDocComment(symbol, checker) : null);
|
|
3680
|
-
return signatures.map((signature) => {
|
|
3758
|
+
return signatures.map((signature, index) => {
|
|
3681
3759
|
const parameters = signature.getParameters().map((param) => {
|
|
3682
3760
|
const paramDecl = param.declarations?.find(ts2.isParameter);
|
|
3683
3761
|
const location = symbol?.declarations?.[0] ?? signature.declaration ?? param.declarations?.[0] ?? param.valueDeclaration;
|
|
@@ -3724,7 +3802,8 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
|
|
|
3724
3802
|
...typePredicateInfo ? { typePredicate: typePredicateInfo } : {}
|
|
3725
3803
|
},
|
|
3726
3804
|
description: functionDoc?.description || undefined,
|
|
3727
|
-
typeParameters
|
|
3805
|
+
typeParameters,
|
|
3806
|
+
overloadIndex: signatures.length > 1 ? index : undefined
|
|
3728
3807
|
};
|
|
3729
3808
|
});
|
|
3730
3809
|
}
|
|
@@ -3785,26 +3864,37 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
|
|
|
3785
3864
|
continue;
|
|
3786
3865
|
const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
|
|
3787
3866
|
const methodDoc = memberSymbol ? parseJSDocComment(memberSymbol, checker) : null;
|
|
3788
|
-
const
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
const
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3867
|
+
const methodType = memberSymbol ? checker.getTypeOfSymbolAtLocation(memberSymbol, member) : checker.getTypeAtLocation(member);
|
|
3868
|
+
const callSignatures = methodType.getCallSignatures();
|
|
3869
|
+
if (callSignatures.length > 0) {
|
|
3870
|
+
const signatures = callSignatures.map((signature, index) => {
|
|
3871
|
+
const parameters = signature.getParameters().map((param) => {
|
|
3872
|
+
const paramDecl = param.declarations?.find(ts2.isParameter);
|
|
3873
|
+
const paramType = paramDecl ? checker.getTypeAtLocation(paramDecl) : checker.getTypeOfSymbolAtLocation(param, member);
|
|
3874
|
+
collectReferencedTypes(paramType, checker, referencedTypes);
|
|
3875
|
+
if (paramDecl) {
|
|
3876
|
+
const paramDoc = getParameterDocumentation(param, paramDecl, checker);
|
|
3877
|
+
return structureParameter(param, paramDecl, paramType, checker, typeRefs, null, paramDoc, referencedTypes);
|
|
3878
|
+
}
|
|
3879
|
+
return {
|
|
3880
|
+
name: param.getName(),
|
|
3881
|
+
required: !(param.flags & ts2.SymbolFlags.Optional),
|
|
3882
|
+
schema: formatTypeReference(paramType, checker, typeRefs, referencedTypes)
|
|
3883
|
+
};
|
|
3884
|
+
});
|
|
3885
|
+
const returnType = signature.getReturnType();
|
|
3886
|
+
if (returnType) {
|
|
3887
|
+
collectReferencedTypes(returnType, checker, referencedTypes);
|
|
3797
3888
|
}
|
|
3798
3889
|
return {
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3890
|
+
parameters,
|
|
3891
|
+
returns: {
|
|
3892
|
+
schema: returnType ? formatTypeReference(returnType, checker, typeRefs, referencedTypes) : { type: "void" }
|
|
3893
|
+
},
|
|
3894
|
+
description: methodDoc?.description,
|
|
3895
|
+
overloadIndex: callSignatures.length > 1 ? index : undefined
|
|
3802
3896
|
};
|
|
3803
3897
|
});
|
|
3804
|
-
const returnType = signature.getReturnType();
|
|
3805
|
-
if (returnType) {
|
|
3806
|
-
collectReferencedTypes(returnType, checker, referencedTypes);
|
|
3807
|
-
}
|
|
3808
3898
|
const flags = {};
|
|
3809
3899
|
if (member.questionToken) {
|
|
3810
3900
|
flags.optional = true;
|
|
@@ -3813,15 +3903,7 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
|
|
|
3813
3903
|
id: methodName,
|
|
3814
3904
|
name: methodName,
|
|
3815
3905
|
kind: "method",
|
|
3816
|
-
signatures
|
|
3817
|
-
{
|
|
3818
|
-
parameters,
|
|
3819
|
-
returns: {
|
|
3820
|
-
schema: returnType ? formatTypeReference(returnType, checker, typeRefs, referencedTypes) : { type: "void" }
|
|
3821
|
-
},
|
|
3822
|
-
description: methodDoc?.description
|
|
3823
|
-
}
|
|
3824
|
-
],
|
|
3906
|
+
signatures,
|
|
3825
3907
|
description: methodDoc?.description ?? (memberSymbol ? getJSDocComment(memberSymbol, checker) : undefined),
|
|
3826
3908
|
flags: Object.keys(flags).length > 0 ? flags : undefined,
|
|
3827
3909
|
tags: methodDoc?.tags
|
|
@@ -4270,7 +4352,7 @@ function buildOpenPkgSpec(context, resolveExternalTypes) {
|
|
|
4270
4352
|
const packageJson = fs2.existsSync(packageJsonPath) ? JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8")) : {};
|
|
4271
4353
|
const spec = {
|
|
4272
4354
|
$schema: SCHEMA_URL,
|
|
4273
|
-
openpkg:
|
|
4355
|
+
openpkg: SCHEMA_VERSION,
|
|
4274
4356
|
meta: {
|
|
4275
4357
|
name: packageJson.name || "unknown",
|
|
4276
4358
|
version: packageJson.version || "1.0.0",
|
|
@@ -4666,6 +4748,48 @@ async function extractPackageSpec(entryFile, packageDir, content, options) {
|
|
|
4666
4748
|
});
|
|
4667
4749
|
return result.spec;
|
|
4668
4750
|
}
|
|
4751
|
+
// src/filtering/merge.ts
|
|
4752
|
+
function unique(values) {
|
|
4753
|
+
return Array.from(new Set(values));
|
|
4754
|
+
}
|
|
4755
|
+
function parseListFlag(value) {
|
|
4756
|
+
if (!value) {
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
const rawItems = Array.isArray(value) ? value : [value];
|
|
4760
|
+
const normalized = rawItems.flatMap((item) => String(item).split(",")).map((item) => item.trim()).filter(Boolean);
|
|
4761
|
+
return normalized.length > 0 ? unique(normalized) : undefined;
|
|
4762
|
+
}
|
|
4763
|
+
function mergeFilters(config, overrides) {
|
|
4764
|
+
const configInclude = config?.include;
|
|
4765
|
+
const configExclude = config?.exclude;
|
|
4766
|
+
const overrideInclude = overrides.include;
|
|
4767
|
+
const overrideExclude = overrides.exclude;
|
|
4768
|
+
let include = configInclude;
|
|
4769
|
+
let exclude = configExclude;
|
|
4770
|
+
let source = include || exclude ? "config" : undefined;
|
|
4771
|
+
const fromConfig = !!(configInclude || configExclude);
|
|
4772
|
+
let fromOverride = false;
|
|
4773
|
+
if (overrideInclude) {
|
|
4774
|
+
include = include ? include.filter((item) => overrideInclude.includes(item)) : overrideInclude;
|
|
4775
|
+
source = source ? "combined" : "override";
|
|
4776
|
+
fromOverride = true;
|
|
4777
|
+
}
|
|
4778
|
+
if (overrideExclude) {
|
|
4779
|
+
exclude = exclude ? unique([...exclude, ...overrideExclude]) : overrideExclude;
|
|
4780
|
+
source = source ? "combined" : "override";
|
|
4781
|
+
fromOverride = true;
|
|
4782
|
+
}
|
|
4783
|
+
include = include ? unique(include) : undefined;
|
|
4784
|
+
exclude = exclude ? unique(exclude) : undefined;
|
|
4785
|
+
return {
|
|
4786
|
+
include,
|
|
4787
|
+
exclude,
|
|
4788
|
+
source,
|
|
4789
|
+
fromConfig,
|
|
4790
|
+
fromOverride
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4669
4793
|
// src/fix/deterministic-fixes.ts
|
|
4670
4794
|
var FIXABLE_DRIFT_TYPES = new Set([
|
|
4671
4795
|
"param-mismatch",
|
|
@@ -7056,6 +7180,390 @@ async function runExamplesWithPackage(examples, options) {
|
|
|
7056
7180
|
} catch {}
|
|
7057
7181
|
}
|
|
7058
7182
|
}
|
|
7183
|
+
// src/scan/summary.ts
|
|
7184
|
+
function extractSpecSummary(spec) {
|
|
7185
|
+
const exports = spec.exports ?? [];
|
|
7186
|
+
const undocumented = [];
|
|
7187
|
+
const drift = [];
|
|
7188
|
+
for (const exp of exports) {
|
|
7189
|
+
const docs = exp.docs;
|
|
7190
|
+
if (!docs)
|
|
7191
|
+
continue;
|
|
7192
|
+
const hasMissing = (docs.missing?.length ?? 0) > 0;
|
|
7193
|
+
const isPartial = (docs.coverageScore ?? 0) < 100;
|
|
7194
|
+
if (hasMissing || isPartial) {
|
|
7195
|
+
undocumented.push(exp.name);
|
|
7196
|
+
}
|
|
7197
|
+
for (const d of docs.drift ?? []) {
|
|
7198
|
+
drift.push({
|
|
7199
|
+
export: exp.name,
|
|
7200
|
+
type: d.type,
|
|
7201
|
+
issue: d.issue,
|
|
7202
|
+
suggestion: d.suggestion
|
|
7203
|
+
});
|
|
7204
|
+
}
|
|
7205
|
+
}
|
|
7206
|
+
return {
|
|
7207
|
+
coverage: spec.docs?.coverageScore ?? 0,
|
|
7208
|
+
exportCount: exports.length,
|
|
7209
|
+
typeCount: spec.types?.length ?? 0,
|
|
7210
|
+
driftCount: drift.length,
|
|
7211
|
+
undocumented,
|
|
7212
|
+
drift
|
|
7213
|
+
};
|
|
7214
|
+
}
|
|
7215
|
+
// src/install/index.ts
|
|
7216
|
+
var DEFAULT_FALLBACK_ORDER = ["bun", "npm"];
|
|
7217
|
+
async function installDependencies(fs8, cwd, runCommand2, options = {}) {
|
|
7218
|
+
const {
|
|
7219
|
+
timeout = 180000,
|
|
7220
|
+
fallbackOrder = DEFAULT_FALLBACK_ORDER,
|
|
7221
|
+
onProgress
|
|
7222
|
+
} = options;
|
|
7223
|
+
const errors = [];
|
|
7224
|
+
onProgress?.({ stage: "installing", message: "Detecting package manager..." });
|
|
7225
|
+
const pmInfo = await detectPackageManager(fs8);
|
|
7226
|
+
if (pmInfo.lockfile) {
|
|
7227
|
+
onProgress?.({
|
|
7228
|
+
stage: "installing",
|
|
7229
|
+
message: `Installing with ${pmInfo.name}...`,
|
|
7230
|
+
progress: 25
|
|
7231
|
+
});
|
|
7232
|
+
const installCmd = getInstallCommand(pmInfo);
|
|
7233
|
+
const result = await runCommand2(installCmd[0], installCmd.slice(1), { cwd, timeout });
|
|
7234
|
+
if (result.exitCode === 0) {
|
|
7235
|
+
onProgress?.({ stage: "installing", message: "Dependencies installed", progress: 100 });
|
|
7236
|
+
return {
|
|
7237
|
+
success: true,
|
|
7238
|
+
packageManager: pmInfo.name
|
|
7239
|
+
};
|
|
7240
|
+
}
|
|
7241
|
+
const errorMsg = result.stderr.slice(0, 150) || `Exit code ${result.exitCode}`;
|
|
7242
|
+
errors.push(`[${installCmd.join(" ")}] ${errorMsg}`);
|
|
7243
|
+
if (result.stderr.includes("workspace:") || result.stderr.includes("EUNSUPPORTEDPROTOCOL")) {
|
|
7244
|
+
onProgress?.({
|
|
7245
|
+
stage: "installing",
|
|
7246
|
+
message: "Workspace protocol detected, trying bun...",
|
|
7247
|
+
progress: 35
|
|
7248
|
+
});
|
|
7249
|
+
}
|
|
7250
|
+
}
|
|
7251
|
+
for (const fallbackPm of fallbackOrder) {
|
|
7252
|
+
if (pmInfo.lockfile && fallbackPm === pmInfo.name)
|
|
7253
|
+
continue;
|
|
7254
|
+
onProgress?.({
|
|
7255
|
+
stage: "installing",
|
|
7256
|
+
message: `Trying ${fallbackPm} fallback...`,
|
|
7257
|
+
progress: 50
|
|
7258
|
+
});
|
|
7259
|
+
const fallbackCmd = getFallbackInstallCommand(fallbackPm);
|
|
7260
|
+
const result = await runCommand2(fallbackCmd[0], fallbackCmd.slice(1), { cwd, timeout });
|
|
7261
|
+
if (result.exitCode === 0) {
|
|
7262
|
+
onProgress?.({ stage: "installing", message: "Dependencies installed", progress: 100 });
|
|
7263
|
+
return {
|
|
7264
|
+
success: true,
|
|
7265
|
+
packageManager: fallbackPm,
|
|
7266
|
+
fallbackUsed: fallbackPm
|
|
7267
|
+
};
|
|
7268
|
+
}
|
|
7269
|
+
const errorMsg = result.stderr.slice(0, 150) || `Exit code ${result.exitCode}`;
|
|
7270
|
+
errors.push(`[${fallbackCmd.join(" ")}] ${errorMsg}`);
|
|
7271
|
+
}
|
|
7272
|
+
onProgress?.({
|
|
7273
|
+
stage: "installing",
|
|
7274
|
+
message: "Could not install dependencies",
|
|
7275
|
+
progress: 100
|
|
7276
|
+
});
|
|
7277
|
+
return {
|
|
7278
|
+
success: false,
|
|
7279
|
+
packageManager: pmInfo.name,
|
|
7280
|
+
error: "All installation attempts failed",
|
|
7281
|
+
errors
|
|
7282
|
+
};
|
|
7283
|
+
}
|
|
7284
|
+
function getFallbackInstallCommand(pm) {
|
|
7285
|
+
switch (pm) {
|
|
7286
|
+
case "npm":
|
|
7287
|
+
return ["npm", "install", "--legacy-peer-deps", "--ignore-scripts"];
|
|
7288
|
+
case "bun":
|
|
7289
|
+
return ["bun", "install"];
|
|
7290
|
+
case "yarn":
|
|
7291
|
+
return ["yarn", "install"];
|
|
7292
|
+
case "pnpm":
|
|
7293
|
+
return ["pnpm", "install"];
|
|
7294
|
+
default:
|
|
7295
|
+
return ["npm", "install"];
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
function createNodeCommandRunner() {
|
|
7299
|
+
return async (cmd, args, options) => {
|
|
7300
|
+
const { execSync } = await import("node:child_process");
|
|
7301
|
+
const fullCmd = [cmd, ...args].join(" ");
|
|
7302
|
+
try {
|
|
7303
|
+
const stdout = execSync(fullCmd, {
|
|
7304
|
+
cwd: options.cwd,
|
|
7305
|
+
stdio: "pipe",
|
|
7306
|
+
timeout: options.timeout ?? 180000
|
|
7307
|
+
});
|
|
7308
|
+
return {
|
|
7309
|
+
exitCode: 0,
|
|
7310
|
+
stdout: stdout?.toString() ?? "",
|
|
7311
|
+
stderr: ""
|
|
7312
|
+
};
|
|
7313
|
+
} catch (error) {
|
|
7314
|
+
const err = error;
|
|
7315
|
+
return {
|
|
7316
|
+
exitCode: err.status ?? 1,
|
|
7317
|
+
stdout: err.stdout?.toString() ?? "",
|
|
7318
|
+
stderr: err.stderr?.toString() ?? err.message ?? "Unknown error"
|
|
7319
|
+
};
|
|
7320
|
+
}
|
|
7321
|
+
};
|
|
7322
|
+
}
|
|
7323
|
+
|
|
7324
|
+
// src/github/index.ts
|
|
7325
|
+
function parseGitHubUrl(input, defaultRef = "main") {
|
|
7326
|
+
const trimmed = input.trim();
|
|
7327
|
+
if (!trimmed) {
|
|
7328
|
+
throw new Error("GitHub URL cannot be empty");
|
|
7329
|
+
}
|
|
7330
|
+
let normalized = trimmed.replace(/^https?:\/\//, "").replace(/^git@github\.com:/, "").replace(/\.git$/, "");
|
|
7331
|
+
normalized = normalized.replace(/^github\.com\//, "");
|
|
7332
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
7333
|
+
if (parts.length < 2) {
|
|
7334
|
+
throw new Error(`Invalid GitHub URL format: "${input}". Expected owner/repo or https://github.com/owner/repo`);
|
|
7335
|
+
}
|
|
7336
|
+
const owner = parts[0];
|
|
7337
|
+
const repo = parts[1];
|
|
7338
|
+
let ref = defaultRef;
|
|
7339
|
+
if (parts.length >= 4 && (parts[2] === "tree" || parts[2] === "blob")) {
|
|
7340
|
+
ref = parts.slice(3).join("/");
|
|
7341
|
+
}
|
|
7342
|
+
if (!owner || !repo) {
|
|
7343
|
+
throw new Error(`Could not parse owner/repo from: "${input}"`);
|
|
7344
|
+
}
|
|
7345
|
+
return { owner, repo, ref };
|
|
7346
|
+
}
|
|
7347
|
+
function buildCloneUrl(parsed) {
|
|
7348
|
+
return `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
7349
|
+
}
|
|
7350
|
+
function buildDisplayUrl(parsed) {
|
|
7351
|
+
return `github.com/${parsed.owner}/${parsed.repo}`;
|
|
7352
|
+
}
|
|
7353
|
+
function buildRawUrl(parsed, filePath) {
|
|
7354
|
+
return `https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/${parsed.ref}/${filePath}`;
|
|
7355
|
+
}
|
|
7356
|
+
async function fetchSpecFromGitHub(parsed) {
|
|
7357
|
+
const urls = [
|
|
7358
|
+
buildRawUrl(parsed, "openpkg.json"),
|
|
7359
|
+
`https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/master/openpkg.json`
|
|
7360
|
+
];
|
|
7361
|
+
for (const url of urls) {
|
|
7362
|
+
try {
|
|
7363
|
+
const response = await fetch(url);
|
|
7364
|
+
if (response.ok) {
|
|
7365
|
+
return await response.json();
|
|
7366
|
+
}
|
|
7367
|
+
} catch {}
|
|
7368
|
+
}
|
|
7369
|
+
return null;
|
|
7370
|
+
}
|
|
7371
|
+
async function fetchSpec(owner, repo, branch = "main") {
|
|
7372
|
+
return fetchSpecFromGitHub({ owner, repo, ref: branch });
|
|
7373
|
+
}
|
|
7374
|
+
|
|
7375
|
+
// src/scan/orchestrator.ts
|
|
7376
|
+
class ScanOrchestrator {
|
|
7377
|
+
fs;
|
|
7378
|
+
options;
|
|
7379
|
+
constructor(fs8, options = {}) {
|
|
7380
|
+
this.fs = fs8;
|
|
7381
|
+
this.options = options;
|
|
7382
|
+
}
|
|
7383
|
+
emit(event) {
|
|
7384
|
+
this.options.onProgress?.(event);
|
|
7385
|
+
}
|
|
7386
|
+
async detectPackage(packageName) {
|
|
7387
|
+
this.emit({ stage: "detecting", message: "Detecting project structure...", progress: 10 });
|
|
7388
|
+
const mono = await detectMonorepo(this.fs);
|
|
7389
|
+
if (mono.isMonorepo) {
|
|
7390
|
+
if (!packageName) {
|
|
7391
|
+
const publicPackages = mono.packages.filter((p) => !p.private);
|
|
7392
|
+
throw new MonorepoRequiresPackageError(publicPackages.map((p) => p.name));
|
|
7393
|
+
}
|
|
7394
|
+
const pkg = findPackageByName(mono.packages, packageName);
|
|
7395
|
+
if (!pkg) {
|
|
7396
|
+
throw new Error(`Package "${packageName}" not found. Available: ${mono.packages.map((p) => p.name).join(", ")}`);
|
|
7397
|
+
}
|
|
7398
|
+
this.emit({ stage: "detecting", message: `Found package: ${pkg.name}`, progress: 15 });
|
|
7399
|
+
return { targetPath: pkg.path, resolvedPackage: pkg.name };
|
|
7400
|
+
}
|
|
7401
|
+
return { targetPath: "." };
|
|
7402
|
+
}
|
|
7403
|
+
async detectEntry(targetPath) {
|
|
7404
|
+
this.emit({ stage: "detecting", message: "Detecting entry point...", progress: 18 });
|
|
7405
|
+
const entry = await detectEntryPoint(this.fs, targetPath);
|
|
7406
|
+
const entryFile = targetPath === "." ? entry.path : `${targetPath}/${entry.path}`;
|
|
7407
|
+
this.emit({
|
|
7408
|
+
stage: "detecting",
|
|
7409
|
+
message: `Entry point: ${entry.path} (from ${entry.source})`,
|
|
7410
|
+
progress: 20
|
|
7411
|
+
});
|
|
7412
|
+
return entryFile;
|
|
7413
|
+
}
|
|
7414
|
+
async install(workDir) {
|
|
7415
|
+
if (!this.options.commandRunner) {
|
|
7416
|
+
return {
|
|
7417
|
+
success: false,
|
|
7418
|
+
packageManager: "npm",
|
|
7419
|
+
error: "No command runner provided"
|
|
7420
|
+
};
|
|
7421
|
+
}
|
|
7422
|
+
this.emit({ stage: "installing", message: "Installing dependencies...", progress: 25 });
|
|
7423
|
+
const result = await installDependencies(this.fs, workDir, this.options.commandRunner, {
|
|
7424
|
+
onProgress: this.options.onProgress
|
|
7425
|
+
});
|
|
7426
|
+
if (result.success) {
|
|
7427
|
+
this.emit({ stage: "installing", message: "Dependencies installed", progress: 45 });
|
|
7428
|
+
} else {
|
|
7429
|
+
this.emit({
|
|
7430
|
+
stage: "installing",
|
|
7431
|
+
message: "Install failed (continuing with limited analysis)",
|
|
7432
|
+
progress: 45
|
|
7433
|
+
});
|
|
7434
|
+
}
|
|
7435
|
+
return result;
|
|
7436
|
+
}
|
|
7437
|
+
async build(workDir, targetPath) {
|
|
7438
|
+
if (!this.options.commandRunner)
|
|
7439
|
+
return;
|
|
7440
|
+
const buildInfo = await detectBuildInfo(this.fs, targetPath);
|
|
7441
|
+
const buildScript = getPrimaryBuildScript(buildInfo);
|
|
7442
|
+
if (!buildScript)
|
|
7443
|
+
return;
|
|
7444
|
+
this.emit({ stage: "building", message: "Running build...", progress: 50 });
|
|
7445
|
+
const result = await this.options.commandRunner("npm", ["run", buildScript], {
|
|
7446
|
+
cwd: workDir,
|
|
7447
|
+
timeout: 300000
|
|
7448
|
+
});
|
|
7449
|
+
const buildMessage = result.exitCode === 0 ? "Build complete" : "Build failed (continuing)";
|
|
7450
|
+
this.emit({ stage: "building", message: buildMessage, progress: 60 });
|
|
7451
|
+
}
|
|
7452
|
+
async analyze(entryFile) {
|
|
7453
|
+
this.emit({ stage: "analyzing", message: "Analyzing documentation...", progress: 65 });
|
|
7454
|
+
const doccov = new DocCov({ resolveExternalTypes: !this.options.skipResolve });
|
|
7455
|
+
const result = await doccov.analyzeFileWithDiagnostics(entryFile);
|
|
7456
|
+
this.emit({ stage: "analyzing", message: "Analysis complete", progress: 90 });
|
|
7457
|
+
return result.spec;
|
|
7458
|
+
}
|
|
7459
|
+
async scan(options) {
|
|
7460
|
+
const parsed = parseGitHubUrl(options.url, options.ref ?? "main");
|
|
7461
|
+
this.emit({
|
|
7462
|
+
stage: "detecting",
|
|
7463
|
+
message: `Scanning ${parsed.owner}/${parsed.repo}...`,
|
|
7464
|
+
progress: 5
|
|
7465
|
+
});
|
|
7466
|
+
const { targetPath, resolvedPackage } = await this.detectPackage(options.package);
|
|
7467
|
+
const entryFile = await this.detectEntry(targetPath);
|
|
7468
|
+
if (!options.skipInstall && this.options.commandRunner) {
|
|
7469
|
+
await this.install(".");
|
|
7470
|
+
await this.build(".", targetPath);
|
|
7471
|
+
}
|
|
7472
|
+
const spec = await this.analyze(entryFile);
|
|
7473
|
+
this.emit({ stage: "complete", message: "Extracting results...", progress: 95 });
|
|
7474
|
+
const summary = extractSpecSummary(spec);
|
|
7475
|
+
this.emit({ stage: "complete", message: "Scan complete", progress: 100 });
|
|
7476
|
+
return {
|
|
7477
|
+
owner: parsed.owner,
|
|
7478
|
+
repo: parsed.repo,
|
|
7479
|
+
ref: parsed.ref,
|
|
7480
|
+
packageName: resolvedPackage ?? options.package,
|
|
7481
|
+
coverage: summary.coverage,
|
|
7482
|
+
exportCount: summary.exportCount,
|
|
7483
|
+
typeCount: summary.typeCount,
|
|
7484
|
+
driftCount: summary.driftCount,
|
|
7485
|
+
undocumented: summary.undocumented,
|
|
7486
|
+
drift: summary.drift
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7491
|
+
class MonorepoRequiresPackageError extends Error {
|
|
7492
|
+
availablePackages;
|
|
7493
|
+
constructor(availablePackages) {
|
|
7494
|
+
super(`Monorepo detected with ${availablePackages.length} packages. ` + `Specify target with --package. Available: ${availablePackages.join(", ")}`);
|
|
7495
|
+
this.name = "MonorepoRequiresPackageError";
|
|
7496
|
+
this.availablePackages = availablePackages;
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
// src/resolve/index.ts
|
|
7500
|
+
import * as path9 from "node:path";
|
|
7501
|
+
async function resolveTarget(fs8, options) {
|
|
7502
|
+
let targetDir = options.cwd;
|
|
7503
|
+
let packageInfo;
|
|
7504
|
+
if (options.package) {
|
|
7505
|
+
const mono = await detectMonorepo(fs8);
|
|
7506
|
+
if (!mono.isMonorepo) {
|
|
7507
|
+
throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
|
|
7508
|
+
}
|
|
7509
|
+
const pkg = findPackageByName(mono.packages, options.package);
|
|
7510
|
+
if (!pkg) {
|
|
7511
|
+
const available = mono.packages.map((p) => p.name).join(", ");
|
|
7512
|
+
throw new Error(`Package "${options.package}" not found. Available: ${available}`);
|
|
7513
|
+
}
|
|
7514
|
+
targetDir = path9.join(options.cwd, pkg.path);
|
|
7515
|
+
packageInfo = pkg;
|
|
7516
|
+
}
|
|
7517
|
+
let entryFile;
|
|
7518
|
+
let entryPointInfo;
|
|
7519
|
+
if (!options.entry) {
|
|
7520
|
+
entryPointInfo = await detectEntryPoint(fs8, getRelativePath(options.cwd, targetDir));
|
|
7521
|
+
entryFile = path9.join(targetDir, entryPointInfo.path);
|
|
7522
|
+
} else {
|
|
7523
|
+
const explicitPath = path9.resolve(targetDir, options.entry);
|
|
7524
|
+
const isDirectory = await isDir(fs8, getRelativePath(options.cwd, explicitPath));
|
|
7525
|
+
if (isDirectory) {
|
|
7526
|
+
targetDir = explicitPath;
|
|
7527
|
+
entryPointInfo = await detectEntryPoint(fs8, getRelativePath(options.cwd, explicitPath));
|
|
7528
|
+
entryFile = path9.join(explicitPath, entryPointInfo.path);
|
|
7529
|
+
} else {
|
|
7530
|
+
entryFile = explicitPath;
|
|
7531
|
+
entryPointInfo = {
|
|
7532
|
+
path: options.entry,
|
|
7533
|
+
source: "explicit",
|
|
7534
|
+
isDeclarationOnly: options.entry.endsWith(".d.ts")
|
|
7535
|
+
};
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
return {
|
|
7539
|
+
targetDir,
|
|
7540
|
+
entryFile,
|
|
7541
|
+
packageInfo,
|
|
7542
|
+
entryPointInfo
|
|
7543
|
+
};
|
|
7544
|
+
}
|
|
7545
|
+
function getRelativePath(base, target) {
|
|
7546
|
+
if (base === target)
|
|
7547
|
+
return ".";
|
|
7548
|
+
const rel = path9.relative(base, target);
|
|
7549
|
+
return rel || ".";
|
|
7550
|
+
}
|
|
7551
|
+
async function isDir(fs8, relativePath) {
|
|
7552
|
+
const hasPackageJson = await fs8.exists(path9.join(relativePath, "package.json"));
|
|
7553
|
+
if (hasPackageJson)
|
|
7554
|
+
return true;
|
|
7555
|
+
const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
|
|
7556
|
+
for (const entry of commonEntryFiles) {
|
|
7557
|
+
if (await fs8.exists(path9.join(relativePath, entry))) {
|
|
7558
|
+
return true;
|
|
7559
|
+
}
|
|
7560
|
+
}
|
|
7561
|
+
return !path9.extname(relativePath);
|
|
7562
|
+
}
|
|
7563
|
+
// src/config/types.ts
|
|
7564
|
+
function defineConfig(config) {
|
|
7565
|
+
return config;
|
|
7566
|
+
}
|
|
7059
7567
|
export {
|
|
7060
7568
|
typecheckExamples,
|
|
7061
7569
|
typecheckExample,
|
|
@@ -7064,20 +7572,25 @@ export {
|
|
|
7064
7572
|
runExamplesWithPackage,
|
|
7065
7573
|
runExamples,
|
|
7066
7574
|
runExample,
|
|
7575
|
+
resolveTarget,
|
|
7067
7576
|
requireExample,
|
|
7068
7577
|
requireDescription,
|
|
7069
7578
|
readPackageJson,
|
|
7070
7579
|
parseMarkdownFiles,
|
|
7071
7580
|
parseMarkdownFile,
|
|
7581
|
+
parseListFlag,
|
|
7072
7582
|
parseJSDocToPatch,
|
|
7583
|
+
parseGitHubUrl,
|
|
7073
7584
|
parseAssertions,
|
|
7074
7585
|
noEmptyReturns,
|
|
7075
7586
|
mergeFixes,
|
|
7587
|
+
mergeFilters,
|
|
7076
7588
|
mergeConfig,
|
|
7077
7589
|
lintExports,
|
|
7078
7590
|
lintExport,
|
|
7079
7591
|
isFixableDrift,
|
|
7080
7592
|
isExecutableLang,
|
|
7593
|
+
installDependencies,
|
|
7081
7594
|
hasNonAssertionComments,
|
|
7082
7595
|
hasDocsImpact,
|
|
7083
7596
|
hasDocsForExport,
|
|
@@ -7097,6 +7610,9 @@ export {
|
|
|
7097
7610
|
findJSDocLocation,
|
|
7098
7611
|
findExportReferences,
|
|
7099
7612
|
findDeprecatedReferences,
|
|
7613
|
+
fetchSpecFromGitHub,
|
|
7614
|
+
fetchSpec,
|
|
7615
|
+
extractSpecSummary,
|
|
7100
7616
|
extractPackageSpec,
|
|
7101
7617
|
extractImports,
|
|
7102
7618
|
extractFunctionCalls,
|
|
@@ -7107,9 +7623,14 @@ export {
|
|
|
7107
7623
|
detectExampleAssertionFailures,
|
|
7108
7624
|
detectEntryPoint,
|
|
7109
7625
|
detectBuildInfo,
|
|
7626
|
+
defineConfig,
|
|
7110
7627
|
createSourceFile,
|
|
7628
|
+
createNodeCommandRunner,
|
|
7111
7629
|
consistentParamStyle,
|
|
7112
7630
|
categorizeDrifts,
|
|
7631
|
+
buildRawUrl,
|
|
7632
|
+
buildDisplayUrl,
|
|
7633
|
+
buildCloneUrl,
|
|
7113
7634
|
blockReferencesExport,
|
|
7114
7635
|
applyPatchToJSDoc,
|
|
7115
7636
|
applyEdits,
|
|
@@ -7118,8 +7639,10 @@ export {
|
|
|
7118
7639
|
analyzeDocsImpact,
|
|
7119
7640
|
analyze,
|
|
7120
7641
|
allRules,
|
|
7642
|
+
ScanOrchestrator,
|
|
7121
7643
|
SandboxFileSystem,
|
|
7122
7644
|
OpenPkg,
|
|
7123
7645
|
NodeFileSystem,
|
|
7646
|
+
MonorepoRequiresPackageError,
|
|
7124
7647
|
DocCov
|
|
7125
7648
|
};
|