@deterministic-code/datasources-cli 0.1.13 → 0.1.15
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/commands.js +9 -4
- package/dist/detect.js +21 -7
- package/dist/env.js +7 -0
- package/dist/generate/CSharpRepositories.js +18 -14
- package/dist/generate/DeterministicProvider.js +25 -16
- package/dist/generate/RepositoryModel.js +10 -4
- package/dist/generate/RepositoryPlan.js +12 -5
- package/dist/generate/RustRepositories.js +42 -31
- package/dist/generate/TypeScriptRepositories.js +22 -18
- package/dist/generate/applyGenerate.js +11 -2
- package/dist/generate/applyMigrations.js +14 -17
- package/dist/generate/casing.js +28 -0
- package/dist/generate/destEnv.js +12 -0
- package/dist/generate/migrateUp.js +8 -41
- package/dist/generate/sqlDialects.js +29 -0
- package/dist/generate/sqlFlags.js +27 -0
- package/dist/migrateInit.js +4 -3
- package/dist/parse.js +102 -25
- package/dist/run.js +42 -67
- package/dist/setup.js +7 -9
- package/dist/version/applyVersion.js +11 -14
- package/dist/version/delta.js +0 -50
- package/dist/version/render.js +1 -11
- package/dist/wireRegister.js +29 -11
- package/dist/wireRustProviders.js +42 -0
- package/node_modules/@deterministic-code/csharp-generators/dist/import-generator.js +16 -12
- package/node_modules/@deterministic-code/csharp-generators/src/import-generator.test.ts +22 -11
- package/node_modules/@deterministic-code/csharp-generators/src/import-generator.ts +21 -13
- package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +2 -6
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.d.ts +2 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.js +37 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.d.ts +2 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.js +6 -0
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.d.ts +1 -1
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.js +3 -6
- package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/version-migrations.js +25 -14
- package/node_modules/@deterministic-code/datasources-code-generators/package.json +10 -8
- package/node_modules/@deterministic-code/deterministic-generators-common/README.md +61 -1
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +128 -1
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification-parser.ts +1 -0
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/version-snapshot.ts +154 -0
- package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/yaml-node.ts +2 -2
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/README.md +61 -1
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/specification-parser.ts +1 -0
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/version-snapshot.ts +154 -0
- package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/yaml-node.ts +2 -2
- package/node_modules/@deterministic-code/rust-generators/dist/import-generator.js +25 -15
- package/node_modules/@deterministic-code/rust-generators/src/import-generator.test.ts +11 -0
- package/node_modules/@deterministic-code/rust-generators/src/import-generator.ts +36 -18
- package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-types.js +6 -1
- package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-validators.js +6 -1
- package/node_modules/@deterministic-code/typescript-generators/dist/generate-routes.js +1 -1
- package/node_modules/@deterministic-code/typescript-generators/dist/generate-services.js +1 -1
- package/node_modules/@deterministic-code/typescript-generators/dist/import-generator.js +29 -21
- package/node_modules/@deterministic-code/typescript-generators/src/emit.ts +2 -5
- package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-types.ts +7 -1
- package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-validators.ts +7 -1
- package/node_modules/@deterministic-code/typescript-generators/src/generate-routes.ts +1 -1
- package/node_modules/@deterministic-code/typescript-generators/src/generate-services.ts +1 -1
- package/node_modules/@deterministic-code/typescript-generators/src/import-generator.test.ts +16 -0
- package/node_modules/@deterministic-code/typescript-generators/src/import-generator.ts +37 -22
- package/package.json +8 -8
package/dist/setup.js
CHANGED
|
@@ -15,16 +15,15 @@ types:
|
|
|
15
15
|
size: 128
|
|
16
16
|
`;
|
|
17
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 = []) => {
|
|
18
|
+
const promptSetup = async (prompt, env, found, explicit = [], askScaffold = false, requested = []) => {
|
|
19
19
|
const defaults = answersForSetup(env, requested);
|
|
20
|
-
const
|
|
21
|
-
message: "
|
|
22
|
-
choices: found.map((value) => ({ name: value, value }))
|
|
23
|
-
|
|
24
|
-
})));
|
|
20
|
+
const picked = explicit.length ? explicit : found.length === 1 ? found : found.length === 0 ? [] : (await prompt.checkbox({
|
|
21
|
+
message: "Languages",
|
|
22
|
+
choices: found.map((value) => ({ name: value, value, checked: true }))
|
|
23
|
+
})).filter(isEcosystem);
|
|
25
24
|
const fromUrl = scheme(defaults.url);
|
|
26
25
|
const selected = await prompt.checkbox({
|
|
27
|
-
message: "
|
|
26
|
+
message: "Datasources",
|
|
28
27
|
choices: providers.map((value) => ({
|
|
29
28
|
name: value,
|
|
30
29
|
value,
|
|
@@ -37,9 +36,8 @@ const promptSetup = async (prompt, env, found, explicit, askScaffold = false, re
|
|
|
37
36
|
});
|
|
38
37
|
const schema = await prompt.input({ message: "schema", default: defaults.schema });
|
|
39
38
|
const scaffold = askScaffold ? await prompt.confirm({ message: "Create deterministic directory", default: true }) : false;
|
|
40
|
-
return {
|
|
39
|
+
return { languages: picked.length ? picked : void 0, providers: selected, url, schema, scaffold };
|
|
41
40
|
};
|
|
42
|
-
const ecosystemOr = (value) => isEcosystem(value) ? value : void 0;
|
|
43
41
|
const envFromAnswers = (answers) => {
|
|
44
42
|
const provider = scheme(answers.url) ?? answers.providers[0] ?? "sqlite";
|
|
45
43
|
return {
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { isAbsolute, join } from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { VersionSnapshot } from "@deterministic-code/deterministic-generators-common/parser";
|
|
3
|
+
import { parseYaml, render } from "./render.js";
|
|
3
4
|
const versionDir = /^version-\d{8}T\d{6}Z-(\d{3})$/;
|
|
4
5
|
const at = (root, path) => isAbsolute(path) ? path : join(root, path);
|
|
5
6
|
const applyVersion = async (command, flags, deps, now = () => /* @__PURE__ */ new Date()) => {
|
|
6
7
|
const root = at(flags.cwd, command.input);
|
|
7
8
|
const names = await deps.fs.readdir(root);
|
|
8
9
|
const versions = names.filter((name) => versionDir.test(name)).sort();
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const diffs = (await Promise.all(
|
|
11
|
+
versions.map(async (dir) => {
|
|
12
|
+
const listed = await deps.fs.readdir(join(root, dir));
|
|
13
|
+
if (!listed.includes("diff.yaml")) return void 0;
|
|
14
|
+
const text = await deps.fs.readFile(join(root, dir, "diff.yaml"));
|
|
15
|
+
return text === "" ? void 0 : parseYaml(text);
|
|
16
|
+
})
|
|
17
|
+
)).filter((diff) => diff !== void 0);
|
|
18
|
+
const before = VersionSnapshot.combine(diffs).documents;
|
|
15
19
|
const current = names.filter((name) => name.endsWith(".yaml")).sort();
|
|
16
20
|
const paths = [.../* @__PURE__ */ new Set([...Object.keys(before), ...current])];
|
|
17
21
|
const yaml = render(
|
|
@@ -39,13 +43,6 @@ const nextSeq = (versions) => {
|
|
|
39
43
|
};
|
|
40
44
|
const pad = (n) => String(n).padStart(3, "0");
|
|
41
45
|
const stamp = (date) => date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
|
|
42
|
-
const optional = async (fs, path) => {
|
|
43
|
-
try {
|
|
44
|
-
return await fs.readFile(path);
|
|
45
|
-
} catch {
|
|
46
|
-
return void 0;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
46
|
export {
|
|
50
47
|
applyVersion
|
|
51
48
|
};
|
package/dist/version/delta.js
CHANGED
|
@@ -4,20 +4,6 @@ const delta = (before, after) => {
|
|
|
4
4
|
const value = build(before, after);
|
|
5
5
|
return value === unchanged ? void 0 : value;
|
|
6
6
|
};
|
|
7
|
-
const apply = (before, next) => {
|
|
8
|
-
if (next == null) return before;
|
|
9
|
-
if (isKeyedCollection(before) && isKeyedSplit(next)) return applyKeyed(before, next);
|
|
10
|
-
if (isObject(before) && isObject(next)) return applyObject(before, next);
|
|
11
|
-
return next;
|
|
12
|
-
};
|
|
13
|
-
const applyFile = (before, envelope) => {
|
|
14
|
-
if (envelope == null) return before;
|
|
15
|
-
if (isObject(envelope)) {
|
|
16
|
-
if (envelope.removed === true && Object.keys(envelope).length === 1) return void 0;
|
|
17
|
-
if (envelope.added === true) return envelope.content;
|
|
18
|
-
}
|
|
19
|
-
return apply(before ?? null, envelope);
|
|
20
|
-
};
|
|
21
7
|
const build = (before, after) => {
|
|
22
8
|
if (isDeepStrictEqual(before, after)) return unchanged;
|
|
23
9
|
if (isKeyedCollection(before) && isKeyedCollection(after)) return keyedSplit(before, after);
|
|
@@ -60,40 +46,6 @@ const objectDelta = (beforeObj, afterObj) => {
|
|
|
60
46
|
if (removed.length) result.removed = removed;
|
|
61
47
|
return Object.keys(result).length === 0 ? unchanged : result;
|
|
62
48
|
};
|
|
63
|
-
const applyKeyed = (beforeVal, split) => {
|
|
64
|
-
const removed = new Set(split.removed ?? []);
|
|
65
|
-
const changed = /* @__PURE__ */ new Map();
|
|
66
|
-
for (const entry of split.changed ?? []) {
|
|
67
|
-
const key = Object.keys(entry)[0];
|
|
68
|
-
changed.set(key, entry[key]);
|
|
69
|
-
}
|
|
70
|
-
return [
|
|
71
|
-
...beforeVal.flatMap((entry) => {
|
|
72
|
-
const key = Object.keys(entry)[0];
|
|
73
|
-
if (removed.has(key)) return [];
|
|
74
|
-
return changed.has(key) ? [{ [key]: apply(entry[key], changed.get(key)) }] : [entry];
|
|
75
|
-
}),
|
|
76
|
-
...split.added ?? []
|
|
77
|
-
];
|
|
78
|
-
};
|
|
79
|
-
const applyObject = (beforeVal, next) => {
|
|
80
|
-
const removed = new Set(Array.isArray(next.removed) ? next.removed : []);
|
|
81
|
-
const out = {};
|
|
82
|
-
for (const key of Object.keys(beforeVal)) {
|
|
83
|
-
if (removed.has(key)) continue;
|
|
84
|
-
out[key] = key in next ? apply(beforeVal[key], next[key]) : beforeVal[key];
|
|
85
|
-
}
|
|
86
|
-
for (const key of Object.keys(next)) {
|
|
87
|
-
if (key === "removed" && Array.isArray(next.removed)) continue;
|
|
88
|
-
if (!(key in beforeVal)) out[key] = next[key];
|
|
89
|
-
}
|
|
90
|
-
return out;
|
|
91
|
-
};
|
|
92
|
-
const isKeyedSplit = (value) => {
|
|
93
|
-
if (!isObject(value)) return false;
|
|
94
|
-
const allowed = /* @__PURE__ */ new Set(["added", "changed", "removed"]);
|
|
95
|
-
return Object.keys(value).every((key) => allowed.has(key));
|
|
96
|
-
};
|
|
97
49
|
const isObject = (value) => value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
98
50
|
const isKeyedCollection = (value) => Array.isArray(value) && value.length > 0 && value.every(
|
|
99
51
|
(item) => item !== null && typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 1
|
|
@@ -107,8 +59,6 @@ const keyedMap = (arr) => {
|
|
|
107
59
|
return map;
|
|
108
60
|
};
|
|
109
61
|
export {
|
|
110
|
-
apply,
|
|
111
|
-
applyFile,
|
|
112
62
|
delta,
|
|
113
63
|
isObject
|
|
114
64
|
};
|
package/dist/version/render.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse, stringify } from "yaml";
|
|
2
|
-
import {
|
|
2
|
+
import { delta, isObject } from "./delta.js";
|
|
3
3
|
const render = (files) => {
|
|
4
4
|
const out = {};
|
|
5
5
|
for (const { path, before, after } of files) {
|
|
@@ -18,20 +18,10 @@ const render = (files) => {
|
|
|
18
18
|
return Object.keys(out).length === 0 ? "" : stringify(out);
|
|
19
19
|
};
|
|
20
20
|
const parseYaml = (text) => parse(text);
|
|
21
|
-
const applyDocument = (before, doc) => {
|
|
22
|
-
const out = { ...before };
|
|
23
|
-
for (const [path, envelope] of Object.entries(doc)) {
|
|
24
|
-
const next = applyFile(out[path], envelope);
|
|
25
|
-
if (next === void 0) delete out[path];
|
|
26
|
-
else out[path] = next;
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
};
|
|
30
21
|
const asDocument = (value, source) => isObject(value) ? value : (() => {
|
|
31
22
|
throw new Error(`${source}: expected a mapping`);
|
|
32
23
|
})();
|
|
33
24
|
export {
|
|
34
|
-
applyDocument,
|
|
35
25
|
asDocument,
|
|
36
26
|
parseYaml,
|
|
37
27
|
render
|
package/dist/wireRegister.js
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
2
|
+
import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
|
|
2
3
|
import { csharpTarget } from "./detect.js";
|
|
4
|
+
import { repositoriesPrefix } from "./generate/casing.js";
|
|
5
|
+
import { rustIdent } from "./generate/RepositoryModel.js";
|
|
3
6
|
const posix = (path) => path.split(sep).join("/");
|
|
4
7
|
const specFrom = (from, to) => {
|
|
5
8
|
const rel = posix(relative(dirname(from), to));
|
|
6
9
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
7
10
|
};
|
|
8
11
|
const hookPath = (cwd, dialect, src) => join(cwd, src ? "src" : ".", dialect === "typescript" ? "datasources-register.ts" : "datasources_register.rs");
|
|
9
|
-
const
|
|
10
|
-
|
|
12
|
+
const registryRel = (dialect, settings) => {
|
|
13
|
+
const casing = createCasingStrategy(dialect, settings);
|
|
14
|
+
const dir = repositoriesPrefix(dialect, casing);
|
|
15
|
+
return dialect === "typescript" ? `${dir}/${casing.convertFileName("repository_registry")}.ts` : `${dir}/mod.rs`;
|
|
16
|
+
};
|
|
17
|
+
const hookContents = (dialect, hook, output, settings = {}) => {
|
|
18
|
+
const casing = createCasingStrategy(dialect, settings);
|
|
19
|
+
return dialect === "typescript" ? `import "${specFrom(hook, join(output, registryRel(dialect, settings)))}";
|
|
20
|
+
` : `#[path = "${specFrom(hook, join(output, registryRel(dialect, settings)))}"]
|
|
11
21
|
pub mod repositories;
|
|
12
|
-
pub use repositories
|
|
22
|
+
pub use repositories::${rustIdent(casing.convertFileName("repository_registry"))}::Repositories;
|
|
13
23
|
`;
|
|
24
|
+
};
|
|
14
25
|
const cargoWithCtor = (cargo) => /^\s*ctor\s*=/m.test(cargo) ? void 0 : cargo.includes("[dependencies]") ? cargo.replace("[dependencies]", `[dependencies]
|
|
15
26
|
ctor = "0.4"`) : `${cargo.trimEnd()}
|
|
16
27
|
|
|
@@ -30,7 +41,7 @@ const optional = async (fs, path) => {
|
|
|
30
41
|
}
|
|
31
42
|
};
|
|
32
43
|
const outputRoot = (cwd, output) => isAbsolute(output) ? output : join(cwd, output);
|
|
33
|
-
const registryPath = (root, dialect) =>
|
|
44
|
+
const registryPath = (root, dialect, settings) => join(root, registryRel(dialect, settings));
|
|
34
45
|
const firstFile = async (fs, cwd, names) => {
|
|
35
46
|
for (const name of names) {
|
|
36
47
|
const path = join(cwd, name);
|
|
@@ -39,6 +50,7 @@ const firstFile = async (fs, cwd, names) => {
|
|
|
39
50
|
}
|
|
40
51
|
return void 0;
|
|
41
52
|
};
|
|
53
|
+
const excludeNodeModules = " <PropertyGroup>\n <DefaultItemExcludes>$(DefaultItemExcludes);node_modules/**</DefaultItemExcludes>\n </PropertyGroup>\n";
|
|
42
54
|
const wireCsharp = async (cwd, output, dryRun, deps) => {
|
|
43
55
|
const entries = await deps.fs.readdir(cwd).catch(() => []);
|
|
44
56
|
const target = csharpTarget(entries);
|
|
@@ -46,28 +58,34 @@ const wireCsharp = async (cwd, output, dryRun, deps) => {
|
|
|
46
58
|
const csprojPath = join(cwd, target);
|
|
47
59
|
const generated = join(outputRoot(cwd, output), "csharp");
|
|
48
60
|
const rel = posix(relative(dirname(csprojPath), generated));
|
|
49
|
-
if (!rel.startsWith("..")) return;
|
|
50
61
|
const include = `${rel}/**/*.cs`;
|
|
51
62
|
const existing = await optional(deps.fs, csprojPath);
|
|
52
|
-
if (existing === void 0
|
|
63
|
+
if (existing === void 0) return;
|
|
64
|
+
const needExclude = !existing.includes("node_modules/**");
|
|
65
|
+
const needInclude = rel.startsWith("..") && !existing.includes(include);
|
|
66
|
+
if (!needExclude && !needInclude) return;
|
|
53
67
|
if (dryRun) {
|
|
54
68
|
deps.log(csprojPath);
|
|
55
69
|
return;
|
|
56
70
|
}
|
|
57
|
-
|
|
71
|
+
const patched = [
|
|
72
|
+
needExclude ? excludeNodeModules : "",
|
|
73
|
+
needInclude ? ` <ItemGroup>
|
|
58
74
|
<Compile Include="${include}" />
|
|
59
75
|
</ItemGroup>
|
|
60
|
-
|
|
76
|
+
` : ""
|
|
77
|
+
].join("");
|
|
78
|
+
await deps.fs.writeFile(csprojPath, existing.replace("</Project>", `${patched}</Project>`));
|
|
61
79
|
deps.log(csprojPath);
|
|
62
80
|
};
|
|
63
|
-
const wireRegister = async (cwd, output, dialect, dryRun, deps) => {
|
|
81
|
+
const wireRegister = async (cwd, output, dialect, dryRun, deps, settings = {}) => {
|
|
64
82
|
if (dialect === "csharp") return wireCsharp(cwd, output, dryRun, deps);
|
|
65
83
|
const root = outputRoot(cwd, output);
|
|
66
84
|
if (relative(cwd, root).startsWith("..")) return;
|
|
67
|
-
if (await optional(deps.fs, registryPath(root, dialect)) === void 0) return;
|
|
85
|
+
if (await optional(deps.fs, registryPath(root, dialect, settings)) === void 0) return;
|
|
68
86
|
const entries = await deps.fs.readdir(cwd).catch(() => []);
|
|
69
87
|
const hook = hookPath(cwd, dialect, entries.includes("src"));
|
|
70
|
-
const text = hookContents(dialect, hook, root);
|
|
88
|
+
const text = hookContents(dialect, hook, root, settings);
|
|
71
89
|
if (dryRun) {
|
|
72
90
|
deps.log(hook);
|
|
73
91
|
return;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { catalog } from "./catalog.js";
|
|
3
|
+
const optional = async (fs, path) => {
|
|
4
|
+
try {
|
|
5
|
+
return await fs.readFile(path);
|
|
6
|
+
} catch {
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
const firstFile = async (fs, cwd, names) => {
|
|
11
|
+
for (const name of names) {
|
|
12
|
+
const path = join(cwd, name);
|
|
13
|
+
const text = await optional(fs, path);
|
|
14
|
+
if (text !== void 0) return { path, text };
|
|
15
|
+
}
|
|
16
|
+
return void 0;
|
|
17
|
+
};
|
|
18
|
+
const rustBindContents = (names) => names.map((name) => `use ${catalog[name].cargo.replaceAll("-", "_")} as _;
|
|
19
|
+
`).join("");
|
|
20
|
+
const attachedProviders = (entry) => entry.includes("datasources_providers") ? void 0 : `mod datasources_providers;
|
|
21
|
+
${entry}`;
|
|
22
|
+
const wireRustProviders = async (cwd, names, dryRun, deps) => {
|
|
23
|
+
const entry = await firstFile(deps.fs, cwd, ["src/lib.rs", "src/main.rs", "lib.rs", "main.rs"]);
|
|
24
|
+
const bind = join(entry ? dirname(entry.path) : cwd, "datasources_providers.rs");
|
|
25
|
+
const text = rustBindContents(names);
|
|
26
|
+
if (dryRun) {
|
|
27
|
+
deps.log(bind);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await deps.fs.writeFile(bind, text);
|
|
31
|
+
deps.log(bind);
|
|
32
|
+
if (!entry) return;
|
|
33
|
+
const next = attachedProviders(entry.text);
|
|
34
|
+
if (next === void 0) return;
|
|
35
|
+
await deps.fs.writeFile(entry.path, next);
|
|
36
|
+
deps.log(entry.path);
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
attachedProviders,
|
|
40
|
+
rustBindContents,
|
|
41
|
+
wireRustProviders
|
|
42
|
+
};
|
|
@@ -10,10 +10,6 @@ const pluralSnake = (entity) => {
|
|
|
10
10
|
parts[parts.length - 1] = pluralize.plural(parts[parts.length - 1]);
|
|
11
11
|
return parts.join("_");
|
|
12
12
|
};
|
|
13
|
-
const DATASOURCE_DIR = "Types/Datasource/Generated";
|
|
14
|
-
const VIEW_DIR = "Types/View/Generated";
|
|
15
|
-
const DATASOURCE_VALIDATOR_DIR = `${DATASOURCE_DIR}/Validators`;
|
|
16
|
-
const VIEW_VALIDATOR_DIR = `${VIEW_DIR}/Validators`;
|
|
17
13
|
class CsharpImportGenerator {
|
|
18
14
|
organizeByFeature;
|
|
19
15
|
flat;
|
|
@@ -26,7 +22,7 @@ class CsharpImportGenerator {
|
|
|
26
22
|
this.casing = createCasing(settings);
|
|
27
23
|
}
|
|
28
24
|
datasource(entity) {
|
|
29
|
-
return this.underBase(this.rel(
|
|
25
|
+
return this.underBase(this.rel(this.layered("types/datasource/generated"), this.casedFile(entity)));
|
|
30
26
|
}
|
|
31
27
|
datasourceRel(entity) {
|
|
32
28
|
return this.datasource(entity);
|
|
@@ -37,7 +33,7 @@ class CsharpImportGenerator {
|
|
|
37
33
|
datasourceValidator(entity) {
|
|
38
34
|
return this.underBase(
|
|
39
35
|
this.rel(
|
|
40
|
-
|
|
36
|
+
this.layered("types/datasource/generated/validators"),
|
|
41
37
|
this.csFile(`datasource_${entity}_validator`)
|
|
42
38
|
)
|
|
43
39
|
);
|
|
@@ -46,7 +42,7 @@ class CsharpImportGenerator {
|
|
|
46
42
|
return this.datasourceValidator(entity);
|
|
47
43
|
}
|
|
48
44
|
view(entity) {
|
|
49
|
-
return this.underBase(this.rel(
|
|
45
|
+
return this.underBase(this.rel(this.layered("types/view/generated"), this.csFile(entity)));
|
|
50
46
|
}
|
|
51
47
|
viewRel(entity) {
|
|
52
48
|
return this.view(entity);
|
|
@@ -56,7 +52,7 @@ class CsharpImportGenerator {
|
|
|
56
52
|
}
|
|
57
53
|
viewValidator(entity) {
|
|
58
54
|
return this.underBase(
|
|
59
|
-
this.rel(
|
|
55
|
+
this.rel(this.layered("types/view/generated/validators"), this.csFile(`${entity}_validator`))
|
|
60
56
|
);
|
|
61
57
|
}
|
|
62
58
|
viewValidatorRel(entity) {
|
|
@@ -71,12 +67,12 @@ class CsharpImportGenerator {
|
|
|
71
67
|
serviceCustom(name, _module) {
|
|
72
68
|
const file = this.csFile(name);
|
|
73
69
|
return this.underBase(
|
|
74
|
-
this.organizeByFeature ?
|
|
70
|
+
this.organizeByFeature ? `${this.featuresDir(featureEntity(name))}/custom/${file}` : `../custom/${file}`
|
|
75
71
|
);
|
|
76
72
|
}
|
|
77
73
|
serviceCustomRel(entity) {
|
|
78
74
|
const file = this.csFile(entity);
|
|
79
|
-
return this.organizeByFeature ?
|
|
75
|
+
return this.organizeByFeature ? `${this.featuresDir(entity)}/custom/${file}` : `../custom/${file}`;
|
|
80
76
|
}
|
|
81
77
|
serviceTest(entity) {
|
|
82
78
|
return this.underBase(this.csFile(`${entity}_service_tests`));
|
|
@@ -102,7 +98,7 @@ class CsharpImportGenerator {
|
|
|
102
98
|
routeCustom(name, _module) {
|
|
103
99
|
const file = this.csFile(`${name}_route`);
|
|
104
100
|
return this.underBase(
|
|
105
|
-
this.organizeByFeature ?
|
|
101
|
+
this.organizeByFeature ? `${this.featuresDir(featureEntity(name))}/custom/${file}` : `../custom/${file}`
|
|
106
102
|
);
|
|
107
103
|
}
|
|
108
104
|
routeTest(entity) {
|
|
@@ -149,7 +145,15 @@ class CsharpImportGenerator {
|
|
|
149
145
|
}
|
|
150
146
|
casedFile(entity) {
|
|
151
147
|
const file = this.csFile(entity);
|
|
152
|
-
return this.organizeByFeature ?
|
|
148
|
+
return this.organizeByFeature ? `${this.featuresDir(entity)}/${file}` : file;
|
|
149
|
+
}
|
|
150
|
+
layered(path) {
|
|
151
|
+
return path.split("/").map(
|
|
152
|
+
(segment) => segment === "custom" ? segment : this.casing.convertDirectories(segment)
|
|
153
|
+
).join("/");
|
|
154
|
+
}
|
|
155
|
+
featuresDir(entity) {
|
|
156
|
+
return `${this.casing.convertDirectories("features")}/${this.casing.directory(entity)}`;
|
|
153
157
|
}
|
|
154
158
|
rel(prefix, file) {
|
|
155
159
|
if (this.organizeByFeature || this.flat) return file;
|
|
@@ -136,6 +136,17 @@ describe("CsharpImportGenerator layered (organize_by_feature unset)", () => {
|
|
|
136
136
|
snake.datasource("notification_type"),
|
|
137
137
|
"Types/Datasource/Generated/notification_type.cs",
|
|
138
138
|
);
|
|
139
|
+
const kebabDirs = createImportGenerator(".", {
|
|
140
|
+
"languages.csharp.casing.directories": "Kebab",
|
|
141
|
+
});
|
|
142
|
+
assert.equal(
|
|
143
|
+
kebabDirs.datasource("user"),
|
|
144
|
+
"types/datasource/generated/User.cs",
|
|
145
|
+
);
|
|
146
|
+
assert.equal(
|
|
147
|
+
kebabDirs.view("user"),
|
|
148
|
+
"types/view/generated/User.cs",
|
|
149
|
+
);
|
|
139
150
|
});
|
|
140
151
|
|
|
141
152
|
it("cases type quals from settings independently of files", () => {
|
|
@@ -173,11 +184,11 @@ describe("CsharpImportGenerator by-feature", () => {
|
|
|
173
184
|
});
|
|
174
185
|
assert.equal(
|
|
175
186
|
imports.datasource("notification_type"),
|
|
176
|
-
"
|
|
187
|
+
"features/notification-type/NotificationType.cs",
|
|
177
188
|
);
|
|
178
189
|
assert.equal(
|
|
179
190
|
imports.datasourceRel("notification_type"),
|
|
180
|
-
"
|
|
191
|
+
"features/notification-type/NotificationType.cs",
|
|
181
192
|
);
|
|
182
193
|
assert.equal(imports.view("user"), "User.cs");
|
|
183
194
|
assert.equal(imports.viewRel("user"), "User.cs");
|
|
@@ -200,31 +211,31 @@ describe("CsharpImportGenerator by-feature", () => {
|
|
|
200
211
|
});
|
|
201
212
|
assert.equal(
|
|
202
213
|
imports.serviceCustom("user"),
|
|
203
|
-
"
|
|
214
|
+
"features/user/custom/User.cs",
|
|
204
215
|
);
|
|
205
216
|
assert.equal(
|
|
206
217
|
imports.serviceCustom("create_card_payment"),
|
|
207
|
-
"
|
|
218
|
+
"features/card-payment/custom/CreateCardPayment.cs",
|
|
208
219
|
);
|
|
209
220
|
assert.equal(
|
|
210
221
|
imports.serviceCustom("update_card_payment"),
|
|
211
|
-
"
|
|
222
|
+
"features/card-payment/custom/UpdateCardPayment.cs",
|
|
212
223
|
);
|
|
213
224
|
assert.equal(
|
|
214
225
|
imports.serviceCustomRel("notification_type"),
|
|
215
|
-
"
|
|
226
|
+
"features/notification-type/custom/NotificationType.cs",
|
|
216
227
|
);
|
|
217
228
|
assert.equal(
|
|
218
229
|
imports.routeCustom("get_health"),
|
|
219
|
-
"
|
|
230
|
+
"features/get-health/custom/GetHealthRoute.cs",
|
|
220
231
|
);
|
|
221
232
|
assert.equal(
|
|
222
233
|
imports.routeCustom("create_card_payment"),
|
|
223
|
-
"
|
|
234
|
+
"features/card-payment/custom/CreateCardPaymentRoute.cs",
|
|
224
235
|
);
|
|
225
236
|
assert.equal(
|
|
226
237
|
imports.serviceCustom("user", "./ignored/module"),
|
|
227
|
-
"
|
|
238
|
+
"features/user/custom/User.cs",
|
|
228
239
|
);
|
|
229
240
|
});
|
|
230
241
|
|
|
@@ -235,11 +246,11 @@ describe("CsharpImportGenerator by-feature", () => {
|
|
|
235
246
|
});
|
|
236
247
|
assert.equal(
|
|
237
248
|
imports.datasource("notification_type"),
|
|
238
|
-
"
|
|
249
|
+
"features/notification_type/NotificationType.cs",
|
|
239
250
|
);
|
|
240
251
|
assert.equal(
|
|
241
252
|
imports.serviceCustomRel("notification_type"),
|
|
242
|
-
"
|
|
253
|
+
"features/notification_type/custom/NotificationType.cs",
|
|
243
254
|
);
|
|
244
255
|
});
|
|
245
256
|
});
|
|
@@ -15,11 +15,6 @@ const pluralSnake = (entity: string): string => {
|
|
|
15
15
|
return parts.join("_");
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
const DATASOURCE_DIR = "Types/Datasource/Generated";
|
|
19
|
-
const VIEW_DIR = "Types/View/Generated";
|
|
20
|
-
const DATASOURCE_VALIDATOR_DIR = `${DATASOURCE_DIR}/Validators`;
|
|
21
|
-
const VIEW_VALIDATOR_DIR = `${VIEW_DIR}/Validators`;
|
|
22
|
-
|
|
23
18
|
export class CsharpImportGenerator implements IImportGenerator {
|
|
24
19
|
private readonly organizeByFeature: boolean;
|
|
25
20
|
private readonly flat: boolean;
|
|
@@ -35,7 +30,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
datasource(entity: string): string {
|
|
38
|
-
return this.underBase(this.rel(
|
|
33
|
+
return this.underBase(this.rel(this.layered("types/datasource/generated"), this.casedFile(entity)));
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
datasourceRel(entity: string): string {
|
|
@@ -49,7 +44,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
49
44
|
datasourceValidator(entity: string): string {
|
|
50
45
|
return this.underBase(
|
|
51
46
|
this.rel(
|
|
52
|
-
|
|
47
|
+
this.layered("types/datasource/generated/validators"),
|
|
53
48
|
this.csFile(`datasource_${entity}_validator`),
|
|
54
49
|
),
|
|
55
50
|
);
|
|
@@ -60,7 +55,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
view(entity: string): string {
|
|
63
|
-
return this.underBase(this.rel(
|
|
58
|
+
return this.underBase(this.rel(this.layered("types/view/generated"), this.csFile(entity)));
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
viewRel(entity: string): string {
|
|
@@ -73,7 +68,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
73
68
|
|
|
74
69
|
viewValidator(entity: string): string {
|
|
75
70
|
return this.underBase(
|
|
76
|
-
this.rel(
|
|
71
|
+
this.rel(this.layered("types/view/generated/validators"), this.csFile(`${entity}_validator`)),
|
|
77
72
|
);
|
|
78
73
|
}
|
|
79
74
|
|
|
@@ -93,7 +88,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
93
88
|
const file = this.csFile(name);
|
|
94
89
|
return this.underBase(
|
|
95
90
|
this.organizeByFeature
|
|
96
|
-
?
|
|
91
|
+
? `${this.featuresDir(featureEntity(name))}/custom/${file}`
|
|
97
92
|
: `../custom/${file}`,
|
|
98
93
|
);
|
|
99
94
|
}
|
|
@@ -101,7 +96,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
101
96
|
serviceCustomRel(entity: string): string {
|
|
102
97
|
const file = this.csFile(entity);
|
|
103
98
|
return this.organizeByFeature
|
|
104
|
-
?
|
|
99
|
+
? `${this.featuresDir(entity)}/custom/${file}`
|
|
105
100
|
: `../custom/${file}`;
|
|
106
101
|
}
|
|
107
102
|
|
|
@@ -137,7 +132,7 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
137
132
|
const file = this.csFile(`${name}_route`);
|
|
138
133
|
return this.underBase(
|
|
139
134
|
this.organizeByFeature
|
|
140
|
-
?
|
|
135
|
+
? `${this.featuresDir(featureEntity(name))}/custom/${file}`
|
|
141
136
|
: `../custom/${file}`,
|
|
142
137
|
);
|
|
143
138
|
}
|
|
@@ -205,10 +200,23 @@ export class CsharpImportGenerator implements IImportGenerator {
|
|
|
205
200
|
private casedFile(entity: string): string {
|
|
206
201
|
const file = this.csFile(entity);
|
|
207
202
|
return this.organizeByFeature
|
|
208
|
-
?
|
|
203
|
+
? `${this.featuresDir(entity)}/${file}`
|
|
209
204
|
: file;
|
|
210
205
|
}
|
|
211
206
|
|
|
207
|
+
private layered(path: string): string {
|
|
208
|
+
return path
|
|
209
|
+
.split("/")
|
|
210
|
+
.map((segment) =>
|
|
211
|
+
segment === "custom" ? segment : this.casing.convertDirectories(segment),
|
|
212
|
+
)
|
|
213
|
+
.join("/");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private featuresDir(entity: string): string {
|
|
217
|
+
return `${this.casing.convertDirectories("features")}/${this.casing.directory(entity)}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
212
220
|
private rel(prefix: string, file: string): string {
|
|
213
221
|
if (this.organizeByFeature || this.flat) return file;
|
|
214
222
|
return `${prefix}/${file}`;
|
package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { referenceTarget } from "./sql/common/reference-target.js";
|
|
1
2
|
import { ChangeCasingMapper, NestedSelectBuilder, PluralizerMapper } from "@deterministic-code/datasources";
|
|
2
3
|
const pascal = new ChangeCasingMapper("snakeCase", "pascalCase");
|
|
3
4
|
const plural = new PluralizerMapper();
|
|
@@ -51,12 +52,7 @@ const build = (type, catalog, ancestors) => {
|
|
|
51
52
|
});
|
|
52
53
|
return { name: entity(type.name), key, properties, relations: [...objects, ...lists] };
|
|
53
54
|
};
|
|
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
|
-
};
|
|
55
|
+
const target = (field) => field.references === undefined ? ["", ""] : referenceTarget(field.references);
|
|
60
56
|
const relationName = (field, table) => {
|
|
61
57
|
const name = entity(field);
|
|
62
58
|
return name.endsWith("Id") && name.length > 2 ? name.slice(0, -2) : entity(table);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { referenceTarget } from "./reference-target.js";
|
|
2
|
+
const needsType = (field) => !field.isArray &&
|
|
3
|
+
!field.isMap &&
|
|
4
|
+
field.references !== undefined &&
|
|
5
|
+
field.type === "string" &&
|
|
6
|
+
field.size === undefined;
|
|
7
|
+
const ready = (field) => field.type !== "string" || field.size !== undefined;
|
|
8
|
+
const lookup = (types, field) => {
|
|
9
|
+
const [table, column] = referenceTarget(field.references);
|
|
10
|
+
return types.find((type) => type.name === table)?.fields.find((item) => item.name === column);
|
|
11
|
+
};
|
|
12
|
+
export const inferReferenceFieldTypes = (types) => {
|
|
13
|
+
const working = types.map((type) => ({ ...type, fields: type.fields.map((field) => ({ ...field })) }));
|
|
14
|
+
for (let pass = 0; pass < working.length; pass++) {
|
|
15
|
+
let changed = false;
|
|
16
|
+
for (const type of working) {
|
|
17
|
+
type.fields = type.fields.map((field) => {
|
|
18
|
+
if (!needsType(field))
|
|
19
|
+
return field;
|
|
20
|
+
const parent = lookup(working, field);
|
|
21
|
+
if (parent === undefined || !ready(parent))
|
|
22
|
+
return field;
|
|
23
|
+
changed = true;
|
|
24
|
+
return {
|
|
25
|
+
...field,
|
|
26
|
+
type: parent.type,
|
|
27
|
+
kind: parent.kind,
|
|
28
|
+
base: parent.base,
|
|
29
|
+
...(parent.size !== undefined ? { size: parent.size } : {}),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!changed)
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return working;
|
|
37
|
+
};
|