forked from vega/ts-json-schema-generator
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
291 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,7 @@ node_modules/ | |
|
||
# local config for auto | ||
.env | ||
|
||
# Other package managers | ||
pnpm-lock.yaml | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,6 +72,7 @@ | |
"@types/jest": "^29.5.12", | ||
"@types/node": "^20.12.7", | ||
"@types/normalize-path": "^3.0.2", | ||
"@types/ts-expose-internals": "npm:ts-expose-internals@^5.4.5", | ||
"ajv": "^8.12.0", | ||
"ajv-formats": "^3.0.1", | ||
"auto": "^11.1.6", | ||
|
@@ -104,6 +105,5 @@ | |
"debug": "tsx --inspect-brk ts-json-schema-generator.ts", | ||
"run": "tsx ts-json-schema-generator.ts", | ||
"release": "yarn build && auto shipit" | ||
}, | ||
"packageManager": "[email protected]" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import ts from "typescript"; | ||
import { Context, type NodeParser } from "../NodeParser.js"; | ||
import type { SubNodeParser } from "../SubNodeParser.js"; | ||
import { AliasType } from "../Type/AliasType.js"; | ||
import type { BaseType } from "../Type/BaseType.js"; | ||
import { DefinitionType } from "../Type/DefinitionType.js"; | ||
import { getKey } from "../Utils/nodeKey.js"; | ||
|
||
/** | ||
* Needs to be registered before 261, 260, 230, 262 node kinds | ||
*/ | ||
export class PromiseNodeParser implements SubNodeParser { | ||
public constructor( | ||
protected typeChecker: ts.TypeChecker, | ||
protected childNodeParser: NodeParser, | ||
) {} | ||
|
||
public supportsNode(node: ts.Node): boolean { | ||
if ( | ||
// 261 interface PromiseInterface extends Promise<T> | ||
!ts.isInterfaceDeclaration(node) && | ||
// 260 class PromiseClass implements Promise<T> | ||
!ts.isClassDeclaration(node) && | ||
// 230 Promise<T> | ||
!ts.isExpressionWithTypeArguments(node) && | ||
// 262 type PromiseAlias = Promise<T>; | ||
!ts.isTypeAliasDeclaration(node) | ||
) { | ||
return false; | ||
} | ||
|
||
const type = this.typeChecker.getTypeAtLocation(node); | ||
|
||
const awaitedType = this.typeChecker.getAwaitedType(type); | ||
|
||
// ignores non awaitable types | ||
if (!awaitedType) { | ||
return false; | ||
} | ||
|
||
// If the awaited type differs from the original type, the type extends promise | ||
// Awaited<Promise<T>> -> T (Promise<T> !== T) | ||
// Awaited<Y> -> Y (Y === Y) | ||
if (awaitedType === type) { | ||
return false; | ||
} | ||
|
||
// In types like: A<T> = T, type C = A<1>, C has the same type as A<1> and 1, | ||
// the awaitedType is NOT the same reference as the type, so a assignability | ||
// check is needed | ||
return ( | ||
!this.typeChecker.isTypeAssignableTo(type, awaitedType) && | ||
!this.typeChecker.isTypeAssignableTo(awaitedType, type) | ||
); | ||
} | ||
|
||
public createType( | ||
node: ts.InterfaceDeclaration | ts.ClassDeclaration | ts.ExpressionWithTypeArguments | ts.TypeAliasDeclaration, | ||
context: Context, | ||
): BaseType { | ||
const type = this.typeChecker.getTypeAtLocation(node); | ||
const awaitedType = this.typeChecker.getAwaitedType(type)!; // supportsNode ensures this | ||
const awaitedNode = this.typeChecker.typeToTypeNode(awaitedType, undefined, ts.NodeBuilderFlags.IgnoreErrors); | ||
|
||
if (!awaitedNode) { | ||
throw new Error( | ||
`Could not find awaited node for type ${node.pos === -1 ? "<unresolved>" : node.getText()}`, | ||
); | ||
} | ||
|
||
const baseNode = this.childNodeParser.createType(awaitedNode, new Context(node)); | ||
|
||
const name = this.getNodeName(node); | ||
|
||
// Nodes without name should just be their awaited type | ||
// export class extends Promise<T> {} -> T | ||
// export class A extends Promise<T> {} -> A (ref to T) | ||
if (!name) { | ||
return baseNode; | ||
} | ||
|
||
return new DefinitionType(name, new AliasType(`promise-${getKey(node, context)}`, baseNode)); | ||
} | ||
|
||
private getNodeName( | ||
node: ts.InterfaceDeclaration | ts.ClassDeclaration | ts.ExpressionWithTypeArguments | ts.TypeAliasDeclaration, | ||
) { | ||
if (ts.isExpressionWithTypeArguments(node)) { | ||
if (!ts.isHeritageClause(node.parent)) { | ||
throw new Error("Expected ExpressionWithTypeArguments to have a HeritageClause parent"); | ||
} | ||
|
||
return node.parent.parent.name?.getText(); | ||
} | ||
|
||
return node.name?.getText(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
export type A = { a: string; b: number[] }; | ||
|
||
export type PromiseAlias = Promise<A>; | ||
|
||
export class PromiseClass extends Promise<A> {} | ||
|
||
export interface PromiseInterface extends Promise<A> {} | ||
|
||
export type LikeType = PromiseLike<A>; | ||
|
||
export type PromiseOrAlias = Promise<A> | A; | ||
|
||
export type LikeOrType = PromiseLike<A> | A; | ||
|
||
export type AndPromise = Promise<A> & { a: string }; | ||
|
||
export type AndLikePromise = PromiseLike<A> & { a: string }; | ||
|
||
// Should not be present | ||
export default class extends Promise<A> {} | ||
|
||
export class LikeClass implements PromiseLike<A> { | ||
then<TResult1 = A, TResult2 = never>( | ||
onfulfilled?: ((value: A) => TResult1 | PromiseLike<TResult1>) | null | undefined, | ||
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined | ||
): PromiseLike<TResult1 | TResult2> { | ||
return new Promise(() => {}); | ||
} | ||
} | ||
|
||
export abstract class LikeAbstractClass implements PromiseLike<A> { | ||
abstract then<TResult1 = A, TResult2 = never>( | ||
onfulfilled?: ((value: A) => TResult1 | PromiseLike<TResult1>) | null | undefined, | ||
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined | ||
); | ||
} | ||
|
||
export interface LikeInterface extends PromiseLike<A> {} | ||
|
||
// Prisma has a base promise type just like this | ||
export interface WithProperty extends Promise<A> { | ||
[Symbol.toStringTag]: "WithProperty"; | ||
} | ||
|
||
export interface ThenableInterface { | ||
then<TResult1 = A, TResult2 = never>( | ||
onfulfilled?: ((value: A) => TResult1 | PromiseLike<TResult1>) | null | undefined, | ||
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined | ||
): PromiseLike<TResult1 | TResult2>; | ||
} | ||
|
||
export class ThenableClass { | ||
then<TResult1 = A, TResult2 = never>( | ||
onfulfilled?: ((value: A) => TResult1 | PromiseLike<TResult1>) | null | undefined, | ||
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined | ||
): PromiseLike<TResult1 | TResult2> { | ||
return new Promise(() => {}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"definitions": { | ||
"A": { | ||
"additionalProperties": false, | ||
"properties": { | ||
"a": { | ||
"type": "string" | ||
}, | ||
"b": { | ||
"items": { | ||
"type": "number" | ||
}, | ||
"type": "array" | ||
} | ||
}, | ||
"required": [ | ||
"a", | ||
"b" | ||
], | ||
"type": "object" | ||
}, | ||
"AndLikePromise": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"AndPromise": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"LikeAbstractClass": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"LikeClass": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"LikeInterface": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"LikeOrType": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"LikeType": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"PromiseAlias": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"PromiseClass": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"PromiseInterface": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"PromiseOrAlias": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"ThenableClass": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"ThenableInterface": { | ||
"$ref": "#/definitions/A" | ||
}, | ||
"WithProperty": { | ||
"$ref": "#/definitions/A" | ||
} | ||
} | ||
} |
Oops, something went wrong.