@openpkg-ts/extract 0.27.1 → 0.28.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-v9c97d62.js";
6
+ } from "../shared/chunk-7hk4mmb3.js";
7
7
 
8
8
  // src/cli/spec.ts
9
9
  import * as fs from "node:fs";
@@ -228,6 +228,16 @@ function buildSchema(type, checker, ctx, _depth = 0) {
228
228
  return { type: "bigint" };
229
229
  if (type.flags & ts.TypeFlags.ESSymbol)
230
230
  return { type: "symbol" };
231
+ if (type.isThisType?.()) {
232
+ const constraint = type.getConstraint?.();
233
+ const symbol2 = constraint?.getSymbol() ?? type.getSymbol();
234
+ if (symbol2 && !isAnonymous(type)) {
235
+ return {
236
+ $ref: `#/types/${symbol2.getName()}`,
237
+ "x-ts-type": "this"
238
+ };
239
+ }
240
+ }
231
241
  if (type.flags & ts.TypeFlags.StringLiteral) {
232
242
  const literal = type.value;
233
243
  return { type: "string", enum: [literal] };
@@ -259,8 +269,10 @@ function buildSchema(type, checker, ctx, _depth = 0) {
259
269
  }
260
270
  return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
261
271
  }
262
- if (type.isIntersection()) {
263
- const filteredTypes = type.types.filter((t) => !(t.flags & ts.TypeFlags.Never));
272
+ const isIntersectionType = type.isIntersection() || !!(type.flags & ts.TypeFlags.Intersection);
273
+ if (isIntersectionType && "types" in type) {
274
+ const intersectionType = type;
275
+ const filteredTypes = intersectionType.types.filter((t) => !(t.flags & ts.TypeFlags.Never));
264
276
  if (filteredTypes.length === 0) {
265
277
  return { type: "never" };
266
278
  }
@@ -283,8 +295,9 @@ function buildSchema(type, checker, ctx, _depth = 0) {
283
295
  return { type: "array", items: {} };
284
296
  }
285
297
  if (checker.isArrayType(type)) {
286
- const typeRef2 = type;
287
- const elementType = typeRef2.typeArguments?.[0];
298
+ const arrayTypeRef = type;
299
+ const arrayTypeArgs = checker.getTypeArguments(arrayTypeRef);
300
+ const elementType = arrayTypeArgs?.[0];
288
301
  if (elementType) {
289
302
  if (ctx) {
290
303
  return withDepth(ctx, () => ({
@@ -297,8 +310,8 @@ function buildSchema(type, checker, ctx, _depth = 0) {
297
310
  return { type: "array" };
298
311
  }
299
312
  if (checker.isTupleType(type)) {
300
- const typeRef2 = type;
301
- const elementTypes = typeRef2.typeArguments ?? [];
313
+ const tupleTypeRef = type;
314
+ const elementTypes = checker.getTypeArguments(tupleTypeRef) ?? [];
302
315
  if (ctx) {
303
316
  return withDepth(ctx, () => {
304
317
  const prevInTupleElement = ctx.inTupleElement;
@@ -323,7 +336,8 @@ function buildSchema(type, checker, ctx, _depth = 0) {
323
336
  };
324
337
  }
325
338
  const typeRef = type;
326
- if (typeRef.target && typeRef.typeArguments && typeRef.typeArguments.length > 0) {
339
+ const typeArgs = typeRef.target ? checker.getTypeArguments(typeRef) : undefined;
340
+ if (typeRef.target && typeArgs && typeArgs.length > 0) {
327
341
  const symbol2 = typeRef.target.getSymbol();
328
342
  const name = symbol2?.getName();
329
343
  if (name && BUILTIN_TYPES.has(name)) {
@@ -335,7 +349,7 @@ function buildSchema(type, checker, ctx, _depth = 0) {
335
349
  return withDepth(ctx, () => {
336
350
  const schema2 = {
337
351
  $ref: `#/types/${name}`,
338
- typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
352
+ typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
339
353
  };
340
354
  if (packageOrigin) {
341
355
  schema2["x-ts-package"] = packageOrigin;
@@ -345,7 +359,7 @@ function buildSchema(type, checker, ctx, _depth = 0) {
345
359
  }
346
360
  const schema = {
347
361
  $ref: `#/types/${name}`,
348
- typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
362
+ typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
349
363
  };
350
364
  if (packageOrigin) {
351
365
  schema["x-ts-package"] = packageOrigin;
@@ -1557,6 +1571,16 @@ async function extractStandardSchemasFromProject(entryFile, baseDir, options = {
1557
1571
 
1558
1572
  // src/types/parameters.ts
1559
1573
  import ts5 from "typescript";
1574
+ function stripUndefinedFromType(type, checker) {
1575
+ if (!type.isUnion())
1576
+ return type;
1577
+ const nonUndefinedTypes = type.types.filter((t) => !(t.flags & ts5.TypeFlags.Undefined));
1578
+ if (nonUndefinedTypes.length === 0)
1579
+ return type;
1580
+ if (nonUndefinedTypes.length === 1)
1581
+ return nonUndefinedTypes[0];
1582
+ return checker.getUnionType(nonUndefinedTypes);
1583
+ }
1560
1584
  function extractParameters(signature, ctx) {
1561
1585
  const { typeChecker: checker } = ctx;
1562
1586
  const result = [];
@@ -1569,12 +1593,20 @@ function extractParameters(signature, ctx) {
1569
1593
  const expandedParams = expandBindingPattern(decl, type, jsdocTags, ctx);
1570
1594
  result.push(...expandedParams);
1571
1595
  } else {
1572
- registerReferencedTypes(type, ctx);
1573
- result.push({
1574
- name: param.getName(),
1575
- schema: buildSchema(type, checker, ctx),
1576
- required: !(param.flags & 16777216)
1577
- });
1596
+ const isOptional = !!(param.flags & 16777216);
1597
+ const effectiveType = isOptional ? stripUndefinedFromType(type, checker) : type;
1598
+ registerReferencedTypes(effectiveType, ctx);
1599
+ const paramName = param.getName();
1600
+ const description = getParamDescription(paramName, jsdocTags);
1601
+ const paramResult = {
1602
+ name: paramName,
1603
+ schema: buildSchema(effectiveType, checker, ctx),
1604
+ required: !isOptional
1605
+ };
1606
+ if (description) {
1607
+ paramResult.description = description;
1608
+ }
1609
+ result.push(paramResult);
1578
1610
  }
1579
1611
  }
1580
1612
  return result;
@@ -1592,13 +1624,14 @@ function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
1592
1624
  const propSymbol = allProperties.get(propertyName);
1593
1625
  if (!propSymbol)
1594
1626
  continue;
1595
- const propType = checker.getTypeOfSymbol(propSymbol);
1596
- registerReferencedTypes(propType, ctx);
1597
1627
  const isOptional = !!(propSymbol.flags & ts5.SymbolFlags.Optional) || element.initializer !== undefined;
1628
+ const propType = checker.getTypeOfSymbol(propSymbol);
1629
+ const effectiveType = isOptional ? stripUndefinedFromType(propType, checker) : propType;
1630
+ registerReferencedTypes(effectiveType, ctx);
1598
1631
  const description = getParamDescription(propertyName, jsdocTags, inferredAlias);
1599
1632
  const param = {
1600
1633
  name: propertyName,
1601
- schema: buildSchema(propType, checker, ctx),
1634
+ schema: buildSchema(effectiveType, checker, ctx),
1602
1635
  required: !isOptional
1603
1636
  };
1604
1637
  if (description) {
@@ -1914,6 +1947,11 @@ function serializeClass(node, ctx) {
1914
1947
  members.push(...inheritedStatic);
1915
1948
  const extendsClause = getExtendsClause(node, checker);
1916
1949
  const implementsClause = getImplementsClause(node, checker);
1950
+ const classFlags = {};
1951
+ const classModifiers = ts7.getModifiers(node);
1952
+ if (classModifiers?.some((m) => m.kind === ts7.SyntaxKind.AbstractKeyword)) {
1953
+ classFlags.abstract = true;
1954
+ }
1917
1955
  return {
1918
1956
  id: name,
1919
1957
  name,
@@ -1927,7 +1965,8 @@ function serializeClass(node, ctx) {
1927
1965
  extends: extendsClause,
1928
1966
  implements: implementsClause?.length ? implementsClause : undefined,
1929
1967
  ...deprecated ? { deprecated: true } : {},
1930
- ...examples.length > 0 ? { examples } : {}
1968
+ ...examples.length > 0 ? { examples } : {},
1969
+ ...Object.keys(classFlags).length > 0 ? { flags: classFlags } : {}
1931
1970
  };
1932
1971
  }
1933
1972
  function getMemberName(member) {
@@ -2026,6 +2065,9 @@ function serializeMethod(node, ctx) {
2026
2065
  if (modifiers?.some((m) => m.kind === ts7.SyntaxKind.AsyncKeyword)) {
2027
2066
  flags.async = true;
2028
2067
  }
2068
+ if (modifiers?.some((m) => m.kind === ts7.SyntaxKind.AbstractKeyword)) {
2069
+ flags.abstract = true;
2070
+ }
2029
2071
  return {
2030
2072
  name,
2031
2073
  kind: "method",
@@ -2062,6 +2104,24 @@ function serializeAccessor(node, ctx) {
2062
2104
  const flags = {};
2063
2105
  if (isStatic(node))
2064
2106
  flags.static = true;
2107
+ let signatures;
2108
+ if (ts7.isSetAccessorDeclaration(node) && node.parameters.length > 0) {
2109
+ const param = node.parameters[0];
2110
+ const paramName = param.name.getText();
2111
+ const paramType = checker.getTypeAtLocation(param);
2112
+ registerReferencedTypes(paramType, ctx);
2113
+ signatures = [
2114
+ {
2115
+ parameters: [
2116
+ {
2117
+ name: paramName,
2118
+ schema: buildSchema(paramType, checker, ctx),
2119
+ required: true
2120
+ }
2121
+ ]
2122
+ }
2123
+ ];
2124
+ }
2065
2125
  return {
2066
2126
  name,
2067
2127
  kind,
@@ -2069,6 +2129,7 @@ function serializeAccessor(node, ctx) {
2069
2129
  tags: tags.length > 0 ? tags : undefined,
2070
2130
  visibility,
2071
2131
  schema,
2132
+ signatures,
2072
2133
  flags: Object.keys(flags).length > 0 ? flags : undefined
2073
2134
  };
2074
2135
  }
@@ -2232,6 +2293,7 @@ function serializeInterface(node, ctx) {
2232
2293
  const typeParameters = extractTypeParameters(node, checker);
2233
2294
  const members = [];
2234
2295
  const methodsByName = new Map;
2296
+ let callSignatureMember = null;
2235
2297
  for (const member of node.members) {
2236
2298
  if (ts9.isPropertySignature(member)) {
2237
2299
  const propMember = serializePropertySignature(member, ctx);
@@ -2239,23 +2301,60 @@ function serializeInterface(node, ctx) {
2239
2301
  members.push(propMember);
2240
2302
  } else if (ts9.isMethodSignature(member)) {
2241
2303
  const methodMember = serializeMethodSignature(member, ctx);
2242
- if (methodMember?.name) {
2243
- if (!methodsByName.has(methodMember.name)) {
2304
+ if (methodMember?.name && methodMember.signatures) {
2305
+ const existing = methodsByName.get(methodMember.name);
2306
+ if (existing && existing.signatures) {
2307
+ const startIndex = existing.signatures.length;
2308
+ const newSigs = methodMember.signatures.map((sig, i) => ({
2309
+ ...sig,
2310
+ overloadIndex: startIndex + i
2311
+ }));
2312
+ if (existing.signatures.length > 0 && existing.signatures[0].overloadIndex === undefined) {
2313
+ existing.signatures = existing.signatures.map((sig, i) => ({
2314
+ ...sig,
2315
+ overloadIndex: i
2316
+ }));
2317
+ }
2318
+ existing.signatures.push(...newSigs);
2319
+ } else {
2244
2320
  methodsByName.set(methodMember.name, methodMember);
2245
2321
  }
2246
2322
  }
2247
2323
  } else if (ts9.isCallSignatureDeclaration(member)) {
2248
- const callMember = serializeCallSignature(member, ctx);
2249
- if (callMember)
2250
- members.push(callMember);
2324
+ const callSig = serializeCallSignature(member, ctx);
2325
+ if (callSig && callSig.signatures) {
2326
+ if (callSignatureMember && callSignatureMember.signatures) {
2327
+ const startIndex = callSignatureMember.signatures.length;
2328
+ const newSigs = callSig.signatures.map((sig, i) => ({
2329
+ ...sig,
2330
+ overloadIndex: startIndex + i
2331
+ }));
2332
+ if (callSignatureMember.signatures.length > 0 && callSignatureMember.signatures[0].overloadIndex === undefined) {
2333
+ callSignatureMember.signatures = callSignatureMember.signatures.map((sig, i) => ({
2334
+ ...sig,
2335
+ overloadIndex: i
2336
+ }));
2337
+ }
2338
+ callSignatureMember.signatures.push(...newSigs);
2339
+ if (callSig.description && !callSignatureMember.description) {
2340
+ callSignatureMember.description = callSig.description;
2341
+ }
2342
+ } else {
2343
+ callSignatureMember = callSig;
2344
+ }
2345
+ }
2251
2346
  } else if (ts9.isIndexSignatureDeclaration(member)) {
2252
2347
  const indexMember = serializeIndexSignature(member, ctx);
2253
2348
  if (indexMember)
2254
2349
  members.push(indexMember);
2255
2350
  }
2256
2351
  }
2352
+ if (callSignatureMember) {
2353
+ members.push(callSignatureMember);
2354
+ }
2257
2355
  members.push(...methodsByName.values());
2258
2356
  const extendsClause = getInterfaceExtends(node, checker);
2357
+ const exportSignatures = callSignatureMember?.signatures && callSignatureMember.signatures.length > 0 ? callSignatureMember.signatures : undefined;
2259
2358
  return {
2260
2359
  id: name,
2261
2360
  name,
@@ -2265,6 +2364,7 @@ function serializeInterface(node, ctx) {
2265
2364
  source,
2266
2365
  typeParameters,
2267
2366
  members: members.length > 0 ? members : undefined,
2367
+ signatures: exportSignatures,
2268
2368
  extends: extendsClause,
2269
2369
  ...deprecated ? { deprecated: true } : {},
2270
2370
  ...examples.length > 0 ? { examples } : {}
@@ -2366,10 +2466,7 @@ function serializeIndexSignature(node, ctx) {
2366
2466
  kind: "index-signature",
2367
2467
  description,
2368
2468
  tags: tags.length > 0 ? tags : undefined,
2369
- schema: {
2370
- type: "object",
2371
- additionalProperties: valueSchema
2372
- }
2469
+ schema: valueSchema
2373
2470
  };
2374
2471
  }
2375
2472
  function getInterfaceExtends(node, checker) {
@@ -2639,10 +2736,10 @@ var SCHEMA_DIALECT_URLS = {
2639
2736
  "draft-07": "http://json-schema.org/draft-07/schema#"
2640
2737
  };
2641
2738
  var TS_PRIMITIVE_NORMALIZATIONS = {
2642
- void: () => ({ type: "null" }),
2739
+ void: () => ({ type: "null", "x-ts-type": "void" }),
2643
2740
  never: () => ({ not: {} }),
2644
2741
  any: () => ({}),
2645
- unknown: () => ({}),
2742
+ unknown: () => ({ "x-ts-type": "unknown" }),
2646
2743
  undefined: () => ({ type: "null" }),
2647
2744
  bigint: () => ({ type: "integer", "x-ts-type": "bigint" }),
2648
2745
  symbol: () => ({ type: "string", "x-ts-type": "symbol" })
@@ -3316,13 +3413,13 @@ async function extract(options) {
3316
3413
  await new Promise((r) => setImmediate(r));
3317
3414
  }
3318
3415
  try {
3319
- const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
3416
+ const { declaration, targetSymbol, isTypeOnly } = resolveExportTarget(symbol, typeChecker);
3320
3417
  if (!declaration) {
3321
3418
  tracker.status = "skipped";
3322
3419
  tracker.skipReason = "no-declaration";
3323
3420
  continue;
3324
3421
  }
3325
- const exp = serializeDeclaration(declaration, symbol, targetSymbol, exportName, ctx);
3422
+ const exp = serializeDeclaration(declaration, symbol, targetSymbol, exportName, ctx, isTypeOnly);
3326
3423
  if (exp) {
3327
3424
  exports.push(exp);
3328
3425
  tracker.status = "success";
@@ -3586,8 +3683,23 @@ function collectForgottenExports(exports, types, program, sourceFile, exportedId
3586
3683
  }
3587
3684
  return forgottenExports;
3588
3685
  }
3686
+ function isTypeOnlyExport(symbol) {
3687
+ const declarations = symbol.declarations ?? [];
3688
+ for (const decl of declarations) {
3689
+ if (ts11.isExportSpecifier(decl)) {
3690
+ if (decl.isTypeOnly)
3691
+ return true;
3692
+ const exportDecl = decl.parent?.parent;
3693
+ if (exportDecl && ts11.isExportDeclaration(exportDecl) && exportDecl.isTypeOnly) {
3694
+ return true;
3695
+ }
3696
+ }
3697
+ }
3698
+ return false;
3699
+ }
3589
3700
  function resolveExportTarget(symbol, checker) {
3590
3701
  let targetSymbol = symbol;
3702
+ const isTypeOnly = isTypeOnlyExport(symbol);
3591
3703
  if (symbol.flags & ts11.SymbolFlags.Alias) {
3592
3704
  const aliasTarget = checker.getAliasedSymbol(symbol);
3593
3705
  if (aliasTarget && aliasTarget !== symbol) {
@@ -3596,9 +3708,9 @@ function resolveExportTarget(symbol, checker) {
3596
3708
  }
3597
3709
  const declarations = targetSymbol.declarations ?? [];
3598
3710
  const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts11.SyntaxKind.ExportSpecifier) || declarations[0];
3599
- return { declaration, targetSymbol };
3711
+ return { declaration, targetSymbol, isTypeOnly };
3600
3712
  }
3601
- function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportName, ctx) {
3713
+ function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportName, ctx, isTypeOnly = false) {
3602
3714
  let result = null;
3603
3715
  if (ts11.isFunctionDeclaration(declaration)) {
3604
3716
  result = serializeFunctionExport(declaration, ctx);
@@ -3622,6 +3734,12 @@ function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportNa
3622
3734
  }
3623
3735
  if (result) {
3624
3736
  result = withExportName(result, exportName);
3737
+ if (isTypeOnly) {
3738
+ result = {
3739
+ ...result,
3740
+ flags: { ...result.flags ?? {}, typeOnly: true }
3741
+ };
3742
+ }
3625
3743
  }
3626
3744
  return result;
3627
3745
  }
package/dist/src/index.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  valibotAdapter,
49
49
  withDescription,
50
50
  zodAdapter
51
- } from "../shared/chunk-v9c97d62.js";
51
+ } from "../shared/chunk-7hk4mmb3.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.27.1",
3
+ "version": "0.28.0",
4
4
  "description": "TypeScript export extraction to OpenPkg spec",
5
5
  "keywords": [
6
6
  "openpkg",