@openpkg-ts/extract 0.24.1 → 0.26.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/bin/tspec.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  detectTsRuntime,
4
4
  extract,
5
5
  resolveCompiledPath
6
- } from "../shared/chunk-2fes3xf8.js";
6
+ } from "../shared/chunk-xamjhe27.js";
7
7
 
8
8
  // src/cli/spec.ts
9
9
  import * as fs from "node:fs";
@@ -428,8 +428,9 @@ function createProgram() {
428
428
  fromDts = found.fromDts;
429
429
  }
430
430
  if (fromDts) {
431
- console.warn(" Using .d.ts file. TSDoc comments may be missing.");
432
- console.warn(` Consider: tspec src/index.ts
431
+ console.log("Mode: Declaration-only (.d.ts)");
432
+ console.log(" Types and signatures will be extracted");
433
+ console.log(` JSDoc comments unavailable (stripped during compilation)
433
434
  `);
434
435
  }
435
436
  if (options.runtime) {
@@ -459,7 +460,8 @@ function createProgram() {
459
460
  resolveExternalTypes: !options.skipResolve,
460
461
  schemaExtraction: options.runtime ? "hybrid" : "static",
461
462
  ...options.only ? { only: options.only.split(",").map((s) => s.trim()) } : {},
462
- ...options.ignore ? { ignore: options.ignore.split(",").map((s) => s.trim()) } : {}
463
+ ...options.ignore ? { ignore: options.ignore.split(",").map((s) => s.trim()) } : {},
464
+ isDtsSource: fromDts
463
465
  });
464
466
  const normalized = normalize(result.spec);
465
467
  const validation = validateSpec(normalized);
@@ -473,6 +475,15 @@ function createProgram() {
473
475
  }
474
476
  fs.writeFileSync(options.output, JSON.stringify(normalized, null, 2));
475
477
  spin.success(`Extracted to ${options.output}`);
478
+ if (result.degradedMode) {
479
+ const { stats } = result.degradedMode;
480
+ const total = normalized.exports.length;
481
+ console.log(`
482
+ Extraction completed in declaration-only mode:`);
483
+ console.log(` ${stats.exportsWithoutDescription}/${total} exports missing descriptions`);
484
+ console.log(` ${stats.paramsWithoutDocs} parameters without documentation`);
485
+ console.log(` ${stats.missingExamples} exports without examples`);
486
+ }
476
487
  if (result.runtimeSchemas) {
477
488
  const { extracted, merged, vendors, method } = result.runtimeSchemas;
478
489
  const via = method ? ` via ${method}` : "";
@@ -831,7 +831,7 @@ function extractSeeTagText(tag) {
831
831
  }
832
832
  return typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment) ?? "";
833
833
  }
834
- function getJSDocComment(node) {
834
+ function getJSDocComment(node, symbol, checker) {
835
835
  const jsDocTags = ts3.getJSDocTags(node);
836
836
  const tags = jsDocTags.map((tag) => {
837
837
  const rawText = typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment) ?? "";
@@ -869,6 +869,13 @@ function getJSDocComment(node) {
869
869
  description = typeof firstDoc.comment === "string" ? firstDoc.comment : ts3.getTextOfJSDocComment(firstDoc.comment);
870
870
  }
871
871
  }
872
+ if (!description && symbol && checker) {
873
+ const docComment = symbol.getDocumentationComment(checker);
874
+ if (docComment.length > 0) {
875
+ description = docComment.map((c) => c.text).join(`
876
+ `);
877
+ }
878
+ }
872
879
  const examples = parseExamplesFromTags(tags);
873
880
  return { description, tags, examples };
874
881
  }
@@ -954,12 +961,13 @@ function isSymbolDeprecated(symbol) {
954
961
  }
955
962
  return false;
956
963
  }
957
- function getJSDocForSignature(signature) {
964
+ function getJSDocForSignature(signature, checker) {
958
965
  const decl = signature.getDeclaration();
959
966
  if (!decl) {
960
967
  return { tags: [], examples: [] };
961
968
  }
962
- return getJSDocComment(decl);
969
+ const symbol = checker?.getSymbolAtLocation(decl);
970
+ return getJSDocComment(decl, symbol, checker);
963
971
  }
964
972
  function extractTypeParametersFromSignature(signature, checker) {
965
973
  const typeParams = signature.getTypeParameters();
@@ -1819,7 +1827,7 @@ function serializeInheritedMember(symbol, inheritedFrom, ctx, isStatic) {
1819
1827
  const params = extractParameters(sig, ctx);
1820
1828
  const returnType = checker.getReturnTypeOfSignature(sig);
1821
1829
  registerReferencedTypes(returnType, ctx);
1822
- const sigDoc = getJSDocForSignature(sig);
1830
+ const sigDoc = getJSDocForSignature(sig, checker);
1823
1831
  return {
1824
1832
  parameters: params.length > 0 ? params : undefined,
1825
1833
  returns: {
@@ -1853,7 +1861,7 @@ function serializeClass(node, ctx) {
1853
1861
  return null;
1854
1862
  const deprecated = isSymbolDeprecated(symbol);
1855
1863
  const declSourceFile = node.getSourceFile();
1856
- const { description, tags, examples } = getJSDocComment(node);
1864
+ const { description, tags, examples } = getJSDocComment(node, symbol, checker);
1857
1865
  const source = getSourceLocation(node, declSourceFile);
1858
1866
  const typeParameters = extractTypeParameters(node, checker);
1859
1867
  const members = [];
@@ -1995,7 +2003,7 @@ function serializeMethod(node, ctx) {
1995
2003
  const params = extractParameters(sig, ctx);
1996
2004
  const returnType = checker.getReturnTypeOfSignature(sig);
1997
2005
  registerReferencedTypes(returnType, ctx);
1998
- const sigDoc = getJSDocForSignature(sig);
2006
+ const sigDoc = getJSDocForSignature(sig, checker);
1999
2007
  const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
2000
2008
  return {
2001
2009
  parameters: params.length > 0 ? params : undefined,
@@ -2103,7 +2111,7 @@ function serializeEnum(node, ctx) {
2103
2111
  return null;
2104
2112
  const deprecated = isSymbolDeprecated(symbol);
2105
2113
  const declSourceFile = node.getSourceFile();
2106
- const { description, tags, examples } = getJSDocComment(node);
2114
+ const { description, tags, examples } = getJSDocComment(node, symbol, checker);
2107
2115
  const source = getSourceLocation(node, declSourceFile);
2108
2116
  const members = node.members.map((member) => {
2109
2117
  const memberSymbol = checker.getSymbolAtLocation(member.name);
@@ -2176,14 +2184,14 @@ function serializeFunctionExport(node, ctx) {
2176
2184
  return null;
2177
2185
  const deprecated = isSymbolDeprecated(symbol);
2178
2186
  const declSourceFile = node.getSourceFile();
2179
- const { description, tags, examples } = getJSDocComment(node);
2187
+ const { description, tags, examples } = getJSDocComment(node, symbol, ctx.typeChecker);
2180
2188
  const source = getSourceLocation(node, declSourceFile);
2181
2189
  const typeParameters = extractTypeParameters(node, ctx.typeChecker);
2182
2190
  const type = ctx.typeChecker.getTypeAtLocation(node);
2183
2191
  const callSignatures = type.getCallSignatures();
2184
2192
  const signatures = callSignatures.map((sig, index) => {
2185
2193
  const params = extractParameters(sig, ctx);
2186
- const sigDoc = getJSDocForSignature(sig);
2194
+ const sigDoc = getJSDocForSignature(sig, ctx.typeChecker);
2187
2195
  const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
2188
2196
  return {
2189
2197
  parameters: params,
@@ -2219,7 +2227,7 @@ function serializeInterface(node, ctx) {
2219
2227
  return null;
2220
2228
  const deprecated = isSymbolDeprecated(symbol);
2221
2229
  const declSourceFile = node.getSourceFile();
2222
- const { description, tags, examples } = getJSDocComment(node);
2230
+ const { description, tags, examples } = getJSDocComment(node, symbol, checker);
2223
2231
  const source = getSourceLocation(node, declSourceFile);
2224
2232
  const typeParameters = extractTypeParameters(node, checker);
2225
2233
  const members = [];
@@ -2294,7 +2302,7 @@ function serializeMethodSignature(node, ctx) {
2294
2302
  const params = extractParameters(sig, ctx);
2295
2303
  const returnType = checker.getReturnTypeOfSignature(sig);
2296
2304
  registerReferencedTypes(returnType, ctx);
2297
- const sigDoc = getJSDocForSignature(sig);
2305
+ const sigDoc = getJSDocForSignature(sig, checker);
2298
2306
  const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
2299
2307
  return {
2300
2308
  parameters: params.length > 0 ? params : undefined,
@@ -2404,7 +2412,7 @@ function serializeTypeAlias(node, ctx) {
2404
2412
  return null;
2405
2413
  const deprecated = isSymbolDeprecated(symbol);
2406
2414
  const declSourceFile = node.getSourceFile();
2407
- const { description, tags, examples } = getJSDocComment(node);
2415
+ const { description, tags, examples } = getJSDocComment(node, symbol, ctx.typeChecker);
2408
2416
  const source = getSourceLocation(node, declSourceFile);
2409
2417
  const typeParameters = extractTypeParameters(node, ctx.typeChecker);
2410
2418
  const type = ctx.typeChecker.getTypeAtLocation(node);
@@ -2600,7 +2608,7 @@ function serializeVariable(node, statement, ctx) {
2600
2608
  return null;
2601
2609
  const deprecated = isSymbolDeprecated(symbol);
2602
2610
  const declSourceFile = node.getSourceFile();
2603
- const { description, tags, examples } = getJSDocComment(statement);
2611
+ const { description, tags, examples } = getJSDocComment(statement, symbol, ctx.typeChecker);
2604
2612
  const source = getSourceLocation(node, declSourceFile);
2605
2613
  const type = ctx.typeChecker.getTypeAtLocation(node);
2606
2614
  const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
@@ -3112,6 +3120,28 @@ function clearTypeDefinitionCache() {
3112
3120
  typeDefinitionCache = null;
3113
3121
  internalTagCache = null;
3114
3122
  }
3123
+ var YIELD_BATCH_SIZE = 5;
3124
+ function computeDegradedStats(exports) {
3125
+ let exportsWithoutDescription = 0;
3126
+ let paramsWithoutDocs = 0;
3127
+ let missingExamples = 0;
3128
+ for (const exp of exports) {
3129
+ if (!exp.description)
3130
+ exportsWithoutDescription++;
3131
+ if (!exp.examples || exp.examples.length === 0)
3132
+ missingExamples++;
3133
+ const signatures = exp.signatures;
3134
+ if (signatures) {
3135
+ for (const sig of signatures) {
3136
+ for (const param of sig.parameters ?? []) {
3137
+ if (!param.description)
3138
+ paramsWithoutDocs++;
3139
+ }
3140
+ }
3141
+ }
3142
+ }
3143
+ return { exportsWithoutDescription, paramsWithoutDocs, missingExamples };
3144
+ }
3115
3145
  var BUILTIN_TYPES2 = new Set([
3116
3146
  "Array",
3117
3147
  "ArrayBuffer",
@@ -3206,7 +3236,9 @@ async function extract(options) {
3206
3236
  resolveExternalTypes,
3207
3237
  includeSchema,
3208
3238
  only,
3209
- ignore
3239
+ ignore,
3240
+ onProgress,
3241
+ isDtsSource
3210
3242
  } = options;
3211
3243
  const diagnostics = [];
3212
3244
  let exports = [];
@@ -3214,7 +3246,7 @@ async function extract(options) {
3214
3246
  const { program, sourceFile } = result;
3215
3247
  if (!sourceFile) {
3216
3248
  return {
3217
- spec: createEmptySpec(entryFile, includeSchema),
3249
+ spec: createEmptySpec(entryFile, includeSchema, isDtsSource),
3218
3250
  diagnostics: [{ message: `Could not load source file: ${entryFile}`, severity: "error" }]
3219
3251
  };
3220
3252
  }
@@ -3222,7 +3254,7 @@ async function extract(options) {
3222
3254
  const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
3223
3255
  if (!moduleSymbol) {
3224
3256
  return {
3225
- spec: createEmptySpec(entryFile, includeSchema),
3257
+ spec: createEmptySpec(entryFile, includeSchema, isDtsSource),
3226
3258
  diagnostics: [{ message: "Could not get module symbol", severity: "warning" }]
3227
3259
  };
3228
3260
  }
@@ -3237,10 +3269,15 @@ async function extract(options) {
3237
3269
  resolveExternalTypes
3238
3270
  });
3239
3271
  ctx.exportedIds = exportedIds;
3240
- for (const symbol of exportedSymbols) {
3272
+ const filteredSymbols = exportedSymbols.filter((s) => shouldIncludeExport(s.getName(), only, ignore));
3273
+ const total = filteredSymbols.length;
3274
+ for (let i = 0;i < filteredSymbols.length; i++) {
3275
+ const symbol = filteredSymbols[i];
3241
3276
  const exportName = symbol.getName();
3242
- if (!shouldIncludeExport(exportName, only, ignore))
3243
- continue;
3277
+ onProgress?.(i + 1, total, exportName);
3278
+ if (i > 0 && i % YIELD_BATCH_SIZE === 0) {
3279
+ await new Promise((r) => setImmediate(r));
3280
+ }
3244
3281
  try {
3245
3282
  const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
3246
3283
  if (!declaration)
@@ -3326,15 +3363,21 @@ async function extract(options) {
3326
3363
  generation: {
3327
3364
  generator: "@openpkg-ts/extract",
3328
3365
  timestamp: new Date().toISOString(),
3329
- ...options.schemaExtraction === "hybrid" ? { schemaExtraction: "hybrid" } : {}
3366
+ mode: isDtsSource ? "declaration-only" : "source",
3367
+ ...options.schemaExtraction === "hybrid" ? { schemaExtraction: "hybrid" } : {},
3368
+ ...isDtsSource && {
3369
+ limitations: ["No JSDoc descriptions", "No @example tags", "No @param descriptions"]
3370
+ }
3330
3371
  }
3331
3372
  };
3332
3373
  const internalForgotten = forgottenExports.filter((f) => !f.isExternal);
3374
+ const degradedMode = isDtsSource ? { reason: "dts-source", stats: computeDegradedStats(normalizedExports) } : undefined;
3333
3375
  return {
3334
3376
  spec,
3335
3377
  diagnostics,
3336
3378
  ...internalForgotten.length > 0 ? { forgottenExports: internalForgotten } : {},
3337
- ...runtimeMetadata ? { runtimeSchemas: runtimeMetadata } : {}
3379
+ ...runtimeMetadata ? { runtimeSchemas: runtimeMetadata } : {},
3380
+ ...degradedMode ? { degradedMode } : {}
3338
3381
  };
3339
3382
  }
3340
3383
  function collectAllRefsWithContext(obj, refs, state) {
@@ -3592,7 +3635,7 @@ function serializeNamespaceMember(symbol, memberName, ctx) {
3592
3635
  const returnType = checker.getReturnTypeOfSignature(sig);
3593
3636
  registerReferencedTypes(returnType, ctx);
3594
3637
  const returnSchema = buildSchema(returnType, ctx.typeChecker, ctx);
3595
- const sigDoc = getJSDocForSignature(sig);
3638
+ const sigDoc = getJSDocForSignature(sig, checker);
3596
3639
  const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
3597
3640
  return {
3598
3641
  parameters: params,
@@ -3686,7 +3729,7 @@ function withExportName(entry, exportName) {
3686
3729
  name: entry.name
3687
3730
  };
3688
3731
  }
3689
- function createEmptySpec(entryFile, includeSchema) {
3732
+ function createEmptySpec(entryFile, includeSchema, isDtsSource) {
3690
3733
  return {
3691
3734
  ...includeSchema ? { $schema: SCHEMA_URL } : {},
3692
3735
  openpkg: SCHEMA_VERSION,
@@ -3694,7 +3737,11 @@ function createEmptySpec(entryFile, includeSchema) {
3694
3737
  exports: [],
3695
3738
  generation: {
3696
3739
  generator: "@openpkg-ts/extract",
3697
- timestamp: new Date().toISOString()
3740
+ timestamp: new Date().toISOString(),
3741
+ mode: isDtsSource ? "declaration-only" : "source",
3742
+ ...isDtsSource && {
3743
+ limitations: ["No JSDoc descriptions", "No @example tags", "No @param descriptions"]
3744
+ }
3698
3745
  }
3699
3746
  };
3700
3747
  }
@@ -43,7 +43,7 @@ declare class TypeRegistry {
43
43
  }
44
44
  import { SpecExample, SpecSource, SpecTag, SpecTypeParameter } from "@openpkg-ts/spec";
45
45
  import ts3 from "typescript";
46
- declare function getJSDocComment(node: ts3.Node): {
46
+ declare function getJSDocComment(node: ts3.Node, symbol?: ts3.Symbol, checker?: ts3.TypeChecker): {
47
47
  description?: string;
48
48
  tags: SpecTag[];
49
49
  examples: SpecExample[];
@@ -237,6 +237,10 @@ interface ExtractOptions {
237
237
  only?: string[];
238
238
  /** Ignore these exports (supports * wildcards) */
239
239
  ignore?: string[];
240
+ /** Progress callback for tracking extraction progress */
241
+ onProgress?: (current: number, total: number, item: string) => void;
242
+ /** Whether source is a .d.ts file (degraded mode - TSDoc may be missing) */
243
+ isDtsSource?: boolean;
240
244
  }
241
245
  interface ExtractResult {
242
246
  spec: OpenPkg;
@@ -255,6 +259,15 @@ interface ExtractResult {
255
259
  /** Extraction method used: 'compiled' or 'direct-ts (runtime)' */
256
260
  method?: string;
257
261
  };
262
+ /** Degraded mode info when extracting from .d.ts files */
263
+ degradedMode?: {
264
+ reason: "dts-source";
265
+ stats: {
266
+ exportsWithoutDescription: number;
267
+ paramsWithoutDocs: number;
268
+ missingExamples: number;
269
+ };
270
+ };
258
271
  }
259
272
  interface Diagnostic {
260
273
  message: string;
package/dist/src/index.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  valibotAdapter,
49
49
  withDescription,
50
50
  zodAdapter
51
- } from "../shared/chunk-2fes3xf8.js";
51
+ } from "../shared/chunk-xamjhe27.js";
52
52
  // src/types/utils.ts
53
53
  function isExported(node) {
54
54
  const modifiers = node.modifiers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/extract",
3
- "version": "0.24.1",
3
+ "version": "0.26.0",
4
4
  "description": "TypeScript export extraction to OpenPkg spec",
5
5
  "keywords": [
6
6
  "openpkg",
@@ -40,7 +40,7 @@
40
40
  "format": "biome format --write src/"
41
41
  },
42
42
  "dependencies": {
43
- "@openpkg-ts/spec": "^0.24.0",
43
+ "@openpkg-ts/spec": "^0.26.0",
44
44
  "chalk": "^5.4.1",
45
45
  "commander": "^12.0.0",
46
46
  "tree-sitter-wasms": "^0.1.13",