• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.49
/src/commands/MigrationGenerateCommand.ts
1
import { format } from "@sqltools/formatter/lib/sqlFormatter"
9✔
2
import ansi from "ansis"
9✔
3
import path from "path"
9✔
4
import process from "process"
9✔
5
import yargs from "yargs"
6
import { DataSource } from "../data-source"
7
import { PlatformTools } from "../platform/PlatformTools"
9✔
8
import { camelCase } from "../util/StringUtils"
9✔
9
import { CommandUtils } from "./CommandUtils"
9✔
10

11
/**
12
 * Generates a new migration file with sql needs to be executed to update schema.
13
 */
14
export class MigrationGenerateCommand implements yargs.CommandModule {
9✔
15
    command = "migration:generate <path>"
9✔
16
    describe =
9✔
17
        "Generates a new migration file with sql needs to be executed to update schema."
18

19
    builder(args: yargs.Argv) {
20
        return args
×
21
            .positional("path", {
22
                type: "string",
23
                describe: "Path of the migration file",
24
                demandOption: true,
25
            })
26
            .option("dataSource", {
27
                alias: "d",
28
                type: "string",
29
                describe:
30
                    "Path to the file where your DataSource instance is defined.",
31
                demandOption: true,
32
            })
33
            .option("p", {
34
                alias: "pretty",
35
                type: "boolean",
36
                default: false,
37
                describe: "Pretty-print generated SQL",
38
            })
39
            .option("o", {
40
                alias: "outputJs",
41
                type: "boolean",
42
                default: false,
43
                describe:
44
                    "Generate a migration file on Javascript instead of Typescript",
45
            })
46
            .option("esm", {
47
                type: "boolean",
48
                default: false,
49
                describe:
50
                    "Generate a migration file on ESM instead of CommonJS",
51
            })
52
            .option("dr", {
53
                alias: "dryrun",
54
                type: "boolean",
55
                default: false,
56
                describe:
57
                    "Prints out the contents of the migration instead of writing it to a file",
58
            })
59
            .option("ch", {
60
                alias: "check",
61
                type: "boolean",
62
                default: false,
63
                describe:
64
                    "Verifies that the current database is up to date and that no migrations are needed. Otherwise exits with code 1.",
65
            })
66
            .option("t", {
67
                alias: "timestamp",
68
                type: "number",
69
                default: false,
70
                describe: "Custom timestamp for the migration name",
71
            })
72
    }
73

74
    async handler(args: yargs.Arguments<any & { path: string }>) {
UNCOV
75
        const timestamp = CommandUtils.getTimestamp(args.timestamp)
×
UNCOV
76
        const extension = args.outputJs ? ".js" : ".ts"
×
UNCOV
77
        const fullPath = args.path.startsWith("/")
×
78
            ? args.path
79
            : path.resolve(process.cwd(), args.path)
UNCOV
80
        const filename = timestamp + "-" + path.basename(fullPath) + extension
×
81

UNCOV
82
        let dataSource: DataSource | undefined = undefined
×
UNCOV
83
        try {
×
UNCOV
84
            dataSource = await CommandUtils.loadDataSource(
×
85
                path.resolve(process.cwd(), args.dataSource as string),
86
            )
UNCOV
87
            dataSource.setOptions({
×
88
                synchronize: false,
89
                migrationsRun: false,
90
                dropSchema: false,
91
                logging: false,
92
            })
UNCOV
93
            await dataSource.initialize()
×
94

UNCOV
95
            const upSqls: string[] = [],
×
UNCOV
96
                downSqls: string[] = []
×
97

UNCOV
98
            try {
×
UNCOV
99
                const sqlInMemory = await dataSource.driver
×
100
                    .createSchemaBuilder()
101
                    .log()
102

UNCOV
103
                if (args.pretty) {
×
104
                    sqlInMemory.upQueries.forEach((upQuery) => {
×
105
                        upQuery.query = MigrationGenerateCommand.prettifyQuery(
×
106
                            upQuery.query,
107
                        )
108
                    })
109
                    sqlInMemory.downQueries.forEach((downQuery) => {
×
110
                        downQuery.query =
×
111
                            MigrationGenerateCommand.prettifyQuery(
112
                                downQuery.query,
113
                            )
114
                    })
115
                }
116

UNCOV
117
                sqlInMemory.upQueries.forEach((upQuery) => {
×
UNCOV
118
                    upSqls.push(
×
119
                        "        await queryRunner.query(`" +
120
                            upQuery.query.replaceAll("`", "\\`") +
121
                            "`" +
122
                            MigrationGenerateCommand.queryParams(
123
                                upQuery.parameters,
124
                            ) +
125
                            ");",
126
                    )
127
                })
UNCOV
128
                sqlInMemory.downQueries.forEach((downQuery) => {
×
UNCOV
129
                    downSqls.push(
×
130
                        "        await queryRunner.query(`" +
131
                            downQuery.query.replaceAll("`", "\\`") +
132
                            "`" +
133
                            MigrationGenerateCommand.queryParams(
134
                                downQuery.parameters,
135
                            ) +
136
                            ");",
137
                    )
138
                })
139
            } finally {
UNCOV
140
                await dataSource.destroy()
×
141
            }
142

UNCOV
143
            if (!upSqls.length) {
×
144
                if (args.check) {
×
145
                    console.log(
×
146
                        ansi.green`No changes in database schema were found`,
147
                    )
148
                    process.exit(0)
×
149
                } else {
150
                    console.log(
×
151
                        ansi.yellow`No changes in database schema were found - cannot generate a migration. To create a new empty migration use "typeorm migration:create" command`,
152
                    )
153
                    process.exit(1)
×
154
                }
UNCOV
155
            } else if (!args.path) {
×
156
                console.log(ansi.yellow`Please specify a migration path`)
×
157
                process.exit(1)
×
158
            }
159

UNCOV
160
            const fileContent = args.outputJs
×
161
                ? MigrationGenerateCommand.getJavascriptTemplate(
162
                      path.basename(fullPath),
163
                      timestamp,
164
                      upSqls,
165
                      downSqls.reverse(),
166
                      args.esm,
167
                  )
168
                : MigrationGenerateCommand.getTemplate(
169
                      path.basename(fullPath),
170
                      timestamp,
171
                      upSqls,
172
                      downSqls.reverse(),
173
                  )
174

UNCOV
175
            if (args.check) {
×
176
                console.log(
×
177
                    ansi.yellow`Unexpected changes in database schema were found in check mode:\n\n${ansi.white(
178
                        fileContent,
179
                    )}`,
180
                )
181
                process.exit(1)
×
182
            }
183

UNCOV
184
            if (args.dryrun) {
×
185
                console.log(
×
186
                    ansi.green(
187
                        `Migration ${ansi.blue(
188
                            fullPath + extension,
189
                        )} has content:\n\n${ansi.white(fileContent)}`,
190
                    ),
191
                )
192
            } else {
193
                const migrationFileName =
UNCOV
194
                    path.dirname(fullPath) + "/" + filename
×
UNCOV
195
                await CommandUtils.createFile(migrationFileName, fileContent)
×
196

UNCOV
197
                console.log(
×
198
                    ansi.green`Migration ${ansi.blue(
199
                        migrationFileName,
200
                    )} has been generated successfully.`,
201
                )
UNCOV
202
                if (args.exitProcess !== false) {
×
203
                    process.exit(0)
×
204
                }
205
            }
206
        } catch (err) {
207
            PlatformTools.logCmdErr("Error during migration generation:", err)
×
208
            process.exit(1)
×
209
        }
210
    }
211

212
    // -------------------------------------------------------------------------
213
    // Protected Static Methods
214
    // -------------------------------------------------------------------------
215

216
    /**
217
     * Formats query parameters for migration queries if parameters actually exist
218
     */
219
    protected static queryParams(parameters: any[] | undefined): string {
UNCOV
220
        if (!parameters || !parameters.length) {
×
UNCOV
221
            return ""
×
222
        }
223

224
        return `, ${JSON.stringify(parameters)}`
×
225
    }
226

227
    /**
228
     * Gets contents of the migration file.
229
     */
230
    protected static getTemplate(
231
        name: string,
232
        timestamp: number,
233
        upSqls: string[],
234
        downSqls: string[],
235
    ): string {
UNCOV
236
        const migrationName = `${camelCase(name, true)}${timestamp}`
×
237

UNCOV
238
        return `import { MigrationInterface, QueryRunner } from "typeorm";
×
239

240
export class ${migrationName} implements MigrationInterface {
241
    name = '${migrationName}'
242

243
    public async up(queryRunner: QueryRunner): Promise<void> {
244
${upSqls.join(`
245
`)}
246
    }
247

248
    public async down(queryRunner: QueryRunner): Promise<void> {
249
${downSqls.join(`
250
`)}
251
    }
252

253
}
254
`
255
    }
256

257
    /**
258
     * Gets contents of the migration file in Javascript.
259
     */
260
    protected static getJavascriptTemplate(
261
        name: string,
262
        timestamp: number,
263
        upSqls: string[],
264
        downSqls: string[],
265
        esm: boolean,
266
    ): string {
UNCOV
267
        const migrationName = `${camelCase(name, true)}${timestamp}`
×
268

UNCOV
269
        const exportMethod = esm ? "export" : "module.exports ="
×
270

UNCOV
271
        return `/**
×
272
 * @typedef {import('typeorm').MigrationInterface} MigrationInterface
273
 */
274

275
/**
276
 * @class
277
 * @implements {MigrationInterface}
278
 */
279
${exportMethod} class ${migrationName} {
280
    name = '${migrationName}'
281

282
    async up(queryRunner) {
283
${upSqls.join(`
284
`)}
285
    }
286

287
    async down(queryRunner) {
288
${downSqls.join(`
289
`)}
290
    }
291
}
292
`
293
    }
294

295
    /**
296
     *
297
     */
298
    protected static prettifyQuery(query: string) {
299
        const formattedQuery = format(query, { indent: "    " })
×
300
        return (
×
301
            "\n" + formattedQuery.replace(/^/gm, "            ") + "\n        "
302
        )
303
    }
304
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc