@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 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
- export { serializeJSDoc, safeParseJson, runExamplesWithPackage, runExamples, runExample, readPackageJson, parseMarkdownFiles, parseMarkdownFile, parseJSDocToPatch, parseAssertions, mergeFixes, isFixableDrift, isExecutableLang, hasNonAssertionComments, hasDocsImpact, hasDocsForExport, getUndocumentedExports, getRunCommand, getPrimaryBuildScript, getInstallCommand, getDocumentedExports, getDocsImpactSummary, generateFixesForExport, generateFix, formatPackageList, findRemovedReferences, findPackageByName, findJSDocLocation, findExportReferences, findDeprecatedReferences, extractPackageSpec, extractImports, extractFunctionCalls, diffSpecWithDocs, detectPackageManager, detectMonorepo, detectExampleRuntimeErrors, detectExampleAssertionFailures, detectEntryPoint, detectBuildInfo, createSourceFile, categorizeDrifts, blockReferencesExport, applyPatchToJSDoc, applyEdits, analyzeProject2 as analyzeProject, analyzeFile, analyzeDocsImpact, analyze, WorkspacePackage, SpecDiffWithDocs, SandboxFileSystem, RunExamplesWithPackageResult, RunExamplesWithPackageOptions, RunExampleOptions, ProjectInfo, PackageManagerInfo, PackageManager, PackageJson, PackageExports, OpenPkgSpec, OpenPkgOptions, OpenPkg4 as OpenPkg, NodeFileSystem, MonorepoType, MonorepoInfo, MarkdownDocFile, MarkdownCodeBlock, JSDocTag, JSDocReturn, JSDocPatch, JSDocParam, JSDocEdit, FixType, FixSuggestion, FilterOptions, FileSystem, ExportReference, ExampleRunResult, EntryPointSource, EntryPointInfo, DocsImpactResult, DocsImpactReference, DocsImpact, DocsChangeType, DocCovOptions, DocCov, DiffWithDocsOptions, Diagnostic, BuildInfo, ApplyEditsResult, AnalyzeProjectOptions, AnalyzeOptions, AnalysisResult };
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 documentedExports = new Set;
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
- documentedExports.add(exportName);
1312
+ documentedExportsSet.add(exportName);
1310
1313
  }
1311
1314
  }
1312
1315
  }
1313
1316
  }
1314
- const missingDocs = diff.nonBreaking.filter((name) => !documentedExports.has(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
- return `${p.name}${optional}`;
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 { diffSpec } from "@openpkg-ts/spec";
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.6",
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.3.1",
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",