@openpkg-ts/extract 0.15.0 → 0.16.1
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
|
@@ -661,12 +661,33 @@ function parseExamplesFromTags(tags) {
|
|
|
661
661
|
}
|
|
662
662
|
return examples;
|
|
663
663
|
}
|
|
664
|
+
function stripParamSeparator(text) {
|
|
665
|
+
if (!text)
|
|
666
|
+
return;
|
|
667
|
+
const stripped = text.replace(/^-\s*/, "").trim();
|
|
668
|
+
return stripped || undefined;
|
|
669
|
+
}
|
|
670
|
+
function stripTypeParamSeparator(text) {
|
|
671
|
+
if (!text)
|
|
672
|
+
return;
|
|
673
|
+
const match = text.match(/^\w+\s+-\s*(.*)$/s);
|
|
674
|
+
if (match) {
|
|
675
|
+
return match[1].trim() || undefined;
|
|
676
|
+
}
|
|
677
|
+
return text.trim() || undefined;
|
|
678
|
+
}
|
|
664
679
|
function getJSDocComment(node) {
|
|
665
680
|
const jsDocTags = ts3.getJSDocTags(node);
|
|
666
|
-
const tags = jsDocTags.map((tag) =>
|
|
667
|
-
|
|
668
|
-
text
|
|
669
|
-
|
|
681
|
+
const tags = jsDocTags.map((tag) => {
|
|
682
|
+
const rawText = typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment) ?? "";
|
|
683
|
+
let text = rawText;
|
|
684
|
+
if (tag.tagName.text === "param") {
|
|
685
|
+
text = stripParamSeparator(rawText) ?? "";
|
|
686
|
+
} else if (tag.tagName.text === "typeParam") {
|
|
687
|
+
text = stripTypeParamSeparator(rawText) ?? "";
|
|
688
|
+
}
|
|
689
|
+
return { name: tag.tagName.text, text };
|
|
690
|
+
});
|
|
670
691
|
const jsDocComments = node.jsDoc;
|
|
671
692
|
let description;
|
|
672
693
|
if (jsDocComments && jsDocComments.length > 0) {
|
|
@@ -694,7 +715,7 @@ function getParamDescription(propertyName, jsdocTags, inferredAlias) {
|
|
|
694
715
|
const isMatch = tagParamName === propertyName || inferredAlias && tagParamName === `${inferredAlias}.${propertyName}` || tagParamName.endsWith(`.${propertyName}`);
|
|
695
716
|
if (isMatch) {
|
|
696
717
|
const comment = typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment);
|
|
697
|
-
return comment
|
|
718
|
+
return stripParamSeparator(comment);
|
|
698
719
|
}
|
|
699
720
|
}
|
|
700
721
|
return;
|
|
@@ -1609,14 +1630,26 @@ async function extract(options) {
|
|
|
1609
1630
|
}
|
|
1610
1631
|
const meta = await getPackageMeta(entryFile, baseDir);
|
|
1611
1632
|
const types = ctx.typeRegistry.getAll();
|
|
1612
|
-
const
|
|
1613
|
-
for (const
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1633
|
+
const forgottenExports = collectForgottenExports(exports, types, program, sourceFile);
|
|
1634
|
+
for (const forgotten of forgottenExports) {
|
|
1635
|
+
const refSummary = forgotten.referencedBy.slice(0, 3).map((r) => `${r.exportName} (${r.location})`).join(", ");
|
|
1636
|
+
const moreRefs = forgotten.referencedBy.length > 3 ? ` +${forgotten.referencedBy.length - 3} more` : "";
|
|
1637
|
+
if (forgotten.isExternal) {
|
|
1638
|
+
diagnostics.push({
|
|
1639
|
+
message: `External type '${forgotten.name}' referenced by: ${refSummary}${moreRefs}`,
|
|
1640
|
+
severity: "info",
|
|
1641
|
+
code: "EXTERNAL_TYPE_REF",
|
|
1642
|
+
suggestion: forgotten.definedIn ? `Type is from: ${forgotten.definedIn}` : "Type is from an external package"
|
|
1643
|
+
});
|
|
1644
|
+
} else {
|
|
1645
|
+
diagnostics.push({
|
|
1646
|
+
message: `Forgotten export: '${forgotten.name}' referenced by: ${refSummary}${moreRefs}`,
|
|
1647
|
+
severity: "warning",
|
|
1648
|
+
code: "FORGOTTEN_EXPORT",
|
|
1649
|
+
suggestion: forgotten.fix ?? `Export this type from your public API`,
|
|
1650
|
+
location: forgotten.definedIn ? { file: forgotten.definedIn } : undefined
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1620
1653
|
}
|
|
1621
1654
|
const externalTypes = types.filter((t) => t.kind === "external");
|
|
1622
1655
|
if (externalTypes.length > 0) {
|
|
@@ -1637,33 +1670,134 @@ async function extract(options) {
|
|
|
1637
1670
|
timestamp: new Date().toISOString()
|
|
1638
1671
|
}
|
|
1639
1672
|
};
|
|
1640
|
-
|
|
1673
|
+
const internalForgotten = forgottenExports.filter((f) => !f.isExternal);
|
|
1674
|
+
return {
|
|
1675
|
+
spec,
|
|
1676
|
+
diagnostics,
|
|
1677
|
+
...internalForgotten.length > 0 ? { forgottenExports: internalForgotten } : {}
|
|
1678
|
+
};
|
|
1641
1679
|
}
|
|
1642
|
-
function
|
|
1680
|
+
function collectAllRefsWithContext(obj, refs, state) {
|
|
1643
1681
|
if (obj === null || obj === undefined)
|
|
1644
1682
|
return;
|
|
1645
1683
|
if (Array.isArray(obj)) {
|
|
1646
|
-
for (
|
|
1647
|
-
|
|
1684
|
+
for (let i = 0;i < obj.length; i++) {
|
|
1685
|
+
collectAllRefsWithContext(obj[i], refs, {
|
|
1686
|
+
...state,
|
|
1687
|
+
path: [...state.path, `[${i}]`]
|
|
1688
|
+
});
|
|
1648
1689
|
}
|
|
1649
1690
|
return;
|
|
1650
1691
|
}
|
|
1651
1692
|
if (typeof obj === "object") {
|
|
1652
1693
|
const record = obj;
|
|
1653
1694
|
if (typeof record.$ref === "string" && record.$ref.startsWith("#/types/")) {
|
|
1654
|
-
|
|
1695
|
+
const typeName = record.$ref.slice("#/types/".length);
|
|
1696
|
+
const existing = refs.get(typeName) ?? [];
|
|
1697
|
+
existing.push({
|
|
1698
|
+
typeName,
|
|
1699
|
+
exportName: state.exportName,
|
|
1700
|
+
location: state.location,
|
|
1701
|
+
path: state.path.join(".") || undefined
|
|
1702
|
+
});
|
|
1703
|
+
refs.set(typeName, existing);
|
|
1704
|
+
}
|
|
1705
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1706
|
+
let newLocation = state.location;
|
|
1707
|
+
if (key === "returnType" || key === "returns")
|
|
1708
|
+
newLocation = "return";
|
|
1709
|
+
else if (key === "parameters" || key === "params")
|
|
1710
|
+
newLocation = "parameter";
|
|
1711
|
+
else if (key === "properties" || key === "members")
|
|
1712
|
+
newLocation = "property";
|
|
1713
|
+
else if (key === "extends" || key === "implements")
|
|
1714
|
+
newLocation = "extends";
|
|
1715
|
+
else if (key === "typeParameters" || key === "typeParams")
|
|
1716
|
+
newLocation = "type-parameter";
|
|
1717
|
+
collectAllRefsWithContext(value, refs, {
|
|
1718
|
+
...state,
|
|
1719
|
+
location: newLocation,
|
|
1720
|
+
path: [...state.path, key]
|
|
1721
|
+
});
|
|
1655
1722
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
function findTypeDefinition(typeName, program, sourceFile) {
|
|
1726
|
+
const checker = program.getTypeChecker();
|
|
1727
|
+
const findInNode = (node) => {
|
|
1728
|
+
if ((ts8.isInterfaceDeclaration(node) || ts8.isTypeAliasDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node)) && node.name?.text === typeName) {
|
|
1729
|
+
const sf = node.getSourceFile();
|
|
1730
|
+
return sf.fileName;
|
|
1658
1731
|
}
|
|
1732
|
+
return ts8.forEachChild(node, findInNode);
|
|
1733
|
+
};
|
|
1734
|
+
const entryResult = findInNode(sourceFile);
|
|
1735
|
+
if (entryResult)
|
|
1736
|
+
return entryResult;
|
|
1737
|
+
for (const sf of program.getSourceFiles()) {
|
|
1738
|
+
if (sf.isDeclarationFile && !sf.fileName.includes("node_modules")) {
|
|
1739
|
+
const result = findInNode(sf);
|
|
1740
|
+
if (result)
|
|
1741
|
+
return result;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts8.SymbolFlags.Type, false);
|
|
1745
|
+
if (symbol?.declarations?.[0]) {
|
|
1746
|
+
return symbol.declarations[0].getSourceFile().fileName;
|
|
1659
1747
|
}
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
function isExternalType2(definedIn) {
|
|
1751
|
+
if (!definedIn)
|
|
1752
|
+
return true;
|
|
1753
|
+
return definedIn.includes("node_modules");
|
|
1754
|
+
}
|
|
1755
|
+
function hasInternalTag(typeName, program, sourceFile) {
|
|
1756
|
+
const checker = program.getTypeChecker();
|
|
1757
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts8.SymbolFlags.Type, false);
|
|
1758
|
+
if (!symbol)
|
|
1759
|
+
return false;
|
|
1760
|
+
const jsTags = symbol.getJsDocTags();
|
|
1761
|
+
return jsTags.some((tag) => tag.name === "internal");
|
|
1660
1762
|
}
|
|
1661
|
-
function
|
|
1763
|
+
function collectForgottenExports(exports, types, program, sourceFile) {
|
|
1662
1764
|
const definedTypes = new Set(types.map((t) => t.id));
|
|
1663
|
-
const referencedTypes = new
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1765
|
+
const referencedTypes = new Map;
|
|
1766
|
+
for (const exp of exports) {
|
|
1767
|
+
collectAllRefsWithContext(exp, referencedTypes, {
|
|
1768
|
+
exportName: exp.id || exp.name,
|
|
1769
|
+
location: "property",
|
|
1770
|
+
path: []
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
for (const type of types) {
|
|
1774
|
+
collectAllRefsWithContext(type, referencedTypes, {
|
|
1775
|
+
exportName: type.id,
|
|
1776
|
+
location: "property",
|
|
1777
|
+
path: []
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
const forgottenExports = [];
|
|
1781
|
+
for (const [typeName, references] of referencedTypes) {
|
|
1782
|
+
if (definedTypes.has(typeName))
|
|
1783
|
+
continue;
|
|
1784
|
+
if (BUILTIN_TYPES2.has(typeName))
|
|
1785
|
+
continue;
|
|
1786
|
+
if (shouldSkipDanglingRef(typeName))
|
|
1787
|
+
continue;
|
|
1788
|
+
if (hasInternalTag(typeName, program, sourceFile))
|
|
1789
|
+
continue;
|
|
1790
|
+
const definedIn = findTypeDefinition(typeName, program, sourceFile);
|
|
1791
|
+
const isExternal = isExternalType2(definedIn);
|
|
1792
|
+
forgottenExports.push({
|
|
1793
|
+
name: typeName,
|
|
1794
|
+
definedIn,
|
|
1795
|
+
referencedBy: references,
|
|
1796
|
+
isExternal,
|
|
1797
|
+
fix: isExternal ? undefined : `export { ${typeName} } from '${definedIn ?? "./types"}'`
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
return forgottenExports;
|
|
1667
1801
|
}
|
|
1668
1802
|
function resolveExportTarget(symbol, checker) {
|
|
1669
1803
|
let targetSymbol = symbol;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -79,6 +79,7 @@ interface ExtractOptions {
|
|
|
79
79
|
interface ExtractResult {
|
|
80
80
|
spec: OpenPkg;
|
|
81
81
|
diagnostics: Diagnostic[];
|
|
82
|
+
forgottenExports?: ForgottenExport[];
|
|
82
83
|
}
|
|
83
84
|
interface Diagnostic {
|
|
84
85
|
message: string;
|
|
@@ -91,6 +92,21 @@ interface Diagnostic {
|
|
|
91
92
|
column?: number;
|
|
92
93
|
};
|
|
93
94
|
}
|
|
95
|
+
/** Context tracking for type references in public API */
|
|
96
|
+
interface TypeReference2 {
|
|
97
|
+
typeName: string;
|
|
98
|
+
exportName: string;
|
|
99
|
+
location: "return" | "parameter" | "property" | "extends" | "type-parameter";
|
|
100
|
+
path?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Structured data for forgotten exports */
|
|
103
|
+
interface ForgottenExport {
|
|
104
|
+
name: string;
|
|
105
|
+
definedIn?: string;
|
|
106
|
+
referencedBy: TypeReference2[];
|
|
107
|
+
isExternal: boolean;
|
|
108
|
+
fix?: string;
|
|
109
|
+
}
|
|
94
110
|
declare function extract(options: ExtractOptions): Promise<ExtractResult>;
|
|
95
111
|
import ts4 from "typescript";
|
|
96
112
|
interface ProgramOptions {
|
|
@@ -309,4 +325,4 @@ declare function findDiscriminatorProperty(unionTypes: ts12.Type[], checker: ts1
|
|
|
309
325
|
import ts13 from "typescript";
|
|
310
326
|
declare function isExported(node: ts13.Node): boolean;
|
|
311
327
|
declare function getNodeName(node: ts13.Node): string | undefined;
|
|
312
|
-
export { zodAdapter, withDescription, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, schemasAreEqual, schemaIsAny, resolveCompiledPath, registerReferencedTypes, registerAdapter, isTypeReference, isSymbolDeprecated, isStandardJSONSchema, isSchemaType, isPureRefSchema, isPrimitiveName, isExported, isBuiltinGeneric, isAnonymous, getSourceLocation, getParamDescription, getNonNullableType, getNodeName, getJSDocComment, findDiscriminatorProperty, findAdapter, extractTypeParameters, extractStandardSchemasFromProject, extractStandardSchemas, extractSchemaType, extractParameters, extract, deduplicateSchemas, createProgram, buildSchema, arktypeAdapter, TypeRegistry, StandardSchemaExtractionResult, StandardSchemaExtractionOutput, StandardJSONSchemaV1, SerializerContext, SchemaExtractionResult, SchemaAdapter, ProgramResult, ProgramOptions, ExtractStandardSchemasOptions, ExtractResult, ExtractOptions, Diagnostic, BUILTIN_TYPE_SCHEMAS };
|
|
328
|
+
export { zodAdapter, withDescription, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, schemasAreEqual, schemaIsAny, resolveCompiledPath, registerReferencedTypes, registerAdapter, isTypeReference, isSymbolDeprecated, isStandardJSONSchema, isSchemaType, isPureRefSchema, isPrimitiveName, isExported, isBuiltinGeneric, isAnonymous, getSourceLocation, getParamDescription, getNonNullableType, getNodeName, getJSDocComment, findDiscriminatorProperty, findAdapter, extractTypeParameters, extractStandardSchemasFromProject, extractStandardSchemas, extractSchemaType, extractParameters, extract, deduplicateSchemas, createProgram, buildSchema, arktypeAdapter, TypeRegistry, TypeReference2 as TypeReference, StandardSchemaExtractionResult, StandardSchemaExtractionOutput, StandardJSONSchemaV1, SerializerContext, SchemaExtractionResult, SchemaAdapter, ProgramResult, ProgramOptions, ForgottenExport, ExtractStandardSchemasOptions, ExtractResult, ExtractOptions, Diagnostic, BUILTIN_TYPE_SCHEMAS };
|
package/dist/src/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
serializeTypeAlias,
|
|
27
27
|
serializeVariable,
|
|
28
28
|
withDescription
|
|
29
|
-
} from "../shared/chunk-
|
|
29
|
+
} from "../shared/chunk-bgnmsw8f.js";
|
|
30
30
|
// src/schema/registry.ts
|
|
31
31
|
function isTypeReference(type) {
|
|
32
32
|
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|