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.
Files changed (105) hide show
  1. package/lib/ModelContainer.js +9 -7
  2. package/lib/client/api.d.ts +14 -1
  3. package/lib/client/api.js +119 -0
  4. package/lib/client/components/Git.d.ts +2 -0
  5. package/lib/client/components/Git.js +116 -0
  6. package/lib/client/components/Layout.js +2 -1
  7. package/lib/client/components/typeInputs/ArrayTypeInput.js +2 -1
  8. package/lib/client/components/typeInputs/EnumTypeInput.d.ts +13 -0
  9. package/lib/client/components/typeInputs/{utils/EnumDeclField.js → EnumTypeInput.js} +10 -10
  10. package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.js +1 -10
  11. package/lib/client/components/typeInputs/TypeInput.js +3 -0
  12. package/lib/client/components/typeInputs/utils/Markdown.js +1 -32
  13. package/lib/client/hooks/useAPIResource.d.ts +1 -0
  14. package/lib/client/hooks/useAPIResource.js +2 -0
  15. package/lib/client/hooks/useMappedAPIResource.d.ts +1 -0
  16. package/lib/client/hooks/useMappedAPIResource.js +19 -0
  17. package/lib/client/routes/Entity.js +18 -24
  18. package/lib/client/routes/Home.js +3 -12
  19. package/lib/client/utils/typeSkeleton.js +10 -16
  20. package/lib/renderers/jsonschema/index.d.ts +1 -1
  21. package/lib/renderers/jsonschema/index.js +30 -7
  22. package/lib/renderers/jsonschema/render.d.ts +5 -1
  23. package/lib/renderers/jsonschema/render.js +35 -16
  24. package/lib/renderers/ts/index.d.ts +1 -1
  25. package/lib/renderers/ts/index.js +37 -6
  26. package/lib/renderers/ts/render.d.ts +2 -0
  27. package/lib/renderers/ts/render.js +55 -32
  28. package/lib/schema/Node.d.ts +1 -0
  29. package/lib/schema/Node.js +6 -1
  30. package/lib/schema/declarations/Declaration.d.ts +3 -1
  31. package/lib/schema/declarations/Declaration.js +4 -0
  32. package/lib/schema/declarations/EntityDecl.d.ts +3 -0
  33. package/lib/schema/declarations/EnumDecl.d.ts +4 -21
  34. package/lib/schema/declarations/EnumDecl.js +16 -79
  35. package/lib/schema/index.d.ts +1 -0
  36. package/lib/schema/index.js +1 -0
  37. package/lib/schema/types/Type.d.ts +8 -2
  38. package/lib/schema/types/Type.js +57 -11
  39. package/lib/schema/types/generic/ArrayType.d.ts +2 -1
  40. package/lib/schema/types/generic/ArrayType.js +2 -1
  41. package/lib/schema/types/generic/EnumType.d.ts +38 -0
  42. package/lib/schema/types/generic/EnumType.js +96 -0
  43. package/lib/schema/types/generic/ObjectType.d.ts +2 -1
  44. package/lib/schema/types/generic/ObjectType.js +4 -0
  45. package/lib/schema/types/primitives/BooleanType.d.ts +2 -1
  46. package/lib/schema/types/primitives/BooleanType.js +1 -0
  47. package/lib/schema/types/primitives/DateType.d.ts +2 -1
  48. package/lib/schema/types/primitives/DateType.js +1 -0
  49. package/lib/schema/types/primitives/FloatType.d.ts +2 -1
  50. package/lib/schema/types/primitives/FloatType.js +1 -0
  51. package/lib/schema/types/primitives/IntegerType.d.ts +2 -1
  52. package/lib/schema/types/primitives/IntegerType.js +1 -0
  53. package/lib/schema/types/primitives/StringType.d.ts +2 -1
  54. package/lib/schema/types/primitives/StringType.js +1 -0
  55. package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +2 -1
  56. package/lib/schema/types/references/GenericArgumentIdentifierType.js +1 -0
  57. package/lib/schema/types/references/IncludeIdentifierType.d.ts +5 -3
  58. package/lib/schema/types/references/IncludeIdentifierType.js +15 -2
  59. package/lib/schema/types/references/NestedEntityMapType.d.ts +2 -1
  60. package/lib/schema/types/references/NestedEntityMapType.js +5 -1
  61. package/lib/schema/types/references/ReferenceIdentifierType.d.ts +2 -1
  62. package/lib/schema/types/references/ReferenceIdentifierType.js +2 -1
  63. package/lib/server/api/declarations.d.ts +1 -0
  64. package/lib/server/api/declarations.js +154 -0
  65. package/lib/server/api/git.d.ts +1 -0
  66. package/lib/server/api/git.js +174 -0
  67. package/lib/server/api/index.d.ts +1 -0
  68. package/lib/server/api/index.js +8 -0
  69. package/lib/server/api/instanceOperations.d.ts +6 -0
  70. package/lib/server/api/instanceOperations.js +82 -0
  71. package/lib/server/api/instances.d.ts +1 -0
  72. package/lib/server/api/instances.js +23 -0
  73. package/lib/server/index.d.ts +22 -1
  74. package/lib/server/index.js +11 -165
  75. package/lib/server/init.d.ts +5 -0
  76. package/lib/server/init.js +56 -0
  77. package/lib/shared/api.d.ts +12 -1
  78. package/lib/shared/utils/array.d.ts +19 -0
  79. package/lib/shared/utils/array.js +27 -0
  80. package/lib/shared/utils/git.d.ts +12 -0
  81. package/lib/shared/utils/git.js +98 -0
  82. package/lib/shared/utils/instances.d.ts +10 -0
  83. package/lib/shared/utils/instances.js +8 -1
  84. package/lib/shared/utils/markdown.d.ts +14 -0
  85. package/lib/shared/utils/markdown.js +42 -0
  86. package/lib/shared/utils/object.d.ts +1 -0
  87. package/lib/shared/utils/object.js +4 -0
  88. package/lib/shared/utils/string.d.ts +1 -0
  89. package/lib/shared/utils/string.js +9 -0
  90. package/lib/tsconfig.tsbuildinfo +1 -1
  91. package/lib/utils/git.d.ts +3 -0
  92. package/lib/utils/git.js +12 -0
  93. package/lib/utils/instances.d.ts +3 -2
  94. package/lib/utils/instances.js +9 -2
  95. package/lib/utils/path.d.ts +1 -0
  96. package/lib/utils/path.js +2 -0
  97. package/lib/utils/references.d.ts +7 -0
  98. package/lib/utils/references.js +40 -0
  99. package/lib/utils/render.d.ts +6 -1
  100. package/lib/utils/render.js +27 -1
  101. package/package.json +8 -1
  102. package/public/css/styles.css +200 -1
  103. package/lib/client/components/typeInputs/utils/EnumDeclField.d.ts +0 -13
  104. package/lib/server/instanceOperations.d.ts +0 -7
  105. 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
- switch (referencedDecl.kind) {
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 { resolveTypeArgumentsInDecls } from "../../schema/index.js";
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
- await mkdir(dirname(options.targetPath), { recursive: true });
8
- await writeFile(options.targetPath, render(options.rendererOptions, resolveTypeArgumentsInDecls(schema.declarations)), {
9
- encoding: "utf-8",
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
- indentation: number;
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
- indentation: 2,
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 = (_options, type) => ({
73
- $ref: `#/$defs/${type.reference.name}`,
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
- oneOf: Object.entries(decl.values.value).map(([caseName, caseDef]) => ({
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.indentation);
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 { mkdir, writeFile } from "fs/promises";
2
- import { dirname } from "path";
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
- await mkdir(dirname(options.targetPath), { recursive: true });
7
- await writeFile(options.targetPath, render(options.rendererOptions, schema.declarations), {
8
- encoding: "utf-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 { applyIndentation, joinSyntax, prefixLines, syntax } from "../../utils/render.js";
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
- : joinSyntax(param.name, " extends ", renderType(options, param.constraint)))
30
- .join(", ")}>`;
31
- const renderArrayType = (options, type) => `${renderType(options, type.items)}[]`;
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) => "boolean";
39
- const renderDateType = (_options, _type) => "Date";
40
- const renderNumericType = (_options, _type) => "number";
41
- const renderStringType = (_options, _type) => "string";
42
- const renderGenericArgumentIdentifierType = (_options, type) => type.argument.name;
43
- const renderReferenceIdentifierType = (_options, type) => type.entity.name + "_ID";
44
- const renderIncludeIdentifierType = (_options, type) => type.reference.name;
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) => joinSyntax(renderDocumentation(decl.comment, decl.isDeprecated), "export interface ", decl.name, " ", renderType(options, addEphemeralUUIDToType(decl)));
75
- const renderEnumDecl = (options, decl) => joinSyntax(renderDocumentation(decl.comment, decl.isDeprecated), "export type ", decl.name, renderTypeParameters(options, decl.parameters), " =", ...Object.entries(decl.values.value).map(([caseName, caseDef]) => applyIndentation(1, joinSyntax(EOL, "| {", EOL, applyIndentation(1, joinSyntax(`${discriminatorKey}: "${caseName}"`, caseDef.type === null
76
- ? ""
77
- : joinSyntax(EOL, caseName + ": ", renderType(options, caseDef.type))), options.indentation), EOL, "}"), options.indentation)));
78
- const renderTypeAliasDecl = (options, decl) => {
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)).join(EOL + EOL);
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
- return renderDeclarations(finalOptions, flatMapAuxiliaryDecls(node => {
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
  };
@@ -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 {
@@ -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 Object.values(node.values.value).reduce((newDeclsAcc, caseDef) => caseDef.type === null ? newDecls : mapNodeTree(callbackFn, caseDef.type, newDeclsAcc), newDecls);
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 { EnumCaseDecl, EnumDecl, SerializedEnumCaseDecl, SerializedEnumDecl } from "./EnumDecl.js";
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 { SerializedType, Type } from "../types/Type.js";
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
- values: Lazy<T>;
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
- values: T;
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>;