ormbridge 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +19 -18
  2. package/dist/cliRunner.js +4 -4
  3. package/dist/cliRunner.js.map +1 -1
  4. package/dist/config.d.ts +2 -2
  5. package/dist/convert.js +30 -1
  6. package/dist/convert.js.map +1 -1
  7. package/dist/emitters/cockroach.d.ts +33 -0
  8. package/dist/emitters/cockroach.js +81 -0
  9. package/dist/emitters/cockroach.js.map +1 -0
  10. package/dist/emitters/doctrine.js +11 -0
  11. package/dist/emitters/doctrine.js.map +1 -1
  12. package/dist/emitters/drizzle.js +39 -4
  13. package/dist/emitters/drizzle.js.map +1 -1
  14. package/dist/emitters/efcore.js +18 -1
  15. package/dist/emitters/efcore.js.map +1 -1
  16. package/dist/emitters/efcoreColumns.js +23 -2
  17. package/dist/emitters/efcoreColumns.js.map +1 -1
  18. package/dist/emitters/gorm.js +20 -1
  19. package/dist/emitters/gorm.js.map +1 -1
  20. package/dist/emitters/jpa.d.ts +36 -0
  21. package/dist/emitters/jpa.js +262 -0
  22. package/dist/emitters/jpa.js.map +1 -0
  23. package/dist/emitters/jpaColumns.d.ts +27 -0
  24. package/dist/emitters/jpaColumns.js +342 -0
  25. package/dist/emitters/jpaColumns.js.map +1 -0
  26. package/dist/emitters/jpaDefaults.d.ts +24 -0
  27. package/dist/emitters/jpaDefaults.js +223 -0
  28. package/dist/emitters/jpaDefaults.js.map +1 -0
  29. package/dist/emitters/jpaEnums.d.ts +11 -0
  30. package/dist/emitters/jpaEnums.js +134 -0
  31. package/dist/emitters/jpaEnums.js.map +1 -0
  32. package/dist/emitters/jpaFormat.d.ts +16 -0
  33. package/dist/emitters/jpaFormat.js +73 -0
  34. package/dist/emitters/jpaFormat.js.map +1 -0
  35. package/dist/emitters/jpaKeys.d.ts +25 -0
  36. package/dist/emitters/jpaKeys.js +110 -0
  37. package/dist/emitters/jpaKeys.js.map +1 -0
  38. package/dist/emitters/jpaNames.d.ts +26 -0
  39. package/dist/emitters/jpaNames.js +234 -0
  40. package/dist/emitters/jpaNames.js.map +1 -0
  41. package/dist/emitters/jpaRelations.d.ts +41 -0
  42. package/dist/emitters/jpaRelations.js +304 -0
  43. package/dist/emitters/jpaRelations.js.map +1 -0
  44. package/dist/emitters/jpaRender.d.ts +10 -0
  45. package/dist/emitters/jpaRender.js +81 -0
  46. package/dist/emitters/jpaRender.js.map +1 -0
  47. package/dist/emitters/jpaScalars.d.ts +32 -0
  48. package/dist/emitters/jpaScalars.js +201 -0
  49. package/dist/emitters/jpaScalars.js.map +1 -0
  50. package/dist/emitters/jpaTable.d.ts +10 -0
  51. package/dist/emitters/jpaTable.js +109 -0
  52. package/dist/emitters/jpaTable.js.map +1 -0
  53. package/dist/emitters/jpaTypes.d.ts +85 -0
  54. package/dist/emitters/jpaTypes.js +36 -0
  55. package/dist/emitters/jpaTypes.js.map +1 -0
  56. package/dist/emitters/laravel.js +11 -0
  57. package/dist/emitters/laravel.js.map +1 -1
  58. package/dist/emitters/prisma.d.ts +1 -1
  59. package/dist/emitters/prisma.js +47 -5
  60. package/dist/emitters/prisma.js.map +1 -1
  61. package/dist/emitters/sequelize.js +82 -6
  62. package/dist/emitters/sequelize.js.map +1 -1
  63. package/dist/emitters/sqlClickhouse.d.ts +43 -0
  64. package/dist/emitters/sqlClickhouse.js +265 -0
  65. package/dist/emitters/sqlClickhouse.js.map +1 -0
  66. package/dist/emitters/sqlClickhouseIndexes.d.ts +3 -0
  67. package/dist/emitters/sqlClickhouseIndexes.js +98 -0
  68. package/dist/emitters/sqlClickhouseIndexes.js.map +1 -0
  69. package/dist/emitters/sqlClickhouseTypes.d.ts +39 -0
  70. package/dist/emitters/sqlClickhouseTypes.js +360 -0
  71. package/dist/emitters/sqlClickhouseTypes.js.map +1 -0
  72. package/dist/emitters/sqlDdl.d.ts +29 -1
  73. package/dist/emitters/sqlDdl.js +141 -21
  74. package/dist/emitters/sqlDdl.js.map +1 -1
  75. package/dist/emitters/sqlalchemy.d.ts +7 -1
  76. package/dist/emitters/sqlalchemy.js +39 -0
  77. package/dist/emitters/sqlalchemy.js.map +1 -1
  78. package/dist/emitters/typeorm.js +44 -9
  79. package/dist/emitters/typeorm.js.map +1 -1
  80. package/dist/formats.d.ts +5 -2
  81. package/dist/formats.js +36 -1
  82. package/dist/formats.js.map +1 -1
  83. package/dist/index.d.ts +4 -0
  84. package/dist/index.js +2 -0
  85. package/dist/index.js.map +1 -1
  86. package/dist/io.js +27 -1
  87. package/dist/io.js.map +1 -1
  88. package/dist/parsers/javaSyntax.d.ts +120 -0
  89. package/dist/parsers/javaSyntax.js +304 -0
  90. package/dist/parsers/javaSyntax.js.map +1 -0
  91. package/dist/parsers/javaValues.d.ts +5 -0
  92. package/dist/parsers/javaValues.js +187 -0
  93. package/dist/parsers/javaValues.js.map +1 -0
  94. package/dist/parsers/jpa.d.ts +17 -0
  95. package/dist/parsers/jpa.js +131 -0
  96. package/dist/parsers/jpa.js.map +1 -0
  97. package/dist/parsers/jpaAnnotations.d.ts +46 -0
  98. package/dist/parsers/jpaAnnotations.js +156 -0
  99. package/dist/parsers/jpaAnnotations.js.map +1 -0
  100. package/dist/parsers/jpaBuild.d.ts +4 -0
  101. package/dist/parsers/jpaBuild.js +297 -0
  102. package/dist/parsers/jpaBuild.js.map +1 -0
  103. package/dist/parsers/jpaClasses.d.ts +27 -0
  104. package/dist/parsers/jpaClasses.js +257 -0
  105. package/dist/parsers/jpaClasses.js.map +1 -0
  106. package/dist/parsers/jpaCollections.d.ts +12 -0
  107. package/dist/parsers/jpaCollections.js +106 -0
  108. package/dist/parsers/jpaCollections.js.map +1 -0
  109. package/dist/parsers/jpaColumns.d.ts +26 -0
  110. package/dist/parsers/jpaColumns.js +361 -0
  111. package/dist/parsers/jpaColumns.js.map +1 -0
  112. package/dist/parsers/jpaEntities.d.ts +9 -0
  113. package/dist/parsers/jpaEntities.js +181 -0
  114. package/dist/parsers/jpaEntities.js.map +1 -0
  115. package/dist/parsers/jpaIndexes.d.ts +12 -0
  116. package/dist/parsers/jpaIndexes.js +87 -0
  117. package/dist/parsers/jpaIndexes.js.map +1 -0
  118. package/dist/parsers/jpaJoins.d.ts +68 -0
  119. package/dist/parsers/jpaJoins.js +131 -0
  120. package/dist/parsers/jpaJoins.js.map +1 -0
  121. package/dist/parsers/jpaModel.d.ts +73 -0
  122. package/dist/parsers/jpaModel.js +2 -0
  123. package/dist/parsers/jpaModel.js.map +1 -0
  124. package/dist/parsers/jpaNotes.d.ts +20 -0
  125. package/dist/parsers/jpaNotes.js +138 -0
  126. package/dist/parsers/jpaNotes.js.map +1 -0
  127. package/dist/parsers/jpaRelations.d.ts +4 -0
  128. package/dist/parsers/jpaRelations.js +242 -0
  129. package/dist/parsers/jpaRelations.js.map +1 -0
  130. package/dist/parsers/jpaTypes.d.ts +23 -0
  131. package/dist/parsers/jpaTypes.js +232 -0
  132. package/dist/parsers/jpaTypes.js.map +1 -0
  133. package/dist/parsers/prisma.js +9 -1
  134. package/dist/parsers/prisma.js.map +1 -1
  135. package/dist/parsers/sqlDdl.js +40 -2
  136. package/dist/parsers/sqlDdl.js.map +1 -1
  137. package/dist/parsers/sqlSyntax.js +15 -1
  138. package/dist/parsers/sqlSyntax.js.map +1 -1
  139. package/dist/parsers/sqlTypes.js +37 -2
  140. package/dist/parsers/sqlTypes.js.map +1 -1
  141. package/dist/parsers/typeorm.js +2 -0
  142. package/dist/parsers/typeorm.js.map +1 -1
  143. package/dist/styles.d.ts +3 -2
  144. package/dist/styles.js +4 -1
  145. package/dist/styles.js.map +1 -1
  146. package/package.json +8 -1
@@ -0,0 +1,131 @@
1
+ import { describeThrown, err, ok } from '../result.js';
2
+ import { getJavaParser, readJavaFile, } from './javaSyntax.js';
3
+ import { Annotated } from './jpaAnnotations.js';
4
+ import { buildSchema } from './jpaBuild.js';
5
+ import { discoverEntities } from './jpaEntities.js';
6
+ /**
7
+ * Paths of files that are not the model: build output, test sources, generated metamodels, module descriptors and
8
+ * database migrations.
9
+ */
10
+ const SKIPPED_PATH = /(?:^|[\\/])(?:target|build|out|\.gradle|node_modules|\.git)[\\/]|(?:^|[\\/])src[\\/]test[\\/]|(?:^|[\\/])db[\\/]migrations?[\\/]|(?:Tests?|_)\.java$|(?:^|[\\/])(?:module|package)-info\.java$/;
11
+ /** True for build output, tests, generated metamodel classes, `module-info.java`, `package-info.java` and migrations. */
12
+ export function isSkippedJpaPath(path) {
13
+ return SKIPPED_PATH.test(path);
14
+ }
15
+ /**
16
+ * Parses Hibernate / JPA entities (Java) into the shared IR using tree-sitter. The source is only read: nothing is
17
+ * compiled or run, and `persistence.xml` and `orm.xml` are not read.
18
+ */
19
+ export async function parseJpa(sources, options) {
20
+ const parserResult = await getJavaParser();
21
+ if (!parserResult.ok) {
22
+ return parserResult;
23
+ }
24
+ const trees = [];
25
+ try {
26
+ return readJpa(parserResult.value, sources, options, trees);
27
+ }
28
+ catch (thrown) {
29
+ return err('PARSE_FAILED', `Failed to read Hibernate / JPA entities from ${sources.map((source) => source.path).join(', ')}. ` +
30
+ `Underlying error: ${describeThrown(thrown)}`);
31
+ }
32
+ finally {
33
+ for (const tree of trees) {
34
+ tree.delete();
35
+ }
36
+ }
37
+ }
38
+ function readJpa(parser, sources, options, trees) {
39
+ const warnings = [];
40
+ const classes = new Map();
41
+ const enums = new Map();
42
+ const files = [];
43
+ for (const source of sources) {
44
+ if (isSkippedJpaPath(source.path)) {
45
+ warnings.push(`${source.path}: skipped (build output, tests, generated metamodels, module descriptors and migrations are not the model).`);
46
+ continue;
47
+ }
48
+ const tree = parser.parse(source.text);
49
+ trees.push(tree);
50
+ const file = readJavaFile(tree.rootNode, source.path);
51
+ files.push(file);
52
+ if (file.hasErrors) {
53
+ warnings.push(`${source.path}: the file contains Java syntax errors; some classes or fields may be missing from the output.`);
54
+ }
55
+ for (const cls of file.classes) {
56
+ const target = cls.kind === 'enum' ? enums : classes;
57
+ const existing = target.get(cls.name);
58
+ if (existing === undefined) {
59
+ target.set(cls.name, cls);
60
+ }
61
+ else if (existing.path !== cls.path || existing.outer !== cls.outer) {
62
+ warnings.push(`Duplicate type name "${cls.name}" (${cls.path}); only the first definition (${existing.path}) was read.`);
63
+ }
64
+ }
65
+ }
66
+ const lookup = (path) => {
67
+ const name = path[path.length - 1];
68
+ if (name === undefined) {
69
+ return undefined;
70
+ }
71
+ const owner = path[path.length - 2];
72
+ const candidates = owner === undefined
73
+ ? [...classes.values(), ...enums.values()]
74
+ : [classes.get(owner), enums.get(owner)].flatMap((item) => item === undefined ? [] : [item]);
75
+ for (const cls of candidates) {
76
+ const field = cls.fields.find((item) => item.name === name &&
77
+ item.modifiers.includes('static') &&
78
+ item.modifiers.includes('final'));
79
+ if (field?.initializer !== undefined) {
80
+ return field.initializer;
81
+ }
82
+ }
83
+ return undefined;
84
+ };
85
+ const project = {
86
+ classes,
87
+ enums,
88
+ converters: new Map(),
89
+ lookup,
90
+ warnings,
91
+ appLabel: options.appLabel,
92
+ };
93
+ findConverters(project);
94
+ const warn = (message) => {
95
+ warnings.push(message);
96
+ };
97
+ const entities = discoverEntities(project, warn).filter((entity) => {
98
+ if (entity.notes.has('Subselect', true)) {
99
+ warn(`${entity.name}: @Subselect maps a SQL query, not a table; the entity was skipped.`);
100
+ return false;
101
+ }
102
+ return true;
103
+ });
104
+ if (entities.length === 0) {
105
+ return err('NO_MODELS_FOUND', `No JPA entities were found in: ${sources.map((source) => source.path).join(', ')}. ` +
106
+ `An entity is a class annotated with @Entity (jakarta.persistence or javax.persistence). ` +
107
+ `Mapping in orm.xml or persistence.xml is not read.`);
108
+ }
109
+ return ok(buildSchema(project, entities));
110
+ }
111
+ /** Finds `AttributeConverter<X, Y>` classes so `@Convert` can say which column type they produce. */
112
+ function findConverters(project) {
113
+ for (const cls of project.classes.values()) {
114
+ const base = cls.interfaces.find((item) => item.name === 'AttributeConverter');
115
+ const attributeType = base?.args[0];
116
+ const columnType = base?.args[1];
117
+ if (attributeType === undefined || columnType === undefined) {
118
+ continue;
119
+ }
120
+ const info = {
121
+ name: cls.name,
122
+ attributeType,
123
+ columnType,
124
+ autoApply: new Annotated(cls.annotations, project.lookup, cls.imports)
125
+ .get('Converter')
126
+ ?.boolean('autoApply') === true,
127
+ };
128
+ project.converters.set(cls.name, info);
129
+ }
130
+ }
131
+ //# sourceMappingURL=jpa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jpa.js","sourceRoot":"","sources":["../../src/parsers/jpa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { ConstLookup, JavaAnnotation, JavaValue } from './javaSyntax.js';
2
+ /** Reads the arguments of one annotation as typed values, evaluating constants on demand. */
3
+ export declare class Ann {
4
+ readonly raw: JavaAnnotation;
5
+ private readonly lookup;
6
+ constructor(raw: JavaAnnotation, lookup: ConstLookup);
7
+ get name(): string;
8
+ has(key: string): boolean;
9
+ value(key: string): JavaValue | undefined;
10
+ /** The text of a string argument, or undefined when it is absent or not a constant string. */
11
+ string(key: string): string | undefined;
12
+ boolean(key: string): boolean | undefined;
13
+ number(key: string): number | undefined;
14
+ /** The last segment of an enum constant argument: `GenerationType.IDENTITY` is `IDENTITY`. */
15
+ member(key: string): string | undefined;
16
+ /** The enum constants of an array (or single) argument, by last segment. */
17
+ members(key: string): string[];
18
+ strings(key: string): string[];
19
+ /** The class named by `Foo.class`. */
20
+ className(key: string): string | undefined;
21
+ classNames(key: string): string[];
22
+ /** Nested annotations of an array (or single) argument, such as `uniqueConstraints`. */
23
+ annotations(key: string): Ann[];
24
+ /** The first nested annotation of an argument (`foreignKey = @ForeignKey(...)`). */
25
+ annotation(key: string): Ann | undefined;
26
+ private items;
27
+ }
28
+ /** The annotations of one class, field or method, looked up by simple name. */
29
+ export declare class Annotated {
30
+ private readonly list;
31
+ private readonly lookup;
32
+ private readonly imports;
33
+ constructor(list: readonly JavaAnnotation[], lookup: ConstLookup, imports?: readonly string[]);
34
+ private matches;
35
+ /** True when a clashing name is the Hibernate annotation, judging by its qualifier and the imports. */
36
+ private isHibernate;
37
+ /** The first annotation with this simple name. `hibernate` selects the Hibernate one of a clashing name. */
38
+ get(name: string, hibernate?: boolean): Ann | undefined;
39
+ has(name: string, hibernate?: boolean): boolean;
40
+ /**
41
+ * Every use of a repeatable annotation: written several times, or inside its container
42
+ * (`@JoinColumns({@JoinColumn(...)})`).
43
+ */
44
+ repeated(name: string, container: string): Ann[];
45
+ names(): string[];
46
+ }
@@ -0,0 +1,156 @@
1
+ import { evaluate } from './javaValues.js';
2
+ /**
3
+ * Annotation names that exist both in JPA and in Hibernate (`org.hibernate.annotations.Table`, `Check`, ...). A
4
+ * lookup for the JPA one skips an annotation that is written, or imported, from the Hibernate package.
5
+ */
6
+ const CLASHING_NAMES = new Set([
7
+ 'Table',
8
+ 'Index',
9
+ 'Check',
10
+ 'ForeignKey',
11
+ 'Type',
12
+ 'Filter',
13
+ 'Cache',
14
+ 'Cascade',
15
+ ]);
16
+ /** The clashing names that only Hibernate has. */
17
+ const HIBERNATE_ONLY = new Set([
18
+ 'Check',
19
+ 'Type',
20
+ 'Filter',
21
+ 'Cache',
22
+ 'Cascade',
23
+ ]);
24
+ /** The full name of an annotation written as a simple name, from the imports of its file. */
25
+ function resolveName(annotation, imports) {
26
+ if (annotation.qualifier !== undefined) {
27
+ return `${annotation.qualifier}.${annotation.name}`;
28
+ }
29
+ const exact = imports.find((item) => item.endsWith(`.${annotation.name}`));
30
+ return exact;
31
+ }
32
+ /** Reads the arguments of one annotation as typed values, evaluating constants on demand. */
33
+ export class Ann {
34
+ raw;
35
+ lookup;
36
+ constructor(raw, lookup) {
37
+ this.raw = raw;
38
+ this.lookup = lookup;
39
+ }
40
+ get name() {
41
+ return this.raw.name;
42
+ }
43
+ has(key) {
44
+ return this.raw.args.has(key);
45
+ }
46
+ value(key) {
47
+ const node = this.raw.args.get(key);
48
+ return node === undefined ? undefined : evaluate(node, this.lookup);
49
+ }
50
+ /** The text of a string argument, or undefined when it is absent or not a constant string. */
51
+ string(key) {
52
+ const value = this.value(key);
53
+ return value?.kind === 'string' ? value.value : undefined;
54
+ }
55
+ boolean(key) {
56
+ const value = this.value(key);
57
+ return value?.kind === 'bool' ? value.value : undefined;
58
+ }
59
+ number(key) {
60
+ const value = this.value(key);
61
+ return value?.kind === 'number' ? value.value : undefined;
62
+ }
63
+ /** The last segment of an enum constant argument: `GenerationType.IDENTITY` is `IDENTITY`. */
64
+ member(key) {
65
+ const value = this.value(key);
66
+ return value?.kind === 'member'
67
+ ? value.path[value.path.length - 1]
68
+ : undefined;
69
+ }
70
+ /** The enum constants of an array (or single) argument, by last segment. */
71
+ members(key) {
72
+ return this.items(key).flatMap((item) => item.kind === 'member' ? [item.path[item.path.length - 1] ?? ''] : []);
73
+ }
74
+ strings(key) {
75
+ return this.items(key).flatMap((item) => item.kind === 'string' ? [item.value] : []);
76
+ }
77
+ /** The class named by `Foo.class`. */
78
+ className(key) {
79
+ const value = this.value(key);
80
+ return value?.kind === 'class' ? value.type : undefined;
81
+ }
82
+ classNames(key) {
83
+ return this.items(key).flatMap((item) => item.kind === 'class' ? [item.type] : []);
84
+ }
85
+ /** Nested annotations of an array (or single) argument, such as `uniqueConstraints`. */
86
+ annotations(key) {
87
+ return this.items(key).flatMap((item) => item.kind === 'annotation' ? [new Ann(item.annotation, this.lookup)] : []);
88
+ }
89
+ /** The first nested annotation of an argument (`foreignKey = @ForeignKey(...)`). */
90
+ annotation(key) {
91
+ return this.annotations(key)[0];
92
+ }
93
+ items(key) {
94
+ const value = this.value(key);
95
+ if (value === undefined) {
96
+ return [];
97
+ }
98
+ return value.kind === 'array' ? value.items : [value];
99
+ }
100
+ }
101
+ /** The annotations of one class, field or method, looked up by simple name. */
102
+ export class Annotated {
103
+ list;
104
+ lookup;
105
+ imports;
106
+ constructor(list, lookup, imports = []) {
107
+ this.list = list;
108
+ this.lookup = lookup;
109
+ this.imports = imports;
110
+ }
111
+ matches(annotation, hibernate) {
112
+ if (!CLASHING_NAMES.has(annotation.name)) {
113
+ return true;
114
+ }
115
+ return this.isHibernate(annotation) === hibernate;
116
+ }
117
+ /** True when a clashing name is the Hibernate annotation, judging by its qualifier and the imports. */
118
+ isHibernate(annotation) {
119
+ const full = resolveName(annotation, this.imports);
120
+ if (full !== undefined) {
121
+ return full.startsWith('org.hibernate.');
122
+ }
123
+ if (HIBERNATE_ONLY.has(annotation.name)) {
124
+ return true;
125
+ }
126
+ // Only a wildcard import tells the two apart: `import org.hibernate.annotations.*;` without the JPA package.
127
+ const fromHibernate = this.imports.includes('org.hibernate.annotations.*');
128
+ const fromJpa = this.imports.some((item) => /^(?:jakarta|javax)\.persistence\.\*$/.test(item));
129
+ return fromHibernate && !fromJpa;
130
+ }
131
+ /** The first annotation with this simple name. `hibernate` selects the Hibernate one of a clashing name. */
132
+ get(name, hibernate = false) {
133
+ const found = this.list.find((annotation) => annotation.name === name && this.matches(annotation, hibernate));
134
+ return found === undefined ? undefined : new Ann(found, this.lookup);
135
+ }
136
+ has(name, hibernate = false) {
137
+ return this.get(name, hibernate) !== undefined;
138
+ }
139
+ /**
140
+ * Every use of a repeatable annotation: written several times, or inside its container
141
+ * (`@JoinColumns({@JoinColumn(...)})`).
142
+ */
143
+ repeated(name, container) {
144
+ const direct = this.list
145
+ .filter((annotation) => annotation.name === name)
146
+ .map((annotation) => new Ann(annotation, this.lookup));
147
+ const nested = this.list
148
+ .filter((annotation) => annotation.name === container)
149
+ .flatMap((annotation) => new Ann(annotation, this.lookup).annotations('value'));
150
+ return [...direct, ...nested];
151
+ }
152
+ names() {
153
+ return this.list.map((annotation) => annotation.name);
154
+ }
155
+ }
156
+ //# sourceMappingURL=jpaAnnotations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jpaAnnotations.js","sourceRoot":"","sources":["../../src/parsers/jpaAnnotations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { IrSchema } from '../ir.js';
2
+ import type { EntityInfo, Project } from './jpaModel.js';
3
+ /** Assembles the IR from the entities of the input. */
4
+ export declare function buildSchema(project: Project, entities: readonly EntityInfo[]): IrSchema;
@@ -0,0 +1,297 @@
1
+ import { buildCollection } from './jpaCollections.js';
2
+ import { entityMembers } from './jpaEntities.js';
3
+ import { buildField, cleanIdentifier, columnNameOf, irFieldName, } from './jpaColumns.js';
4
+ import { buildIndexes } from './jpaIndexes.js';
5
+ import { joinPairs, modelOwner, tableNameOf, } from './jpaJoins.js';
6
+ import { resolveRelations } from './jpaRelations.js';
7
+ import { inheritanceNotes, tableNotes } from './jpaNotes.js';
8
+ /** True when an entity has a table of its own (single-table subclasses and abstract table-per-class roots do not). */
9
+ function hasTable(entity) {
10
+ if (entity.inheritance === 'singleTable' && entity.root !== entity) {
11
+ return false;
12
+ }
13
+ return !(entity.inheritance === 'tablePerClass' && entity.isAbstract);
14
+ }
15
+ /** Assembles the IR from the entities of the input. */
16
+ export function buildSchema(project, entities) {
17
+ const warn = (message) => {
18
+ project.warnings.push(message);
19
+ };
20
+ for (const entity of entities) {
21
+ entity.members = entityMembers(project, entity, warn);
22
+ }
23
+ const byName = new Map(entities.map((entity) => [
24
+ entity.name,
25
+ entity,
26
+ ]));
27
+ const tables = entities.filter(hasTable);
28
+ const built = new Map();
29
+ const resolver = {
30
+ project,
31
+ entities: byName,
32
+ warn,
33
+ fieldsOf: (entity) => built.get(modelOwner(entity)) ?? [],
34
+ keyOf: (entity) => keyColumns(entity, resolver, 0),
35
+ };
36
+ // Columns ------------------------------------------------------------------------------------
37
+ for (const entity of tables) {
38
+ const keyCount = entity.members.filter((member) => member.isId).length;
39
+ built.set(entity, entity.members
40
+ .filter((member) => member.kind === 'basic')
41
+ .map((member) => buildField(member, { project, entity, warn }, keyCount === 1)));
42
+ }
43
+ // Models --------------------------------------------------------------------------------------
44
+ const models = new Map();
45
+ const outputs = new Map();
46
+ const enums = new Map();
47
+ const relationOf = new Map();
48
+ const inverse = [];
49
+ const foreign = [];
50
+ for (const entity of tables) {
51
+ const output = resolveRelations(entity, entity.members, resolver);
52
+ outputs.set(entity, output);
53
+ inverse.push(...output.inverse);
54
+ foreign.push(...output.foreign);
55
+ }
56
+ const taken = new Set(entities.map((entity) => entity.name));
57
+ const extraModels = [];
58
+ for (const entity of tables) {
59
+ const output = outputs.get(entity);
60
+ const fieldsBuilt = built.get(entity) ?? [];
61
+ if (output === undefined) {
62
+ continue;
63
+ }
64
+ const model = assemble(entity, fieldsBuilt, output, resolver, enums);
65
+ models.set(entity, model);
66
+ for (const placed of output.relations) {
67
+ relationOf.set(`${entity.name}.${placed.relation.name}`, placed.relation);
68
+ }
69
+ for (const member of entity.members) {
70
+ if (member.kind !== 'collection') {
71
+ continue;
72
+ }
73
+ const collection = buildCollection(entity, member, project, resolver, taken);
74
+ if (collection !== undefined) {
75
+ taken.add(collection.model.name);
76
+ extraModels.push(collection.model);
77
+ if (collection.enumDefinition !== undefined) {
78
+ enums.set(collection.enumDefinition.name, collection.enumDefinition);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ for (const { model: owner, relation } of foreign) {
84
+ const model = models.get(owner);
85
+ if (model === undefined) {
86
+ continue;
87
+ }
88
+ let name = relation.name;
89
+ while (model.fields.some((field) => field.name === name) ||
90
+ model.relations.some((item) => item.name === name)) {
91
+ name = `${name}Ref`;
92
+ }
93
+ const placed = { ...relation, name };
94
+ model.relations.push(placed);
95
+ relationOf.set(`${owner.name}.${name}`, placed);
96
+ }
97
+ for (const link of inverse) {
98
+ const owning = relationOf.get(`${link.model.name}.${link.attribute}`);
99
+ if (owning === undefined) {
100
+ project.warnings.push(`${link.model.name}: mappedBy = "${link.attribute}" (declared as ${link.relatedName}) names no relationship of ${link.model.name} in the input; the inverse side was not linked.`);
101
+ }
102
+ else if (owning.relatedName === undefined) {
103
+ owning.relatedName = link.relatedName;
104
+ }
105
+ }
106
+ const all = [...models.values(), ...extraModels];
107
+ dropDanglingRelations(all, warn);
108
+ return {
109
+ models: all,
110
+ enums: [...enums.values()],
111
+ warnings: project.warnings,
112
+ };
113
+ }
114
+ /** Removes relationships whose target has no table (an abstract table-per-class root, a skipped entity). */
115
+ function dropDanglingRelations(models, warn) {
116
+ const known = new Set(models.map((model) => model.name));
117
+ for (const model of models) {
118
+ model.relations = model.relations.filter((relation) => {
119
+ if (known.has(relation.targetModel)) {
120
+ return true;
121
+ }
122
+ warn(`${model.name}.${relation.name}: the target ${relation.targetModel} has no table of its own in the input (an abstract table-per-class entity, or one that was skipped); the relationship was dropped.`);
123
+ return false;
124
+ });
125
+ if (model.compositeForeignKeys !== undefined) {
126
+ model.compositeForeignKeys = model.compositeForeignKeys.filter((key) => known.has(key.targetModel));
127
+ }
128
+ }
129
+ }
130
+ /** The primary key columns of an entity's table. */
131
+ function keyColumns(entity, resolver, depth) {
132
+ const table = modelOwner(entity);
133
+ if (table.inheritance === 'joined' &&
134
+ table.parent !== undefined &&
135
+ depth < 8) {
136
+ const parentKeys = keyColumns(table.parent, resolver, depth + 1);
137
+ const joinColumn = table.notes
138
+ .get('PrimaryKeyJoinColumn')
139
+ ?.string('name');
140
+ return parentKeys.map((key) => parentKeys.length === 1 && joinColumn
141
+ ? { ...key, columnName: cleanIdentifier(joinColumn) }
142
+ : key);
143
+ }
144
+ const keys = [];
145
+ for (const member of table.members) {
146
+ if (!member.isId) {
147
+ continue;
148
+ }
149
+ if (member.kind === 'relation' && depth < 8) {
150
+ const target = resolver.entities.get(member.attr.type.name);
151
+ const pairs = target === undefined
152
+ ? undefined
153
+ : joinPairs(member, target, resolver, `${table.name}.${member.attr.name}`);
154
+ if (pairs !== undefined) {
155
+ keys.push(...pairs.map((pair) => ({
156
+ name: irFieldName(member),
157
+ columnName: pair.columnName,
158
+ })));
159
+ }
160
+ }
161
+ else if (member.kind === 'basic') {
162
+ keys.push({
163
+ name: irFieldName(member),
164
+ columnName: columnNameOf(member),
165
+ });
166
+ }
167
+ }
168
+ return keys;
169
+ }
170
+ function recordEnum(enums, item, place, warn) {
171
+ const definition = item.enumDefinition;
172
+ if (definition === undefined) {
173
+ return;
174
+ }
175
+ const known = enums.get(definition.name);
176
+ if (known === undefined) {
177
+ enums.set(definition.name, definition);
178
+ return;
179
+ }
180
+ if (known.values.some((value, index) => value.dbValue !== definition.values[index]?.dbValue)) {
181
+ warn(`${place}: the enum ${definition.name} is stored differently in different columns (name and ordinal); the IR keeps one definition, with the names.`);
182
+ if (definition.values.every((value) => value.dbValue === value.name)) {
183
+ enums.set(definition.name, definition);
184
+ }
185
+ }
186
+ }
187
+ function assemble(entity, built, output, resolver, enums) {
188
+ const warn = resolver.warn;
189
+ const placed = [...output.relations];
190
+ // A read-only copy of a column (insertable = false, updatable = false) is the relationship's column.
191
+ const columnsTaken = new Set(placed.map((item) => item.relation.columnName.toLowerCase()));
192
+ const kept = [];
193
+ for (const item of built) {
194
+ const path = item.member.path.join('.');
195
+ const consumedBy = output.consumed.get(path);
196
+ if (consumedBy !== undefined) {
197
+ continue;
198
+ }
199
+ const column = item.field.columnName.toLowerCase();
200
+ if (item.readOnly && columnsTaken.has(column)) {
201
+ continue;
202
+ }
203
+ if (kept.some((other) => other.field.columnName.toLowerCase() === column)) {
204
+ warn(`${entity.name}.${path}: another attribute already maps the column "${item.field.columnName}"; this one was skipped.`);
205
+ continue;
206
+ }
207
+ kept.push(item);
208
+ columnsTaken.add(column);
209
+ recordEnum(enums, item, `${entity.name}.${path}`, warn);
210
+ }
211
+ const fields = [
212
+ ...kept.map((item) => item.field),
213
+ ...output.extraFields,
214
+ ];
215
+ // Inheritance --------------------------------------------------------------------------------------
216
+ const extra = inheritanceNotes(entity, fields, resolver);
217
+ // Primary key --------------------------------------------------------------------------------------
218
+ const keyMembers = entity.members.filter((member) => member.isId);
219
+ const names = keyMembers.flatMap((member) => {
220
+ const via = output.consumed.get(member.path.join('.'));
221
+ return [via ?? irFieldName(member)];
222
+ });
223
+ const keyNames = [...new Set(names)];
224
+ let compositePrimaryKey;
225
+ if (extra.primaryKeyRelation !== undefined) {
226
+ // Joined inheritance: the key is the foreign key to the parent table.
227
+ }
228
+ else if (keyNames.length === 0) {
229
+ warn(`${entity.name}: the entity has no @Id (or @EmbeddedId); other formats expect every model to have a primary key.`);
230
+ }
231
+ else if (keyNames.length > 1) {
232
+ compositePrimaryKey = keyNames;
233
+ for (const item of kept) {
234
+ if (keyNames.includes(item.field.name)) {
235
+ item.field.isPrimaryKey = false;
236
+ }
237
+ }
238
+ }
239
+ const relationItems = placed;
240
+ for (const item of relationItems) {
241
+ if (compositePrimaryKey !== undefined) {
242
+ delete item.relation.isPrimaryKey;
243
+ }
244
+ else if (keyNames.length === 1 && keyNames[0] === item.relation.name) {
245
+ item.relation.isPrimaryKey = true;
246
+ }
247
+ }
248
+ for (const item of kept) {
249
+ if (keyNames.length === 1 && keyNames[0] === item.field.name) {
250
+ item.field.isPrimaryKey = true;
251
+ }
252
+ }
253
+ // Indexes ------------------------------------------------------------------------------------------
254
+ const nameOf = new Map();
255
+ for (const field of fields) {
256
+ nameOf.set(field.columnName.toLowerCase(), field.name);
257
+ nameOf.set(field.name, field.name);
258
+ }
259
+ for (const item of relationItems) {
260
+ nameOf.set(item.relation.columnName.toLowerCase(), item.relation.name);
261
+ nameOf.set(item.relation.name, item.relation.name);
262
+ }
263
+ const naturalIds = kept
264
+ .filter((item) => item.naturalId)
265
+ .map((item) => item.field.name);
266
+ const indexResult = buildIndexes(entity, nameOf, naturalIds, warn);
267
+ for (const field of fields) {
268
+ if (indexResult.uniqueFields.has(field.name) && !field.isPrimaryKey) {
269
+ field.isUnique = true;
270
+ }
271
+ }
272
+ const indexes = indexResult.indexes;
273
+ tableNotes(entity, kept, warn);
274
+ const schema = entity.notes
275
+ .get('Table')
276
+ ?.string('schema');
277
+ const model = {
278
+ name: entity.name,
279
+ tableName: tableNameOf(entity),
280
+ appLabel: resolver.project.appLabel,
281
+ fields: [...extra.leadingFields, ...fields, ...extra.trailingFields],
282
+ relations: [
283
+ ...extra.relations,
284
+ ...relationItems
285
+ .sort((first, second) => first.order - second.order)
286
+ .map((item) => item.relation),
287
+ ],
288
+ indexes,
289
+ ...(compositePrimaryKey === undefined ? {} : { compositePrimaryKey }),
290
+ ...(output.composite.length === 0
291
+ ? {}
292
+ : { compositeForeignKeys: output.composite }),
293
+ ...(schema === undefined || schema === '' ? {} : { schema }),
294
+ };
295
+ return model;
296
+ }
297
+ //# sourceMappingURL=jpaBuild.js.map