/
MigrationGenerateCommand.ts
187 lines (163 loc) · 7.65 KB
/
MigrationGenerateCommand.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
import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader";
import {CommandUtils} from "./CommandUtils";
import {Connection} from "../connection/Connection";
import {createConnection} from "../index";
import {MysqlDriver} from "../driver/mysql/MysqlDriver";
import {camelCase} from "../util/StringUtils";
import * as yargs from "yargs";
import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver";
import chalk from "chalk";
import { format } from "@sqltools/formatter/lib/sqlFormatter";
/**
* Generates a new migration file with sql needs to be executed to update schema.
*/
export class MigrationGenerateCommand implements yargs.CommandModule {
command = "migration:generate";
describe = "Generates a new migration file with sql needs to be executed to update schema.";
aliases = "migrations:generate";
builder(args: yargs.Argv) {
return args
.option("c", {
alias: "connection",
default: "default",
describe: "Name of the connection on which run a query."
})
.option("n", {
alias: "name",
describe: "Name of the migration class.",
demand: true
})
.option("d", {
alias: "dir",
describe: "Directory where migration should be created."
})
.option("p", {
alias: "pretty",
type: "boolean",
default: false,
describe: "Pretty-print generated SQL",
})
.option("f", {
alias: "config",
default: "ormconfig",
describe: "Name of the file with connection configuration."
});
}
async handler(args: yargs.Arguments) {
if (args._[0] === "migrations:generate") {
console.log("'migrations:generate' is deprecated, please use 'migration:generate' instead");
}
const timestamp = new Date().getTime();
const filename = timestamp + "-" + args.name + ".ts";
let directory = args.dir;
// if directory is not set then try to open tsconfig and find default path there
if (!directory) {
try {
const connectionOptionsReader = new ConnectionOptionsReader({
root: process.cwd(),
configName: args.config as any
});
const connectionOptions = await connectionOptionsReader.get(args.connection as any);
directory = connectionOptions.cli ? connectionOptions.cli.migrationsDir : undefined;
} catch (err) { }
}
let connection: Connection|undefined = undefined;
try {
const connectionOptionsReader = new ConnectionOptionsReader({
root: process.cwd(),
configName: args.config as any
});
const connectionOptions = await connectionOptionsReader.get(args.connection as any);
Object.assign(connectionOptions, {
synchronize: false,
migrationsRun: false,
dropSchema: false,
logging: false
});
connection = await createConnection(connectionOptions);
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
if (args.pretty) {
sqlInMemory.upQueries.forEach(upQuery => {
upQuery.query = MigrationGenerateCommand.prettifyQuery(upQuery.query);
});
sqlInMemory.downQueries.forEach(downQuery => {
downQuery.query = MigrationGenerateCommand.prettifyQuery(downQuery.query);
});
}
const upSqls: string[] = [], downSqls: string[] = [];
// mysql is exceptional here because it uses ` character in to escape names in queries, that's why for mysql
// we are using simple quoted string instead of template string syntax
if (connection.driver instanceof MysqlDriver || connection.driver instanceof AuroraDataApiDriver) {
sqlInMemory.upQueries.forEach(upQuery => {
upSqls.push(" await queryRunner.query(\"" + upQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");");
});
sqlInMemory.downQueries.forEach(downQuery => {
downSqls.push(" await queryRunner.query(\"" + downQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");");
});
} else {
sqlInMemory.upQueries.forEach(upQuery => {
upSqls.push(" await queryRunner.query(`" + upQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");");
});
sqlInMemory.downQueries.forEach(downQuery => {
downSqls.push(" await queryRunner.query(`" + downQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");");
});
}
if (upSqls.length) {
if (args.name) {
const fileContent = MigrationGenerateCommand.getTemplate(args.name as any, timestamp, upSqls, downSqls.reverse());
const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename;
await CommandUtils.createFile(path, fileContent);
console.log(chalk.green(`Migration ${chalk.blue(path)} has been generated successfully.`));
} else {
console.log(chalk.yellow("Please specify migration name"));
}
} else {
console.log(chalk.yellow(`No changes in database schema were found - cannot generate a migration. To create a new empty migration use "typeorm migration:create" command`));
}
await connection.close();
} catch (err) {
if (connection) await (connection as Connection).close();
console.log(chalk.black.bgRed("Error during migration generation:"));
console.error(err);
process.exit(1);
}
}
// -------------------------------------------------------------------------
// Protected Static Methods
// -------------------------------------------------------------------------
/**
* Formats query parameters for migration queries if parameters actually exist
*/
protected static queryParams(parameters: any[] | undefined): string {
if (!parameters || !parameters.length) {
return "";
}
return `, ${JSON.stringify(parameters)}`;
}
/**
* Gets contents of the migration file.
*/
protected static getTemplate(name: string, timestamp: number, upSqls: string[], downSqls: string[]): string {
const migrationName = `${camelCase(name, true)}${timestamp}`;
return `import {MigrationInterface, QueryRunner} from "typeorm";
export class ${migrationName} implements MigrationInterface {
name = '${migrationName}'
public async up(queryRunner: QueryRunner): Promise<void> {
${upSqls.join(`
`)}
}
public async down(queryRunner: QueryRunner): Promise<void> {
${downSqls.join(`
`)}
}
}
`;
}
/**
*
*/
protected static prettifyQuery(query: string) {
const formattedQuery = format(query, { indent: " " });
return "\n" + formattedQuery.replace(/^/gm, " ") + "\n ";
}
}