tsondb 0.2.0 → 0.3.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/lib/ModelContainer.js +9 -7
- package/lib/client/api.d.ts +14 -1
- package/lib/client/api.js +119 -0
- package/lib/client/components/Git.d.ts +2 -0
- package/lib/client/components/Git.js +116 -0
- package/lib/client/components/Layout.js +2 -1
- package/lib/client/components/typeInputs/ArrayTypeInput.js +2 -1
- package/lib/client/components/typeInputs/EnumTypeInput.d.ts +13 -0
- package/lib/client/components/typeInputs/{utils/EnumDeclField.js → EnumTypeInput.js} +10 -10
- package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.js +1 -10
- package/lib/client/components/typeInputs/TypeInput.js +3 -0
- package/lib/client/components/typeInputs/utils/Markdown.js +1 -32
- package/lib/client/hooks/useAPIResource.d.ts +1 -0
- package/lib/client/hooks/useAPIResource.js +2 -0
- package/lib/client/hooks/useMappedAPIResource.d.ts +1 -0
- package/lib/client/hooks/useMappedAPIResource.js +19 -0
- package/lib/client/routes/Entity.js +18 -24
- package/lib/client/routes/Home.js +3 -12
- package/lib/client/utils/typeSkeleton.js +10 -16
- package/lib/renderers/jsonschema/index.d.ts +1 -1
- package/lib/renderers/jsonschema/index.js +30 -7
- package/lib/renderers/jsonschema/render.d.ts +5 -1
- package/lib/renderers/jsonschema/render.js +35 -16
- package/lib/renderers/ts/index.d.ts +1 -1
- package/lib/renderers/ts/index.js +37 -6
- package/lib/renderers/ts/render.d.ts +2 -0
- package/lib/renderers/ts/render.js +55 -32
- package/lib/schema/Node.d.ts +1 -0
- package/lib/schema/Node.js +6 -1
- package/lib/schema/declarations/Declaration.d.ts +3 -1
- package/lib/schema/declarations/Declaration.js +4 -0
- package/lib/schema/declarations/EntityDecl.d.ts +3 -0
- package/lib/schema/declarations/EnumDecl.d.ts +4 -21
- package/lib/schema/declarations/EnumDecl.js +16 -79
- package/lib/schema/index.d.ts +1 -0
- package/lib/schema/index.js +1 -0
- package/lib/schema/types/Type.d.ts +8 -2
- package/lib/schema/types/Type.js +57 -11
- package/lib/schema/types/generic/ArrayType.d.ts +2 -1
- package/lib/schema/types/generic/ArrayType.js +2 -1
- package/lib/schema/types/generic/EnumType.d.ts +38 -0
- package/lib/schema/types/generic/EnumType.js +96 -0
- package/lib/schema/types/generic/ObjectType.d.ts +2 -1
- package/lib/schema/types/generic/ObjectType.js +4 -0
- package/lib/schema/types/primitives/BooleanType.d.ts +2 -1
- package/lib/schema/types/primitives/BooleanType.js +1 -0
- package/lib/schema/types/primitives/DateType.d.ts +2 -1
- package/lib/schema/types/primitives/DateType.js +1 -0
- package/lib/schema/types/primitives/FloatType.d.ts +2 -1
- package/lib/schema/types/primitives/FloatType.js +1 -0
- package/lib/schema/types/primitives/IntegerType.d.ts +2 -1
- package/lib/schema/types/primitives/IntegerType.js +1 -0
- package/lib/schema/types/primitives/StringType.d.ts +2 -1
- package/lib/schema/types/primitives/StringType.js +1 -0
- package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +2 -1
- package/lib/schema/types/references/GenericArgumentIdentifierType.js +1 -0
- package/lib/schema/types/references/IncludeIdentifierType.d.ts +5 -3
- package/lib/schema/types/references/IncludeIdentifierType.js +15 -2
- package/lib/schema/types/references/NestedEntityMapType.d.ts +2 -1
- package/lib/schema/types/references/NestedEntityMapType.js +5 -1
- package/lib/schema/types/references/ReferenceIdentifierType.d.ts +2 -1
- package/lib/schema/types/references/ReferenceIdentifierType.js +2 -1
- package/lib/server/api/declarations.d.ts +1 -0
- package/lib/server/api/declarations.js +154 -0
- package/lib/server/api/git.d.ts +1 -0
- package/lib/server/api/git.js +174 -0
- package/lib/server/api/index.d.ts +1 -0
- package/lib/server/api/index.js +8 -0
- package/lib/server/api/instanceOperations.d.ts +6 -0
- package/lib/server/api/instanceOperations.js +82 -0
- package/lib/server/api/instances.d.ts +1 -0
- package/lib/server/api/instances.js +23 -0
- package/lib/server/index.d.ts +22 -1
- package/lib/server/index.js +11 -165
- package/lib/server/init.d.ts +5 -0
- package/lib/server/init.js +56 -0
- package/lib/shared/api.d.ts +12 -1
- package/lib/shared/utils/array.d.ts +19 -0
- package/lib/shared/utils/array.js +27 -0
- package/lib/shared/utils/git.d.ts +12 -0
- package/lib/shared/utils/git.js +98 -0
- package/lib/shared/utils/instances.d.ts +10 -0
- package/lib/shared/utils/instances.js +8 -1
- package/lib/shared/utils/markdown.d.ts +14 -0
- package/lib/shared/utils/markdown.js +42 -0
- package/lib/shared/utils/object.d.ts +1 -0
- package/lib/shared/utils/object.js +4 -0
- package/lib/shared/utils/string.d.ts +1 -0
- package/lib/shared/utils/string.js +9 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/git.d.ts +3 -0
- package/lib/utils/git.js +12 -0
- package/lib/utils/instances.d.ts +3 -2
- package/lib/utils/instances.js +9 -2
- package/lib/utils/path.d.ts +1 -0
- package/lib/utils/path.js +2 -0
- package/lib/utils/references.d.ts +7 -0
- package/lib/utils/references.js +40 -0
- package/lib/utils/render.d.ts +6 -1
- package/lib/utils/render.js +27 -1
- package/package.json +8 -1
- package/public/css/styles.css +200 -1
- package/lib/client/components/typeInputs/utils/EnumDeclField.d.ts +0 -13
- package/lib/server/instanceOperations.d.ts +0 -7
- package/lib/server/instanceOperations.js +0 -67
|
@@ -26,25 +26,19 @@ export const createTypeSkeleton = (getDeclFromDeclName, type) => {
|
|
|
26
26
|
if (referencedDecl === undefined) {
|
|
27
27
|
return undefined;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
case "TypeAliasDecl":
|
|
31
|
-
return createTypeSkeleton(getDeclFromDeclName, referencedDecl.type);
|
|
32
|
-
case "EnumDecl": {
|
|
33
|
-
const firstCase = Object.entries(referencedDecl.values)[0];
|
|
34
|
-
if (firstCase[1].type === null) {
|
|
35
|
-
return { kind: firstCase[0] };
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
kind: firstCase[0],
|
|
39
|
-
[firstCase[0]]: createTypeSkeleton(getDeclFromDeclName, firstCase[1].type),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
default:
|
|
43
|
-
return assertExhaustive(referencedDecl);
|
|
44
|
-
}
|
|
29
|
+
return createTypeSkeleton(getDeclFromDeclName, referencedDecl.type);
|
|
45
30
|
}
|
|
46
31
|
case "NestedEntityMapType":
|
|
47
32
|
return {};
|
|
33
|
+
case "EnumType": {
|
|
34
|
+
const firstCase = Object.entries(type.values)[0];
|
|
35
|
+
return {
|
|
36
|
+
kind: firstCase[0],
|
|
37
|
+
...(firstCase[1].type === null
|
|
38
|
+
? {}
|
|
39
|
+
: { [firstCase[0]]: createTypeSkeleton(getDeclFromDeclName, firstCase[1].type) }),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
48
42
|
default:
|
|
49
43
|
return assertExhaustive(type);
|
|
50
44
|
}
|
|
@@ -2,5 +2,5 @@ import { Output } from "../Output.js";
|
|
|
2
2
|
import { JsonSchemaRendererOptions } from "./render.js";
|
|
3
3
|
export declare const JsonSchemaOutput: (options: {
|
|
4
4
|
targetPath: string;
|
|
5
|
-
rendererOptions?: JsonSchemaRendererOptions
|
|
5
|
+
rendererOptions?: Partial<JsonSchemaRendererOptions>;
|
|
6
6
|
}) => Output;
|
|
@@ -1,12 +1,35 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
2
|
-
import { dirname } from "path";
|
|
3
|
-
import {
|
|
1
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname, join, relative } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { groupDeclarationsBySourceUrl, resolveTypeArgumentsInDecls } from "../../schema/index.js";
|
|
5
|
+
import { commonPrefix } from "../../shared/utils/string.js";
|
|
4
6
|
import { render } from "./render.js";
|
|
7
|
+
const extension = ".schema.json";
|
|
5
8
|
export const JsonSchemaOutput = (options) => ({
|
|
6
9
|
run: async (schema) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
if (options.rendererOptions?.preserveFiles === true) {
|
|
11
|
+
await rm(options.targetPath, { recursive: true, force: true });
|
|
12
|
+
await mkdir(options.targetPath, { recursive: true });
|
|
13
|
+
const declarationsBySourceUrl = groupDeclarationsBySourceUrl(resolveTypeArgumentsInDecls(schema.declarations));
|
|
14
|
+
const sourceRootPath = fileURLToPath(commonPrefix(...Object.keys(declarationsBySourceUrl)));
|
|
15
|
+
if (sourceRootPath) {
|
|
16
|
+
for (const [sourceUrl, decls] of Object.entries(declarationsBySourceUrl)) {
|
|
17
|
+
const sourcePath = fileURLToPath(sourceUrl);
|
|
18
|
+
const relativePath = dirname(relative(sourceRootPath, sourcePath));
|
|
19
|
+
const newDir = join(options.targetPath, relativePath);
|
|
20
|
+
const newPath = join(newDir, basename(sourcePath, extname(sourcePath)) + extension);
|
|
21
|
+
await mkdir(newDir, { recursive: true });
|
|
22
|
+
await writeFile(newPath, render(options.rendererOptions, decls), {
|
|
23
|
+
encoding: "utf-8",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
30
|
+
await writeFile(options.targetPath, render(options.rendererOptions, resolveTypeArgumentsInDecls(schema.declarations)), {
|
|
31
|
+
encoding: "utf-8",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
11
34
|
},
|
|
12
35
|
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Decl } from "../../schema/declarations/Declaration.js";
|
|
2
2
|
export type JsonSchemaRendererOptions = {
|
|
3
|
-
|
|
3
|
+
format: "minified" | "tabs" | {
|
|
4
|
+
kind: "spaces";
|
|
5
|
+
indentation?: number;
|
|
6
|
+
};
|
|
7
|
+
preserveFiles: boolean;
|
|
4
8
|
};
|
|
5
9
|
export declare const render: (options: Partial<JsonSchemaRendererOptions> | undefined, declarations: readonly Decl[]) => string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dirname, relative } from "node:path";
|
|
1
2
|
import { addEphemeralUUIDToType, createEntityIdentifierTypeAsDecl, isEntityDecl, } from "../../schema/declarations/EntityDecl.js";
|
|
2
3
|
import { TypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
3
4
|
import { flatMapAuxiliaryDecls, NodeKind } from "../../schema/Node.js";
|
|
@@ -5,8 +6,11 @@ import { isNestedEntityMapType, } from "../../schema/types/references/NestedEnti
|
|
|
5
6
|
import { getParentDecl } from "../../schema/types/Type.js";
|
|
6
7
|
import { discriminatorKey } from "../../shared/enum.js";
|
|
7
8
|
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
9
|
+
import { ensureSpecialDirStart } from "../../utils/path.js";
|
|
10
|
+
const defaultIndentation = 2;
|
|
8
11
|
const defaultOptions = {
|
|
9
|
-
|
|
12
|
+
format: { kind: "spaces" },
|
|
13
|
+
preserveFiles: false,
|
|
10
14
|
};
|
|
11
15
|
const renderArrayType = (options, type) => ({
|
|
12
16
|
type: "array",
|
|
@@ -69,15 +73,34 @@ const renderGenericArgumentIdentifierType = (_options, _type) => {
|
|
|
69
73
|
const renderReferenceIdentifierType = (_options, type) => ({
|
|
70
74
|
$ref: `#/$defs/${type.entity.name}_ID`,
|
|
71
75
|
});
|
|
72
|
-
const renderIncludeIdentifierType = (
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
const renderIncludeIdentifierType = (options, type) => {
|
|
77
|
+
const sourceUrl = getParentDecl(type)?.sourceUrl ?? "";
|
|
78
|
+
const filePath = options.preserveFiles && sourceUrl !== type.reference.sourceUrl
|
|
79
|
+
? ensureSpecialDirStart(relative(dirname(sourceUrl), type.reference.sourceUrl))
|
|
80
|
+
: "";
|
|
81
|
+
return {
|
|
82
|
+
$ref: `${filePath}#/$defs/${type.reference.name}`,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
75
85
|
const renderNestedEntityMapType = (_options, type) => ({
|
|
76
86
|
type: "object",
|
|
77
87
|
additionalProperties: {
|
|
78
88
|
$ref: `#/$defs/${type.name}`,
|
|
79
89
|
},
|
|
80
90
|
});
|
|
91
|
+
const renderEnumType = (options, type) => ({
|
|
92
|
+
oneOf: Object.entries(type.values).map(([caseName, caseDef]) => ({
|
|
93
|
+
type: "object",
|
|
94
|
+
deprecated: caseDef.isDeprecated,
|
|
95
|
+
properties: {
|
|
96
|
+
[discriminatorKey]: {
|
|
97
|
+
const: caseName,
|
|
98
|
+
},
|
|
99
|
+
...(caseDef.type === null ? {} : { [caseName]: renderType(options, caseDef.type) }),
|
|
100
|
+
},
|
|
101
|
+
required: [discriminatorKey, ...(caseDef === null ? [] : [caseName])],
|
|
102
|
+
})),
|
|
103
|
+
});
|
|
81
104
|
const renderType = (options, type) => {
|
|
82
105
|
switch (type.kind) {
|
|
83
106
|
case NodeKind.ArrayType:
|
|
@@ -102,6 +125,8 @@ const renderType = (options, type) => {
|
|
|
102
125
|
return renderIncludeIdentifierType(options, type);
|
|
103
126
|
case NodeKind.NestedEntityMapType:
|
|
104
127
|
return renderNestedEntityMapType(options, type);
|
|
128
|
+
case NodeKind.EnumType:
|
|
129
|
+
return renderEnumType(options, type);
|
|
105
130
|
default:
|
|
106
131
|
return assertExhaustive(type, "Unknown type");
|
|
107
132
|
}
|
|
@@ -114,17 +139,7 @@ const renderEntityDecl = (options, decl) => ({
|
|
|
114
139
|
const renderEnumDecl = (options, decl) => ({
|
|
115
140
|
description: decl.comment,
|
|
116
141
|
deprecated: decl.isDeprecated,
|
|
117
|
-
|
|
118
|
-
type: "object",
|
|
119
|
-
deprecated: caseDef.isDeprecated,
|
|
120
|
-
properties: {
|
|
121
|
-
[discriminatorKey]: {
|
|
122
|
-
const: caseName,
|
|
123
|
-
},
|
|
124
|
-
...(caseDef.type === null ? {} : { [caseName]: renderType(options, caseDef.type) }),
|
|
125
|
-
},
|
|
126
|
-
required: [discriminatorKey, ...(caseDef === null ? [] : [caseName])],
|
|
127
|
-
})),
|
|
142
|
+
...renderEnumType(options, decl.type.value),
|
|
128
143
|
});
|
|
129
144
|
const renderTypeAliasDecl = (options, decl) => ({
|
|
130
145
|
description: decl.comment,
|
|
@@ -160,5 +175,9 @@ export const render = (options = defaultOptions, declarations) => {
|
|
|
160
175
|
}
|
|
161
176
|
return undefined;
|
|
162
177
|
}, declarations)),
|
|
163
|
-
}, undefined, finalOptions.
|
|
178
|
+
}, undefined, finalOptions.format === "minified"
|
|
179
|
+
? undefined
|
|
180
|
+
: finalOptions.format === "tabs"
|
|
181
|
+
? "\t"
|
|
182
|
+
: finalOptions.format.indentation ?? defaultIndentation);
|
|
164
183
|
};
|
|
@@ -2,5 +2,5 @@ import { Output } from "../Output.js";
|
|
|
2
2
|
import { TypeScriptRendererOptions } from "./render.js";
|
|
3
3
|
export declare const TypeScriptOutput: (options: {
|
|
4
4
|
targetPath: string;
|
|
5
|
-
rendererOptions?: TypeScriptRendererOptions
|
|
5
|
+
rendererOptions?: Partial<TypeScriptRendererOptions>;
|
|
6
6
|
}) => Output;
|
|
@@ -1,11 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, join, relative } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { groupDeclarationsBySourceUrl } from "../../schema/declarations/Declaration.js";
|
|
6
|
+
import { commonPrefix } from "../../shared/utils/string.js";
|
|
3
7
|
import { render } from "./render.js";
|
|
8
|
+
const debug = Debug("tsondb:renderer:ts");
|
|
9
|
+
const extension = ".d.ts";
|
|
4
10
|
export const TypeScriptOutput = (options) => ({
|
|
5
11
|
run: async (schema) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
if (options.rendererOptions?.preserveFiles === true) {
|
|
13
|
+
debug("emitting declarations to multiple files...");
|
|
14
|
+
await rm(options.targetPath, { recursive: true, force: true });
|
|
15
|
+
await mkdir(options.targetPath, { recursive: true });
|
|
16
|
+
const declarationsBySourceUrl = groupDeclarationsBySourceUrl(schema.declarations);
|
|
17
|
+
const sourceRootPath = fileURLToPath(commonPrefix(...Object.keys(declarationsBySourceUrl)));
|
|
18
|
+
debug("common source root path: %s", sourceRootPath);
|
|
19
|
+
if (sourceRootPath) {
|
|
20
|
+
for (const [sourceUrl, decls] of Object.entries(declarationsBySourceUrl)) {
|
|
21
|
+
const sourcePath = fileURLToPath(sourceUrl);
|
|
22
|
+
const relativePath = dirname(relative(sourceRootPath, sourcePath));
|
|
23
|
+
const newDir = join(options.targetPath, relativePath);
|
|
24
|
+
const newPath = join(newDir, basename(sourcePath, extname(sourcePath)) + extension);
|
|
25
|
+
await mkdir(newDir, { recursive: true });
|
|
26
|
+
await writeFile(newPath, render(options.rendererOptions, decls), {
|
|
27
|
+
encoding: "utf-8",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
debug("emitted declaration files to %s", options.targetPath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
debug("emitting declarations to single file...");
|
|
35
|
+
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
36
|
+
await writeFile(options.targetPath, render(options.rendererOptions, schema.declarations), {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
});
|
|
39
|
+
debug("emitted declarations to %s", options.targetPath);
|
|
40
|
+
}
|
|
10
41
|
},
|
|
11
42
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Decl } from "../../schema/declarations/Declaration.js";
|
|
2
2
|
export type TypeScriptRendererOptions = {
|
|
3
3
|
indentation: number;
|
|
4
|
+
objectTypeKeyword: "interface" | "type";
|
|
5
|
+
preserveFiles: boolean;
|
|
4
6
|
};
|
|
5
7
|
export declare const render: (options: Partial<TypeScriptRendererOptions> | undefined, declarations: readonly Decl[]) => string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
|
+
import { dirname, relative } from "node:path";
|
|
2
3
|
import { addEphemeralUUIDToType, createEntityIdentifierTypeAsDecl, isEntityDecl, } from "../../schema/declarations/EntityDecl.js";
|
|
3
4
|
import { TypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
4
5
|
import { flatMapAuxiliaryDecls, NodeKind } from "../../schema/Node.js";
|
|
@@ -8,11 +9,14 @@ import { getParentDecl } from "../../schema/types/Type.js";
|
|
|
8
9
|
import { discriminatorKey } from "../../shared/enum.js";
|
|
9
10
|
import { toCamelCase } from "../../shared/utils/string.js";
|
|
10
11
|
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
11
|
-
import {
|
|
12
|
+
import { ensureSpecialDirStart } from "../../utils/path.js";
|
|
13
|
+
import { combineSyntaxes, indent, prefixLines, syntax } from "../../utils/render.js";
|
|
12
14
|
const defaultOptions = {
|
|
13
15
|
indentation: 2,
|
|
16
|
+
objectTypeKeyword: "interface",
|
|
17
|
+
preserveFiles: false,
|
|
14
18
|
};
|
|
15
|
-
const renderDocumentation = (comment, isDeprecated) => comment === undefined
|
|
19
|
+
const renderDocumentation = (comment, isDeprecated) => syntax `${comment === undefined
|
|
16
20
|
? ""
|
|
17
21
|
: syntax `/**
|
|
18
22
|
${prefixLines(" * ", comment, true)}${isDeprecated
|
|
@@ -20,29 +24,36 @@ ${prefixLines(" * ", comment, true)}${isDeprecated
|
|
|
20
24
|
* @deprecated`
|
|
21
25
|
: ""}
|
|
22
26
|
*/
|
|
23
|
-
`;
|
|
24
|
-
const renderTypeParameters = (options, params) => params.length === 0
|
|
27
|
+
`}`;
|
|
28
|
+
const renderTypeParameters = (options, params) => syntax `${params.length === 0
|
|
25
29
|
? ""
|
|
26
|
-
: `<${params
|
|
27
|
-
.map(param => param.constraint === undefined
|
|
30
|
+
: syntax `<${combineSyntaxes(params.map(param => param.constraint === undefined
|
|
28
31
|
? param.name
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const wrapAsObject = (options, syntax) => joinSyntax("{", EOL, applyIndentation(1, syntax, options.indentation), EOL, "}");
|
|
32
|
+
: syntax `${param.name} extends ${renderType(options, param.constraint)}`), ", ")}>`}`;
|
|
33
|
+
const renderArrayType = (options, type) => syntax `${renderType(options, type.items)}[]`;
|
|
34
|
+
const wrapAsObject = (options, str) => syntax `{${EOL}${indent(options.indentation, 1, str)}${EOL}}`;
|
|
33
35
|
const renderObjectType = (options, type) => {
|
|
34
|
-
return wrapAsObject(options, Object.entries(type.properties)
|
|
35
|
-
.map(([name, config]) => joinSyntax(renderDocumentation(config.comment, config.isDeprecated), name, config.isRequired ? "" : "?", ": ", renderType(options, config.type)))
|
|
36
|
-
.join(Object.values(type.properties).some(prop => prop.comment !== undefined) ? EOL + EOL : EOL));
|
|
36
|
+
return wrapAsObject(options, combineSyntaxes(Object.entries(type.properties).map(([name, config]) => syntax `${renderDocumentation(config.comment, config.isDeprecated)}${name}${config.isRequired ? "" : "?"}: ${renderType(options, config.type)}`), Object.values(type.properties).some(prop => prop.comment !== undefined) ? EOL + EOL : EOL));
|
|
37
37
|
};
|
|
38
|
-
const renderBooleanType = (_options, _type) =>
|
|
39
|
-
const renderDateType = (_options, _type) =>
|
|
40
|
-
const renderNumericType = (_options, _type) =>
|
|
41
|
-
const renderStringType = (_options, _type) =>
|
|
42
|
-
const renderGenericArgumentIdentifierType = (_options, type) => type.argument.name
|
|
43
|
-
const renderReferenceIdentifierType = (_options, type) =>
|
|
44
|
-
|
|
38
|
+
const renderBooleanType = (_options, _type) => syntax `boolean`;
|
|
39
|
+
const renderDateType = (_options, _type) => syntax `Date`;
|
|
40
|
+
const renderNumericType = (_options, _type) => syntax `number`;
|
|
41
|
+
const renderStringType = (_options, _type) => syntax `string`;
|
|
42
|
+
const renderGenericArgumentIdentifierType = (_options, type) => syntax `${type.argument.name}`;
|
|
43
|
+
const renderReferenceIdentifierType = (_options, type) => [
|
|
44
|
+
{ [type.entity.sourceUrl]: [type.entity.name + "_ID"] },
|
|
45
|
+
type.entity.name + "_ID",
|
|
46
|
+
];
|
|
47
|
+
const renderIncludeIdentifierType = (options, type) => combineSyntaxes([
|
|
48
|
+
[{ [type.reference.sourceUrl]: [type.reference.name] }, type.reference.name],
|
|
49
|
+
type.args.length === 0
|
|
50
|
+
? ""
|
|
51
|
+
: syntax `<${combineSyntaxes(type.args.map(arg => renderType(options, arg)), ", ")}>`,
|
|
52
|
+
]);
|
|
45
53
|
const renderNestedEntityMapType = (options, type) => wrapAsObject(options, syntax `[${toCamelCase(type.secondaryEntity.name)}Id: string]: ${type.name}`);
|
|
54
|
+
const renderEnumType = (options, type) => combineSyntaxes(Object.entries(type.values).map(([caseName, caseDef]) => indent(options.indentation, 1, syntax `${EOL}| {${EOL}${indent(options.indentation, 1, syntax `${discriminatorKey}: "${caseName}"${caseDef.type === null
|
|
55
|
+
? ""
|
|
56
|
+
: syntax `${EOL}${caseName}: ${renderType(options, caseDef.type)}`}`)}${EOL}}`)));
|
|
46
57
|
const renderType = (options, type) => {
|
|
47
58
|
switch (type.kind) {
|
|
48
59
|
case NodeKind.ArrayType:
|
|
@@ -67,20 +78,17 @@ const renderType = (options, type) => {
|
|
|
67
78
|
return renderIncludeIdentifierType(options, type);
|
|
68
79
|
case NodeKind.NestedEntityMapType:
|
|
69
80
|
return renderNestedEntityMapType(options, type);
|
|
81
|
+
case NodeKind.EnumType:
|
|
82
|
+
return renderEnumType(options, type);
|
|
70
83
|
default:
|
|
71
84
|
return assertExhaustive(type, "Unknown type");
|
|
72
85
|
}
|
|
73
86
|
};
|
|
74
|
-
const renderEntityDecl = (options, decl) =>
|
|
75
|
-
const renderEnumDecl = (options, decl) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const type = decl.type.value;
|
|
80
|
-
return isObjectType(type)
|
|
81
|
-
? joinSyntax(renderDocumentation(decl.comment, decl.isDeprecated), "export interface ", decl.name, renderTypeParameters(options, decl.parameters), " ", renderType(options, type))
|
|
82
|
-
: joinSyntax(renderDocumentation(decl.comment), "export type ", decl.name, renderTypeParameters(options, decl.parameters), " = ", renderType(options, type));
|
|
83
|
-
};
|
|
87
|
+
const renderEntityDecl = (options, decl) => syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export ${options.objectTypeKeyword} ${decl.name} ${options.objectTypeKeyword === "type" ? "= " : ""}${renderType(options, addEphemeralUUIDToType(decl))}`;
|
|
88
|
+
const renderEnumDecl = (options, decl) => syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export type ${decl.name}${renderTypeParameters(options, decl.parameters)} =${renderEnumType(options, decl.type.value)}`;
|
|
89
|
+
const renderTypeAliasDecl = (options, decl) => isObjectType(decl.type.value)
|
|
90
|
+
? syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export ${options.objectTypeKeyword} ${decl.name}${renderTypeParameters(options, decl.parameters)} ${options.objectTypeKeyword === "type" ? "= " : ""}${renderType(options, decl.type.value)}`
|
|
91
|
+
: syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export type ${decl.name}${renderTypeParameters(options, decl.parameters)} = ${renderType(options, decl.type.value)}`;
|
|
84
92
|
const renderDecl = (options, decl) => {
|
|
85
93
|
switch (decl.kind) {
|
|
86
94
|
case NodeKind.EntityDecl:
|
|
@@ -93,10 +101,22 @@ const renderDecl = (options, decl) => {
|
|
|
93
101
|
return assertExhaustive(decl, "Unknown declaration");
|
|
94
102
|
}
|
|
95
103
|
};
|
|
96
|
-
const renderDeclarations = (options, declarations) => declarations.map(decl => renderDecl(options, decl))
|
|
104
|
+
const renderDeclarations = (options, declarations) => combineSyntaxes(declarations.map(decl => renderDecl(options, decl)), EOL + EOL);
|
|
105
|
+
const renderImports = (currentUrl, imports) => {
|
|
106
|
+
const importsSyntax = Object.entries(imports)
|
|
107
|
+
.filter(([sourceUrl]) => sourceUrl !== currentUrl)
|
|
108
|
+
.map(([sourceUrl, names]) => [
|
|
109
|
+
ensureSpecialDirStart(relative(dirname(currentUrl), sourceUrl)),
|
|
110
|
+
names,
|
|
111
|
+
])
|
|
112
|
+
.toSorted(([sourceUrlA], [sourceUrlB]) => sourceUrlA.localeCompare(sourceUrlB))
|
|
113
|
+
.map(([sourceUrl, names]) => `import { ${names.toSorted((a, b) => a.localeCompare(b)).join(", ")} } from "${sourceUrl}"`)
|
|
114
|
+
.join(EOL);
|
|
115
|
+
return importsSyntax.length > 0 ? importsSyntax + EOL + EOL : "";
|
|
116
|
+
};
|
|
97
117
|
export const render = (options = defaultOptions, declarations) => {
|
|
98
118
|
const finalOptions = { ...defaultOptions, ...options };
|
|
99
|
-
|
|
119
|
+
const [imports, content] = renderDeclarations(finalOptions, flatMapAuxiliaryDecls(node => {
|
|
100
120
|
if (isNestedEntityMapType(node)) {
|
|
101
121
|
return TypeAliasDecl(getParentDecl(node)?.sourceUrl ?? "", {
|
|
102
122
|
name: node.name,
|
|
@@ -109,4 +129,7 @@ export const render = (options = defaultOptions, declarations) => {
|
|
|
109
129
|
}
|
|
110
130
|
return undefined;
|
|
111
131
|
}, declarations));
|
|
132
|
+
return finalOptions.preserveFiles
|
|
133
|
+
? renderImports(declarations[0].sourceUrl, imports) + content
|
|
134
|
+
: content;
|
|
112
135
|
};
|
package/lib/schema/Node.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface NodeKind {
|
|
|
19
19
|
ReferenceIdentifierType: "ReferenceIdentifierType";
|
|
20
20
|
IncludeIdentifierType: "IncludeIdentifierType";
|
|
21
21
|
NestedEntityMapType: "NestedEntityMapType";
|
|
22
|
+
EnumType: "EnumType";
|
|
22
23
|
}
|
|
23
24
|
export declare const NodeKind: NodeKind;
|
|
24
25
|
export interface BaseNode {
|
package/lib/schema/Node.js
CHANGED
|
@@ -18,6 +18,7 @@ export const NodeKind = enumOfObject({
|
|
|
18
18
|
ReferenceIdentifierType: null,
|
|
19
19
|
IncludeIdentifierType: null,
|
|
20
20
|
NestedEntityMapType: null,
|
|
21
|
+
EnumType: null,
|
|
21
22
|
});
|
|
22
23
|
export const flatMapAuxiliaryDecls = (callbackFn, declarations) => {
|
|
23
24
|
const mapNodeTree = (callbackFn, node, decls) => {
|
|
@@ -28,7 +29,7 @@ export const flatMapAuxiliaryDecls = (callbackFn, declarations) => {
|
|
|
28
29
|
}
|
|
29
30
|
case NodeKind.EnumDecl: {
|
|
30
31
|
const newDecls = callbackFn(node, decls);
|
|
31
|
-
return
|
|
32
|
+
return mapNodeTree(callbackFn, node.type.value, newDecls);
|
|
32
33
|
}
|
|
33
34
|
case NodeKind.TypeAliasDecl: {
|
|
34
35
|
const newDecls = callbackFn(node, decls);
|
|
@@ -52,6 +53,10 @@ export const flatMapAuxiliaryDecls = (callbackFn, declarations) => {
|
|
|
52
53
|
case NodeKind.IncludeIdentifierType:
|
|
53
54
|
case NodeKind.NestedEntityMapType:
|
|
54
55
|
return callbackFn(node, decls);
|
|
56
|
+
case NodeKind.EnumType: {
|
|
57
|
+
const newDecls = callbackFn(node, decls);
|
|
58
|
+
return Object.values(node.values).reduce((newDeclsAcc, caseDef) => caseDef.type === null ? newDecls : mapNodeTree(callbackFn, caseDef.type, newDeclsAcc), newDecls);
|
|
59
|
+
}
|
|
55
60
|
default:
|
|
56
61
|
return assertExhaustive(node);
|
|
57
62
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { BaseNode, GetReferences, Node, Serializer } from "../Node.js";
|
|
2
2
|
import { SerializedTypeParameter, TypeParameter } from "../parameters/TypeParameter.js";
|
|
3
|
+
import { EnumCaseDecl, SerializedEnumCaseDecl } from "../types/generic/EnumType.js";
|
|
3
4
|
import { ObjectType, SerializedObjectType } from "../types/generic/ObjectType.js";
|
|
4
5
|
import { SerializedType, Type } from "../types/Type.js";
|
|
5
6
|
import { ValidatorHelpers } from "../validation/type.js";
|
|
6
7
|
import { EntityDecl, SerializedEntityDecl } from "./EntityDecl.js";
|
|
7
|
-
import {
|
|
8
|
+
import { EnumDecl, SerializedEnumDecl } from "./EnumDecl.js";
|
|
8
9
|
import { SerializedTypeAliasDecl, TypeAliasDecl } from "./TypeAliasDecl.js";
|
|
9
10
|
export type TypeArguments<Params extends TypeParameter[]> = {
|
|
10
11
|
[K in keyof Params]: Params[K] extends TypeParameter<string, infer T> ? T : Type;
|
|
@@ -42,3 +43,4 @@ export declare const isDeclWithoutTypeParameters: (decl: Decl) => decl is DeclP<
|
|
|
42
43
|
export declare const resolveTypeArgumentsInDecls: (decls: readonly Decl[]) => DeclP<[]>[];
|
|
43
44
|
export declare const serializeDecl: Serializer<Decl, SerializedDecl>;
|
|
44
45
|
export declare const getReferencesForDecl: GetReferences<Decl>;
|
|
46
|
+
export declare const groupDeclarationsBySourceUrl: (decls: readonly Decl[]) => Partial<Record<string, Decl[]>>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
2
2
|
import { NodeKind } from "../Node.js";
|
|
3
3
|
import { getNestedDeclarationsInArrayType } from "../types/generic/ArrayType.js";
|
|
4
|
+
import { getNestedDeclarationsInEnumType, } from "../types/generic/EnumType.js";
|
|
4
5
|
import { getNestedDeclarationsInObjectType, } from "../types/generic/ObjectType.js";
|
|
5
6
|
import { getNestedDeclarationsInIncludeIdentifierType } from "../types/references/IncludeIdentifierType.js";
|
|
6
7
|
import { getNestedDeclarationsInNestedEntityMapType } from "../types/references/NestedEntityMapType.js";
|
|
@@ -35,6 +36,8 @@ export const getNestedDeclarations = (addedDecls, node) => {
|
|
|
35
36
|
return getNestedDeclarationsInIncludeIdentifierType(addedDecls, node);
|
|
36
37
|
case NodeKind.NestedEntityMapType:
|
|
37
38
|
return getNestedDeclarationsInNestedEntityMapType(addedDecls, node);
|
|
39
|
+
case NodeKind.EnumType:
|
|
40
|
+
return getNestedDeclarationsInEnumType(addedDecls, node);
|
|
38
41
|
default:
|
|
39
42
|
return assertExhaustive(node);
|
|
40
43
|
}
|
|
@@ -96,3 +99,4 @@ export const getReferencesForDecl = (decl, value) => {
|
|
|
96
99
|
return assertExhaustive(decl);
|
|
97
100
|
}
|
|
98
101
|
};
|
|
102
|
+
export const groupDeclarationsBySourceUrl = (decls) => Object.groupBy(decls, decl => decl.sourceUrl);
|
|
@@ -9,6 +9,7 @@ import { BaseDecl, GetNestedDeclarations, SerializedBaseDecl } from "./Declarati
|
|
|
9
9
|
import { TypeAliasDecl } from "./TypeAliasDecl.js";
|
|
10
10
|
export interface EntityDecl<Name extends string = string, T extends ObjectType = ObjectType> extends BaseDecl<Name, []> {
|
|
11
11
|
kind: NodeKind["EntityDecl"];
|
|
12
|
+
namePlural: string;
|
|
12
13
|
type: Lazy<T>;
|
|
13
14
|
/**
|
|
14
15
|
* @default "name"
|
|
@@ -27,6 +28,7 @@ export interface EntityDecl<Name extends string = string, T extends ObjectType =
|
|
|
27
28
|
}
|
|
28
29
|
export interface SerializedEntityDecl<Name extends string = string, T extends SerializedObjectType = SerializedObjectType> extends SerializedBaseDecl<Name, []> {
|
|
29
30
|
kind: NodeKind["EntityDecl"];
|
|
31
|
+
namePlural: string;
|
|
30
32
|
type: T;
|
|
31
33
|
/**
|
|
32
34
|
* @default "name"
|
|
@@ -45,6 +47,7 @@ export interface SerializedEntityDecl<Name extends string = string, T extends Se
|
|
|
45
47
|
}
|
|
46
48
|
export declare const EntityDecl: <Name extends string, T extends ObjectType>(sourceUrl: string, options: {
|
|
47
49
|
name: Name;
|
|
50
|
+
namePlural: string;
|
|
48
51
|
comment?: string;
|
|
49
52
|
type: () => T;
|
|
50
53
|
/**
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { Lazy } from "../../utils/lazy.js";
|
|
2
2
|
import { GetReferences, Node, NodeKind, Serializer } from "../Node.js";
|
|
3
3
|
import { SerializedTypeParameter, TypeParameter } from "../parameters/TypeParameter.js";
|
|
4
|
-
import {
|
|
4
|
+
import { EnumCaseDecl, EnumType, SerializedEnumCaseDecl, SerializedEnumType } from "../types/generic/EnumType.js";
|
|
5
|
+
import { Type } from "../types/Type.js";
|
|
5
6
|
import { ValidatorHelpers } from "../validation/type.js";
|
|
6
7
|
import { BaseDecl, GetNestedDeclarations, SerializedBaseDecl, TypeArguments } from "./Declaration.js";
|
|
7
8
|
export interface EnumDecl<Name extends string = string, T extends Record<string, EnumCaseDecl> = Record<string, EnumCaseDecl>, Params extends TypeParameter[] = TypeParameter[]> extends BaseDecl<Name, Params> {
|
|
8
9
|
kind: NodeKind["EnumDecl"];
|
|
9
|
-
|
|
10
|
+
type: Lazy<EnumType<T>>;
|
|
10
11
|
isDeprecated?: boolean;
|
|
11
12
|
}
|
|
12
13
|
export interface SerializedEnumDecl<Name extends string = string, T extends Record<string, SerializedEnumCaseDecl> = Record<string, SerializedEnumCaseDecl>, Params extends SerializedTypeParameter[] = SerializedTypeParameter[]> extends SerializedBaseDecl<Name, Params> {
|
|
13
14
|
kind: NodeKind["EnumDecl"];
|
|
14
|
-
|
|
15
|
+
type: SerializedEnumType<T>;
|
|
15
16
|
isDeprecated?: boolean;
|
|
16
17
|
}
|
|
17
18
|
export declare const GenEnumDecl: <Name extends string, T extends Record<string, EnumCaseDecl>, Params extends TypeParameter[]>(sourceUrl: string, options: {
|
|
@@ -31,23 +32,5 @@ export declare const isEnumDecl: (node: Node) => node is EnumDecl;
|
|
|
31
32
|
export declare const getNestedDeclarationsInEnumDecl: GetNestedDeclarations<EnumDecl>;
|
|
32
33
|
export declare const validateEnumDecl: (helpers: ValidatorHelpers, decl: EnumDecl, args: Type[], value: unknown) => Error[];
|
|
33
34
|
export declare const resolveTypeArgumentsInEnumDecl: <Params extends TypeParameter[]>(decl: EnumDecl<string, Record<string, EnumCaseDecl>, Params>, args: TypeArguments<Params>) => EnumDecl<string, Record<string, EnumCaseDecl>, []>;
|
|
34
|
-
export interface EnumCaseDecl<T extends Type | null = Type | null> {
|
|
35
|
-
kind: NodeKind["EnumCaseDecl"];
|
|
36
|
-
type: T;
|
|
37
|
-
comment?: string;
|
|
38
|
-
isDeprecated?: boolean;
|
|
39
|
-
}
|
|
40
|
-
export interface SerializedEnumCaseDecl<T extends SerializedType | null = SerializedType | null> {
|
|
41
|
-
kind: NodeKind["EnumCaseDecl"];
|
|
42
|
-
type: T;
|
|
43
|
-
comment?: string;
|
|
44
|
-
isDeprecated?: boolean;
|
|
45
|
-
}
|
|
46
|
-
export declare const EnumCaseDecl: <T extends Type | null>(options: {
|
|
47
|
-
type: T;
|
|
48
|
-
comment?: string;
|
|
49
|
-
isDeprecated?: boolean;
|
|
50
|
-
}) => EnumCaseDecl<T>;
|
|
51
|
-
export { EnumCaseDecl as EnumCase };
|
|
52
35
|
export declare const serializeEnumDecl: Serializer<EnumDecl, SerializedEnumDecl>;
|
|
53
36
|
export declare const getReferencesForEnumDecl: GetReferences<EnumDecl>;
|