@deterministic-code/datasources-cli 0.1.10 → 0.1.12
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/generate/CSharpRepositories.js +28 -18
- package/dist/generate/DeterministicProvider.js +35 -1
- package/dist/generate/RepositoryPlan.js +7 -0
- package/dist/generate/RustRepositories.js +34 -17
- package/dist/generate/TypeScriptRepositories.js +24 -11
- package/dist/generate/applyGenerate.js +6 -1
- package/dist/generate/applyMigrations.js +5 -1
- package/dist/generate/datasourceYaml.js +22 -0
- package/dist/generate/loadSpec.js +6 -1
- package/dist/generate/storedProcedures.js +5 -0
- package/dist/generate/writeLists.js +19 -0
- package/dist/parse.js +8 -2
- package/dist/run.js +6 -10
- package/dist/setup.js +4 -2
- package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.d.ts +5 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +64 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-procedures.js +22 -10
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-sql.d.ts +0 -1
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-sql.js +18 -4
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/procedures/helpers.d.ts +5 -8
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/procedures/helpers.js +2 -29
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/write-inclusion.d.ts +9 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/write-inclusion.js +9 -0
- package/node_modules/@deterministic-code/datasources-code-generators/package.json +9 -8
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +67 -2
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/parser/specification-parser.ts +82 -2
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification.ts +2 -0
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/parser/specification-parser.ts +82 -2
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/specification.ts +2 -0
- package/package.json +8 -8
|
@@ -7,7 +7,29 @@ import {
|
|
|
7
7
|
pluralPascal,
|
|
8
8
|
repositoryFile
|
|
9
9
|
} from "./RepositoryModel.js";
|
|
10
|
-
|
|
10
|
+
import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
|
|
11
|
+
const csharpOptionsBody = (model) => `Entity = "${quote(model.entity)}",
|
|
12
|
+
EntityMapper = new DictionaryMapper<string>(new Dictionary<string, string> { ["${quote(model.entity)}"] = "${quote(model.table)}" }),
|
|
13
|
+
PropertyMappings = new DictionaryMapper<string>(new Dictionary<string, string>
|
|
14
|
+
{
|
|
15
|
+
${model.propertyMappings.map((pair) => ` ["${quote(pair.from)}"] = "${quote(pair.to)}",`).join("\n")}
|
|
16
|
+
}),
|
|
17
|
+
Ids = ${writeList("csharp", model.ids)},
|
|
18
|
+
ReadProperties = ${writeList("csharp", model.readProperties)},
|
|
19
|
+
CreateProperties = ${writeList("csharp", model.createProperties)},
|
|
20
|
+
UpdateProperties = ${writeList("csharp", model.updateProperties)},`;
|
|
21
|
+
const csharpOptions = (model, indent) => `new()
|
|
22
|
+
${" ".repeat(indent)}{
|
|
23
|
+
${pad(csharpOptionsBody(model), indent + 4)}
|
|
24
|
+
${" ".repeat(indent)}}`;
|
|
25
|
+
const optionsMethod = (model, type) => {
|
|
26
|
+
const dialects = dialectsOf(model);
|
|
27
|
+
return dialects.length === 0 ? ` static CRUDRepositoryOptions<${type}> Options() => ${csharpOptions(model, 4)};` : ` static CRUDRepositoryOptions<${type}> Options(IConnection connection) =>
|
|
28
|
+
new CRUDRepositoryBuilder<${type}>()
|
|
29
|
+
.Add(${csharpOptions(model, 12)})${dialects.map(([engine, planned]) => `
|
|
30
|
+
.Add("${quote(engine)}", ${csharpOptions({ entity: model.entity, ...planned }, 12)})`).join("")}
|
|
31
|
+
.For(connection);`;
|
|
32
|
+
};
|
|
11
33
|
const enrichment = (item, host = "ctor") => {
|
|
12
34
|
const via = childField("csharp", item.via, item.viaType);
|
|
13
35
|
const head = `"${quote(item.destination)}", "${quote(item.source)}", "${quote(item.relatedKey)}"`;
|
|
@@ -45,16 +67,17 @@ const occMethods = (model, type) => {
|
|
|
45
67
|
}
|
|
46
68
|
`;
|
|
47
69
|
};
|
|
70
|
+
const optionsCall = (model) => dialectsOf(model).length === 0 ? "Options()" : "Options(connection)";
|
|
48
71
|
const createReadonly = (model, type) => {
|
|
49
72
|
const children = childModels(model);
|
|
50
73
|
if (children.length === 0 && model.enrichments.length === 0)
|
|
51
74
|
return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection) =>
|
|
52
|
-
new ReadonlyRepository<${type}>(connection,
|
|
75
|
+
new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});`;
|
|
53
76
|
const locals = children.map((child) => ` var ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`);
|
|
54
77
|
const enrich = model.enrichments.map((item) => ` ${enrichment(item, "readonly")}`);
|
|
55
78
|
return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection)
|
|
56
79
|
{
|
|
57
|
-
var repo = new ReadonlyRepository<${type}>(connection,
|
|
80
|
+
var repo = new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});
|
|
58
81
|
${locals.join("\n")}
|
|
59
82
|
${enrich.join("\n")}
|
|
60
83
|
return repo;
|
|
@@ -69,17 +92,10 @@ const csharpRepositories = (models) => {
|
|
|
69
92
|
...children.map((child) => ` ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`),
|
|
70
93
|
...model.enrichments.map((item) => ` ${enrichment(item)}`)
|
|
71
94
|
];
|
|
72
|
-
const
|
|
73
|
-
{
|
|
74
|
-
${model.propertyMappings.map((pair) => ` ["${quote(pair.from)}"] = "${quote(pair.to)}",`).join("\n")}
|
|
75
|
-
}),
|
|
76
|
-
`;
|
|
77
|
-
const constructor = lines.length === 0 ? ` public ${model.typeName}Repository(IConnection connection) : base(connection, Options()) { }` : ` public ${model.typeName}Repository(IConnection connection) : base(connection, Options())
|
|
95
|
+
const constructor = lines.length === 0 ? ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)}) { }` : ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)})
|
|
78
96
|
{
|
|
79
97
|
${lines.join("\n")}
|
|
80
98
|
}`;
|
|
81
|
-
const ids = model.ids.map((name) => `"${quote(name)}"`).join(", ");
|
|
82
|
-
const read = model.readProperties.map((name) => `"${quote(name)}"`).join(", ");
|
|
83
99
|
const implementsOcc = model.optimisticConcurrency === void 0 ? "" : `, ICRUDRepository<${type}>`;
|
|
84
100
|
const usings = model.optimisticConcurrency === void 0 ? "using Datasources;\n" : "using Datasources;\nusing static Datasources.Cmp;\n";
|
|
85
101
|
return content(
|
|
@@ -93,13 +109,7 @@ ${fields.length === 0 ? "" : `${fields.join("\n")}
|
|
|
93
109
|
|
|
94
110
|
${createReadonly(model, type)}
|
|
95
111
|
${occMethods(model, type)}
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
Entity = "${quote(model.entity)}",
|
|
99
|
-
EntityMapper = new DictionaryMapper<string>(new Dictionary<string, string> { ["${quote(model.entity)}"] = "${quote(model.table)}" }),
|
|
100
|
-
${mapping} Ids = [${ids}],
|
|
101
|
-
ReadProperties = [${read}],
|
|
102
|
-
};
|
|
112
|
+
${optionsMethod(model, type)}
|
|
103
113
|
}
|
|
104
114
|
`
|
|
105
115
|
);
|
|
@@ -10,6 +10,7 @@ import { csharpRepositories } from "./CSharpRepositories.js";
|
|
|
10
10
|
import { rustRepositories } from "./RustRepositories.js";
|
|
11
11
|
import { typescriptRepositories } from "./TypeScriptRepositories.js";
|
|
12
12
|
import { planRepositories } from "./RepositoryPlan.js";
|
|
13
|
+
import { datasourceEnginesOf, engineDatasourceOf, overlayDatasource } from "./datasourceYaml.js";
|
|
13
14
|
const place = (dir, entries) => entries.map((entry) => ({ ...entry, filename: `${dir}/${entry.filename}` }));
|
|
14
15
|
const typePacks = {
|
|
15
16
|
csharp: { datasource: csharpDatasourceTypes, views: csharpViewTypes },
|
|
@@ -22,6 +23,35 @@ const repositoryPacks = {
|
|
|
22
23
|
rust: rustRepositories
|
|
23
24
|
};
|
|
24
25
|
const context = (request) => ({ reader: request.reader, settings: request.settings });
|
|
26
|
+
const optionsOf = (model) => ({
|
|
27
|
+
table: model.table,
|
|
28
|
+
ids: model.ids,
|
|
29
|
+
readProperties: model.readProperties,
|
|
30
|
+
createProperties: model.createProperties,
|
|
31
|
+
updateProperties: model.updateProperties,
|
|
32
|
+
propertyMappings: model.propertyMappings
|
|
33
|
+
});
|
|
34
|
+
const withProviderWrites = async (request, models) => {
|
|
35
|
+
const byProvider = {};
|
|
36
|
+
for (const engine of await datasourceEnginesOf(request.reader, request.settings)) {
|
|
37
|
+
const yaml = await engineDatasourceOf(request.reader, engine);
|
|
38
|
+
if (yaml === void 0) continue;
|
|
39
|
+
byProvider[engine] = planRepositories(
|
|
40
|
+
await DeterministicParser(overlayDatasource(request.reader, yaml)).parse(request.settings),
|
|
41
|
+
request.dialect,
|
|
42
|
+
request.settings
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return Object.keys(byProvider).length === 0 ? models : models.map((model) => ({
|
|
46
|
+
...model,
|
|
47
|
+
optionsByProvider: Object.fromEntries(
|
|
48
|
+
Object.entries(byProvider).map(([engine, planned]) => [
|
|
49
|
+
engine,
|
|
50
|
+
optionsOf(planned.find((item) => item.specName === model.specName) ?? model)
|
|
51
|
+
])
|
|
52
|
+
)
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
25
55
|
const dialectsOf = (settings) => {
|
|
26
56
|
const out = [];
|
|
27
57
|
for (const name of (settings["backend.datasources"] ?? "").split(",")) {
|
|
@@ -44,7 +74,11 @@ const deterministic = {
|
|
|
44
74
|
},
|
|
45
75
|
generateRepositories: async (request) => {
|
|
46
76
|
const spec = await DeterministicParser(request.reader).parse(request.settings);
|
|
47
|
-
|
|
77
|
+
const models = planRepositories(spec, request.dialect, request.settings);
|
|
78
|
+
return place(
|
|
79
|
+
`${request.dialect}/repositories`,
|
|
80
|
+
repositoryPacks[request.dialect](await withProviderWrites(request, models), request.settings)
|
|
81
|
+
);
|
|
48
82
|
},
|
|
49
83
|
generateDatasource: async (request) => place("sql", await generateSql(context(request), dialectsOf(request.settings)))
|
|
50
84
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
|
|
2
2
|
import { createDatasourceNaming } from "@deterministic-code/generators-common/datasource-naming";
|
|
3
3
|
import { identityColumns, isCollectionField, persistedColumnFields } from "@deterministic-code/generators-common/spec-types";
|
|
4
|
+
import { writeInclusionOf } from "@deterministic-code/datasources-code-generators/sql/common/write-inclusion";
|
|
4
5
|
const referenceOf = (references) => {
|
|
5
6
|
if (references === void 0) return void 0;
|
|
6
7
|
const [type, field] = [references].flat().join(".").split(".");
|
|
@@ -70,6 +71,12 @@ const planRepositories = (spec, dialect, settings) => {
|
|
|
70
71
|
}),
|
|
71
72
|
ids: identityColumns(tableType, table).map(property),
|
|
72
73
|
readProperties: columns.map((field) => property(field.name)),
|
|
74
|
+
createProperties: columns.filter(
|
|
75
|
+
(field) => writeInclusionOf(field, identityColumns(tableType, table), overlays.get(field.name)).includeInCreate
|
|
76
|
+
).map((field) => property(field.name)),
|
|
77
|
+
updateProperties: columns.filter(
|
|
78
|
+
(field) => writeInclusionOf(field, identityColumns(tableType, table), overlays.get(field.name)).includeInUpdate
|
|
79
|
+
).map((field) => property(field.name)),
|
|
73
80
|
optimisticConcurrency: concurrencyToken(table, tableType, property),
|
|
74
81
|
enrichments: relations(authored, full, casing, types, expanded, repoName, identity)
|
|
75
82
|
}
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import { content } from "@deterministic-code/generators-common/generate-entry";
|
|
2
2
|
import { childField, children as childModels, isLinked, pluralSnake, repositoryFile } from "./RepositoryModel.js";
|
|
3
|
-
|
|
4
|
-
const
|
|
3
|
+
import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
|
|
4
|
+
const rustString = (value) => `"${quote(value)}"`;
|
|
5
|
+
const uses = (model) => dialectsOf(model).length === 0 ? `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
|
|
6
|
+
use std::sync::Arc;` : `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryBuilder, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
|
|
5
7
|
use std::sync::Arc;`;
|
|
6
|
-
const
|
|
7
|
-
const mapping = model.propertyMappings.length === 0 ? "" : `
|
|
8
|
-
${model.propertyMappings.map((pair) => `
|
|
9
|
-
|
|
8
|
+
const rustFill = (model) => {
|
|
9
|
+
const mapping = model.propertyMappings.length === 0 ? "" : `options.property_mappings = Some(Arc::new(DictionaryMapper::new([
|
|
10
|
+
${model.propertyMappings.map((pair) => ` (${rustString(pair.from)}.into(), ${rustString(pair.to)}.into()),`).join("\n")}
|
|
11
|
+
])));
|
|
10
12
|
`;
|
|
11
|
-
return `
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
${mapping}
|
|
17
|
-
|
|
13
|
+
return `let mut options = CRUDRepositoryOptions::default();
|
|
14
|
+
options.entity = ${rustString(model.entity)}.into();
|
|
15
|
+
options.entity_mapper = Some(Arc::new(DictionaryMapper::new([
|
|
16
|
+
(${rustString(model.entity)}.into(), ${rustString(model.table)}.into()),
|
|
17
|
+
])));
|
|
18
|
+
${mapping}options.ids = ${writeList("rust", model.ids)};
|
|
19
|
+
options.read_properties = ${writeList("rust", model.readProperties)};
|
|
20
|
+
options.create_properties = Some(${writeList("rust", model.createProperties)});
|
|
21
|
+
options.update_properties = Some(${writeList("rust", model.updateProperties)});`;
|
|
22
|
+
};
|
|
23
|
+
const rustBlock = (model) => {
|
|
24
|
+
const dialects = dialectsOf(model);
|
|
25
|
+
return dialects.length === 0 ? rustFill(model) : `let options = CRUDRepositoryBuilder::new()
|
|
26
|
+
.add({
|
|
27
|
+
${pad(rustFill(model), 8)}
|
|
28
|
+
options
|
|
29
|
+
})${dialects.map(([engine, planned]) => `
|
|
30
|
+
.add_for(${rustString(engine)}, {
|
|
31
|
+
${pad(rustFill({ entity: model.entity, ...planned }), 8)}
|
|
32
|
+
options
|
|
33
|
+
})`).join("")}
|
|
34
|
+
.for_connection(connection.as_ref())?;`;
|
|
18
35
|
};
|
|
36
|
+
const crudOptions = (model) => pad(rustBlock(model), 4);
|
|
19
37
|
const readonlyOptions = (model) => {
|
|
20
38
|
const mapping = model.propertyMappings.length === 0 ? "" : ` options.property_mappings = Some(Arc::new(DictionaryMapper::new([
|
|
21
39
|
${model.propertyMappings.map((pair) => ` (${rustString(pair.from)}.into(), ${rustString(pair.to)}.into()),`).join("\n")}
|
|
@@ -162,8 +180,7 @@ ${where}
|
|
|
162
180
|
}
|
|
163
181
|
`;
|
|
164
182
|
};
|
|
165
|
-
const
|
|
166
|
-
const plain = (model, nested) => `${uses}
|
|
183
|
+
const plain = (model, nested) => `${uses(model)}
|
|
167
184
|
|
|
168
185
|
pub fn ${model.specName}_repository(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
|
|
169
186
|
${crudOptions(model)}
|
|
@@ -180,7 +197,7 @@ const wrapped = (model, nested) => {
|
|
|
180
197
|
).join("\n");
|
|
181
198
|
const assign = children.map((child) => child.specName).join(", ");
|
|
182
199
|
const enrich = model.enrichments.map((item) => ` ${enrichment(item, true, nested)}`).join("\n");
|
|
183
|
-
return `${uses}
|
|
200
|
+
return `${uses(model)}
|
|
184
201
|
use std::ops::Deref;
|
|
185
202
|
|
|
186
203
|
pub struct ${type} {
|
|
@@ -190,7 +207,7 @@ ${fields}
|
|
|
190
207
|
|
|
191
208
|
impl ${type} {
|
|
192
209
|
pub fn new(connection: Arc<dyn Connection>) -> Result<Self> {
|
|
193
|
-
${pad(crudOptions(model))}
|
|
210
|
+
${pad(crudOptions(model), 4)}
|
|
194
211
|
${childLets(children, nested)}
|
|
195
212
|
let mut repo = CRUDRepository::new(Arc::clone(&connection), options)?;
|
|
196
213
|
${enrich}
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
|
|
2
2
|
import { content } from "@deterministic-code/generators-common/generate-entry";
|
|
3
3
|
import { childClass, childField, children as childModels, isLinked, pluralCamel, repositoryFile } from "./RepositoryModel.js";
|
|
4
|
-
|
|
4
|
+
import { dialectsOf, quote, writeList } from "./writeLists.js";
|
|
5
|
+
const withEntity = (model, planned) => ({ entity: model.entity, ...planned });
|
|
6
|
+
const entityAndMapping = (model) => {
|
|
7
|
+
const mapping = model.propertyMappings.length === 0 ? "" : `propertyMappings: new DictionaryMapper([${model.propertyMappings.map((pair) => `["${quote(pair.from)}", "${quote(pair.to)}"]`).join(", ")}]), `;
|
|
8
|
+
return `entity: "${quote(model.entity)}", entityMapper: new DictionaryMapper([["${quote(model.entity)}", "${quote(model.table)}"]]), ${mapping}`;
|
|
9
|
+
};
|
|
10
|
+
const tsOptions = (model) => `{ ${entityAndMapping(model)}ids: ${writeList("typescript", model.ids)}, createProperties: ${writeList("typescript", model.createProperties)}, updateProperties: ${writeList("typescript", model.updateProperties)}, readProperties: ${writeList("typescript", model.readProperties)} }`;
|
|
11
|
+
const tsReadonly = (model) => `{ ${entityAndMapping(model)}readProperties: ${writeList("typescript", model.readProperties)} }`;
|
|
12
|
+
const tsOptionsExpr = (model) => dialectsOf(model).length === 0 ? tsOptions(model) : "options(connection)";
|
|
13
|
+
const tsBuilder = (name, model, write) => `const ${name} = (connection: IConnection) =>
|
|
14
|
+
new CRUDRepositoryBuilder<${model.typeName}>()
|
|
15
|
+
.add(${write(model)})${dialectsOf(model).map(([engine, planned]) => `
|
|
16
|
+
.add("${quote(engine)}", ${write(withEntity(model, planned))})`).join("")}
|
|
17
|
+
.for(connection);
|
|
18
|
+
|
|
19
|
+
`;
|
|
20
|
+
const tsOptionsFn = (model) => dialectsOf(model).length === 0 ? "" : `${tsBuilder("options", model, tsOptions)}${tsBuilder("readonlyOptions", model, tsReadonly)}`;
|
|
5
21
|
const enrichment = (item, host) => {
|
|
6
22
|
const via = host === "this" ? `this.${childField("typescript", item.via, item.viaType)}` : childField("typescript", item.via, item.viaType);
|
|
7
23
|
const related = item.relatedProperty === void 0 ? "" : `, relatedProperty: "${quote(item.relatedProperty)}"`;
|
|
@@ -12,7 +28,8 @@ const enrichment = (item, host) => {
|
|
|
12
28
|
}
|
|
13
29
|
return `${host}.addEnriched({ destinationProperty: "${quote(item.destination)}", sourceProperty: "${quote(item.source)}", relatedKey: "${quote(item.relatedKey)}"${related}, type: "${type}" }, ${via});`;
|
|
14
30
|
};
|
|
15
|
-
const createReadonly = (model
|
|
31
|
+
const createReadonly = (model) => {
|
|
32
|
+
const options = dialectsOf(model).length === 0 ? tsReadonly(model) : "readonlyOptions(connection)";
|
|
16
33
|
const children = childModels(model);
|
|
17
34
|
if (children.length === 0 && model.enrichments.length === 0)
|
|
18
35
|
return ` static createReadonly = (connection: IConnection) => new ReadonlyRepository<${model.typeName}>(connection, ${options});`;
|
|
@@ -56,11 +73,6 @@ const typescriptRepositories = (models, settings) => {
|
|
|
56
73
|
const files = models.map((model) => {
|
|
57
74
|
const folder = model.typeHome === "view" ? "views" : "datasource";
|
|
58
75
|
const children = childModels(model);
|
|
59
|
-
const mapping = model.propertyMappings.length === 0 ? "" : `propertyMappings: new DictionaryMapper([${model.propertyMappings.map((pair) => `["${quote(pair.from)}", "${quote(pair.to)}"]`).join(", ")}]), `;
|
|
60
|
-
const head = `{ entity: "${quote(model.entity)}", entityMapper: new DictionaryMapper([["${quote(model.entity)}", "${quote(model.table)}"]]), ${mapping}`;
|
|
61
|
-
const read = `readProperties: [${model.readProperties.map((name) => `"${quote(name)}"`).join(", ")}] }`;
|
|
62
|
-
const crudOptions = `${head}ids: [${model.ids.map((id) => `"${quote(id)}"`).join(", ")}], ${read}`;
|
|
63
|
-
const readonlyOptions = `${head}${read}`;
|
|
64
76
|
const fields = children.map((child) => ` readonly ${childField("typescript", child.specName, child.typeName)}: ${childClass(child.typeName)};`);
|
|
65
77
|
const wrap = occWrap(model);
|
|
66
78
|
const lines = [
|
|
@@ -68,20 +80,21 @@ const typescriptRepositories = (models, settings) => {
|
|
|
68
80
|
...model.enrichments.map((item) => ` ${enrichment(item, "this")}`)
|
|
69
81
|
];
|
|
70
82
|
const imports = children.map((child) => `import { ${childClass(child.typeName)} } from "${repoImport(child.specName)}";`);
|
|
71
|
-
const
|
|
83
|
+
const builder = dialectsOf(model).length === 0 ? "" : "CRUDRepositoryBuilder, ";
|
|
84
|
+
const core = model.optimisticConcurrency === void 0 ? `import { CRUDRepository, ${builder}DictionaryMapper, ReadonlyRepository, type IConnection } from "@deterministic-code/datasources";` : `import { CRUDRepository, ${builder}DatasourceError, DictionaryMapper, eq, ReadonlyRepository, Where, type IConnection } from "@deterministic-code/datasources";`;
|
|
72
85
|
return content(
|
|
73
86
|
repositoryFile("typescript", model),
|
|
74
87
|
`import type { ${model.typeName} } from "../types/generated/${folder}/${casing.convertFileName(model.specName)}";
|
|
75
88
|
${core}
|
|
76
89
|
${imports.length === 0 ? "" : `${imports.join("\n")}
|
|
77
90
|
`}
|
|
78
|
-
export class ${model.typeName}Repository extends CRUDRepository<${model.typeName}> {
|
|
91
|
+
${tsOptionsFn(model)}export class ${model.typeName}Repository extends CRUDRepository<${model.typeName}> {
|
|
79
92
|
${fields.length === 0 ? "" : `${fields.join("\n")}
|
|
80
93
|
`} constructor(connection: IConnection) {
|
|
81
|
-
super(connection, ${
|
|
94
|
+
super(connection, ${tsOptionsExpr(model)});
|
|
82
95
|
${wrap}${lines.length === 0 ? "" : `${lines.join("\n")}
|
|
83
96
|
`} }
|
|
84
|
-
${createReadonly(model
|
|
97
|
+
${createReadonly(model)}
|
|
85
98
|
}
|
|
86
99
|
`
|
|
87
100
|
);
|
|
@@ -2,13 +2,18 @@ import { isAbsolute, join } from "node:path";
|
|
|
2
2
|
import { memoryReader } from "@deterministic-code/generators-common/deterministic-reader";
|
|
3
3
|
import { loadSpec } from "./loadSpec.js";
|
|
4
4
|
import { providerByName } from "./providers.js";
|
|
5
|
+
import { withStoredProcedures } from "./storedProcedures.js";
|
|
5
6
|
import { writeEntries } from "./writeEntries.js";
|
|
6
7
|
import { wireRegister } from "../wireRegister.js";
|
|
7
8
|
const at = (root, path) => isAbsolute(path) ? path : join(root, path);
|
|
8
9
|
const applyGenerate = async (command, flags, deps) => {
|
|
9
10
|
const provider = providerByName(command.provider);
|
|
10
11
|
const loaded = await loadSpec(deps.fs, at(flags.cwd, command.input));
|
|
11
|
-
const request = {
|
|
12
|
+
const request = {
|
|
13
|
+
dialect: command.dialect,
|
|
14
|
+
reader: memoryReader(loaded.files),
|
|
15
|
+
settings: withStoredProcedures(loaded.settings, command.storedProcedures)
|
|
16
|
+
};
|
|
12
17
|
const entries = await {
|
|
13
18
|
all: async () => [
|
|
14
19
|
...await provider.generateTypes(request),
|
|
@@ -3,6 +3,7 @@ import { generate as generateSql } from "@deterministic-code/datasources-code-ge
|
|
|
3
3
|
import { memoryReader } from "@deterministic-code/generators-common/deterministic-reader";
|
|
4
4
|
import { loadSpec } from "./loadSpec.js";
|
|
5
5
|
import { providerByName } from "./providers.js";
|
|
6
|
+
import { withStoredProcedures } from "./storedProcedures.js";
|
|
6
7
|
import { writeEntries } from "./writeEntries.js";
|
|
7
8
|
const at = (root, path) => isAbsolute(path) ? path : join(root, path);
|
|
8
9
|
const afterMigrations = (filename) => filename.replace(/^[^/]+\/migrations\//, "");
|
|
@@ -16,7 +17,10 @@ const applyMigrations = async (command, flags, deps) => {
|
|
|
16
17
|
];
|
|
17
18
|
if (command.migrationsPath !== void 0 && listed.length > 1)
|
|
18
19
|
throw new Error("--migrations-path requires a single SQL dialect in backend.datasources");
|
|
19
|
-
const entries = await generateSql({
|
|
20
|
+
const entries = await generateSql({
|
|
21
|
+
reader: memoryReader(loaded.files),
|
|
22
|
+
settings: withStoredProcedures(loaded.settings, command.storedProcedures)
|
|
23
|
+
});
|
|
20
24
|
const remapped = command.migrationsPath === void 0 ? entries : entries.map((entry) => ({ ...entry, filename: afterMigrations(entry.filename) }));
|
|
21
25
|
const root = at(flags.cwd, command.migrationsPath ?? command.migrationsRoot);
|
|
22
26
|
if (flags.dryRun) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const known = ["sqlite", "mysql", "postgres", "sqlserver", "oracle"];
|
|
2
|
+
const overlayDatasource = (reader, yaml) => ({
|
|
3
|
+
read: (name) => name === "datasource.yaml" ? Promise.resolve(yaml) : reader.read(name),
|
|
4
|
+
exists: (name) => name === "datasource.yaml" ? Promise.resolve(true) : reader.exists(name)
|
|
5
|
+
});
|
|
6
|
+
const engineDatasourceOf = async (reader, engine) => await reader.exists(`datasource.${engine}.yaml`) ? reader.read(`datasource.${engine}.yaml`) : void 0;
|
|
7
|
+
const datasourceEnginesOf = async (reader, settings) => {
|
|
8
|
+
const engines = /* @__PURE__ */ new Set();
|
|
9
|
+
for (const name of (settings["backend.datasources"] ?? "").split(",")) {
|
|
10
|
+
const key = name.trim();
|
|
11
|
+
if (key !== "") engines.add(key);
|
|
12
|
+
}
|
|
13
|
+
for (const name of known) {
|
|
14
|
+
if (await reader.exists(`datasource.${name}.yaml`)) engines.add(name);
|
|
15
|
+
}
|
|
16
|
+
return [...engines];
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
datasourceEnginesOf,
|
|
20
|
+
engineDatasourceOf,
|
|
21
|
+
overlayDatasource
|
|
22
|
+
};
|
|
@@ -22,8 +22,13 @@ const loadSpec = async (fs, dir) => {
|
|
|
22
22
|
if (text !== void 0) files[name] = text;
|
|
23
23
|
}
|
|
24
24
|
if (files["types.yaml"] === void 0) throw new Error(`missing types.yaml in ${dir}`);
|
|
25
|
-
files["datasource.yaml"] ??= defaultDatasourceYaml;
|
|
26
25
|
const names = await fs.readdir(dir).catch(() => []);
|
|
26
|
+
for (const name of names) {
|
|
27
|
+
if (!/^datasource\.[a-z0-9]+\.yaml$/.test(name)) continue;
|
|
28
|
+
const text = await readOptional(fs, join(dir, name));
|
|
29
|
+
if (text !== void 0) files[name] = text;
|
|
30
|
+
}
|
|
31
|
+
files["datasource.yaml"] ??= files["datasource.sqlite.yaml"] ?? defaultDatasourceYaml;
|
|
27
32
|
const diffs = names.filter((name) => versionDir.test(name)).sort();
|
|
28
33
|
const versionFiles = [];
|
|
29
34
|
for (const name of diffs) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const same = (left, right) => left.length === right.length && left.every((name, index) => name === right[index]);
|
|
2
|
+
const samePairs = (left, right) => left.length === right.length && left.every((pair, index) => pair.from === right[index]?.from && pair.to === right[index]?.to);
|
|
3
|
+
const sameOptions = (left, right) => left.table === right.table && same(left.ids, right.ids) && same(left.readProperties, right.readProperties) && same(left.createProperties, right.createProperties) && same(left.updateProperties, right.updateProperties) && samePairs(left.propertyMappings, right.propertyMappings);
|
|
4
|
+
const dialectsOf = (model) => Object.entries(model.optionsByProvider ?? {}).filter(([, planned]) => !sameOptions(model, planned));
|
|
5
|
+
const quote = (value) => value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
6
|
+
const pad = (text, spaces) => {
|
|
7
|
+
const prefix = " ".repeat(spaces);
|
|
8
|
+
return text.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
9
|
+
};
|
|
10
|
+
const csharpList = (names) => `[${names.map((name) => `"${quote(name)}"`).join(", ")}]`;
|
|
11
|
+
const tsList = (names) => `[${names.map((name) => `"${quote(name)}"`).join(", ")}]`;
|
|
12
|
+
const rustList = (names) => `vec![${names.map((name) => `"${quote(name)}".into()`).join(", ")}]`;
|
|
13
|
+
const writeList = (dialect, names) => dialect === "csharp" ? csharpList(names) : dialect === "typescript" ? tsList(names) : rustList(names);
|
|
14
|
+
export {
|
|
15
|
+
dialectsOf,
|
|
16
|
+
pad,
|
|
17
|
+
quote,
|
|
18
|
+
writeList
|
|
19
|
+
};
|
package/dist/parse.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isDialect } from "./generate/GenerateRequest.js";
|
|
|
3
3
|
const generateTargets = ["all", "types", "repositories", "datasource"];
|
|
4
4
|
const help = `Usage: datasources <command> [options]
|
|
5
5
|
|
|
6
|
-
setup
|
|
6
|
+
setup [provider...] Interactive .env, provider install, and optional deterministic/
|
|
7
7
|
--cwd <dir> Consumer project directory (default: .)
|
|
8
8
|
--ecosystem ts|rust|csharp Required when more than one project type exists
|
|
9
9
|
--dry-run Print package-manager commands without running them
|
|
@@ -29,12 +29,14 @@ generate <all|types|repositories|datasource>
|
|
|
29
29
|
--no-migrate Skip migrate-up (generate all applies them)
|
|
30
30
|
--migrations-root <dir> Root prefix for generate all (default: sql)
|
|
31
31
|
--migrations-path <dir> One migrations directory (single SQL dialect)
|
|
32
|
+
--stored-procedures Emit stored-procedure migrations when the dialect can
|
|
32
33
|
|
|
33
34
|
migrations create SQL migrations from the spec (migraters layout)
|
|
34
35
|
--provider <name> Spec provider (default: deterministic)
|
|
35
36
|
--input <dir> Deterministic project (default: ./deterministic)
|
|
36
37
|
--migrations-root <dir> Root prefix (default: sql)
|
|
37
38
|
--migrations-path <dir> One migrations directory (single SQL dialect)
|
|
39
|
+
--stored-procedures Emit stored-procedure migrations when the dialect can
|
|
38
40
|
|
|
39
41
|
version Snapshot top-level YAML as the next version-*/diff.yaml
|
|
40
42
|
--cwd <dir> Consumer project directory (default: .)
|
|
@@ -54,6 +56,7 @@ const parseArgs = (argv, cwd = ".") => {
|
|
|
54
56
|
output: "generated",
|
|
55
57
|
migrations: true,
|
|
56
58
|
migrate: true,
|
|
59
|
+
storedProcedures: false,
|
|
57
60
|
migrationsRoot: "sql",
|
|
58
61
|
migrationsPath: ""
|
|
59
62
|
};
|
|
@@ -76,6 +79,7 @@ const parseArgs = (argv, cwd = ".") => {
|
|
|
76
79
|
else if (arg.startsWith("--output=")) generate.output = arg.slice("--output=".length);
|
|
77
80
|
else if (arg === "--no-migrations") generate.migrations = false;
|
|
78
81
|
else if (arg === "--no-migrate") generate.migrate = false;
|
|
82
|
+
else if (arg === "--stored-procedures") generate.storedProcedures = true;
|
|
79
83
|
else if (arg === "--migrations-root") generate.migrationsRoot = take(argv, ++i, "--migrations-root");
|
|
80
84
|
else if (arg.startsWith("--migrations-root=")) generate.migrationsRoot = arg.slice("--migrations-root=".length);
|
|
81
85
|
else if (arg === "--migrations-path") generate.migrationsPath = take(argv, ++i, "--migrations-path");
|
|
@@ -88,7 +92,7 @@ const parseArgs = (argv, cwd = ".") => {
|
|
|
88
92
|
};
|
|
89
93
|
const commandOf = (verb, names, generate) => {
|
|
90
94
|
if (verb === void 0 || verb === "help") return { kind: "help" };
|
|
91
|
-
if (verb === "setup") return { kind: "setup" };
|
|
95
|
+
if (verb === "setup") return { kind: "setup", providers: names };
|
|
92
96
|
if (verb === "add") return { kind: "add", providers: names };
|
|
93
97
|
if (verb === "remove") return { kind: "remove", providers: names };
|
|
94
98
|
if (verb === "generate") return generateCommand(names, generate);
|
|
@@ -105,6 +109,7 @@ ${help}`);
|
|
|
105
109
|
kind: "migrations-create",
|
|
106
110
|
provider: generate.provider,
|
|
107
111
|
input: generate.input,
|
|
112
|
+
storedProcedures: generate.storedProcedures,
|
|
108
113
|
migrationsRoot: generate.migrationsRoot,
|
|
109
114
|
...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
|
|
110
115
|
};
|
|
@@ -130,6 +135,7 @@ ${help}`);
|
|
|
130
135
|
output: generate.output,
|
|
131
136
|
migrations: generate.migrations,
|
|
132
137
|
migrate: generate.migrate,
|
|
138
|
+
storedProcedures: generate.storedProcedures,
|
|
133
139
|
migrationsRoot: generate.migrationsRoot,
|
|
134
140
|
...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
|
|
135
141
|
};
|
package/dist/run.js
CHANGED
|
@@ -3,12 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { catalog, requireProviders } from "./catalog.js";
|
|
4
4
|
import { addCommands, removeCommands } from "./commands.js";
|
|
5
5
|
import { csharpTarget, detect, resolveEcosystem } from "./detect.js";
|
|
6
|
-
import {
|
|
7
|
-
answersFromEnv,
|
|
8
|
-
parseEnv,
|
|
9
|
-
serializeEnv,
|
|
10
|
-
updateEnvProviders
|
|
11
|
-
} from "./env.js";
|
|
6
|
+
import { parseEnv, serializeEnv, updateEnvProviders } from "./env.js";
|
|
12
7
|
import { defaultDatasourceYaml } from "./generate/loadSpec.js";
|
|
13
8
|
import {
|
|
14
9
|
cargoProviders,
|
|
@@ -21,7 +16,7 @@ import { applyMigrateUp } from "./generate/migrateUp.js";
|
|
|
21
16
|
import { migrateInitCommands, migrateLanguage, sqlMigrateProviders } from "./migrateInit.js";
|
|
22
17
|
import { help, parseArgs } from "./parse.js";
|
|
23
18
|
import { applyVersion } from "./version/applyVersion.js";
|
|
24
|
-
import { envFromAnswers, promptSetup, starterTypesYaml } from "./setup.js";
|
|
19
|
+
import { answersForSetup, envFromAnswers, promptSetup, starterTypesYaml } from "./setup.js";
|
|
25
20
|
import { wireRegister } from "./wireRegister.js";
|
|
26
21
|
const run = async (argv, deps, cwd = ".") => {
|
|
27
22
|
try {
|
|
@@ -35,7 +30,7 @@ const run = async (argv, deps, cwd = ".") => {
|
|
|
35
30
|
};
|
|
36
31
|
const dispatch = async (command, flags, deps) => {
|
|
37
32
|
if (command.kind === "help") return deps.log(help.trimEnd());
|
|
38
|
-
if (command.kind === "setup") return applySetup(flags, deps);
|
|
33
|
+
if (command.kind === "setup") return applySetup(flags, deps, command.providers);
|
|
39
34
|
if (command.kind === "add") return applyAddRemove(flags, deps, requireProviders(needNames(command.providers, "add")), []);
|
|
40
35
|
if (command.kind === "remove") return applyAddRemove(flags, deps, [], requireProviders(needNames(command.providers, "remove")));
|
|
41
36
|
if (command.kind === "version") return applyVersion(command, flags, deps);
|
|
@@ -48,6 +43,7 @@ const dispatch = async (command, flags, deps) => {
|
|
|
48
43
|
kind: "migrations-create",
|
|
49
44
|
provider: command.provider,
|
|
50
45
|
input: command.input,
|
|
46
|
+
storedProcedures: command.storedProcedures,
|
|
51
47
|
migrationsRoot: command.migrationsRoot,
|
|
52
48
|
...command.migrationsPath === void 0 ? {} : { migrationsPath: command.migrationsPath }
|
|
53
49
|
},
|
|
@@ -59,7 +55,7 @@ const dispatch = async (command, flags, deps) => {
|
|
|
59
55
|
const needNames = (names, verb) => names.length ? names : (() => {
|
|
60
56
|
throw new Error(`Usage: datasources ${verb} <provider...>`);
|
|
61
57
|
})();
|
|
62
|
-
const applySetup = async (flags, deps) => {
|
|
58
|
+
const applySetup = async (flags, deps, requested) => {
|
|
63
59
|
const entries = await deps.fs.readdir(flags.cwd);
|
|
64
60
|
const found = detect(entries);
|
|
65
61
|
const existing = await readEnv(deps.fs, flags.cwd);
|
|
@@ -67,7 +63,7 @@ const applySetup = async (flags, deps) => {
|
|
|
67
63
|
const typesPath = join(specDir, "types.yaml");
|
|
68
64
|
const datasourcePath = join(specDir, "datasource.yaml");
|
|
69
65
|
const missingSpec = await optional(deps.fs, typesPath) === void 0;
|
|
70
|
-
const answers = flags.yes ? { ...
|
|
66
|
+
const answers = flags.yes ? { ...answersForSetup(existing, requested), ecosystem: flags.ecosystem, scaffold: false } : await promptSetup(deps.prompt, existing, found, flags.ecosystem, missingSpec, requested);
|
|
71
67
|
const ecosystem = resolveEcosystem(found, answers.ecosystem ?? flags.ecosystem);
|
|
72
68
|
const selected = requireProviders(answers.providers);
|
|
73
69
|
const installed = await readInstalled(deps.fs, flags.cwd, ecosystem, entries);
|
package/dist/setup.js
CHANGED
|
@@ -14,8 +14,9 @@ types:
|
|
|
14
14
|
type: string
|
|
15
15
|
size: 128
|
|
16
16
|
`;
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const answersForSetup = (env, requested = []) => requested.length ? answersFromEnv({ ...env, INSTALLED_PROVIDERS: requested.join(",") }) : answersFromEnv(env);
|
|
18
|
+
const promptSetup = async (prompt, env, found, explicit, askScaffold = false, requested = []) => {
|
|
19
|
+
const defaults = answersForSetup(env, requested);
|
|
19
20
|
const ecosystem = explicit ?? (found.length === 1 ? found[0] : found.length === 0 ? void 0 : ecosystemOr(await prompt.select({
|
|
20
21
|
message: "Project type",
|
|
21
22
|
choices: found.map((value) => ({ name: value, value })),
|
|
@@ -48,6 +49,7 @@ const envFromAnswers = (answers) => {
|
|
|
48
49
|
};
|
|
49
50
|
};
|
|
50
51
|
export {
|
|
52
|
+
answersForSetup,
|
|
51
53
|
envFromAnswers,
|
|
52
54
|
promptSetup,
|
|
53
55
|
starterTypesYaml
|
package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { NestedSelectBuilder } from "@deterministic-code/datasources";
|
|
2
|
+
import type { NestedSelectType } from "@deterministic-code/datasources";
|
|
3
|
+
import type { Type } from "@deterministic-code/deterministic-generators-common/parser";
|
|
4
|
+
export declare const nestedSelectFromSpec: (types: readonly Type[], root: string) => NestedSelectType;
|
|
5
|
+
export { NestedSelectBuilder };
|
package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ChangeCasingMapper, NestedSelectBuilder, PluralizerMapper } from "@deterministic-code/datasources";
|
|
2
|
+
const pascal = new ChangeCasingMapper("snakeCase", "pascalCase");
|
|
3
|
+
const plural = new PluralizerMapper();
|
|
4
|
+
export const nestedSelectFromSpec = (types, root) => {
|
|
5
|
+
const tagged = types.some((item) => item.tags?.includes("datasource_type"));
|
|
6
|
+
const catalog = new Map(types
|
|
7
|
+
.filter((item) => !tagged || item.tags?.includes("datasource_type"))
|
|
8
|
+
.map((item) => [item.name, item]));
|
|
9
|
+
const name = catalog.has(root)
|
|
10
|
+
? root
|
|
11
|
+
: [...catalog.keys()].find((item) => entity(item) === root);
|
|
12
|
+
if (name === undefined)
|
|
13
|
+
throw new Error(`Unknown nested select type "${root}"`);
|
|
14
|
+
return build(catalog.get(name), catalog, new Set());
|
|
15
|
+
};
|
|
16
|
+
export { NestedSelectBuilder };
|
|
17
|
+
const build = (type, catalog, ancestors) => {
|
|
18
|
+
const next = new Set(ancestors);
|
|
19
|
+
next.add(type.name);
|
|
20
|
+
const key = entity(type.fields.find((field) => field.isId)?.name ?? type.ids?.[0] ?? "id");
|
|
21
|
+
const properties = type.fields
|
|
22
|
+
.filter((field) => field.references === undefined)
|
|
23
|
+
.map((field) => entity(field.name));
|
|
24
|
+
const objects = type.fields.flatMap((field) => {
|
|
25
|
+
const [table, column] = target(field);
|
|
26
|
+
if (!table || !catalog.has(table) || next.has(table))
|
|
27
|
+
return [];
|
|
28
|
+
return [{
|
|
29
|
+
name: relationName(field.name, table),
|
|
30
|
+
kind: "object",
|
|
31
|
+
from: entity(field.name),
|
|
32
|
+
to: entity(column),
|
|
33
|
+
target: build(catalog.get(table), catalog, next),
|
|
34
|
+
}];
|
|
35
|
+
});
|
|
36
|
+
const lists = [...catalog.values()].flatMap((child) => {
|
|
37
|
+
if (next.has(child.name))
|
|
38
|
+
return [];
|
|
39
|
+
return child.fields.flatMap((field) => {
|
|
40
|
+
const [table, column] = target(field);
|
|
41
|
+
if (table !== type.name || !column)
|
|
42
|
+
return [];
|
|
43
|
+
return [{
|
|
44
|
+
name: plural.map(entity(child.name)),
|
|
45
|
+
kind: "list",
|
|
46
|
+
from: key,
|
|
47
|
+
to: entity(field.name),
|
|
48
|
+
target: build(child, catalog, next),
|
|
49
|
+
}];
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return { name: entity(type.name), key, properties, relations: [...objects, ...lists] };
|
|
53
|
+
};
|
|
54
|
+
const target = (field) => {
|
|
55
|
+
if (Array.isArray(field.references) && field.references.length === 2)
|
|
56
|
+
return [field.references[0] ?? "", field.references[1] ?? ""];
|
|
57
|
+
const [table, column] = String(field.references ?? "").split(".");
|
|
58
|
+
return [table ?? "", column ?? ""];
|
|
59
|
+
};
|
|
60
|
+
const relationName = (field, table) => {
|
|
61
|
+
const name = entity(field);
|
|
62
|
+
return name.endsWith("Id") && name.length > 2 ? name.slice(0, -2) : entity(table);
|
|
63
|
+
};
|
|
64
|
+
const entity = (name) => pascal.map(name);
|