Skip to content

Commit

Permalink
Merge pull request #1909 from vega/next
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
domoritz committed Apr 8, 2024
2 parents 6b44791 + 9353127 commit 249d706
Show file tree
Hide file tree
Showing 25 changed files with 1,290 additions and 1,045 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
run: yarn jest test/ --collectCoverage=true

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"jest.autoRun": "off"
}
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ const config = {
type: "*", // Or <type-name> if you want to generate schema for that one type only
};

const output_path = "path/to/output/file";
const outputPath = "path/to/output/file";

const schema = tsj.createGenerator(config).createSchema(config.type);
const schemaString = JSON.stringify(schema, null, 2);
fs.writeFile(output_path, schemaString, (err) => {
fs.writeFile(outputPath, schemaString, (err) => {
if (err) throw err;
});
```
Expand Down Expand Up @@ -131,9 +131,10 @@ const program = createProgram(config);
const parser = createParser(program, config);
const generator = new SchemaGenerator(program, parser, formatter, config);
const schema = generator.createSchema(config.type);
const outputPath = "path/to/output/file";

const schemaString = JSON.stringify(schema, null, 2);
fs.writeFile(output_path, schemaString, (err) => {
fs.writeFile(outputPath, schemaString, (err) => {
if (err) throw err;
});
```
Expand Down Expand Up @@ -183,9 +184,10 @@ const parser = createParser(program, config, (prs) => {
const formatter = createFormatter(config);
const generator = new SchemaGenerator(program, parser, formatter, config);
const schema = generator.createSchema(config.type);
const outputPath = "path/to/output/file";

const schemaString = JSON.stringify(schema, null, 2);
fs.writeFile(output_path, schemaString, (err) => {
fs.writeFile(outputPath, schemaString, (err) => {
if (err) throw err;
});
```
Expand Down
48 changes: 24 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,40 +44,40 @@
"node": ">=10.0.0"
},
"dependencies": {
"@types/json-schema": "^7.0.12",
"commander": "^11.0.0",
"@types/json-schema": "^7.0.15",
"commander": "^12.0.0",
"glob": "^8.0.3",
"json5": "^2.2.3",
"normalize-path": "^3.0.0",
"safe-stable-stringify": "^2.4.3",
"typescript": "~5.3.2"
"typescript": "~5.4.2"
},
"devDependencies": {
"@auto-it/conventional-commits": "^11.0.0",
"@auto-it/first-time-contributor": "^11.0.0",
"@babel/core": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"@babel/preset-typescript": "^7.22.5",
"@auto-it/conventional-commits": "^11.0.4",
"@auto-it/first-time-contributor": "^11.0.4",
"@babel/core": "^7.23.5",
"@babel/preset-env": "^7.23.5",
"@babel/preset-typescript": "^7.23.3",
"@types/glob": "^8.1.0",
"@types/jest": "^29.5.3",
"@types/node": "^20.4.5",
"@types/normalize-path": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.4",
"@types/normalize-path": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^6.13.2",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"auto": "^11.0.0",
"chai": "^4.3.7",
"ajv-formats": "^3.0.1",
"auto": "^11.0.4",
"chai": "^5.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.46.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.6.2",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"prettier": "^3.0.0",
"ts-node": "^10.9.1",
"vega": "^5.25.0",
"vega-lite": "^5.14.1"
"prettier": "^3.1.1",
"ts-node": "^10.9.2",
"vega": "^5.27.0",
"vega-lite": "^5.16.3"
},
"scripts": {
"prepublishOnly": "yarn build",
Expand Down
19 changes: 19 additions & 0 deletions src/SchemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,25 @@ export class SchemaGenerator {
case ts.SyntaxKind.ArrowFunction:
allTypes.set(`NamedParameters<typeof ${this.getFullName(node, typeChecker)}>`, node);
return;
case ts.SyntaxKind.ExportSpecifier: {
const exportSpecifierNode = node as ts.ExportSpecifier;
const symbol = typeChecker.getExportSpecifierLocalTargetSymbol(exportSpecifierNode);
if (symbol?.declarations?.length === 1) {
const declaration = symbol.declarations[0];
if (declaration.kind === ts.SyntaxKind.ImportSpecifier) {
// Handling the `Foo` in `import { Foo } from "./lib"; export { Foo };`
const importSpecifierNode = declaration as ts.ImportSpecifier;
const type = typeChecker.getTypeAtLocation(importSpecifierNode);
if (type.symbol?.declarations?.length === 1) {
this.inspectNode(type.symbol.declarations[0], typeChecker, allTypes);
}
} else {
// Handling the `Bar` in `export { Bar } from './lib';`
this.inspectNode(declaration, typeChecker, allTypes);
}
}
return;
}
default:
ts.forEachChild(node, (subnode) => this.inspectNode(subnode, typeChecker, allTypes));
return;
Expand Down
2 changes: 1 addition & 1 deletion src/Utils/deepMerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function deepMerge(
a: { [key: string]: JSONSchema7Definition },
b: { [key: string]: JSONSchema7Definition }
): { [x: string]: JSONSchema7Definition } {
const output = { ...a, ...b };
const output = { ...structuredClone(a), ...structuredClone(b) };

for (const key in a) {
if (b.hasOwnProperty(key)) {
Expand Down
20 changes: 12 additions & 8 deletions src/Utils/extractLiterals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,35 @@ import { DefinitionType } from "../Type/DefinitionType";
import { EnumType } from "../Type/EnumType";
import { LiteralType } from "../Type/LiteralType";
import { UnionType } from "../Type/UnionType";
import { derefAnnotatedType } from "./derefType";

function* _extractLiterals(type: BaseType): Iterable<string> {
if (!type) {
return;
}
if (type instanceof LiteralType) {
yield type.getValue().toString();

const dereffedType = derefAnnotatedType(type);

if (dereffedType instanceof LiteralType) {
yield dereffedType.getValue().toString();
return;
}
if (type instanceof UnionType || type instanceof EnumType) {
for (const t of type.getTypes()) {
if (dereffedType instanceof UnionType || dereffedType instanceof EnumType) {
for (const t of dereffedType.getTypes()) {
yield* _extractLiterals(t);
}
return;
}
if (type instanceof AliasType || type instanceof DefinitionType) {
yield* _extractLiterals(type.getType());
if (dereffedType instanceof AliasType || dereffedType instanceof DefinitionType) {
yield* _extractLiterals(dereffedType.getType());
return;
}
if (type instanceof BooleanType) {
if (dereffedType instanceof BooleanType) {
yield* _extractLiterals(new UnionType([new LiteralType("true"), new LiteralType("false")]));
return;
}

throw new UnknownTypeError(type);
throw new UnknownTypeError(dereffedType);
}

export function extractLiterals(type: BaseType): string[] {
Expand Down
2 changes: 1 addition & 1 deletion src/Utils/nodeKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ export function getKey(node: Node, context: Context): string {
const id = ids.join("-");
const args = context.getArguments();

return args.length ? `${id}<${args.map((arg) => arg.getId()).join(",")}>` : id;
return args.length ? `${id}<${args.map((arg) => arg?.getId()).join(",")}>` : id;
}
3 changes: 2 additions & 1 deletion test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ export function assertValidSchema(
* @default {strict:false}
*/
ajvOptions?: AjvOptions;
mainTsOnly?: boolean;
}
) {
return (): void => {
const config: Config = {
...DEFAULT_CONFIG,
path: `${basePath}/${relativePath}/*.ts`,
path: `${basePath}/${relativePath}/${options?.mainTsOnly ? "main" : "*"}.ts`,
skipTypeCheck: !!process.env.FAST_TEST,
type,
...config_,
Expand Down
2 changes: 2 additions & 0 deletions test/valid-data-other.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe("valid-data-other", () => {
it("generic-anonymous", assertValidSchema("generic-anonymous", "MyObject"));
it("generic-recursive", assertValidSchema("generic-recursive", "MyObject"));
it("generic-hell", assertValidSchema("generic-hell", "MyObject"));
it("generic-default-conditional", assertValidSchema("generic-default-conditional", "MyObject"));
it("generic-default", assertValidSchema("generic-default", "MyObject"));
it("generic-nested", assertValidSchema("generic-nested", "MyObject"));
it("generic-prefixed-number", assertValidSchema("generic-prefixed-number", "MyObject"));
Expand Down Expand Up @@ -101,4 +102,5 @@ describe("valid-data-other", () => {
ajvOptions: { $data: true },
})
);
it("re-export-with-asterisk", assertValidSchema("re-export-with-asterisk", "*", undefined, { mainTsOnly: true }));
});
10 changes: 10 additions & 0 deletions test/valid-data-type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ describe("valid-data-type", () => {
it("type-intersection-conflict", assertValidSchema("type-intersection-conflict", "MyObject"));
it("type-intersection-partial-conflict", assertValidSchema("type-intersection-partial-conflict", "MyType"));
it("type-intersection-partial-conflict-ref", assertValidSchema("type-intersection-partial-conflict", "MyType"));
it(
"type-intersection-partial-conflict-union",
assertValidSchema("type-intersection-partial-conflict-union", "MyType")
);
it(
"type-intersection-partial-conflict-union-alias",
assertValidSchema("type-intersection-partial-conflict-union-alias", "MyType")
);
it(
"type-intersection-recursive-interface",
assertValidSchema("type-intersection-recursive-interface", "Intersection")
Expand Down Expand Up @@ -140,4 +148,6 @@ describe("valid-data-type", () => {
it("type-satisfies", assertValidSchema("type-satisfies", "MyType"));

it("ignore-export", assertValidSchema("ignore-export", "*"));

it("lowercase", assertValidSchema("lowercase", "MyType"));
});
5 changes: 5 additions & 0 deletions test/valid-data/generic-default-conditional/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type MyObject = ConditionalGeneric;

export type ConditionalGeneric<T = string extends 'foo' ? 'bar' : 'baz'> = {
foo: T;
}
22 changes: 22 additions & 0 deletions test/valid-data/generic-default-conditional/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$ref": "#/definitions/MyObject",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ConditionalGeneric": {
"additionalProperties": false,
"properties": {
"foo": {
"const": "baz",
"type": "string"
}
},
"required": [
"foo"
],
"type": "object"
},
"MyObject": {
"$ref": "#/definitions/ConditionalGeneric"
}
}
}
4 changes: 4 additions & 0 deletions test/valid-data/lowercase/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** The built-in color schemes, cased. */
type Foo = "Accent";

export type MyType = Lowercase<Foo>;
10 changes: 10 additions & 0 deletions test/valid-data/lowercase/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$ref": "#/definitions/MyType",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyType": {
"const": "accent",
"type": "string"
}
}
}
8 changes: 8 additions & 0 deletions test/valid-data/re-export-with-asterisk/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum MyEnum {
Foo = "foo",
Bar = "bar",
}

export interface MyObject {
id: string;
}
3 changes: 3 additions & 0 deletions test/valid-data/re-export-with-asterisk/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { MyObject } from "./lib";
import { MyEnum } from "./lib";
export { MyEnum };
24 changes: 24 additions & 0 deletions test/valid-data/re-export-with-asterisk/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyEnum": {
"enum": [
"foo",
"bar"
],
"type": "string"
},
"MyObject": {
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
type X = 'a' | 'b'

type Foo = {
foo: X
}

export type A = Foo & {
foo: 'a'
}

export type B = Foo & {
foo: 'b'
}

export type MyType = A | B
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/MyType",
"definitions": {
"MyType": {
"anyOf": [
{
"$ref": "#/definitions/A"
},
{
"$ref": "#/definitions/B"
}
]
},
"A": {
"type": "object",
"additionalProperties": false,
"properties": {
"foo": {
"type": "string",
"const": "a"
}
},
"required": [
"foo"
]
},
"B": {
"type": "object",
"additionalProperties": false,
"properties": {
"foo": {
"type": "string",
"const": "b"
}
},
"required": [
"foo"
]
}
}
}
Loading

0 comments on commit 249d706

Please sign in to comment.