forked from typeorm/typeorm
/
ConnectionOptionsReader.ts
215 lines (175 loc) · 8.13 KB
/
ConnectionOptionsReader.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import appRootPath from "app-root-path";
import {ConnectionOptions} from "./ConnectionOptions";
import {PlatformTools} from "../platform/PlatformTools";
import {ConnectionOptionsEnvReader} from "./options-reader/ConnectionOptionsEnvReader";
import {ConnectionOptionsYmlReader} from "./options-reader/ConnectionOptionsYmlReader";
import {ConnectionOptionsXmlReader} from "./options-reader/ConnectionOptionsXmlReader";
/**
* Reads connection options from the ormconfig.
* Can read from multiple file extensions including env, json, js, xml and yml.
*/
export class ConnectionOptionsReader {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected options?: {
/**
* Directory where ormconfig should be read from.
* By default its your application root (where your app package.json is located).
*/
root?: string,
/**
* Filename of the ormconfig configuration. By default its equal to "ormconfig".
*/
configName?: string
}) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Returns all connection options read from the ormconfig.
*/
async all(): Promise<ConnectionOptions[]> {
const options = await this.load();
if (!options)
throw new Error(`No connection options were found in any orm configuration files.`);
return options;
}
/**
* Gets a connection with a given name read from ormconfig.
* If connection with such name would not be found then it throw error.
*/
async get(name: string): Promise<ConnectionOptions> {
const allOptions = await this.all();
const targetOptions = allOptions.find(options => options.name === name || (name === "default" && !options.name));
if (!targetOptions)
throw new Error(`Cannot find connection ${name} because its not defined in any orm configuration files.`);
return targetOptions;
}
/**
* Checks if there is a TypeORM configuration file.
*/
async has(name: string): Promise<boolean> {
const allOptions = await this.load();
if (!allOptions)
return false;
const targetOptions = allOptions.find(options => options.name === name || (name === "default" && !options.name));
return !!targetOptions;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Loads all connection options from a configuration file.
*
* todo: get in count NODE_ENV somehow
*/
protected async load(): Promise<ConnectionOptions[]|undefined> {
let connectionOptions: ConnectionOptions|ConnectionOptions[]|undefined = undefined;
const fileFormats = ["env", "js", "cjs", "ts", "json", "yml", "yaml", "xml"];
// Detect if baseFilePath contains file extension
const possibleExtension = this.baseFilePath.substr(this.baseFilePath.lastIndexOf("."));
const fileExtension = fileFormats.find(extension => `.${extension}` === possibleExtension);
// try to find any of following configuration formats
const foundFileFormat = fileExtension || fileFormats.find(format => {
return PlatformTools.fileExist(this.baseFilePath + "." + format);
});
// if .env file found then load all its variables into process.env using dotenv package
if (foundFileFormat === "env") {
PlatformTools.dotenv(this.baseFilePath);
} else if (PlatformTools.fileExist(".env")) {
PlatformTools.dotenv(".env");
}
// Determine config file name
const configFile = fileExtension ? this.baseFilePath : this.baseFilePath + "." + foundFileFormat;
// try to find connection options from any of available sources of configuration
if (PlatformTools.getEnvVariable("TYPEORM_CONNECTION") || PlatformTools.getEnvVariable("TYPEORM_URL")) {
connectionOptions = new ConnectionOptionsEnvReader().read();
} else if (foundFileFormat === "js" || foundFileFormat === "cjs") {
connectionOptions = await require(configFile);
} else if (foundFileFormat === "ts") {
connectionOptions = await require(configFile);
} else if (foundFileFormat === "json") {
connectionOptions = require(configFile);
} else if (foundFileFormat === "yml") {
connectionOptions = new ConnectionOptionsYmlReader().read(configFile);
} else if (foundFileFormat === "yaml") {
connectionOptions = new ConnectionOptionsYmlReader().read(configFile);
} else if (foundFileFormat === "xml") {
connectionOptions = await new ConnectionOptionsXmlReader().read(configFile);
}
// normalize and return connection options
if (connectionOptions) {
return this.normalizeConnectionOptions(connectionOptions);
}
return undefined;
}
/**
* Normalize connection options.
*/
protected normalizeConnectionOptions(connectionOptions: ConnectionOptions|ConnectionOptions[]): ConnectionOptions[] {
if (!(Array.isArray(connectionOptions)))
connectionOptions = [connectionOptions];
connectionOptions.forEach(options => {
if (options.entities) {
const entities = (options.entities as any[]).map(entity => {
if (typeof entity === "string" && entity.substr(0, 1) !== "/")
return this.baseDirectory + "/" + entity;
return entity;
});
Object.assign(connectionOptions, { entities: entities });
}
if (options.subscribers) {
const subscribers = (options.subscribers as any[]).map(subscriber => {
if (typeof subscriber === "string" && subscriber.substr(0, 1) !== "/")
return this.baseDirectory + "/" + subscriber;
return subscriber;
});
Object.assign(connectionOptions, { subscribers: subscribers });
}
if (options.migrations) {
const migrations = (options.migrations as any[]).map(migration => {
if (typeof migration === "string" && migration.substr(0, 1) !== "/")
return this.baseDirectory + "/" + migration;
return migration;
});
Object.assign(connectionOptions, { migrations: migrations });
}
// make database path file in sqlite relative to package.json
if (options.type === "sqlite" || options.type === "better-sqlite3") {
if (typeof options.database === "string" &&
options.database.substr(0, 1) !== "/" && // unix absolute
options.database.substr(1, 2) !== ":\\" && // windows absolute
options.database !== ":memory:") {
Object.assign(options, {
database: this.baseDirectory + "/" + options.database
});
}
}
});
return connectionOptions;
}
/**
* Gets directory where configuration file should be located and configuration file name.
*/
protected get baseFilePath(): string {
return this.baseDirectory + "/" + this.baseConfigName;
}
/**
* Gets directory where configuration file should be located.
*/
protected get baseDirectory(): string {
if (this.options && this.options.root)
return this.options.root;
return appRootPath.path;
}
/**
* Gets configuration file name.
*/
protected get baseConfigName(): string {
if (this.options && this.options.configName)
return this.options.configName;
return "ormconfig";
}
}