@mikro-orm/entity-generator 7.1.0-dev.43 → 7.1.0-dev.44

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.
@@ -6,6 +6,7 @@ import { writeFile } from 'node:fs/promises';
6
6
  import { DefineEntitySourceFile } from './DefineEntitySourceFile.js';
7
7
  import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
8
8
  import { NativeEnumSourceFile } from './NativeEnumSourceFile.js';
9
+ import { RoutineSourceFile } from './RoutineSourceFile.js';
9
10
  import { SourceFile } from './SourceFile.js';
10
11
  /** Generates entity source files by introspecting an existing database schema. */
11
12
  export class EntityGenerator {
@@ -33,6 +34,8 @@ export class EntityGenerator {
33
34
  async generate(options = {}) {
34
35
  options = Utils.mergeConfig({}, this.#config.get('entityGenerator'), options);
35
36
  const schema = await DatabaseSchema.create(this.#connection, this.#platform, this.#config, undefined, undefined, options.takeTables, options.skipTables);
37
+ // The generator emits whatever it can find, so always load routines here.
38
+ await schema.loadRoutines(this.#connection, this.#platform);
36
39
  const metadata = await this.getEntityMetadata(schema, options);
37
40
  const defaultPath = `${this.#config.get('baseDir')}/generated-entities`;
38
41
  const baseDir = fs.normalizePath(options.path ?? defaultPath);
@@ -54,6 +57,9 @@ export class EntityGenerator {
54
57
  for (const nativeEnum of Object.values(schema.getNativeEnums())) {
55
58
  this.#sources.push(new NativeEnumSourceFile({}, this.#namingStrategy, this.#platform, options, nativeEnum));
56
59
  }
60
+ for (const routine of schema.getRoutines()) {
61
+ this.#sources.push(new RoutineSourceFile(routine, this.#namingStrategy, this.#platform, options));
62
+ }
57
63
  const files = this.#sources.map(file => [file.getBaseName(), file.generate()]);
58
64
  if (options.save) {
59
65
  fs.ensureDir(baseDir);
@@ -0,0 +1,17 @@
1
+ import type { GenerateOptions, NamingStrategy, Platform } from '@mikro-orm/core';
2
+ import type { SqlRoutineDef } from '@mikro-orm/sql';
3
+ /** Always emits the class-less `new Routine(...)` form; there is no decorator form for routines. */
4
+ export declare class RoutineSourceFile {
5
+ private readonly routine;
6
+ private readonly namingStrategy;
7
+ private readonly platform;
8
+ private readonly options;
9
+ constructor(routine: SqlRoutineDef, namingStrategy: NamingStrategy, platform: Platform, options: GenerateOptions);
10
+ generate(): string;
11
+ getBaseName(extension?: string): string;
12
+ getClassName(): string;
13
+ private buildConfig;
14
+ private formatParams;
15
+ private formatReturns;
16
+ private emitRoutine;
17
+ }
@@ -0,0 +1,136 @@
1
+ const identifierRegex = /^(?:[$_\p{ID_Start}])(?:[$\p{ID_Continue}])*$/u;
2
+ function quote(val) {
3
+ // Backslashes first so subsequent escapes don't double up the `\` we just added.
4
+ const escaped = val
5
+ .replaceAll('\\', '\\\\')
6
+ .replaceAll('\r', '\\r')
7
+ .replaceAll('\n', '\\n')
8
+ .replaceAll('\t', '\\t')
9
+ .replaceAll(`'`, `\\'`);
10
+ return `'${escaped}'`;
11
+ }
12
+ function safeKey(name) {
13
+ return identifierRegex.test(name) ? name : quote(name);
14
+ }
15
+ function quoteMultiline(val) {
16
+ if (val.includes('\n') || val.includes('`') || val.includes('${')) {
17
+ return `\`${val.replaceAll('\\', '\\\\').replaceAll('`', '\\`').replaceAll('${', '\\${')}\``;
18
+ }
19
+ return quote(val);
20
+ }
21
+ function toPascalCase(name) {
22
+ const pascal = name
23
+ .split(/[_\s-]+/)
24
+ .filter(Boolean)
25
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
26
+ .join('');
27
+ // A routine name starting with a digit (e.g. `2fa_check`) would yield an invalid identifier;
28
+ // prefix `_` so the emitted `export const ...` parses.
29
+ return /^[A-Za-z_$]/.test(pascal) ? pascal : `_${pascal}`;
30
+ }
31
+ // `satisfies` ties this list to `RoutineRuntimeType` so the runtime set can't drift from the union.
32
+ const ROUTINE_RUNTIME_TYPES = [
33
+ 'string',
34
+ 'number',
35
+ 'boolean',
36
+ 'bigint',
37
+ 'Buffer',
38
+ 'Date',
39
+ 'object',
40
+ 'any',
41
+ ];
42
+ const ROUTINE_RUNTIME_TYPE_SET = new Set(ROUTINE_RUNTIME_TYPES);
43
+ function narrowRuntimeType(runtimeType) {
44
+ return runtimeType && ROUTINE_RUNTIME_TYPE_SET.has(runtimeType) ? runtimeType : 'any';
45
+ }
46
+ /** Always emits the class-less `new Routine(...)` form; there is no decorator form for routines. */
47
+ export class RoutineSourceFile {
48
+ routine;
49
+ namingStrategy;
50
+ platform;
51
+ options;
52
+ constructor(routine, namingStrategy, platform, options) {
53
+ this.routine = routine;
54
+ this.namingStrategy = namingStrategy;
55
+ this.platform = platform;
56
+ this.options = options;
57
+ }
58
+ generate() {
59
+ return this.emitRoutine(this.getClassName(), this.buildConfig());
60
+ }
61
+ getBaseName(extension = '.ts') {
62
+ return `${this.options.fileName(this.getClassName())}${extension}`;
63
+ }
64
+ getClassName() {
65
+ return toPascalCase(this.routine.name);
66
+ }
67
+ buildConfig() {
68
+ const lines = [];
69
+ lines.push(` name: ${quote(this.routine.name)},`);
70
+ lines.push(` type: ${quote(this.routine.type)},`);
71
+ if (this.routine.schema) {
72
+ lines.push(` schema: ${quote(this.routine.schema)},`);
73
+ }
74
+ if (this.routine.language) {
75
+ lines.push(` language: ${quote(this.routine.language)},`);
76
+ }
77
+ if (this.routine.security) {
78
+ lines.push(` security: ${quote(this.routine.security)},`);
79
+ }
80
+ if (this.routine.deterministic != null) {
81
+ lines.push(` deterministic: ${this.routine.deterministic},`);
82
+ }
83
+ if (this.routine.comment) {
84
+ lines.push(` comment: ${quote(this.routine.comment)},`);
85
+ }
86
+ if (this.routine.params.length > 0) {
87
+ lines.push(` params: ${this.formatParams(this.routine.params)},`);
88
+ }
89
+ else {
90
+ lines.push(` params: {},`);
91
+ }
92
+ if (this.routine.returns) {
93
+ lines.push(` returns: ${this.formatReturns(this.routine.returns)},`);
94
+ }
95
+ if (this.routine.body) {
96
+ lines.push(` body: ${quoteMultiline(this.routine.body)},`);
97
+ }
98
+ else if (this.routine.expression) {
99
+ lines.push(` expression: ${quoteMultiline(this.routine.expression)},`);
100
+ }
101
+ return `{\n${lines.join('\n')}\n}`;
102
+ }
103
+ formatParams(params) {
104
+ const lines = params.map(p => {
105
+ const parts = [`type: ${quote(p.type)}`];
106
+ if (p.direction !== 'in') {
107
+ parts.push(`direction: ${quote(p.direction)}`);
108
+ }
109
+ if (p.direction === 'out' || p.direction === 'inout') {
110
+ parts.push(`ref: true`);
111
+ }
112
+ if (p.nullable) {
113
+ parts.push(`nullable: true`);
114
+ }
115
+ if (p.defaultRaw) {
116
+ parts.push(`defaultRaw: ${quote(p.defaultRaw)}`);
117
+ }
118
+ return ` ${safeKey(p.name)}: { ${parts.join(', ')} },`;
119
+ });
120
+ return `{\n${lines.join('\n')}\n }`;
121
+ }
122
+ formatReturns(returns) {
123
+ const parts = [];
124
+ // Narrow to `RoutineRuntimeType`; unrecognised types collapse to `'any'`.
125
+ const inferred = returns.runtimeType ?? this.platform.getMappedType(returns.type)?.runtimeType;
126
+ parts.push(`runtimeType: ${quote(narrowRuntimeType(inferred))}`);
127
+ parts.push(`columnType: ${quote(returns.type)}`);
128
+ if (returns.nullable) {
129
+ parts.push(`nullable: true`);
130
+ }
131
+ return `{ ${parts.join(', ')} }`;
132
+ }
133
+ emitRoutine(className, config) {
134
+ return `import { Routine } from '@mikro-orm/core';\n\nexport const ${className} = new Routine(${config});\n`;
135
+ }
136
+ }
package/index.d.ts CHANGED
@@ -3,3 +3,4 @@
3
3
  * @module entity-generator
4
4
  */
5
5
  export * from './EntityGenerator.js';
6
+ export * from './RoutineSourceFile.js';
package/index.js CHANGED
@@ -3,3 +3,4 @@
3
3
  * @module entity-generator
4
4
  */
5
5
  export * from './EntityGenerator.js';
6
+ export * from './RoutineSourceFile.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/entity-generator",
3
- "version": "7.1.0-dev.43",
3
+ "version": "7.1.0-dev.44",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -47,13 +47,13 @@
47
47
  "copy": "node ../../scripts/copy.mjs"
48
48
  },
49
49
  "dependencies": {
50
- "@mikro-orm/sql": "7.1.0-dev.43"
50
+ "@mikro-orm/sql": "7.1.0-dev.44"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@mikro-orm/core": "^7.0.17"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.1.0-dev.43"
56
+ "@mikro-orm/core": "7.1.0-dev.44"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"