@doccov/sdk 0.5.6 → 0.5.8
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 +89 -3
- package/dist/index.js +365 -11
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -159,8 +159,10 @@ interface DocsImpact {
|
|
|
159
159
|
interface DocsImpactResult {
|
|
160
160
|
/** Files with impacted references */
|
|
161
161
|
impactedFiles: DocsImpact[];
|
|
162
|
-
/** New exports that have no documentation */
|
|
162
|
+
/** New exports (from this diff) that have no documentation */
|
|
163
163
|
missingDocs: string[];
|
|
164
|
+
/** ALL exports from the spec that have no documentation in the scanned files */
|
|
165
|
+
allUndocumented: string[];
|
|
164
166
|
/** Statistics */
|
|
165
167
|
stats: {
|
|
166
168
|
/** Total markdown files scanned */
|
|
@@ -171,6 +173,10 @@ interface DocsImpactResult {
|
|
|
171
173
|
referencesFound: number;
|
|
172
174
|
/** References impacted by changes */
|
|
173
175
|
impactedReferences: number;
|
|
176
|
+
/** Total exports in the spec */
|
|
177
|
+
totalExports: number;
|
|
178
|
+
/** Exports found documented in markdown */
|
|
179
|
+
documentedExports: number;
|
|
174
180
|
};
|
|
175
181
|
}
|
|
176
182
|
/**
|
|
@@ -255,7 +261,7 @@ declare function getDocumentedExports(markdownFiles: MarkdownDocFile[], exportNa
|
|
|
255
261
|
* Get all exports that lack documentation
|
|
256
262
|
*/
|
|
257
263
|
declare function getUndocumentedExports(markdownFiles: MarkdownDocFile[], exportNames: string[]): string[];
|
|
258
|
-
import { OpenPkg as OpenPkg3, SpecDiff as SpecDiff2 } from "@openpkg-ts/spec";
|
|
264
|
+
import { CategorizedBreaking, OpenPkg as OpenPkg3, SpecDiff as SpecDiff2 } from "@openpkg-ts/spec";
|
|
259
265
|
/**
|
|
260
266
|
* Extended spec diff result with docs impact
|
|
261
267
|
*/
|
|
@@ -264,6 +270,8 @@ interface SpecDiffWithDocs extends SpecDiff2 {
|
|
|
264
270
|
docsImpact?: DocsImpactResult;
|
|
265
271
|
/** Member-level changes for classes (methods added/removed/changed) */
|
|
266
272
|
memberChanges?: MemberChange[];
|
|
273
|
+
/** Breaking changes categorized by severity (high/medium/low) */
|
|
274
|
+
categorizedBreaking?: CategorizedBreaking[];
|
|
267
275
|
}
|
|
268
276
|
/**
|
|
269
277
|
* Options for diffSpecWithDocs
|
|
@@ -756,4 +764,82 @@ declare function readPackageJson(fs: FileSystem, dir: string): Promise<PackageJs
|
|
|
756
764
|
* ```
|
|
757
765
|
*/
|
|
758
766
|
declare function analyzeProject2(fs: FileSystem, options?: AnalyzeProjectOptions): Promise<ProjectInfo>;
|
|
759
|
-
|
|
767
|
+
import { SpecExport as SpecExport4 } from "@openpkg-ts/spec";
|
|
768
|
+
import { SpecExport as SpecExport3 } from "@openpkg-ts/spec";
|
|
769
|
+
type LintSeverity = "error" | "warn" | "off";
|
|
770
|
+
interface LintViolation {
|
|
771
|
+
rule: string;
|
|
772
|
+
severity: "error" | "warn";
|
|
773
|
+
message: string;
|
|
774
|
+
line?: number;
|
|
775
|
+
fixable: boolean;
|
|
776
|
+
}
|
|
777
|
+
interface LintRule {
|
|
778
|
+
name: string;
|
|
779
|
+
defaultSeverity: LintSeverity;
|
|
780
|
+
check(exp: SpecExport3, rawJSDoc?: string): LintViolation[];
|
|
781
|
+
fix?(exp: SpecExport3, rawJSDoc?: string): JSDocPatch | null;
|
|
782
|
+
}
|
|
783
|
+
interface LintConfig {
|
|
784
|
+
rules: Record<string, LintSeverity>;
|
|
785
|
+
}
|
|
786
|
+
interface LintResult {
|
|
787
|
+
violations: LintViolation[];
|
|
788
|
+
errorCount: number;
|
|
789
|
+
warningCount: number;
|
|
790
|
+
fixableCount: number;
|
|
791
|
+
}
|
|
792
|
+
/** All available lint rules */
|
|
793
|
+
declare const allRules: LintRule[];
|
|
794
|
+
/** Default configuration with rule defaults */
|
|
795
|
+
declare function getDefaultConfig(): LintConfig;
|
|
796
|
+
/** Get a rule by name */
|
|
797
|
+
declare function getRule(name: string): LintRule | undefined;
|
|
798
|
+
/** Lint a single */
|
|
799
|
+
declare function lintExport(exp: SpecExport4, rawJSDoc?: string, config?: LintConfig): LintViolation[];
|
|
800
|
+
/** Lint multiple exports and aggregate results */
|
|
801
|
+
declare function lintExports(exports: Array<{
|
|
802
|
+
export: SpecExport4;
|
|
803
|
+
rawJSDoc?: string;
|
|
804
|
+
}>, config?: LintConfig): LintResult;
|
|
805
|
+
/** Merge user config with defaults */
|
|
806
|
+
declare function mergeConfig(userConfig: Partial<LintConfig>): LintConfig;
|
|
807
|
+
declare const consistentParamStyle: LintRule;
|
|
808
|
+
declare const noEmptyReturns: LintRule;
|
|
809
|
+
declare const requireDescription: LintRule;
|
|
810
|
+
declare const requireExample: LintRule;
|
|
811
|
+
interface ExampleTypeError {
|
|
812
|
+
/** Index of the example in the examples array */
|
|
813
|
+
exampleIndex: number;
|
|
814
|
+
/** Line number within the example (1-based) */
|
|
815
|
+
line: number;
|
|
816
|
+
/** Column number (1-based) */
|
|
817
|
+
column: number;
|
|
818
|
+
/** Error message from TypeScript */
|
|
819
|
+
message: string;
|
|
820
|
+
/** TypeScript diagnostic code */
|
|
821
|
+
code: number;
|
|
822
|
+
}
|
|
823
|
+
interface TypecheckResult {
|
|
824
|
+
/** All type errors found across examples */
|
|
825
|
+
errors: ExampleTypeError[];
|
|
826
|
+
/** Number of examples that passed */
|
|
827
|
+
passed: number;
|
|
828
|
+
/** Number of examples that failed */
|
|
829
|
+
failed: number;
|
|
830
|
+
}
|
|
831
|
+
interface TypecheckOptions {
|
|
832
|
+
/** Path to tsconfig.json (auto-detected if not provided) */
|
|
833
|
+
tsconfig?: string;
|
|
834
|
+
/** Package name for imports (auto-detected from package.json if not provided) */
|
|
835
|
+
packageName?: string;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Type-check a single example
|
|
839
|
+
*/
|
|
840
|
+
declare function typecheckExample(example: string, packagePath: string, options?: TypecheckOptions): ExampleTypeError[];
|
|
841
|
+
/**
|
|
842
|
+
* Type-check multiple examples
|
|
843
|
+
*/
|
|
844
|
+
declare function typecheckExamples(examples: string[], packagePath: string, options?: TypecheckOptions): TypecheckResult;
|
|
845
|
+
export { typecheckExamples, typecheckExample, serializeJSDoc, safeParseJson, runExamplesWithPackage, runExamples, runExample, requireExample, requireDescription, readPackageJson, parseMarkdownFiles, parseMarkdownFile, parseJSDocToPatch, parseAssertions, noEmptyReturns, mergeFixes, mergeConfig, lintExports, lintExport, isFixableDrift, isExecutableLang, hasNonAssertionComments, hasDocsImpact, hasDocsForExport, getUndocumentedExports, getRunCommand, getRule, getPrimaryBuildScript, getInstallCommand, getDocumentedExports, getDocsImpactSummary, getDefaultConfig, generateFixesForExport, generateFix, formatPackageList, findRemovedReferences, findPackageByName, findJSDocLocation, findExportReferences, findDeprecatedReferences, extractPackageSpec, extractImports, extractFunctionCalls, diffSpecWithDocs, detectPackageManager, detectMonorepo, detectExampleRuntimeErrors, detectExampleAssertionFailures, detectEntryPoint, detectBuildInfo, createSourceFile, consistentParamStyle, categorizeDrifts, blockReferencesExport, applyPatchToJSDoc, applyEdits, analyzeProject2 as analyzeProject, analyzeFile, analyzeDocsImpact, analyze, allRules, WorkspacePackage, TypecheckResult, TypecheckOptions, SpecDiffWithDocs, SandboxFileSystem, RunExamplesWithPackageResult, RunExamplesWithPackageOptions, RunExampleOptions, ProjectInfo, PackageManagerInfo, PackageManager, PackageJson, PackageExports, OpenPkgSpec, OpenPkgOptions, OpenPkg4 as OpenPkg, NodeFileSystem, MonorepoType, MonorepoInfo, MemberChange, MarkdownDocFile, MarkdownCodeBlock, LintViolation, LintSeverity, LintRule, LintResult, LintConfig, JSDocTag, JSDocReturn, JSDocPatch, JSDocParam, JSDocEdit, FixType, FixSuggestion, FilterOptions, FileSystem, ExportReference, ExampleTypeError, ExampleRunResult, EntryPointSource, EntryPointInfo, DocsImpactResult, DocsImpactReference, DocsImpact, DocsChangeType, DocCovOptions, DocCov, DiffWithDocsOptions, Diagnostic, BuildInfo, ApplyEditsResult, AnalyzeProjectOptions, AnalyzeOptions, AnalysisResult };
|
package/dist/index.js
CHANGED
|
@@ -1201,8 +1201,11 @@ function blockReferencesExport(block, exportName) {
|
|
|
1201
1201
|
return calls.includes(exportName);
|
|
1202
1202
|
}
|
|
1203
1203
|
// src/markdown/analyzer.ts
|
|
1204
|
-
function getChangeType(exportName, diff) {
|
|
1204
|
+
function getChangeType(exportName, diff, newExportNames) {
|
|
1205
1205
|
if (diff.breaking.includes(exportName)) {
|
|
1206
|
+
if (!newExportNames.includes(exportName)) {
|
|
1207
|
+
return "removed";
|
|
1208
|
+
}
|
|
1206
1209
|
return "signature-changed";
|
|
1207
1210
|
}
|
|
1208
1211
|
return null;
|
|
@@ -1284,7 +1287,7 @@ function analyzeDocsImpact(diff, markdownFiles, newExportNames = [], memberChang
|
|
|
1284
1287
|
if (exportsWithoutMemberChanges.length > 0) {
|
|
1285
1288
|
const refs = findExportReferences([{ ...mdFile, codeBlocks: [block] }], exportsWithoutMemberChanges);
|
|
1286
1289
|
for (const ref of refs) {
|
|
1287
|
-
const changeType = getChangeType(ref.exportName, diff);
|
|
1290
|
+
const changeType = getChangeType(ref.exportName, diff, newExportNames);
|
|
1288
1291
|
if (!changeType)
|
|
1289
1292
|
continue;
|
|
1290
1293
|
const refKey = `${ref.file}:${ref.line}:${ref.exportName}`;
|
|
@@ -1301,17 +1304,18 @@ function analyzeDocsImpact(diff, markdownFiles, newExportNames = [], memberChang
|
|
|
1301
1304
|
}
|
|
1302
1305
|
}
|
|
1303
1306
|
}
|
|
1304
|
-
const
|
|
1307
|
+
const documentedExportsSet = new Set;
|
|
1305
1308
|
for (const file of markdownFiles) {
|
|
1306
1309
|
for (const block of file.codeBlocks) {
|
|
1307
1310
|
for (const exportName of newExportNames) {
|
|
1308
1311
|
if (block.code.includes(exportName)) {
|
|
1309
|
-
|
|
1312
|
+
documentedExportsSet.add(exportName);
|
|
1310
1313
|
}
|
|
1311
1314
|
}
|
|
1312
1315
|
}
|
|
1313
1316
|
}
|
|
1314
|
-
const missingDocs = diff.nonBreaking.filter((name) => !
|
|
1317
|
+
const missingDocs = diff.nonBreaking.filter((name) => !documentedExportsSet.has(name));
|
|
1318
|
+
const allUndocumented = newExportNames.filter((name) => !documentedExportsSet.has(name));
|
|
1315
1319
|
const totalCodeBlocks = markdownFiles.reduce((sum, f) => sum + f.codeBlocks.length, 0);
|
|
1316
1320
|
const allReferences = findExportReferences(markdownFiles, [...changedExports, ...diff.nonBreaking]);
|
|
1317
1321
|
let impactedReferences = 0;
|
|
@@ -1321,11 +1325,14 @@ function analyzeDocsImpact(diff, markdownFiles, newExportNames = [], memberChang
|
|
|
1321
1325
|
return {
|
|
1322
1326
|
impactedFiles: Array.from(impactByFile.values()),
|
|
1323
1327
|
missingDocs,
|
|
1328
|
+
allUndocumented,
|
|
1324
1329
|
stats: {
|
|
1325
1330
|
filesScanned: markdownFiles.length,
|
|
1326
1331
|
codeBlocksFound: totalCodeBlocks,
|
|
1327
1332
|
referencesFound: allReferences.length,
|
|
1328
|
-
impactedReferences
|
|
1333
|
+
impactedReferences,
|
|
1334
|
+
totalExports: newExportNames.length,
|
|
1335
|
+
documentedExports: documentedExportsSet.size
|
|
1329
1336
|
}
|
|
1330
1337
|
};
|
|
1331
1338
|
}
|
|
@@ -1405,7 +1412,17 @@ function diffMemberChanges(oldSpec, newSpec, changedClassNames) {
|
|
|
1405
1412
|
}
|
|
1406
1413
|
}
|
|
1407
1414
|
}
|
|
1408
|
-
return changes;
|
|
1415
|
+
return deduplicateMemberChanges(changes);
|
|
1416
|
+
}
|
|
1417
|
+
function deduplicateMemberChanges(changes) {
|
|
1418
|
+
const seen = new Set;
|
|
1419
|
+
return changes.filter((mc) => {
|
|
1420
|
+
const key = `${mc.className}:${mc.memberName}:${mc.changeType}`;
|
|
1421
|
+
if (seen.has(key))
|
|
1422
|
+
return false;
|
|
1423
|
+
seen.add(key);
|
|
1424
|
+
return true;
|
|
1425
|
+
});
|
|
1409
1426
|
}
|
|
1410
1427
|
function toExportMap(exports) {
|
|
1411
1428
|
const map = new Map;
|
|
@@ -1446,10 +1463,32 @@ function formatSignature(member) {
|
|
|
1446
1463
|
const sig = member.signatures[0];
|
|
1447
1464
|
const params = sig.parameters?.map((p) => {
|
|
1448
1465
|
const optional = p.required === false ? "?" : "";
|
|
1449
|
-
|
|
1466
|
+
const typeName = extractTypeName(p.schema);
|
|
1467
|
+
return typeName ? `${p.name}${optional}: ${typeName}` : `${p.name}${optional}`;
|
|
1450
1468
|
}) ?? [];
|
|
1451
1469
|
return `${member.name}(${params.join(", ")})`;
|
|
1452
1470
|
}
|
|
1471
|
+
function extractTypeName(schema) {
|
|
1472
|
+
if (!schema || typeof schema !== "object") {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const s = schema;
|
|
1476
|
+
if (typeof s.$ref === "string") {
|
|
1477
|
+
const parts = s.$ref.split("/");
|
|
1478
|
+
return parts[parts.length - 1];
|
|
1479
|
+
}
|
|
1480
|
+
if (typeof s.type === "string") {
|
|
1481
|
+
return s.type;
|
|
1482
|
+
}
|
|
1483
|
+
if (typeof s.tsType === "string") {
|
|
1484
|
+
const tsType = s.tsType;
|
|
1485
|
+
if (tsType.length > 30) {
|
|
1486
|
+
return tsType.slice(0, 27) + "...";
|
|
1487
|
+
}
|
|
1488
|
+
return tsType;
|
|
1489
|
+
}
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1453
1492
|
function hasSignatureChanged(oldMember, newMember) {
|
|
1454
1493
|
const oldSigs = oldMember.signatures ?? [];
|
|
1455
1494
|
const newSigs = newMember.signatures ?? [];
|
|
@@ -1543,14 +1582,19 @@ function splitCamelCase(str) {
|
|
|
1543
1582
|
return str.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(" ");
|
|
1544
1583
|
}
|
|
1545
1584
|
// src/markdown/diff-with-docs.ts
|
|
1546
|
-
import {
|
|
1585
|
+
import {
|
|
1586
|
+
categorizeBreakingChanges,
|
|
1587
|
+
diffSpec
|
|
1588
|
+
} from "@openpkg-ts/spec";
|
|
1547
1589
|
function diffSpecWithDocs(oldSpec, newSpec, options = {}) {
|
|
1548
1590
|
const baseDiff = diffSpec(oldSpec, newSpec);
|
|
1549
1591
|
const memberChanges = diffMemberChanges(oldSpec, newSpec, baseDiff.breaking);
|
|
1592
|
+
const categorizedBreaking = categorizeBreakingChanges(baseDiff.breaking, oldSpec, newSpec, memberChanges);
|
|
1550
1593
|
if (!options.markdownFiles?.length) {
|
|
1551
1594
|
return {
|
|
1552
1595
|
...baseDiff,
|
|
1553
|
-
memberChanges: memberChanges.length > 0 ? memberChanges : undefined
|
|
1596
|
+
memberChanges: memberChanges.length > 0 ? memberChanges : undefined,
|
|
1597
|
+
categorizedBreaking: categorizedBreaking.length > 0 ? categorizedBreaking : undefined
|
|
1554
1598
|
};
|
|
1555
1599
|
}
|
|
1556
1600
|
const newExportNames = newSpec.exports?.map((e) => e.name) ?? [];
|
|
@@ -1558,7 +1602,8 @@ function diffSpecWithDocs(oldSpec, newSpec, options = {}) {
|
|
|
1558
1602
|
return {
|
|
1559
1603
|
...baseDiff,
|
|
1560
1604
|
docsImpact,
|
|
1561
|
-
memberChanges: memberChanges.length > 0 ? memberChanges : undefined
|
|
1605
|
+
memberChanges: memberChanges.length > 0 ? memberChanges : undefined,
|
|
1606
|
+
categorizedBreaking: categorizedBreaking.length > 0 ? categorizedBreaking : undefined
|
|
1562
1607
|
};
|
|
1563
1608
|
}
|
|
1564
1609
|
function hasDocsImpact(diff) {
|
|
@@ -5893,18 +5938,323 @@ async function analyzeProject(fs7, options = {}) {
|
|
|
5893
5938
|
]);
|
|
5894
5939
|
return { packageManager, monorepo, entryPoint, build };
|
|
5895
5940
|
}
|
|
5941
|
+
// src/lint/rules/consistent-param-style.ts
|
|
5942
|
+
var consistentParamStyle = {
|
|
5943
|
+
name: "consistent-param-style",
|
|
5944
|
+
defaultSeverity: "off",
|
|
5945
|
+
check(exp, rawJSDoc) {
|
|
5946
|
+
if (!rawJSDoc)
|
|
5947
|
+
return [];
|
|
5948
|
+
const violations = [];
|
|
5949
|
+
const paramRegex = /@param\s+(?:\{[^}]+\}\s+)?(\S+)\s+([^@\n]+)/g;
|
|
5950
|
+
let match;
|
|
5951
|
+
while ((match = paramRegex.exec(rawJSDoc)) !== null) {
|
|
5952
|
+
const paramName = match[1];
|
|
5953
|
+
const rest = match[2].trim();
|
|
5954
|
+
if (rest && !rest.startsWith("-") && !rest.startsWith("–")) {
|
|
5955
|
+
violations.push({
|
|
5956
|
+
rule: "consistent-param-style",
|
|
5957
|
+
severity: "warn",
|
|
5958
|
+
message: `Parameter '${paramName}' should use dash separator: @param ${paramName} - description`,
|
|
5959
|
+
fixable: true
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
return violations;
|
|
5964
|
+
},
|
|
5965
|
+
fix(_exp, rawJSDoc) {
|
|
5966
|
+
if (!rawJSDoc)
|
|
5967
|
+
return null;
|
|
5968
|
+
const patch = parseJSDocToPatch(rawJSDoc);
|
|
5969
|
+
if (!patch.params || patch.params.length === 0)
|
|
5970
|
+
return null;
|
|
5971
|
+
return patch;
|
|
5972
|
+
}
|
|
5973
|
+
};
|
|
5974
|
+
// src/lint/rules/no-empty-returns.ts
|
|
5975
|
+
var noEmptyReturns = {
|
|
5976
|
+
name: "no-empty-returns",
|
|
5977
|
+
defaultSeverity: "warn",
|
|
5978
|
+
check(exp, rawJSDoc) {
|
|
5979
|
+
if (!rawJSDoc)
|
|
5980
|
+
return [];
|
|
5981
|
+
const returnsMatch = rawJSDoc.match(/@returns?\s*(?:\{[^}]*\})?\s*$/m);
|
|
5982
|
+
if (returnsMatch) {
|
|
5983
|
+
return [
|
|
5984
|
+
{
|
|
5985
|
+
rule: "no-empty-returns",
|
|
5986
|
+
severity: "warn",
|
|
5987
|
+
message: `Export '${exp.name}' has @returns without a description`,
|
|
5988
|
+
fixable: false
|
|
5989
|
+
}
|
|
5990
|
+
];
|
|
5991
|
+
}
|
|
5992
|
+
const returnsTypeOnly = rawJSDoc.match(/@returns?\s+\{[^}]+\}\s*$/m);
|
|
5993
|
+
if (returnsTypeOnly) {
|
|
5994
|
+
return [
|
|
5995
|
+
{
|
|
5996
|
+
rule: "no-empty-returns",
|
|
5997
|
+
severity: "warn",
|
|
5998
|
+
message: `Export '${exp.name}' has @returns without a description`,
|
|
5999
|
+
fixable: false
|
|
6000
|
+
}
|
|
6001
|
+
];
|
|
6002
|
+
}
|
|
6003
|
+
return [];
|
|
6004
|
+
}
|
|
6005
|
+
};
|
|
6006
|
+
// src/lint/rules/require-description.ts
|
|
6007
|
+
var requireDescription = {
|
|
6008
|
+
name: "require-description",
|
|
6009
|
+
defaultSeverity: "warn",
|
|
6010
|
+
check(exp) {
|
|
6011
|
+
if (!exp.description || exp.description.trim() === "") {
|
|
6012
|
+
return [
|
|
6013
|
+
{
|
|
6014
|
+
rule: "require-description",
|
|
6015
|
+
severity: "warn",
|
|
6016
|
+
message: `Export '${exp.name}' is missing a description`,
|
|
6017
|
+
fixable: false
|
|
6018
|
+
}
|
|
6019
|
+
];
|
|
6020
|
+
}
|
|
6021
|
+
return [];
|
|
6022
|
+
}
|
|
6023
|
+
};
|
|
6024
|
+
// src/lint/rules/require-example.ts
|
|
6025
|
+
var requireExample = {
|
|
6026
|
+
name: "require-example",
|
|
6027
|
+
defaultSeverity: "off",
|
|
6028
|
+
check(exp) {
|
|
6029
|
+
if (exp.kind !== "function" && exp.kind !== "method") {
|
|
6030
|
+
return [];
|
|
6031
|
+
}
|
|
6032
|
+
if (!exp.examples || exp.examples.length === 0) {
|
|
6033
|
+
return [
|
|
6034
|
+
{
|
|
6035
|
+
rule: "require-example",
|
|
6036
|
+
severity: "warn",
|
|
6037
|
+
message: `Function '${exp.name}' is missing an @example`,
|
|
6038
|
+
fixable: false
|
|
6039
|
+
}
|
|
6040
|
+
];
|
|
6041
|
+
}
|
|
6042
|
+
return [];
|
|
6043
|
+
}
|
|
6044
|
+
};
|
|
6045
|
+
// src/lint/engine.ts
|
|
6046
|
+
var allRules = [
|
|
6047
|
+
requireDescription,
|
|
6048
|
+
requireExample,
|
|
6049
|
+
noEmptyReturns,
|
|
6050
|
+
consistentParamStyle
|
|
6051
|
+
];
|
|
6052
|
+
function getDefaultConfig() {
|
|
6053
|
+
const rules = {};
|
|
6054
|
+
for (const rule of allRules) {
|
|
6055
|
+
rules[rule.name] = rule.defaultSeverity;
|
|
6056
|
+
}
|
|
6057
|
+
return { rules };
|
|
6058
|
+
}
|
|
6059
|
+
function getRule(name) {
|
|
6060
|
+
return allRules.find((r) => r.name === name);
|
|
6061
|
+
}
|
|
6062
|
+
function lintExport(exp, rawJSDoc, config = getDefaultConfig()) {
|
|
6063
|
+
const violations = [];
|
|
6064
|
+
for (const rule of allRules) {
|
|
6065
|
+
const severity = config.rules[rule.name];
|
|
6066
|
+
if (severity === "off")
|
|
6067
|
+
continue;
|
|
6068
|
+
const ruleViolations = rule.check(exp, rawJSDoc);
|
|
6069
|
+
for (const v of ruleViolations) {
|
|
6070
|
+
violations.push({
|
|
6071
|
+
...v,
|
|
6072
|
+
severity: severity === "error" ? "error" : "warn"
|
|
6073
|
+
});
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
return violations;
|
|
6077
|
+
}
|
|
6078
|
+
function lintExports(exports, config = getDefaultConfig()) {
|
|
6079
|
+
const violations = [];
|
|
6080
|
+
for (const { export: exp, rawJSDoc } of exports) {
|
|
6081
|
+
violations.push(...lintExport(exp, rawJSDoc, config));
|
|
6082
|
+
}
|
|
6083
|
+
return {
|
|
6084
|
+
violations,
|
|
6085
|
+
errorCount: violations.filter((v) => v.severity === "error").length,
|
|
6086
|
+
warningCount: violations.filter((v) => v.severity === "warn").length,
|
|
6087
|
+
fixableCount: violations.filter((v) => v.fixable).length
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
function mergeConfig(userConfig) {
|
|
6091
|
+
const defaults = getDefaultConfig();
|
|
6092
|
+
return {
|
|
6093
|
+
rules: {
|
|
6094
|
+
...defaults.rules,
|
|
6095
|
+
...userConfig.rules
|
|
6096
|
+
}
|
|
6097
|
+
};
|
|
6098
|
+
}
|
|
6099
|
+
// src/typecheck/example-typechecker.ts
|
|
6100
|
+
import * as fs7 from "node:fs";
|
|
6101
|
+
import * as path8 from "node:path";
|
|
6102
|
+
import ts3 from "typescript";
|
|
6103
|
+
function stripCodeBlockMarkers2(code) {
|
|
6104
|
+
return code.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
|
|
6105
|
+
}
|
|
6106
|
+
function getPackageName(packagePath) {
|
|
6107
|
+
const pkgJsonPath = path8.join(packagePath, "package.json");
|
|
6108
|
+
if (fs7.existsSync(pkgJsonPath)) {
|
|
6109
|
+
try {
|
|
6110
|
+
const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
|
|
6111
|
+
return pkgJson.name;
|
|
6112
|
+
} catch {
|
|
6113
|
+
return;
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
return;
|
|
6117
|
+
}
|
|
6118
|
+
function findTsConfig(packagePath) {
|
|
6119
|
+
let dir = packagePath;
|
|
6120
|
+
while (dir !== path8.dirname(dir)) {
|
|
6121
|
+
const tsConfigPath = path8.join(dir, "tsconfig.json");
|
|
6122
|
+
if (fs7.existsSync(tsConfigPath)) {
|
|
6123
|
+
return tsConfigPath;
|
|
6124
|
+
}
|
|
6125
|
+
dir = path8.dirname(dir);
|
|
6126
|
+
}
|
|
6127
|
+
return;
|
|
6128
|
+
}
|
|
6129
|
+
function createVirtualSource(example, packageName, exportNames) {
|
|
6130
|
+
const cleanCode = stripCodeBlockMarkers2(example);
|
|
6131
|
+
const lines = [];
|
|
6132
|
+
if (packageName && exportNames && exportNames.length > 0) {
|
|
6133
|
+
lines.push(`import { ${exportNames.join(", ")} } from '${packageName}';`);
|
|
6134
|
+
lines.push("");
|
|
6135
|
+
}
|
|
6136
|
+
lines.push(cleanCode);
|
|
6137
|
+
return lines.join(`
|
|
6138
|
+
`);
|
|
6139
|
+
}
|
|
6140
|
+
function getCompilerOptions(tsconfigPath) {
|
|
6141
|
+
if (tsconfigPath && fs7.existsSync(tsconfigPath)) {
|
|
6142
|
+
const configFile = ts3.readConfigFile(tsconfigPath, ts3.sys.readFile);
|
|
6143
|
+
if (!configFile.error) {
|
|
6144
|
+
const parsed = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys, path8.dirname(tsconfigPath));
|
|
6145
|
+
return {
|
|
6146
|
+
...parsed.options,
|
|
6147
|
+
noEmit: true,
|
|
6148
|
+
skipLibCheck: true
|
|
6149
|
+
};
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
return {
|
|
6153
|
+
target: ts3.ScriptTarget.ES2022,
|
|
6154
|
+
module: ts3.ModuleKind.ESNext,
|
|
6155
|
+
moduleResolution: ts3.ModuleResolutionKind.Bundler,
|
|
6156
|
+
esModuleInterop: true,
|
|
6157
|
+
strict: true,
|
|
6158
|
+
noEmit: true,
|
|
6159
|
+
skipLibCheck: true
|
|
6160
|
+
};
|
|
6161
|
+
}
|
|
6162
|
+
function typecheckExample(example, packagePath, options = {}) {
|
|
6163
|
+
const errors = [];
|
|
6164
|
+
const cleanCode = stripCodeBlockMarkers2(example);
|
|
6165
|
+
const packageName = options.packageName ?? getPackageName(packagePath);
|
|
6166
|
+
const tsconfigPath = options.tsconfig ?? findTsConfig(packagePath);
|
|
6167
|
+
const compilerOptions = getCompilerOptions(tsconfigPath);
|
|
6168
|
+
const virtualSource = createVirtualSource(cleanCode, packageName);
|
|
6169
|
+
const virtualFileName = path8.join(packagePath, "__doccov_example__.ts");
|
|
6170
|
+
const hasImport = packageName !== undefined;
|
|
6171
|
+
const lineOffset = hasImport ? 2 : 0;
|
|
6172
|
+
const sourceFile = ts3.createSourceFile(virtualFileName, virtualSource, ts3.ScriptTarget.ES2022, true);
|
|
6173
|
+
const defaultHost = ts3.createCompilerHost(compilerOptions);
|
|
6174
|
+
const customHost = {
|
|
6175
|
+
...defaultHost,
|
|
6176
|
+
getSourceFile: (fileName, languageVersion) => {
|
|
6177
|
+
if (fileName === virtualFileName) {
|
|
6178
|
+
return sourceFile;
|
|
6179
|
+
}
|
|
6180
|
+
return defaultHost.getSourceFile(fileName, languageVersion);
|
|
6181
|
+
},
|
|
6182
|
+
fileExists: (fileName) => {
|
|
6183
|
+
if (fileName === virtualFileName)
|
|
6184
|
+
return true;
|
|
6185
|
+
return defaultHost.fileExists(fileName);
|
|
6186
|
+
},
|
|
6187
|
+
readFile: (fileName) => {
|
|
6188
|
+
if (fileName === virtualFileName)
|
|
6189
|
+
return virtualSource;
|
|
6190
|
+
return defaultHost.readFile(fileName);
|
|
6191
|
+
}
|
|
6192
|
+
};
|
|
6193
|
+
const program = ts3.createProgram([virtualFileName], compilerOptions, customHost);
|
|
6194
|
+
const diagnostics = ts3.getPreEmitDiagnostics(program, sourceFile);
|
|
6195
|
+
for (const diagnostic of diagnostics) {
|
|
6196
|
+
if (diagnostic.file && diagnostic.start !== undefined) {
|
|
6197
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
6198
|
+
const exampleLine = line - lineOffset + 1;
|
|
6199
|
+
if (exampleLine < 1)
|
|
6200
|
+
continue;
|
|
6201
|
+
errors.push({
|
|
6202
|
+
exampleIndex: 0,
|
|
6203
|
+
line: exampleLine,
|
|
6204
|
+
column: character + 1,
|
|
6205
|
+
message: ts3.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6206
|
+
`),
|
|
6207
|
+
code: diagnostic.code
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
return errors;
|
|
6212
|
+
}
|
|
6213
|
+
function typecheckExamples(examples, packagePath, options = {}) {
|
|
6214
|
+
const allErrors = [];
|
|
6215
|
+
let passed = 0;
|
|
6216
|
+
let failed = 0;
|
|
6217
|
+
for (let i = 0;i < examples.length; i++) {
|
|
6218
|
+
const example = examples[i];
|
|
6219
|
+
if (!example || typeof example !== "string" || !example.trim()) {
|
|
6220
|
+
continue;
|
|
6221
|
+
}
|
|
6222
|
+
const errors = typecheckExample(example, packagePath, options);
|
|
6223
|
+
for (const error of errors) {
|
|
6224
|
+
allErrors.push({ ...error, exampleIndex: i });
|
|
6225
|
+
}
|
|
6226
|
+
if (errors.length > 0) {
|
|
6227
|
+
failed++;
|
|
6228
|
+
} else {
|
|
6229
|
+
passed++;
|
|
6230
|
+
}
|
|
6231
|
+
}
|
|
6232
|
+
return {
|
|
6233
|
+
errors: allErrors,
|
|
6234
|
+
passed,
|
|
6235
|
+
failed
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
5896
6238
|
export {
|
|
6239
|
+
typecheckExamples,
|
|
6240
|
+
typecheckExample,
|
|
5897
6241
|
serializeJSDoc,
|
|
5898
6242
|
safeParseJson,
|
|
5899
6243
|
runExamplesWithPackage,
|
|
5900
6244
|
runExamples,
|
|
5901
6245
|
runExample,
|
|
6246
|
+
requireExample,
|
|
6247
|
+
requireDescription,
|
|
5902
6248
|
readPackageJson,
|
|
5903
6249
|
parseMarkdownFiles,
|
|
5904
6250
|
parseMarkdownFile,
|
|
5905
6251
|
parseJSDocToPatch,
|
|
5906
6252
|
parseAssertions,
|
|
6253
|
+
noEmptyReturns,
|
|
5907
6254
|
mergeFixes,
|
|
6255
|
+
mergeConfig,
|
|
6256
|
+
lintExports,
|
|
6257
|
+
lintExport,
|
|
5908
6258
|
isFixableDrift,
|
|
5909
6259
|
isExecutableLang,
|
|
5910
6260
|
hasNonAssertionComments,
|
|
@@ -5912,10 +6262,12 @@ export {
|
|
|
5912
6262
|
hasDocsForExport,
|
|
5913
6263
|
getUndocumentedExports,
|
|
5914
6264
|
getRunCommand,
|
|
6265
|
+
getRule,
|
|
5915
6266
|
getPrimaryBuildScript,
|
|
5916
6267
|
getInstallCommand2 as getInstallCommand,
|
|
5917
6268
|
getDocumentedExports,
|
|
5918
6269
|
getDocsImpactSummary,
|
|
6270
|
+
getDefaultConfig,
|
|
5919
6271
|
generateFixesForExport,
|
|
5920
6272
|
generateFix,
|
|
5921
6273
|
formatPackageList,
|
|
@@ -5935,6 +6287,7 @@ export {
|
|
|
5935
6287
|
detectEntryPoint,
|
|
5936
6288
|
detectBuildInfo,
|
|
5937
6289
|
createSourceFile,
|
|
6290
|
+
consistentParamStyle,
|
|
5938
6291
|
categorizeDrifts,
|
|
5939
6292
|
blockReferencesExport,
|
|
5940
6293
|
applyPatchToJSDoc,
|
|
@@ -5943,6 +6296,7 @@ export {
|
|
|
5943
6296
|
analyzeFile,
|
|
5944
6297
|
analyzeDocsImpact,
|
|
5945
6298
|
analyze,
|
|
6299
|
+
allRules,
|
|
5946
6300
|
SandboxFileSystem,
|
|
5947
6301
|
OpenPkg,
|
|
5948
6302
|
NodeFileSystem,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doccov/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "DocCov SDK - Documentation coverage and drift detection for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@openpkg-ts/spec": "^0.
|
|
42
|
+
"@openpkg-ts/spec": "^0.4.0",
|
|
43
43
|
"@vercel/sandbox": "^1.0.3",
|
|
44
44
|
"mdast": "^3.0.0",
|
|
45
45
|
"remark-mdx": "^3.1.0",
|