@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.
Files changed (62) hide show
  1. package/dist/commands.js +9 -4
  2. package/dist/detect.js +21 -7
  3. package/dist/env.js +7 -0
  4. package/dist/generate/CSharpRepositories.js +18 -14
  5. package/dist/generate/DeterministicProvider.js +25 -16
  6. package/dist/generate/RepositoryModel.js +10 -4
  7. package/dist/generate/RepositoryPlan.js +12 -5
  8. package/dist/generate/RustRepositories.js +42 -31
  9. package/dist/generate/TypeScriptRepositories.js +22 -18
  10. package/dist/generate/applyGenerate.js +11 -2
  11. package/dist/generate/applyMigrations.js +14 -17
  12. package/dist/generate/casing.js +28 -0
  13. package/dist/generate/destEnv.js +12 -0
  14. package/dist/generate/migrateUp.js +8 -41
  15. package/dist/generate/sqlDialects.js +29 -0
  16. package/dist/generate/sqlFlags.js +27 -0
  17. package/dist/migrateInit.js +4 -3
  18. package/dist/parse.js +102 -25
  19. package/dist/run.js +42 -67
  20. package/dist/setup.js +7 -9
  21. package/dist/version/applyVersion.js +11 -14
  22. package/dist/version/delta.js +0 -50
  23. package/dist/version/render.js +1 -11
  24. package/dist/wireRegister.js +29 -11
  25. package/dist/wireRustProviders.js +42 -0
  26. package/node_modules/@deterministic-code/csharp-generators/dist/import-generator.js +16 -12
  27. package/node_modules/@deterministic-code/csharp-generators/src/import-generator.test.ts +22 -11
  28. package/node_modules/@deterministic-code/csharp-generators/src/import-generator.ts +21 -13
  29. package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +2 -6
  30. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.d.ts +2 -0
  31. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.js +37 -0
  32. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.d.ts +2 -0
  33. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.js +6 -0
  34. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.d.ts +1 -1
  35. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.js +3 -6
  36. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/version-migrations.js +25 -14
  37. package/node_modules/@deterministic-code/datasources-code-generators/package.json +10 -8
  38. package/node_modules/@deterministic-code/deterministic-generators-common/README.md +61 -1
  39. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +128 -1
  40. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification-parser.ts +1 -0
  41. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/version-snapshot.ts +154 -0
  42. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/yaml-node.ts +2 -2
  43. package/node_modules/@deterministic-code/deterministic-specifications-typescript/README.md +61 -1
  44. package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/specification-parser.ts +1 -0
  45. package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/version-snapshot.ts +154 -0
  46. package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/yaml-node.ts +2 -2
  47. package/node_modules/@deterministic-code/rust-generators/dist/import-generator.js +25 -15
  48. package/node_modules/@deterministic-code/rust-generators/src/import-generator.test.ts +11 -0
  49. package/node_modules/@deterministic-code/rust-generators/src/import-generator.ts +36 -18
  50. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-types.js +6 -1
  51. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-validators.js +6 -1
  52. package/node_modules/@deterministic-code/typescript-generators/dist/generate-routes.js +1 -1
  53. package/node_modules/@deterministic-code/typescript-generators/dist/generate-services.js +1 -1
  54. package/node_modules/@deterministic-code/typescript-generators/dist/import-generator.js +29 -21
  55. package/node_modules/@deterministic-code/typescript-generators/src/emit.ts +2 -5
  56. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-types.ts +7 -1
  57. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-validators.ts +7 -1
  58. package/node_modules/@deterministic-code/typescript-generators/src/generate-routes.ts +1 -1
  59. package/node_modules/@deterministic-code/typescript-generators/src/generate-services.ts +1 -1
  60. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.test.ts +16 -0
  61. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.ts +37 -22
  62. package/package.json +8 -8
@@ -0,0 +1,154 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parse, stringify } from "yaml";
4
+ import type { YamlMap, YamlValue } from "./yaml-node.ts";
5
+
6
+ const versionDir = /^version-\d{8}T\d{6}Z-\d{3}$/;
7
+ const own = (obj: object, key: string) => Object.prototype.hasOwnProperty.call(obj, key);
8
+
9
+ export class VersionSnapshot {
10
+ readonly documents: Readonly<Record<string, YamlValue | undefined>>;
11
+
12
+ private constructor(diffsInOrder: Array<YamlValue | null | undefined>) {
13
+ const docs: Record<string, YamlValue | undefined> = {};
14
+ for (const diff of diffsInOrder) {
15
+ if (diff == null) continue;
16
+ if (!this.isMap(diff)) throw new Error("expected a mapping");
17
+ for (const [path, envelope] of Object.entries(diff)) {
18
+ const next = this.applyFile(docs[path], envelope);
19
+ if (this.isNull(next)) delete docs[path];
20
+ else docs[path] = next;
21
+ }
22
+ }
23
+ this.documents = docs;
24
+ }
25
+
26
+ static combine(diffsInOrder: Array<YamlValue | null | undefined>): VersionSnapshot {
27
+ return new VersionSnapshot(diffsInOrder);
28
+ }
29
+
30
+ static async read(deterministicRoot: string): Promise<VersionSnapshot> {
31
+ const names = (await readdir(deterministicRoot, { withFileTypes: true }))
32
+ .filter((entry) => entry.isDirectory() && versionDir.test(entry.name))
33
+ .map((entry) => entry.name)
34
+ .sort();
35
+ const snapshot = new VersionSnapshot([]);
36
+ return VersionSnapshot.combine(await Promise.all(names.map((name) => snapshot.diffOf(deterministicRoot, name))));
37
+ }
38
+
39
+ yaml(path: string): string {
40
+ if (!own(this.documents, path)) throw new Error(`missing ${path}`);
41
+ return stringify(this.documents[path]);
42
+ }
43
+
44
+ private async diffOf(root: string, name: string): Promise<YamlValue | undefined> {
45
+ let text: string;
46
+ try {
47
+ text = await readFile(join(root, name, "diff.yaml"), "utf8");
48
+ } catch {
49
+ return undefined;
50
+ }
51
+ if (text === "") return undefined;
52
+ let rootValue: YamlValue;
53
+ try {
54
+ rootValue = parse(text) as YamlValue;
55
+ } catch (error) {
56
+ throw new Error(`${name}/diff.yaml: ${String(error)}`);
57
+ }
58
+ if (!this.isMap(rootValue)) throw new Error(`${name}/diff.yaml: expected a mapping`);
59
+ return rootValue;
60
+ }
61
+
62
+ private applyFile(before: YamlValue | undefined, envelope: YamlValue | undefined): YamlValue | undefined {
63
+ if (this.isNull(envelope)) return before;
64
+ if (this.isMap(envelope) && this.isTrue(envelope, "removed") && Object.keys(envelope).length === 1)
65
+ return undefined;
66
+ if (this.isMap(envelope) && this.isTrue(envelope, "added")) return envelope.content;
67
+ return this.apply(before, envelope);
68
+ }
69
+
70
+ private apply(before: YamlValue | undefined, next: YamlValue | undefined): YamlValue | undefined {
71
+ if (this.isNull(next)) return before;
72
+ if (this.isKeyed(before) && this.isMap(next) && this.isSplit(next)) return this.applyKeyed(before, next);
73
+ if (this.isMap(before) && this.isMap(next)) return this.applyObject(before, next);
74
+ return next;
75
+ }
76
+
77
+ private applyKeyed(beforeVal: YamlMap[], split: YamlMap): YamlValue {
78
+ const removed = new Set(this.strings(split, "removed"));
79
+ const changed = new Map(
80
+ this.seq(split.changed).flatMap((entry) => {
81
+ const pair = this.firstPair(entry);
82
+ return pair ? [[pair.key, pair.value] as const] : [];
83
+ }),
84
+ );
85
+ return [
86
+ ...this.seq(split.added),
87
+ ...beforeVal.flatMap((entry) => {
88
+ const pair = this.firstPair(entry);
89
+ if (!pair || removed.has(pair.key)) return [];
90
+ return [
91
+ changed.has(pair.key)
92
+ ? { [pair.key]: this.apply(pair.value, changed.get(pair.key)) as YamlValue }
93
+ : entry,
94
+ ];
95
+ }),
96
+ ];
97
+ }
98
+
99
+ private applyObject(beforeVal: YamlMap, next: YamlMap): YamlMap {
100
+ const removed = new Set(this.strings(next, "removed"));
101
+ const output: Record<string, YamlValue> = {};
102
+ for (const [key, value] of Object.entries(beforeVal)) {
103
+ if (removed.has(key)) continue;
104
+ output[key] = (own(next, key) ? this.apply(value, next[key]) : value) as YamlValue;
105
+ }
106
+ for (const [key, value] of Object.entries(next)) {
107
+ if (key === "removed" && Array.isArray(value)) continue;
108
+ if (!own(beforeVal, key)) output[key] = value;
109
+ }
110
+ return output;
111
+ }
112
+
113
+ private isSplit(map: YamlMap) {
114
+ return Object.keys(map).every((key) => key === "added" || key === "changed" || key === "removed");
115
+ }
116
+
117
+ private isKeyed(value: YamlValue | undefined): value is YamlMap[] {
118
+ return (
119
+ Array.isArray(value) &&
120
+ value.length > 0 &&
121
+ value.every((item) => this.isMap(item) && Object.keys(item).length === 1)
122
+ );
123
+ }
124
+
125
+ private seq(value: YamlValue | undefined): YamlValue[] {
126
+ return Array.isArray(value) ? value : [];
127
+ }
128
+
129
+ private strings(map: YamlMap, key: string) {
130
+ return this.seq(map[key]).map((item) => (this.isScalar(item) ? String(item ?? "") : ""));
131
+ }
132
+
133
+ private firstPair(entry: YamlValue | undefined) {
134
+ if (!this.isMap(entry)) return undefined;
135
+ const key = Object.keys(entry)[0];
136
+ return key === undefined ? undefined : { key, value: entry[key] };
137
+ }
138
+
139
+ private isMap(value: YamlValue | undefined | null): value is YamlMap {
140
+ return typeof value === "object" && value !== null && !Array.isArray(value);
141
+ }
142
+
143
+ private isTrue(map: YamlMap, key: string) {
144
+ return map[key] === true || map[key] === "true" || map[key] === "True";
145
+ }
146
+
147
+ private isNull(value: YamlValue | undefined | null) {
148
+ return value == null;
149
+ }
150
+
151
+ private isScalar(value: YamlValue | undefined) {
152
+ return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
153
+ }
154
+ }
@@ -1,8 +1,8 @@
1
1
  import { parse } from "yaml";
2
2
 
3
3
  type YamlLiteral = string | number | boolean | null;
4
- type YamlMap = { readonly [key: string]: YamlValue };
5
- type YamlValue = YamlLiteral | YamlValue[] | YamlMap;
4
+ export type YamlMap = { readonly [key: string]: YamlValue };
5
+ export type YamlValue = YamlLiteral | YamlValue[] | YamlMap;
6
6
  type FieldDefault = YamlLiteral | YamlValue[] | YamlMap;
7
7
  type Named = { name: string; node: YamlNode };
8
8
 
@@ -45,7 +45,7 @@ class RustImportGenerator {
45
45
  return this.underBase(path);
46
46
  }
47
47
  datasourceRel(entity) {
48
- return this.rel("types/generated/datasource", this.datasource(entity));
48
+ return this.rel(this.layered("types/generated/datasource"), this.datasource(entity));
49
49
  }
50
50
  datasourceQual(entity) {
51
51
  return this.typeQual(
@@ -63,7 +63,7 @@ class RustImportGenerator {
63
63
  }
64
64
  datasourceValidatorRel(entity) {
65
65
  return this.rel(
66
- "types/generated/datasource/validators",
66
+ this.layered("types/generated/datasource/validators"),
67
67
  this.datasourceValidator(entity)
68
68
  );
69
69
  }
@@ -73,7 +73,7 @@ class RustImportGenerator {
73
73
  return this.underBase(path);
74
74
  }
75
75
  viewRel(entity) {
76
- return this.rel("types/generated/views", this.view(entity));
76
+ return this.rel(this.layered("types/generated/views"), this.view(entity));
77
77
  }
78
78
  viewQual(entity) {
79
79
  return this.typeQual(this.viewRel(entity), this.casing.convertTypes(entity));
@@ -87,7 +87,7 @@ class RustImportGenerator {
87
87
  }
88
88
  viewValidatorRel(entity) {
89
89
  return this.rel(
90
- "types/generated/views/validators",
90
+ this.layered("types/generated/views/validators"),
91
91
  this.viewValidator(entity)
92
92
  );
93
93
  }
@@ -97,31 +97,31 @@ class RustImportGenerator {
97
97
  return this.underBase(path);
98
98
  }
99
99
  serviceRel(entity) {
100
- return this.rel("services/generated", this.service(entity));
100
+ return this.rel(this.layered("services/generated"), this.service(entity));
101
101
  }
102
102
  serviceCustom(name, module) {
103
103
  return this.resolveCustom(name, module, "services");
104
104
  }
105
105
  serviceCustomRel(entity) {
106
106
  const file = `${this.serviceStem(entity)}.rs`;
107
- return this.organizeByFeature ? `features/${this.casing.directory(entity)}/custom/${file}` : `services/custom/${file}`;
107
+ return this.organizeByFeature ? `${this.featuresDir(entity)}/custom/${file}` : `${this.layered("services")}/custom/${file}`;
108
108
  }
109
109
  serviceTest(entity) {
110
110
  return this.test(this.service(entity), `${entity}_service`);
111
111
  }
112
112
  serviceTestRel(entity) {
113
- return this.rel("services/generated/__tests__", this.serviceTest(entity));
113
+ return this.rel(this.layered("services/generated/__tests__"), this.serviceTest(entity));
114
114
  }
115
115
  serviceIntegrationTest(entity) {
116
116
  const file = this.rsFile(`${entity}_service_integration_tests`);
117
117
  if (this.organizeByFeature) {
118
- return `features/${this.casing.directory(entity)}/__tests__/${file}`;
118
+ return `${this.featuresDir(entity)}/__tests__/${file}`;
119
119
  }
120
120
  return file;
121
121
  }
122
122
  serviceIntegrationTestRel(entity) {
123
123
  return this.rel(
124
- "services/generated/__tests__",
124
+ this.layered("services/generated/__tests__"),
125
125
  this.serviceIntegrationTest(entity)
126
126
  );
127
127
  }
@@ -134,7 +134,7 @@ class RustImportGenerator {
134
134
  return this.underBase(path);
135
135
  }
136
136
  routeRel(entity) {
137
- return this.rel("routes/generated", this.route(entity));
137
+ return this.rel(this.layered("routes/generated"), this.route(entity));
138
138
  }
139
139
  routeCustom(name, module) {
140
140
  return this.resolveCustom(name, module, "routes");
@@ -166,15 +166,17 @@ class RustImportGenerator {
166
166
  return this.routeStem(entity);
167
167
  }
168
168
  appWiring() {
169
- return this.organizeByFeature ? "features/app_wiring.rs" : "app_wiring.rs";
169
+ return this.organizeByFeature ? `${this.casing.convertDirectories("features")}/app_wiring.rs` : "app_wiring.rs";
170
170
  }
171
171
  validatorFn(kind, entity, fn) {
172
172
  if (this.organizeByFeature) {
173
173
  const file = kind === "datasource" ? this.datasourceValidator(entity) : this.viewValidator(entity);
174
174
  return `${this.spec("", file)}::${fn}`;
175
175
  }
176
- const ns = kind === "datasource" ? "crate::types::generated::datasource::validators" : "crate::types::generated::views::validators";
177
- return `${ns}::${fn}`;
176
+ const ns = this.layered(
177
+ kind === "datasource" ? "types/generated/datasource/validators" : "types/generated/views/validators"
178
+ ).replaceAll("/", "::");
179
+ return `crate::${ns}::${fn}`;
178
180
  }
179
181
  apiPath(entity) {
180
182
  return pluralSnake(entity).replace(/_/g, "-");
@@ -196,8 +198,16 @@ class RustImportGenerator {
196
198
  rsFile(stem) {
197
199
  return `${this.casing.fileBase(stem)}.rs`;
198
200
  }
201
+ layered(path) {
202
+ return path.split("/").map(
203
+ (segment) => segment === "custom" || segment === "__tests__" ? segment : this.casing.convertDirectories(segment)
204
+ ).join("/");
205
+ }
206
+ featuresDir(entity) {
207
+ return `${this.casing.convertDirectories("features")}/${this.casing.directory(entity)}`;
208
+ }
199
209
  featurePath(entity, file) {
200
- return `features/${this.casing.directory(entity)}/${file}`;
210
+ return `${this.featuresDir(entity)}/${file}`;
201
211
  }
202
212
  serviceStem(entity) {
203
213
  return this.casing.fileBase(`${entity}_service`);
@@ -217,7 +227,7 @@ class RustImportGenerator {
217
227
  const stubFn = layer === "services" ? "generateCustomServiceStub" : "generateCustomRouteStub";
218
228
  const file = this.rsFile(name);
219
229
  const entity = (featureEntityFromClass(name) || "shared").replace(/-/g, "_");
220
- const defaultStub = this.organizeByFeature ? `features/${this.casing.directory(entity)}/custom/${file}` : `../custom/${file}`;
230
+ const defaultStub = this.organizeByFeature ? `${this.featuresDir(entity)}/custom/${file}` : `../custom/${file}`;
221
231
  if (this.organizeByFeature) {
222
232
  if (!mod || !mod.startsWith(".") || mod.startsWith("./services/") || mod.startsWith("./routes/")) {
223
233
  return defaultStub;
@@ -95,6 +95,17 @@ describe("RustImportGenerator layered (organize_by_feature unset)", () => {
95
95
  imports.validatorFn("view", "user", "validate_user"),
96
96
  "crate::types::generated::views::validators::validate_user",
97
97
  );
98
+ const pascalDirs = createImportGenerator(".", {
99
+ "languages.rust.casing.directories": "Pascal",
100
+ });
101
+ assert.equal(
102
+ pascalDirs.datasourceRel("user"),
103
+ "Types/Generated/Datasource/user.rs",
104
+ );
105
+ assert.equal(
106
+ pascalDirs.validatorFn("datasource", "user", "validate_datasource_user"),
107
+ "crate::Types::Generated::Datasource::Validators::validate_datasource_user",
108
+ );
98
109
  assert.equal(imports.apiPath("card_payment"), "card-payments");
99
110
  assert.equal(imports.apiPath("user"), "users");
100
111
  assert.equal(imports.frontend("src/main.rs"), "frontend/src/main.rs");
@@ -67,7 +67,7 @@ export class RustImportGenerator implements IImportGenerator {
67
67
  }
68
68
 
69
69
  datasourceRel(entity: string): string {
70
- return this.rel("types/generated/datasource", this.datasource(entity));
70
+ return this.rel(this.layered("types/generated/datasource"), this.datasource(entity));
71
71
  }
72
72
 
73
73
  datasourceQual(entity: string): string {
@@ -88,7 +88,7 @@ export class RustImportGenerator implements IImportGenerator {
88
88
 
89
89
  datasourceValidatorRel(entity: string): string {
90
90
  return this.rel(
91
- "types/generated/datasource/validators",
91
+ this.layered("types/generated/datasource/validators"),
92
92
  this.datasourceValidator(entity),
93
93
  );
94
94
  }
@@ -102,7 +102,7 @@ export class RustImportGenerator implements IImportGenerator {
102
102
  }
103
103
 
104
104
  viewRel(entity: string): string {
105
- return this.rel("types/generated/views", this.view(entity));
105
+ return this.rel(this.layered("types/generated/views"), this.view(entity));
106
106
  }
107
107
 
108
108
  viewQual(entity: string): string {
@@ -119,7 +119,7 @@ export class RustImportGenerator implements IImportGenerator {
119
119
 
120
120
  viewValidatorRel(entity: string): string {
121
121
  return this.rel(
122
- "types/generated/views/validators",
122
+ this.layered("types/generated/views/validators"),
123
123
  this.viewValidator(entity),
124
124
  );
125
125
  }
@@ -133,7 +133,7 @@ export class RustImportGenerator implements IImportGenerator {
133
133
  }
134
134
 
135
135
  serviceRel(entity: string): string {
136
- return this.rel("services/generated", this.service(entity));
136
+ return this.rel(this.layered("services/generated"), this.service(entity));
137
137
  }
138
138
 
139
139
  serviceCustom(name: string, module?: string): string {
@@ -143,8 +143,8 @@ export class RustImportGenerator implements IImportGenerator {
143
143
  serviceCustomRel(entity: string): string {
144
144
  const file = `${this.serviceStem(entity)}.rs`;
145
145
  return this.organizeByFeature
146
- ? `features/${this.casing.directory(entity)}/custom/${file}`
147
- : `services/custom/${file}`;
146
+ ? `${this.featuresDir(entity)}/custom/${file}`
147
+ : `${this.layered("services")}/custom/${file}`;
148
148
  }
149
149
 
150
150
  serviceTest(entity: string): string {
@@ -152,20 +152,20 @@ export class RustImportGenerator implements IImportGenerator {
152
152
  }
153
153
 
154
154
  serviceTestRel(entity: string): string {
155
- return this.rel("services/generated/__tests__", this.serviceTest(entity));
155
+ return this.rel(this.layered("services/generated/__tests__"), this.serviceTest(entity));
156
156
  }
157
157
 
158
158
  serviceIntegrationTest(entity: string): string {
159
159
  const file = this.rsFile(`${entity}_service_integration_tests`);
160
160
  if (this.organizeByFeature) {
161
- return `features/${this.casing.directory(entity)}/__tests__/${file}`;
161
+ return `${this.featuresDir(entity)}/__tests__/${file}`;
162
162
  }
163
163
  return file;
164
164
  }
165
165
 
166
166
  serviceIntegrationTestRel(entity: string): string {
167
167
  return this.rel(
168
- "services/generated/__tests__",
168
+ this.layered("services/generated/__tests__"),
169
169
  this.serviceIntegrationTest(entity),
170
170
  );
171
171
  }
@@ -183,7 +183,7 @@ export class RustImportGenerator implements IImportGenerator {
183
183
  }
184
184
 
185
185
  routeRel(entity: string): string {
186
- return this.rel("routes/generated", this.route(entity));
186
+ return this.rel(this.layered("routes/generated"), this.route(entity));
187
187
  }
188
188
 
189
189
  routeCustom(name: string, module?: string): string {
@@ -224,7 +224,9 @@ export class RustImportGenerator implements IImportGenerator {
224
224
  }
225
225
 
226
226
  appWiring(): string {
227
- return this.organizeByFeature ? "features/app_wiring.rs" : "app_wiring.rs";
227
+ return this.organizeByFeature
228
+ ? `${this.casing.convertDirectories("features")}/app_wiring.rs`
229
+ : "app_wiring.rs";
228
230
  }
229
231
 
230
232
  validatorFn(
@@ -239,11 +241,12 @@ export class RustImportGenerator implements IImportGenerator {
239
241
  : this.viewValidator(entity);
240
242
  return `${this.spec("", file)}::${fn}`;
241
243
  }
242
- const ns =
244
+ const ns = this.layered(
243
245
  kind === "datasource"
244
- ? "crate::types::generated::datasource::validators"
245
- : "crate::types::generated::views::validators";
246
- return `${ns}::${fn}`;
246
+ ? "types/generated/datasource/validators"
247
+ : "types/generated/views/validators",
248
+ ).replaceAll("/", "::");
249
+ return `crate::${ns}::${fn}`;
247
250
  }
248
251
 
249
252
  apiPath(entity: string): string {
@@ -271,8 +274,23 @@ export class RustImportGenerator implements IImportGenerator {
271
274
  return `${this.casing.fileBase(stem)}.rs`;
272
275
  }
273
276
 
277
+ private layered(path: string): string {
278
+ return path
279
+ .split("/")
280
+ .map((segment) =>
281
+ segment === "custom" || segment === "__tests__"
282
+ ? segment
283
+ : this.casing.convertDirectories(segment),
284
+ )
285
+ .join("/");
286
+ }
287
+
288
+ private featuresDir(entity: string): string {
289
+ return `${this.casing.convertDirectories("features")}/${this.casing.directory(entity)}`;
290
+ }
291
+
274
292
  private featurePath(entity: string, file: string): string {
275
- return `features/${this.casing.directory(entity)}/${file}`;
293
+ return `${this.featuresDir(entity)}/${file}`;
276
294
  }
277
295
 
278
296
  private serviceStem(entity: string): string {
@@ -306,7 +324,7 @@ export class RustImportGenerator implements IImportGenerator {
306
324
  featureEntityFromClass(name) || "shared"
307
325
  ).replace(/-/g, "_");
308
326
  const defaultStub = this.organizeByFeature
309
- ? `features/${this.casing.directory(entity)}/custom/${file}`
327
+ ? `${this.featuresDir(entity)}/custom/${file}`
310
328
  : `../custom/${file}`;
311
329
  if (this.organizeByFeature) {
312
330
  if (
@@ -1,14 +1,19 @@
1
1
  import { verifyEntries } from "@deterministic-code/generators-common/reference-verifier";
2
+ import { createCasing } from "./common/default-casing.js";
2
3
  import { Emit } from "./emit.js";
3
4
  import { generateViewTypes } from "./emit-shaped-types.js";
4
5
  import { referencesBackend } from "./inline-inherited.js";
6
+ const layered = (settings, path) => {
7
+ const casing = createCasing(settings);
8
+ return path.split("/").map((segment) => casing.convertDirectories(segment)).join("/");
9
+ };
5
10
  const generate = async (ctx) => {
6
11
  const referenceBackendType = referencesBackend(ctx.settings);
7
12
  const views = new Emit(ctx.settings).imports.frontend("src/types");
8
13
  const entries = await generateViewTypes(ctx, {
9
14
  referenceBackendType,
10
15
  basePath: views,
11
- datasourceBasePath: referenceBackendType ? "types/generated/datasource" : views
16
+ datasourceBasePath: referenceBackendType ? layered(ctx.settings, "types/generated/datasource") : views
12
17
  });
13
18
  if (!referenceBackendType) verifyEntries(entries);
14
19
  return entries;
@@ -1,5 +1,10 @@
1
1
  import { verifyEntries } from "@deterministic-code/generators-common/reference-verifier";
2
+ import { createCasing } from "./common/default-casing.js";
2
3
  import { Emit } from "./emit.js";
4
+ const layered = (settings, path) => {
5
+ const casing = createCasing(settings);
6
+ return path.split("/").map((segment) => casing.convertDirectories(segment)).join("/");
7
+ };
3
8
  import { generate as generateViewTypeValidators } from "./generate-view-type-validators.js";
4
9
  import { referencesBackend } from "./inline-inherited.js";
5
10
  import {
@@ -24,7 +29,7 @@ const generate = async (ctx) => {
24
29
  schemaInheritTmpl
25
30
  },
26
31
  basePath: validators,
27
- datasourceBasePath: referenceBackendType ? "types/generated/datasource/validators" : validators
32
+ datasourceBasePath: referenceBackendType ? layered(ctx.settings, "types/generated/datasource/validators") : validators
28
33
  });
29
34
  if (!referenceBackendType) verifyEntries(entries);
30
35
  return entries;
@@ -44,7 +44,7 @@ class Generator extends Emit {
44
44
  return entries;
45
45
  }
46
46
  customModuleKey(emitPath) {
47
- return customRelKey(emitPath, "routes");
47
+ return customRelKey(emitPath, this.casing.convertDirectories("routes"));
48
48
  }
49
49
  entityRouter(candidate) {
50
50
  const { simpleDoc, descriptionDoc } = this.settings;
@@ -129,7 +129,7 @@ class Generator extends Emit {
129
129
  }
130
130
  /** Map emit paths like `../custom/foo.ts` to Rel keys `services/custom/foo.ts`. */
131
131
  customModuleKey(emitPath) {
132
- return customRelKey(emitPath, "services");
132
+ return customRelKey(emitPath, this.casing.convertDirectories("services"));
133
133
  }
134
134
  custom(entry) {
135
135
  const { simpleDoc, descriptionDoc } = this.settings;