@doccov/sdk 0.8.0 → 0.9.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.
Files changed (2) hide show
  1. package/dist/index.js +121 -10
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3034,6 +3034,8 @@ function parseJSDocBlock(commentText) {
3034
3034
  params: [],
3035
3035
  throws: [],
3036
3036
  examples: [],
3037
+ structuredExamples: [],
3038
+ seeAlso: [],
3037
3039
  tags: [],
3038
3040
  rawParamNames: []
3039
3041
  };
@@ -3154,6 +3156,43 @@ function parseThrowsContent(content) {
3154
3156
  description: processInlineLinks(remaining)
3155
3157
  };
3156
3158
  }
3159
+ function parseExampleContent(content) {
3160
+ const trimmed = content.trim();
3161
+ if (!trimmed)
3162
+ return null;
3163
+ const lines = trimmed.split(`
3164
+ `);
3165
+ let title;
3166
+ let description;
3167
+ let language;
3168
+ let codeStartIndex = 0;
3169
+ const firstLine = lines[0]?.trim() ?? "";
3170
+ const looksLikeCode = /^(import|const|let|var|function|class|export|async|await|if|for|while|return|\/\/|\/\*|\{|\[)/.test(firstLine);
3171
+ if (firstLine && !firstLine.startsWith("```") && !looksLikeCode) {
3172
+ title = firstLine;
3173
+ codeStartIndex = 1;
3174
+ }
3175
+ const codeBlockStart = lines.findIndex((l, i) => i >= codeStartIndex && l.trim().startsWith("```"));
3176
+ if (codeBlockStart !== -1) {
3177
+ const fenceMatch = lines[codeBlockStart].match(/```(\w+)?/);
3178
+ language = fenceMatch?.[1];
3179
+ if (codeBlockStart > codeStartIndex) {
3180
+ const descLines = lines.slice(codeStartIndex, codeBlockStart).filter((l) => l.trim());
3181
+ if (descLines.length > 0) {
3182
+ description = descLines.join(`
3183
+ `).trim();
3184
+ }
3185
+ }
3186
+ const codeBlockEnd = lines.findIndex((l, i) => i > codeBlockStart && l.trim() === "```");
3187
+ const codeLines = codeBlockEnd === -1 ? lines.slice(codeBlockStart + 1) : lines.slice(codeBlockStart + 1, codeBlockEnd);
3188
+ const code2 = codeLines.join(`
3189
+ `);
3190
+ return { code: code2, title, description, language };
3191
+ }
3192
+ const code = lines.slice(codeStartIndex).join(`
3193
+ `).trim();
3194
+ return { code, title, language: "ts" };
3195
+ }
3157
3196
  function findMatchingBrace(text, startIndex) {
3158
3197
  if (text[startIndex] !== "{")
3159
3198
  return -1;
@@ -3246,22 +3285,30 @@ function processTagContent(result, tag, content) {
3246
3285
  if (example) {
3247
3286
  result.examples.push(example);
3248
3287
  }
3249
- const langMatch = trimmedContent.match(/^```(\w+)/);
3288
+ const structuredExample = parseExampleContent(trimmedContent);
3289
+ if (structuredExample) {
3290
+ result.structuredExamples.push(structuredExample);
3291
+ }
3250
3292
  result.tags.push({
3251
3293
  name: "example",
3252
3294
  text: example,
3253
- language: langMatch?.[1]
3295
+ language: structuredExample?.language
3254
3296
  });
3255
3297
  break;
3256
3298
  }
3257
3299
  case "see": {
3258
3300
  const linkTargets = extractAllLinkTargets(trimmedContent);
3259
3301
  for (const target of linkTargets) {
3302
+ result.seeAlso.push(target);
3260
3303
  result.tags.push({ name: "link", text: target, reference: target });
3261
3304
  result.tags.push({ name: "see", text: target, reference: target });
3262
3305
  }
3263
3306
  if (linkTargets.length === 0) {
3264
- result.tags.push({ name: "see", text: trimmedContent, reference: trimmedContent });
3307
+ const plainRef = trimmedContent.trim().split(/\s+/)[0];
3308
+ if (plainRef) {
3309
+ result.seeAlso.push(plainRef);
3310
+ }
3311
+ result.tags.push({ name: "see", text: trimmedContent, reference: plainRef || trimmedContent });
3265
3312
  }
3266
3313
  break;
3267
3314
  }
@@ -3389,6 +3436,8 @@ function parseJSDocText(commentText) {
3389
3436
  type: p.type
3390
3437
  })),
3391
3438
  examples: parsed.examples.length > 0 ? parsed.examples : undefined,
3439
+ structuredExamples: parsed.structuredExamples.length > 0 ? parsed.structuredExamples : undefined,
3440
+ seeAlso: parsed.seeAlso.length > 0 ? parsed.seeAlso : undefined,
3392
3441
  tags: parsed.tags.length > 0 ? parsed.tags : undefined,
3393
3442
  rawParamNames: parsed.rawParamNames
3394
3443
  };
@@ -3528,6 +3577,23 @@ function extractPresentationMetadata(doc) {
3528
3577
  }
3529
3578
 
3530
3579
  // src/analysis/serializers/classes.ts
3580
+ function extractClassRelations(heritage, parsedDoc) {
3581
+ const relations = [];
3582
+ if (heritage.extends) {
3583
+ relations.push({ type: "extends", target: heritage.extends });
3584
+ }
3585
+ if (heritage.implements) {
3586
+ for (const impl of heritage.implements) {
3587
+ relations.push({ type: "implements", target: impl });
3588
+ }
3589
+ }
3590
+ if (parsedDoc?.seeAlso) {
3591
+ for (const ref of parsedDoc.seeAlso) {
3592
+ relations.push({ type: "see-also", target: ref });
3593
+ }
3594
+ }
3595
+ return relations;
3596
+ }
3531
3597
  function serializeClass(declaration, symbol, context) {
3532
3598
  const { checker, typeRegistry } = context;
3533
3599
  const typeRefs = typeRegistry.getTypeRefs();
@@ -3539,6 +3605,7 @@ function serializeClass(declaration, symbol, context) {
3539
3605
  const description = parsedDoc?.description ?? getJSDocComment(symbol, context.checker);
3540
3606
  const metadata = extractPresentationMetadata(parsedDoc);
3541
3607
  const decorators = extractDecorators(declaration);
3608
+ const relations = extractClassRelations(heritage, parsedDoc);
3542
3609
  const exportEntry = {
3543
3610
  id: symbol.getName(),
3544
3611
  name: symbol.getName(),
@@ -3553,7 +3620,8 @@ function serializeClass(declaration, symbol, context) {
3553
3620
  ...heritage.implements && heritage.implements.length > 0 ? { implements: heritage.implements } : {},
3554
3621
  tags: parsedDoc?.tags,
3555
3622
  examples: parsedDoc?.examples,
3556
- decorators
3623
+ decorators,
3624
+ related: relations.length > 0 ? relations : undefined
3557
3625
  };
3558
3626
  const typeDefinition = {
3559
3627
  id: symbol.getName(),
@@ -3565,7 +3633,8 @@ function serializeClass(declaration, symbol, context) {
3565
3633
  members: members.length > 0 ? members : undefined,
3566
3634
  ...heritage.extends ? { extends: heritage.extends } : {},
3567
3635
  ...heritage.implements && heritage.implements.length > 0 ? { implements: heritage.implements } : {},
3568
- tags: parsedDoc?.tags
3636
+ tags: parsedDoc?.tags,
3637
+ related: relations.length > 0 ? relations : undefined
3569
3638
  };
3570
3639
  return {
3571
3640
  exportEntry,
@@ -3888,6 +3957,22 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
3888
3957
  };
3889
3958
  });
3890
3959
  }
3960
+ function extractFunctionRelations(declaration, checker, parsedDoc) {
3961
+ const relations = [];
3962
+ if (declaration.type && ts2.isTypeReferenceNode(declaration.type)) {
3963
+ const typeName = declaration.type.typeName.getText();
3964
+ const builtIns = ["Promise", "Array", "Map", "Set", "Record", "Partial", "Required", "Pick", "Omit"];
3965
+ if (!builtIns.includes(typeName)) {
3966
+ relations.push({ type: "returns", target: typeName });
3967
+ }
3968
+ }
3969
+ if (parsedDoc?.seeAlso) {
3970
+ for (const ref of parsedDoc.seeAlso) {
3971
+ relations.push({ type: "see-also", target: ref });
3972
+ }
3973
+ }
3974
+ return relations;
3975
+ }
3891
3976
  function serializeFunctionExport(declaration, symbol, context) {
3892
3977
  const { checker } = context;
3893
3978
  const funcSymbol = checker.getSymbolAtLocation(declaration.name || declaration);
@@ -3896,6 +3981,7 @@ function serializeFunctionExport(declaration, symbol, context) {
3896
3981
  const metadata = extractPresentationMetadata(parsedDoc);
3897
3982
  const type = checker.getTypeAtLocation(declaration.name || declaration);
3898
3983
  const callSignatures = type.getCallSignatures();
3984
+ const relations = extractFunctionRelations(declaration, checker, parsedDoc);
3899
3985
  return {
3900
3986
  id: symbol.getName(),
3901
3987
  name: symbol.getName(),
@@ -3906,7 +3992,8 @@ function serializeFunctionExport(declaration, symbol, context) {
3906
3992
  description,
3907
3993
  source: getSourceLocation(declaration),
3908
3994
  examples: parsedDoc?.examples,
3909
- tags: parsedDoc?.tags
3995
+ tags: parsedDoc?.tags,
3996
+ related: relations.length > 0 ? relations : undefined
3910
3997
  };
3911
3998
  }
3912
3999
 
@@ -3994,6 +4081,25 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
3994
4081
  }
3995
4082
  return members;
3996
4083
  }
4084
+ function extractInterfaceRelations(declaration, parsedDoc) {
4085
+ const relations = [];
4086
+ if (declaration.heritageClauses) {
4087
+ for (const clause of declaration.heritageClauses) {
4088
+ if (clause.token === ts2.SyntaxKind.ExtendsKeyword) {
4089
+ for (const type of clause.types) {
4090
+ const typeName = type.expression.getText();
4091
+ relations.push({ type: "extends", target: typeName });
4092
+ }
4093
+ }
4094
+ }
4095
+ }
4096
+ if (parsedDoc?.seeAlso) {
4097
+ for (const ref of parsedDoc.seeAlso) {
4098
+ relations.push({ type: "see-also", target: ref });
4099
+ }
4100
+ }
4101
+ return relations;
4102
+ }
3997
4103
  function serializeInterface(declaration, symbol, context) {
3998
4104
  const { checker, typeRegistry } = context;
3999
4105
  const parsedDoc = parseJSDocComment(symbol, checker);
@@ -4003,6 +4109,7 @@ function serializeInterface(declaration, symbol, context) {
4003
4109
  const typeRefs = typeRegistry.getTypeRefs();
4004
4110
  const typeParameters = serializeTypeParameterDeclarations(declaration.typeParameters, checker, referencedTypes);
4005
4111
  const members = serializeInterfaceMembers(declaration, checker, typeRefs, referencedTypes);
4112
+ const relations = extractInterfaceRelations(declaration, parsedDoc);
4006
4113
  const exportEntry = {
4007
4114
  id: symbol.getName(),
4008
4115
  name: symbol.getName(),
@@ -4014,7 +4121,8 @@ function serializeInterface(declaration, symbol, context) {
4014
4121
  members,
4015
4122
  typeParameters,
4016
4123
  tags: parsedDoc?.tags,
4017
- examples: parsedDoc?.examples
4124
+ examples: parsedDoc?.examples,
4125
+ related: relations.length > 0 ? relations : undefined
4018
4126
  };
4019
4127
  const schema = interfaceToSchema(declaration, checker, typeRefs, referencedTypes);
4020
4128
  const typeDefinition = {
@@ -4026,7 +4134,8 @@ function serializeInterface(declaration, symbol, context) {
4026
4134
  members,
4027
4135
  description,
4028
4136
  source: getSourceLocation(declaration),
4029
- tags: parsedDoc?.tags
4137
+ tags: parsedDoc?.tags,
4138
+ related: relations.length > 0 ? relations : undefined
4030
4139
  };
4031
4140
  return {
4032
4141
  exportEntry,
@@ -5206,7 +5315,9 @@ function generateAssertionFix(drift, exportEntry) {
5206
5315
  const oldValue = oldValueMatch?.[1];
5207
5316
  if (!oldValue)
5208
5317
  return null;
5209
- const updatedExample = oldExample.replace(new RegExp(`//\\s*=>\\s*${escapeRegex(oldValue)}`, "g"), `// => ${newValue}`);
5318
+ const oldExampleCode = typeof oldExample === "string" ? oldExample : oldExample.code;
5319
+ const updatedCode = oldExampleCode.replace(new RegExp(`//\\s*=>\\s*${escapeRegex(oldValue)}`, "g"), `// => ${newValue}`);
5320
+ const updatedExample = typeof oldExample === "string" ? updatedCode : { ...oldExample, code: updatedCode };
5210
5321
  const updatedExamples = [...examples];
5211
5322
  updatedExamples[exampleIndex] = updatedExample;
5212
5323
  return {
@@ -5538,7 +5649,7 @@ function serializeJSDoc(patch, indent = "") {
5538
5649
  }
5539
5650
  lines.push(tagLine);
5540
5651
  }
5541
- if (patch.deprecated && patch.deprecated !== false) {
5652
+ if (patch.deprecated && typeof patch.deprecated === "string") {
5542
5653
  lines.push(`@deprecated ${patch.deprecated}`);
5543
5654
  }
5544
5655
  if (patch.examples) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/sdk",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
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.6.0",
42
+ "@openpkg-ts/spec": "^0.7.0",
43
43
  "@vercel/sandbox": "^1.0.3",
44
44
  "mdast": "^3.0.0",
45
45
  "remark-mdx": "^3.1.0",