tsondb 0.11.3 → 0.12.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/dist/src/bin/tsondb.js +5 -0
- package/dist/src/node/config.d.ts +33 -4
- package/dist/src/node/config.js +20 -2
- package/dist/src/node/renderers/ts/index.js +3 -2
- package/dist/src/node/renderers/ts/render.d.ts +2 -0
- package/dist/src/node/renderers/ts/render.js +16 -6
- package/dist/src/node/server/api/git.js +1 -0
- package/dist/src/node/utils/render.d.ts +1 -0
- package/dist/src/node/utils/render.js +1 -1
- package/package.json +1 -1
package/dist/src/bin/tsondb.js
CHANGED
|
@@ -13,6 +13,7 @@ import { join } from "node:path";
|
|
|
13
13
|
import { cwd } from "node:process";
|
|
14
14
|
import { pathToFileURL } from "node:url";
|
|
15
15
|
import { parseArguments } from "simple-cli-args";
|
|
16
|
+
import { validateConfigForFormatting, validateConfigForGeneration, validateConfigForServer, validateConfigForTesting, } from "../node/config.js";
|
|
16
17
|
import { format, generateOutputs, serve, validate } from "../node/index.js";
|
|
17
18
|
const debug = Debug("tsondb:cli");
|
|
18
19
|
const passedArguments = parseArguments({
|
|
@@ -80,14 +81,17 @@ if (passedArguments.command === undefined) {
|
|
|
80
81
|
switch (passedArguments.command.name) {
|
|
81
82
|
case "generate":
|
|
82
83
|
debug(`running command: generate`);
|
|
84
|
+
validateConfigForGeneration(config);
|
|
83
85
|
await generateOutputs(config.schema, config.outputs);
|
|
84
86
|
break;
|
|
85
87
|
case "serve":
|
|
86
88
|
debug(`running command: serve`);
|
|
89
|
+
validateConfigForServer(config);
|
|
87
90
|
await serve(config.schema, config.dataRootPath, config.defaultLocales, config.homeLayoutSections, config.serverOptions, config.customStylesheetPath);
|
|
88
91
|
break;
|
|
89
92
|
case "validate":
|
|
90
93
|
debug(`running command: validate`);
|
|
94
|
+
validateConfigForTesting(config);
|
|
91
95
|
if (passedArguments.command.options?.checkReferentialIntegrity !== undefined) {
|
|
92
96
|
debug(`check referential integrity: ${passedArguments.command.options.checkReferentialIntegrity ? "yes" : "no"}`);
|
|
93
97
|
}
|
|
@@ -102,6 +106,7 @@ switch (passedArguments.command.name) {
|
|
|
102
106
|
break;
|
|
103
107
|
case "format":
|
|
104
108
|
debug(`running command: format`);
|
|
109
|
+
validateConfigForFormatting(config);
|
|
105
110
|
await format(config.schema, config.dataRootPath);
|
|
106
111
|
break;
|
|
107
112
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { Output } from "../shared/output.ts";
|
|
2
2
|
import type { EntityDecl } from "./schema/index.ts";
|
|
3
3
|
import type { Schema } from "./schema/Schema.ts";
|
|
4
|
+
/**
|
|
5
|
+
* The main configuration type for TSONDB.
|
|
6
|
+
*/
|
|
4
7
|
export type Config = {
|
|
5
8
|
serverOptions?: ServerOptions;
|
|
6
9
|
schema: Schema;
|
|
7
|
-
outputs
|
|
8
|
-
defaultLocales
|
|
9
|
-
dataRootPath
|
|
10
|
+
outputs?: Output[];
|
|
11
|
+
defaultLocales?: string[];
|
|
12
|
+
dataRootPath?: string;
|
|
10
13
|
homeLayoutSections?: HomeLayoutSection[];
|
|
11
14
|
customStylesheetPath?: string;
|
|
12
15
|
};
|
|
@@ -18,4 +21,30 @@ export type HomeLayoutSection = {
|
|
|
18
21
|
comment?: string;
|
|
19
22
|
entities: EntityDecl[];
|
|
20
23
|
};
|
|
21
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The configuration type required for generation commands.
|
|
26
|
+
*/
|
|
27
|
+
export type GenerationConfig = {
|
|
28
|
+
schema: Schema;
|
|
29
|
+
outputs: Output[];
|
|
30
|
+
};
|
|
31
|
+
export declare const validateConfigForGeneration: (config: Config) => asserts config is GenerationConfig;
|
|
32
|
+
/**
|
|
33
|
+
* The configuration type required for any commands that need to read data stored in the database.
|
|
34
|
+
*/
|
|
35
|
+
export type DataConfig = {
|
|
36
|
+
schema: Schema;
|
|
37
|
+
dataRootPath: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The configuration type required for running the server for the editor.
|
|
41
|
+
*/
|
|
42
|
+
export type ServerConfig = DataConfig & {
|
|
43
|
+
serverOptions?: ServerOptions;
|
|
44
|
+
defaultLocales: string[];
|
|
45
|
+
homeLayoutSections?: HomeLayoutSection[];
|
|
46
|
+
customStylesheetPath?: string;
|
|
47
|
+
};
|
|
48
|
+
export declare const validateConfigForServer: (config: Config) => asserts config is ServerConfig;
|
|
49
|
+
export declare const validateConfigForTesting: (config: Config) => asserts config is DataConfig;
|
|
50
|
+
export declare const validateConfigForFormatting: (config: Config) => asserts config is DataConfig;
|
package/dist/src/node/config.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
export const
|
|
2
|
-
if (config.
|
|
1
|
+
export const validateConfigForGeneration = config => {
|
|
2
|
+
if ((config.outputs?.length ?? 0) === 0) {
|
|
3
|
+
throw new Error("At least one output must be specified in the config.");
|
|
4
|
+
}
|
|
5
|
+
};
|
|
6
|
+
export const validateConfigForServer = config => {
|
|
7
|
+
if ((config.defaultLocales?.length ?? 0) === 0) {
|
|
3
8
|
throw new Error("At least one default locale must be specified in the config.");
|
|
4
9
|
}
|
|
10
|
+
if (config.dataRootPath === undefined) {
|
|
11
|
+
throw new Error("A data root path must be specified in the config.");
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export const validateConfigForTesting = config => {
|
|
15
|
+
if (config.dataRootPath === undefined) {
|
|
16
|
+
throw new Error("A data root path must be specified in the config.");
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const validateConfigForFormatting = config => {
|
|
20
|
+
if (config.dataRootPath === undefined) {
|
|
21
|
+
throw new Error("A data root path must be specified in the config.");
|
|
22
|
+
}
|
|
5
23
|
};
|
|
@@ -7,6 +7,7 @@ import { groupDeclarationsBySourceUrl } from "../../schema/declarations/Declarat
|
|
|
7
7
|
import { render } from "./render.js";
|
|
8
8
|
const debug = Debug("tsondb:renderer:ts");
|
|
9
9
|
const extension = ".d.ts";
|
|
10
|
+
const pragma = "// Generated by TSONDB -- do not edit manually.\n\n";
|
|
10
11
|
export const TypeScriptOutput = (options) => ({
|
|
11
12
|
run: async (schema) => {
|
|
12
13
|
if (options.rendererOptions?.preserveFiles === true) {
|
|
@@ -23,7 +24,7 @@ export const TypeScriptOutput = (options) => ({
|
|
|
23
24
|
const newDir = join(options.targetPath, relativePath);
|
|
24
25
|
const newPath = join(newDir, basename(sourcePath, extname(sourcePath)) + extension);
|
|
25
26
|
await mkdir(newDir, { recursive: true });
|
|
26
|
-
await writeFile(newPath, render(options.rendererOptions, decls ?? []), {
|
|
27
|
+
await writeFile(newPath, pragma + render(options.rendererOptions, decls ?? []), {
|
|
27
28
|
encoding: "utf-8",
|
|
28
29
|
});
|
|
29
30
|
}
|
|
@@ -33,7 +34,7 @@ export const TypeScriptOutput = (options) => ({
|
|
|
33
34
|
else {
|
|
34
35
|
debug("emitting declarations to single file...");
|
|
35
36
|
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
36
|
-
await writeFile(options.targetPath, render(options.rendererOptions, schema.declarations), {
|
|
37
|
+
await writeFile(options.targetPath, pragma + render(options.rendererOptions, schema.declarations), {
|
|
37
38
|
encoding: "utf-8",
|
|
38
39
|
});
|
|
39
40
|
debug("emitted declarations to %s", options.targetPath);
|
|
@@ -3,5 +3,7 @@ export type TypeScriptRendererOptions = {
|
|
|
3
3
|
indentation: number;
|
|
4
4
|
objectTypeKeyword: "interface" | "type";
|
|
5
5
|
preserveFiles: boolean;
|
|
6
|
+
generateEntityMapType: boolean;
|
|
7
|
+
addIdentifierToEntities: boolean;
|
|
6
8
|
};
|
|
7
9
|
export declare const render: (options: Partial<TypeScriptRendererOptions> | undefined, declarations: readonly Decl[]) => string;
|
|
@@ -13,11 +13,13 @@ import { isObjectType } from "../../schema/types/generic/ObjectType.js";
|
|
|
13
13
|
import { isNestedEntityMapType } from "../../schema/types/references/NestedEntityMapType.js";
|
|
14
14
|
import { ReferenceIdentifierType } from "../../schema/types/references/ReferenceIdentifierType.js";
|
|
15
15
|
import { ensureSpecialDirStart } from "../../utils/path.js";
|
|
16
|
-
import { combineSyntaxes, indent, prefixLines, syntax } from "../../utils/render.js";
|
|
16
|
+
import { combineSyntaxes, emptyRenderResult, indent, prefixLines, syntax, } from "../../utils/render.js";
|
|
17
17
|
const defaultOptions = {
|
|
18
18
|
indentation: 2,
|
|
19
19
|
objectTypeKeyword: "interface",
|
|
20
20
|
preserveFiles: false,
|
|
21
|
+
generateEntityMapType: false,
|
|
22
|
+
addIdentifierToEntities: false,
|
|
21
23
|
};
|
|
22
24
|
const renderDocumentation = (comment, isDeprecated) => syntax `${comment === undefined
|
|
23
25
|
? ""
|
|
@@ -91,7 +93,7 @@ const renderType = (options, type) => {
|
|
|
91
93
|
return assertExhaustive(type, "Unknown type");
|
|
92
94
|
}
|
|
93
95
|
};
|
|
94
|
-
const renderEntityDecl = (options, decl) => syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export ${options.objectTypeKeyword} ${decl.name} ${options.objectTypeKeyword === "type" ? "= " : ""}${renderType(options, addEphemeralUUIDToType(decl))}`;
|
|
96
|
+
const renderEntityDecl = (options, decl) => syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export ${options.objectTypeKeyword} ${decl.name} ${options.objectTypeKeyword === "type" ? "= " : ""}${renderType(options, options.addIdentifierToEntities ? addEphemeralUUIDToType(decl) : decl.type.value)}`;
|
|
95
97
|
const renderEnumDecl = (options, decl) => syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export type ${decl.name}${renderTypeParameters(options, decl.parameters)} =${renderEnumType(options, decl.type.value)}`;
|
|
96
98
|
const renderTypeAliasDecl = (options, decl) => isObjectType(decl.type.value)
|
|
97
99
|
? syntax `${renderDocumentation(decl.comment, decl.isDeprecated)}export ${options.objectTypeKeyword} ${decl.name}${renderTypeParameters(options, decl.parameters)} ${options.objectTypeKeyword === "type" ? "= " : ""}${renderType(options, decl.type.value)}`
|
|
@@ -123,8 +125,15 @@ const renderImports = (currentUrl, imports) => {
|
|
|
123
125
|
.join(EOL);
|
|
124
126
|
return importsSyntax.length > 0 ? importsSyntax + EOL + EOL : "";
|
|
125
127
|
};
|
|
128
|
+
const renderEntityMapType = (options, declarations) => syntax `export type EntityMap = {${EOL}${indent(options.indentation, 1, combineSyntaxes(declarations
|
|
129
|
+
.filter(isEntityDecl)
|
|
130
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
131
|
+
.map(decl => syntax `${decl.name}: ${decl.name}`), EOL))}${EOL}}${EOL + EOL}`;
|
|
126
132
|
export const render = (options = defaultOptions, declarations) => {
|
|
127
133
|
const finalOptions = { ...defaultOptions, ...options };
|
|
134
|
+
const [_, entityMap] = finalOptions.generateEntityMapType
|
|
135
|
+
? renderEntityMapType(finalOptions, declarations)
|
|
136
|
+
: emptyRenderResult;
|
|
128
137
|
const [imports, content] = renderDeclarations(finalOptions, flatMapAuxiliaryDecls((parentNodes, node) => {
|
|
129
138
|
if (isNestedEntityMapType(node)) {
|
|
130
139
|
return TypeAliasDecl(asDecl(parentNodes[0])?.sourceUrl ?? "", {
|
|
@@ -138,8 +147,9 @@ export const render = (options = defaultOptions, declarations) => {
|
|
|
138
147
|
}
|
|
139
148
|
return undefined;
|
|
140
149
|
}, declarations));
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
return (entityMap +
|
|
151
|
+
(finalOptions.preserveFiles
|
|
152
|
+
? (declarations[0] === undefined ? "" : renderImports(declarations[0].sourceUrl, imports)) +
|
|
153
|
+
content
|
|
154
|
+
: content));
|
|
145
155
|
};
|
|
@@ -228,6 +228,7 @@ gitApi.post("/pull", async (req, res) => {
|
|
|
228
228
|
const status = await req.git.status();
|
|
229
229
|
const remotes = await req.git.getRemotes();
|
|
230
230
|
await req.git.pull(remotes[0]?.name, status.current ?? undefined);
|
|
231
|
+
await reinit(req);
|
|
231
232
|
res.set("Content-Type", "text/plain");
|
|
232
233
|
res.status(200).send("Pull successful");
|
|
233
234
|
}
|
|
@@ -4,6 +4,7 @@ export declare const joinSyntax: (...syntaxes: (string | undefined)[]) => string
|
|
|
4
4
|
export type RenderResult = [imports: {
|
|
5
5
|
[sourcePath: string]: string[];
|
|
6
6
|
}, syntax: string];
|
|
7
|
+
export declare const emptyRenderResult: RenderResult;
|
|
7
8
|
export declare const combineSyntaxes: (syntaxes: (RenderResult | string | undefined)[], separator?: string) => RenderResult;
|
|
8
9
|
export declare const syntax: (strings: TemplateStringsArray, ...values: (RenderResult | string | undefined)[]) => RenderResult;
|
|
9
10
|
export declare const indent: (spaces: number, indentLevel: number, text: RenderResult) => RenderResult;
|
|
@@ -6,7 +6,7 @@ export const prefixLines = (prefix, text, includeEmptyLines = false) => text
|
|
|
6
6
|
.join(EOL);
|
|
7
7
|
export const applyIndentation = (indentLevel, text, spaces) => prefixLines(" ".repeat(spaces * indentLevel), text);
|
|
8
8
|
export const joinSyntax = (...syntaxes) => syntaxes.filter(syntax => syntax !== undefined).join("");
|
|
9
|
-
const emptyRenderResult = [{}, ""];
|
|
9
|
+
export const emptyRenderResult = [{}, ""];
|
|
10
10
|
export const combineSyntaxes = (syntaxes, separator = "") => syntaxes
|
|
11
11
|
.filter(syntax => syntax !== undefined)
|
|
12
12
|
.reduce((acc, value, i) => typeof value === "string"
|