fragment-ts 1.0.43 ā 1.0.45
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.
- package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
- package/dist/cli/commands/diagnostics.command.js +17 -73
- package/dist/cli/commands/diagnostics.command.js.map +1 -1
- package/dist/cli/commands/env.command.d.ts +10 -0
- package/dist/cli/commands/env.command.d.ts.map +1 -0
- package/dist/cli/commands/env.command.js +222 -0
- package/dist/cli/commands/env.command.js.map +1 -0
- package/dist/cli/commands/generate.command.js +9 -9
- package/dist/cli/commands/init.command.d.ts.map +1 -1
- package/dist/cli/commands/init.command.js +65 -35
- package/dist/cli/commands/init.command.js.map +1 -1
- package/dist/cli/commands/migrate.command.d.ts +14 -41
- package/dist/cli/commands/migrate.command.d.ts.map +1 -1
- package/dist/cli/commands/migrate.command.js +182 -424
- package/dist/cli/commands/migrate.command.js.map +1 -1
- package/dist/cli/commands/test.command.d.ts.map +1 -1
- package/dist/cli/commands/test.command.js +5 -51
- package/dist/cli/commands/test.command.js.map +1 -1
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/scanner/component-scanner.d.ts +0 -4
- package/dist/core/scanner/component-scanner.d.ts.map +1 -1
- package/dist/core/scanner/component-scanner.js +0 -15
- package/dist/core/scanner/component-scanner.js.map +1 -1
- package/dist/shared/config.utils.d.ts +0 -27
- package/dist/shared/config.utils.d.ts.map +1 -1
- package/dist/shared/config.utils.js +7 -38
- package/dist/shared/config.utils.js.map +1 -1
- package/dist/shared/env.utils.d.ts +6 -18
- package/dist/shared/env.utils.d.ts.map +1 -1
- package/dist/shared/env.utils.js +84 -39
- package/dist/shared/env.utils.js.map +1 -1
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js +2 -12
- package/dist/testing/runner.js.map +1 -1
- package/dist/typeorm/typeorm-module.d.ts.map +1 -1
- package/dist/typeorm/typeorm-module.js +4 -16
- package/dist/typeorm/typeorm-module.js.map +1 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +1 -20
- package/dist/web/application.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/commands/diagnostics.command.ts +16 -87
- package/src/cli/commands/env.command.ts +224 -0
- package/src/cli/commands/generate.command.ts +9 -9
- package/src/cli/commands/init.command.ts +68 -37
- package/src/cli/commands/migrate.command.ts +244 -528
- package/src/cli/commands/test.command.ts +5 -61
- package/src/cli/index.ts +21 -0
- package/src/core/scanner/component-scanner.ts +0 -15
- package/src/shared/config.utils.ts +10 -54
- package/src/shared/env.utils.ts +50 -44
- package/src/testing/runner.ts +2 -11
- package/src/typeorm/typeorm-module.ts +5 -14
- package/src/web/application.ts +1 -21
|
@@ -3,22 +3,19 @@ import * as fs from "fs-extra";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import ora from "ora";
|
|
6
|
-
import { execSync, spawn } from "child_process";
|
|
7
|
-
import { TypeORMModule } from "../../typeorm/typeorm-module";
|
|
8
6
|
import { DataSource } from "typeorm";
|
|
7
|
+
import { TypeORMModule } from "../../typeorm/typeorm-module";
|
|
9
8
|
import { ConfigUtils } from "../../shared/config.utils";
|
|
10
|
-
import {
|
|
9
|
+
import { TsConfigUtils } from "../../shared/tsconfig.utils";
|
|
10
|
+
import { globSync } from "glob";
|
|
11
11
|
|
|
12
12
|
export class MigrateCommand {
|
|
13
13
|
private static tsNodeAvailable: boolean | null = null;
|
|
14
|
-
private static tsConfigExists: boolean | null = null;
|
|
15
14
|
|
|
16
15
|
static register(program: Command): void {
|
|
17
16
|
program
|
|
18
17
|
.command("migrate")
|
|
19
|
-
.description(
|
|
20
|
-
"Run all pending migrations (auto-detect TypeScript/JavaScript)",
|
|
21
|
-
)
|
|
18
|
+
.description("Run all pending migrations")
|
|
22
19
|
.action(async () => {
|
|
23
20
|
await this.runMigrations();
|
|
24
21
|
});
|
|
@@ -95,257 +92,208 @@ export class MigrateCommand {
|
|
|
95
92
|
});
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
// -----------------------------------------
|
|
99
|
-
// DETECTION METHODS
|
|
100
|
-
// -----------------------------------------
|
|
101
|
-
|
|
102
95
|
/**
|
|
103
|
-
*
|
|
96
|
+
* Detect if we should use TypeScript or JavaScript
|
|
104
97
|
*/
|
|
105
|
-
private static
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
// Check if ts-node is installed in the user's project
|
|
110
|
-
require.resolve("ts-node");
|
|
111
|
-
this.tsNodeAvailable = true;
|
|
112
|
-
} catch {
|
|
113
|
-
try {
|
|
114
|
-
// Check if ts-node is installed globally or in parent modules
|
|
115
|
-
require("ts-node");
|
|
116
|
-
this.tsNodeAvailable = true;
|
|
117
|
-
} catch {
|
|
118
|
-
this.tsNodeAvailable = false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return this.tsNodeAvailable;
|
|
98
|
+
private static setupEnvironment(): boolean {
|
|
99
|
+
return this.shouldUseTypeScript();
|
|
123
100
|
}
|
|
124
101
|
|
|
125
102
|
/**
|
|
126
|
-
*
|
|
103
|
+
* Detect if we should use TypeScript or JavaScript
|
|
104
|
+
* - Check if tsconfig.json exists AND has decorator support
|
|
105
|
+
* - Check if TypeScript files exist in include patterns
|
|
106
|
+
* - Check if ts-node is available
|
|
127
107
|
*/
|
|
128
|
-
private static
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
path.join(process.cwd(), "tsconfig.json"),
|
|
133
|
-
);
|
|
134
|
-
return this.tsConfigExists;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Detect if TypeScript files exist in the project
|
|
139
|
-
*/
|
|
140
|
-
private static detectProjectType(): "typescript" | "javascript" | "mixed" {
|
|
141
|
-
const srcDir = path.join(process.cwd(), "src");
|
|
142
|
-
const hasSrcDir = fs.existsSync(srcDir);
|
|
108
|
+
private static shouldUseTypeScript(): boolean {
|
|
109
|
+
if (!TsConfigUtils.exists()) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
143
112
|
|
|
144
|
-
|
|
145
|
-
|
|
113
|
+
if (!TsConfigUtils.hasDecoratorSupport()) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
146
116
|
|
|
147
|
-
|
|
148
|
-
|
|
117
|
+
if (!this.isTsNodeAvailable()) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
149
120
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
fs.readdirSync(distDir).some((file) => file.endsWith(".js"));
|
|
121
|
+
const includePatterns = TsConfigUtils.getIncludePatterns();
|
|
122
|
+
const cwd = process.cwd();
|
|
153
123
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
124
|
+
for (const pattern of includePatterns) {
|
|
125
|
+
try {
|
|
126
|
+
const files = globSync(pattern, { cwd });
|
|
127
|
+
if (
|
|
128
|
+
files.some(
|
|
129
|
+
(file: string) => file.endsWith(".ts") && !file.endsWith(".d.ts"),
|
|
130
|
+
)
|
|
131
|
+
) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
// Ignore glob errors
|
|
136
|
+
}
|
|
137
|
+
}
|
|
157
138
|
|
|
158
|
-
|
|
159
|
-
return this.hasTsConfig() ? "typescript" : "javascript";
|
|
139
|
+
return false;
|
|
160
140
|
}
|
|
161
141
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
private static shouldUseTypeScript(): boolean {
|
|
166
|
-
const projectType = this.detectProjectType();
|
|
167
|
-
const tsNodeAvailable = this.isTsNodeAvailable();
|
|
168
|
-
|
|
169
|
-
// Use the same logic as EnvUtils for consistency
|
|
170
|
-
const isDevMode = EnvUtils.isDevelopmentMode();
|
|
171
|
-
|
|
172
|
-
// In development mode with ts-node available, use TypeScript
|
|
173
|
-
if (isDevMode && tsNodeAvailable) {
|
|
174
|
-
return true;
|
|
142
|
+
private static isTsNodeAvailable(): boolean {
|
|
143
|
+
if (this.tsNodeAvailable !== null) {
|
|
144
|
+
return this.tsNodeAvailable;
|
|
175
145
|
}
|
|
176
146
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
147
|
+
try {
|
|
148
|
+
require.resolve("ts-node");
|
|
149
|
+
this.tsNodeAvailable = true;
|
|
150
|
+
} catch {
|
|
151
|
+
this.tsNodeAvailable = false;
|
|
180
152
|
}
|
|
181
153
|
|
|
182
|
-
|
|
183
|
-
return projectType === "typescript" && tsNodeAvailable;
|
|
154
|
+
return this.tsNodeAvailable;
|
|
184
155
|
}
|
|
185
156
|
|
|
186
|
-
/**
|
|
187
|
-
* Register ts-node if needed
|
|
188
|
-
*/
|
|
189
157
|
private static registerTsNodeIfNeeded(useTypeScript: boolean): void {
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
require("tsconfig-paths/register");
|
|
199
|
-
} catch {
|
|
200
|
-
// tsconfig-paths not available, that's okay
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
console.log(chalk.gray("š Using ts-node for TypeScript migrations"));
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error(
|
|
206
|
-
chalk.yellow(
|
|
207
|
-
"ā ļø Failed to register ts-node. Falling back to JavaScript.",
|
|
208
|
-
),
|
|
209
|
-
);
|
|
158
|
+
if (useTypeScript && this.isTsNodeAvailable()) {
|
|
159
|
+
require("ts-node").register({
|
|
160
|
+
transpileOnly: true,
|
|
161
|
+
compilerOptions: {
|
|
162
|
+
module: "commonjs",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
210
165
|
}
|
|
211
166
|
}
|
|
212
167
|
|
|
213
168
|
/**
|
|
214
|
-
* Get entity paths
|
|
169
|
+
* Get entity and migration paths based on current mode
|
|
170
|
+
* Uses fragment.json config when available, otherwise infers from tsconfig
|
|
215
171
|
*/
|
|
216
|
-
private static
|
|
172
|
+
private static getPaths(useTypeScript: boolean): {
|
|
173
|
+
entities: string[];
|
|
174
|
+
migrations: string[];
|
|
175
|
+
seeds: string;
|
|
176
|
+
} {
|
|
217
177
|
const dbConfig = ConfigUtils.getDatabaseConfig();
|
|
218
|
-
return dbConfig.entities || [];
|
|
219
|
-
}
|
|
220
178
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
179
|
+
const resolveExtension = (pattern: string): string => {
|
|
180
|
+
return useTypeScript
|
|
181
|
+
? pattern.replace(/\.js$/, ".ts")
|
|
182
|
+
: pattern.replace(/\.ts$/, ".js");
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (dbConfig.entities || dbConfig.migrations) {
|
|
186
|
+
const entities = dbConfig.entities
|
|
187
|
+
? dbConfig.entities.map(resolveExtension)
|
|
188
|
+
: [useTypeScript ? "src/**/*.entity.ts" : "dist/**/*.entity.js"];
|
|
189
|
+
|
|
190
|
+
const migrations = dbConfig.migrations
|
|
191
|
+
? dbConfig.migrations.map(resolveExtension)
|
|
192
|
+
: [
|
|
193
|
+
useTypeScript
|
|
194
|
+
? "src/migrations/**/*.ts"
|
|
195
|
+
: "dist/migrations/**/*.js",
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const migrationDir = this.extractMigrationDirectory(
|
|
199
|
+
migrations[0],
|
|
200
|
+
useTypeScript,
|
|
201
|
+
);
|
|
202
|
+
const seedsDir = path.join(migrationDir, "..", "seeds");
|
|
228
203
|
|
|
229
|
-
|
|
230
|
-
* Get resolved entity paths based on current mode
|
|
231
|
-
*/
|
|
232
|
-
private static getResolvedEntityPaths(useTypeScript: boolean): string[] {
|
|
233
|
-
const configPaths = this.getEntityPathsFromConfig();
|
|
234
|
-
|
|
235
|
-
if (configPaths.length > 0) {
|
|
236
|
-
// Use configured paths but resolve based on current mode
|
|
237
|
-
return configPaths.map((pattern) => {
|
|
238
|
-
if (useTypeScript) {
|
|
239
|
-
return pattern.replace(/\.js$/, ".ts");
|
|
240
|
-
} else {
|
|
241
|
-
return pattern.replace(/\.ts$/, ".js");
|
|
242
|
-
}
|
|
243
|
-
});
|
|
204
|
+
return { entities, migrations, seeds: seedsDir };
|
|
244
205
|
}
|
|
245
206
|
|
|
246
|
-
|
|
247
|
-
|
|
207
|
+
const rootDir = TsConfigUtils.getRootDir();
|
|
208
|
+
const outDir = TsConfigUtils.getOutDir();
|
|
209
|
+
|
|
210
|
+
const entities = [
|
|
211
|
+
useTypeScript ? `${rootDir}/**/*.entity.ts` : `${outDir}/**/*.entity.js`,
|
|
212
|
+
];
|
|
213
|
+
const migrations = [
|
|
214
|
+
useTypeScript
|
|
215
|
+
? `${rootDir}/migrations/**/*.ts`
|
|
216
|
+
: `${outDir}/migrations/**/*.js`,
|
|
217
|
+
];
|
|
218
|
+
const seeds = useTypeScript ? `${rootDir}/seeds` : `${outDir}/seeds`;
|
|
219
|
+
|
|
220
|
+
return { entities, migrations, seeds };
|
|
248
221
|
}
|
|
249
222
|
|
|
250
223
|
/**
|
|
251
|
-
*
|
|
224
|
+
* Extract the actual directory path from a glob pattern for file creation
|
|
252
225
|
*/
|
|
253
|
-
private static
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return
|
|
259
|
-
if (useTypeScript) {
|
|
260
|
-
return pattern.replace(/\.js$/, ".ts");
|
|
261
|
-
} else {
|
|
262
|
-
return pattern.replace(/\.ts$/, ".js");
|
|
263
|
-
}
|
|
264
|
-
});
|
|
226
|
+
private static extractMigrationDirectory(
|
|
227
|
+
pattern: string,
|
|
228
|
+
useTypeScript: boolean,
|
|
229
|
+
): string {
|
|
230
|
+
if (pattern.includes("/**/*")) {
|
|
231
|
+
return pattern.split("/**/*")[0];
|
|
265
232
|
}
|
|
266
233
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
234
|
+
if (pattern.includes("/*.")) {
|
|
235
|
+
return pattern.split("/*")[0];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (pattern.endsWith(".ts") || pattern.endsWith(".js")) {
|
|
239
|
+
return path.dirname(pattern);
|
|
240
|
+
}
|
|
272
241
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// -----------------------------------------
|
|
242
|
+
return useTypeScript ? "src/migrations" : "dist/migrations";
|
|
243
|
+
}
|
|
276
244
|
|
|
277
245
|
private static async generateMigration(
|
|
278
246
|
nameOrPath: string | undefined,
|
|
279
247
|
options: { name?: string },
|
|
280
248
|
): Promise<void> {
|
|
281
|
-
const useTypeScript = this.
|
|
282
|
-
const spinner = ora(
|
|
249
|
+
const useTypeScript = this.setupEnvironment();
|
|
250
|
+
const spinner = ora(
|
|
251
|
+
`Generating ${useTypeScript ? "TypeScript" : "JavaScript"} migration...`,
|
|
252
|
+
).start();
|
|
283
253
|
let dataSource: DataSource | null = null;
|
|
284
254
|
|
|
285
255
|
try {
|
|
286
|
-
// Register ts-node if using TypeScript
|
|
287
256
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
288
257
|
|
|
289
|
-
|
|
290
|
-
const configOverride = {
|
|
291
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
292
|
-
migrations: this.getResolvedMigrationPaths(useTypeScript),
|
|
293
|
-
};
|
|
258
|
+
const { entities, migrations } = this.getPaths(useTypeScript);
|
|
259
|
+
const configOverride = { entities, migrations };
|
|
294
260
|
|
|
295
261
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
296
262
|
if (!dataSource?.isInitialized) {
|
|
297
263
|
throw new Error("Failed to initialize DataSource");
|
|
298
264
|
}
|
|
299
265
|
|
|
300
|
-
// Verify entities are loaded
|
|
301
266
|
this.verifyEntities(dataSource);
|
|
302
267
|
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
268
|
+
const migrationsPattern = migrations[0];
|
|
269
|
+
const migrationsDir = this.extractMigrationDirectory(
|
|
270
|
+
migrationsPattern,
|
|
271
|
+
useTypeScript,
|
|
307
272
|
);
|
|
308
273
|
await fs.ensureDir(migrationsDir);
|
|
309
274
|
|
|
310
275
|
const existingMigrations = fs.existsSync(migrationsDir)
|
|
311
276
|
? fs
|
|
312
277
|
.readdirSync(migrationsDir)
|
|
313
|
-
.filter(
|
|
278
|
+
.filter(
|
|
279
|
+
(f: string) =>
|
|
280
|
+
f.endsWith(useTypeScript ? ".ts" : ".js") &&
|
|
281
|
+
!f.endsWith(".d.ts"),
|
|
282
|
+
)
|
|
314
283
|
: [];
|
|
315
|
-
|
|
316
284
|
const isFirstMigration = existingMigrations.length === 0;
|
|
317
285
|
|
|
318
|
-
// Generate schema SQL by comparing entities with database
|
|
319
286
|
spinner.text = "Analyzing schema changes...";
|
|
320
287
|
const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
|
|
321
|
-
|
|
322
288
|
const upQueries = sqlInMemory.upQueries || [];
|
|
323
289
|
const downQueries = sqlInMemory.downQueries || [];
|
|
324
290
|
const hasChanges = upQueries.length > 0;
|
|
325
291
|
|
|
326
|
-
// Handle no changes scenario
|
|
327
292
|
if (!hasChanges && !isFirstMigration) {
|
|
328
293
|
spinner.info("No schema changes detected. Migration not generated.");
|
|
329
294
|
return;
|
|
330
295
|
}
|
|
331
296
|
|
|
332
|
-
if (!hasChanges && isFirstMigration) {
|
|
333
|
-
const helpMessage = useTypeScript
|
|
334
|
-
? " - Your entities are in the correct location\n" +
|
|
335
|
-
" - Entity paths in fragment.json are correct\n" +
|
|
336
|
-
" - Entities are decorated with @Entity()"
|
|
337
|
-
: " - Your entities are compiled (run 'npm run build')\n" +
|
|
338
|
-
" - Entity paths in fragment.json point to dist/**/*.entity.js\n" +
|
|
339
|
-
" - Entities are decorated with @Entity()";
|
|
340
|
-
|
|
341
|
-
spinner.warn(
|
|
342
|
-
"No schema changes detected on first migration. Check that:\n" +
|
|
343
|
-
helpMessage,
|
|
344
|
-
);
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Determine migration name
|
|
349
297
|
const migrationName =
|
|
350
298
|
options.name ||
|
|
351
299
|
(nameOrPath ? path.basename(nameOrPath).replace(/\.ts$/, "") : null) ||
|
|
@@ -353,18 +301,10 @@ export class MigrateCommand {
|
|
|
353
301
|
|
|
354
302
|
const timestamp = Date.now();
|
|
355
303
|
const className = `${migrationName}${timestamp}`;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return this.buildQueryStatement(query);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
const downStatements = downQueries.map((query) => {
|
|
364
|
-
return this.buildQueryStatement(query);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// Generate migration file content
|
|
304
|
+
const upStatements = upQueries.map((q) => this.buildQueryStatement(q));
|
|
305
|
+
const downStatements = downQueries.map((q) =>
|
|
306
|
+
this.buildQueryStatement(q),
|
|
307
|
+
);
|
|
368
308
|
const migrationContent = this.buildMigrationTemplate(
|
|
369
309
|
className,
|
|
370
310
|
upStatements,
|
|
@@ -372,193 +312,51 @@ export class MigrateCommand {
|
|
|
372
312
|
isFirstMigration,
|
|
373
313
|
);
|
|
374
314
|
|
|
375
|
-
// Write migration file using config directory
|
|
376
315
|
const fileExt = useTypeScript ? ".ts" : ".js";
|
|
377
316
|
const fileName = `${timestamp}-${migrationName}${fileExt}`;
|
|
378
317
|
const filePath = path.join(migrationsDir, fileName);
|
|
379
|
-
|
|
380
318
|
await fs.writeFile(filePath, migrationContent, "utf-8");
|
|
381
319
|
|
|
382
|
-
const fileType = useTypeScript ? "TypeScript" : "JavaScript";
|
|
383
320
|
spinner.succeed(
|
|
384
|
-
`${
|
|
385
|
-
` ${chalk.gray("Location:")} ${filePath}\n` +
|
|
386
|
-
` ${chalk.gray("Queries:")} ${upQueries.length} up, ${downQueries.length} down\n` +
|
|
387
|
-
` ${chalk.gray("Mode:")} ${useTypeScript ? "TypeScript (ts-node)" : "JavaScript"}`,
|
|
321
|
+
`${useTypeScript ? "TypeScript" : "JavaScript"} migration generated: ${chalk.cyan(fileName)}`,
|
|
388
322
|
);
|
|
389
323
|
} catch (error) {
|
|
390
324
|
spinner.fail("Migration generation failed");
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (error.message.includes("Cannot find module") && useTypeScript) {
|
|
396
|
-
console.error(
|
|
397
|
-
chalk.yellow(
|
|
398
|
-
"\nš” Tip: Try one of these solutions:\n" +
|
|
399
|
-
" 1. Install ts-node: npm install -D ts-node\n" +
|
|
400
|
-
" 2. Compile your project first: npm run build\n" +
|
|
401
|
-
" 3. Ensure your entities are in src/**/*.entity.ts",
|
|
402
|
-
),
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (error.stack) {
|
|
407
|
-
console.error(chalk.gray("\nStack trace:"));
|
|
408
|
-
console.error(chalk.gray(error.stack));
|
|
409
|
-
}
|
|
410
|
-
} else {
|
|
411
|
-
console.error(chalk.red(String(error)));
|
|
412
|
-
}
|
|
413
|
-
|
|
325
|
+
console.error(
|
|
326
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
327
|
+
);
|
|
414
328
|
process.exit(1);
|
|
415
329
|
} finally {
|
|
416
330
|
if (dataSource?.isInitialized) {
|
|
417
331
|
try {
|
|
418
332
|
await dataSource.destroy();
|
|
419
|
-
} catch
|
|
420
|
-
// Ignore cleanup errors
|
|
421
|
-
}
|
|
333
|
+
} catch {}
|
|
422
334
|
}
|
|
423
335
|
}
|
|
424
336
|
}
|
|
425
337
|
|
|
426
|
-
/**
|
|
427
|
-
* Verify entities are loaded and provide helpful debugging
|
|
428
|
-
*/
|
|
429
|
-
private static verifyEntities(dataSource: DataSource): void {
|
|
430
|
-
const entities = dataSource.entityMetadatas;
|
|
431
|
-
|
|
432
|
-
if (entities.length === 0) {
|
|
433
|
-
throw new Error(
|
|
434
|
-
"No entities found! Make sure:\n" +
|
|
435
|
-
" 1. Your entities are decorated with @Entity()\n" +
|
|
436
|
-
" 2. Entity paths in fragment.json are correct\n" +
|
|
437
|
-
" 3. For JavaScript mode: run 'npm run build' to compile\n" +
|
|
438
|
-
" 4. For TypeScript mode: entities should be in src/**/*.entity.ts",
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
console.log(chalk.gray(`\nš¦ Loaded ${entities.length} entity/entities:`));
|
|
443
|
-
entities.forEach((entity) => {
|
|
444
|
-
console.log(chalk.gray(` - ${entity.name} (${entity.tableName})`));
|
|
445
|
-
});
|
|
446
|
-
console.log();
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Build a single query statement with proper escaping
|
|
451
|
-
*/
|
|
452
|
-
private static buildQueryStatement(query: {
|
|
453
|
-
query: string;
|
|
454
|
-
parameters?: any[];
|
|
455
|
-
}): string {
|
|
456
|
-
// Escape backslashes, backticks, and dollar signs for template literals
|
|
457
|
-
let sql = query.query
|
|
458
|
-
.replace(/\\/g, "\\\\") // Escape backslashes first
|
|
459
|
-
.replace(/`/g, "\\`") // Escape backticks
|
|
460
|
-
.replace(/\$/g, "\\$") // Escape dollar signs
|
|
461
|
-
.replace(/\r\n/g, " ") // Remove Windows line breaks
|
|
462
|
-
.replace(/\n/g, " ") // Remove Unix line breaks
|
|
463
|
-
.replace(/\s+/g, " ") // Collapse multiple spaces
|
|
464
|
-
.trim();
|
|
465
|
-
|
|
466
|
-
// Handle parameters if present
|
|
467
|
-
if (query.parameters && query.parameters.length > 0) {
|
|
468
|
-
const params = JSON.stringify(query.parameters);
|
|
469
|
-
return `await queryRunner.query(\`${sql}\`, ${params});`;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return `await queryRunner.query(\`${sql}\`);`;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// -----------------------------------------
|
|
476
|
-
// MIGRATION EXECUTION METHODS
|
|
477
|
-
// -----------------------------------------
|
|
478
|
-
|
|
479
338
|
private static async runMigrations(): Promise<void> {
|
|
480
|
-
const useTypeScript = this.
|
|
481
|
-
const spinner = ora(
|
|
339
|
+
const useTypeScript = this.setupEnvironment();
|
|
340
|
+
const spinner = ora(
|
|
341
|
+
`Running ${useTypeScript ? "TypeScript" : "JavaScript"} migrations...`,
|
|
342
|
+
).start();
|
|
482
343
|
let dataSource: DataSource | null = null;
|
|
483
344
|
|
|
484
345
|
try {
|
|
485
|
-
// Register ts-node if using TypeScript
|
|
486
346
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const configOverride = {
|
|
490
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
491
|
-
migrations: this.getResolvedMigrationPaths(useTypeScript),
|
|
492
|
-
};
|
|
347
|
+
const { entities, migrations } = this.getPaths(useTypeScript);
|
|
348
|
+
const configOverride = { entities, migrations };
|
|
493
349
|
|
|
494
350
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
495
|
-
|
|
496
|
-
// Check if migrations exist using config paths
|
|
497
|
-
const migrationPaths = this.getResolvedMigrationPaths(useTypeScript);
|
|
498
|
-
const migrationsDir = path.dirname(
|
|
499
|
-
migrationPaths[0].replace("/**/*", ""),
|
|
500
|
-
);
|
|
501
|
-
const fileExt = useTypeScript ? ".ts" : ".js";
|
|
502
|
-
|
|
503
|
-
const hasMigrations =
|
|
504
|
-
fs.existsSync(migrationsDir) &&
|
|
505
|
-
fs.readdirSync(migrationsDir).filter((f) => f.endsWith(fileExt))
|
|
506
|
-
.length > 0;
|
|
507
|
-
|
|
508
|
-
if (!hasMigrations) {
|
|
509
|
-
const mode = useTypeScript ? "TypeScript" : "JavaScript";
|
|
510
|
-
spinner.info(
|
|
511
|
-
`No ${mode.toLowerCase()} migrations found in ${migrationsDir}.`,
|
|
512
|
-
);
|
|
513
|
-
|
|
514
|
-
if (useTypeScript && this.hasJavaScriptMigrations()) {
|
|
515
|
-
console.log(
|
|
516
|
-
chalk.yellow(
|
|
517
|
-
`\nš” JavaScript migrations found. Try:\n` +
|
|
518
|
-
` 1. fragment migrate:run (in JavaScript mode)\n` +
|
|
519
|
-
` 2. Or compile your TypeScript: npm run build`,
|
|
520
|
-
),
|
|
521
|
-
);
|
|
522
|
-
} else if (!useTypeScript && this.hasTypeScriptMigrations()) {
|
|
523
|
-
console.log(
|
|
524
|
-
chalk.yellow(
|
|
525
|
-
`\nš” TypeScript migrations found. Try:\n` +
|
|
526
|
-
` 1. Install ts-node: npm install -D ts-node\n` +
|
|
527
|
-
` 2. Then run: fragment migrate:run\n` +
|
|
528
|
-
` 3. Or compile first: npm run build`,
|
|
529
|
-
),
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const mode = useTypeScript ? "TypeScript" : "JavaScript";
|
|
537
|
-
spinner.text = `Running ${mode} migrations...`;
|
|
538
|
-
|
|
539
351
|
await TypeORMModule.runMigrations();
|
|
540
|
-
spinner.succeed(
|
|
352
|
+
spinner.succeed(
|
|
353
|
+
`${useTypeScript ? "TypeScript" : "JavaScript"} migrations completed successfully`,
|
|
354
|
+
);
|
|
541
355
|
} catch (error) {
|
|
542
356
|
spinner.fail("Migration failed");
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
console.error(chalk.red("\nā TypeScript module not found."));
|
|
547
|
-
console.error(
|
|
548
|
-
chalk.yellow(
|
|
549
|
-
"\nš” Try one of these solutions:\n" +
|
|
550
|
-
" 1. Install ts-node: npm install -D ts-node\n" +
|
|
551
|
-
" 2. Compile your TypeScript: npm run build\n" +
|
|
552
|
-
" 3. Check that your migration files are in src/migrations/*.ts",
|
|
553
|
-
),
|
|
554
|
-
);
|
|
555
|
-
} else {
|
|
556
|
-
console.error(chalk.red(error.message));
|
|
557
|
-
}
|
|
558
|
-
} else {
|
|
559
|
-
console.error(chalk.red(String(error)));
|
|
560
|
-
}
|
|
561
|
-
|
|
357
|
+
console.error(
|
|
358
|
+
chalk.red(error instanceof Error ? error.message : String(error)),
|
|
359
|
+
);
|
|
562
360
|
process.exit(1);
|
|
563
361
|
} finally {
|
|
564
362
|
if (dataSource?.isInitialized) {
|
|
@@ -568,20 +366,13 @@ export class MigrateCommand {
|
|
|
568
366
|
}
|
|
569
367
|
|
|
570
368
|
private static async createMigration(name: string): Promise<void> {
|
|
571
|
-
|
|
572
|
-
const useTypeScript = true;
|
|
369
|
+
const useTypeScript = this.setupEnvironment();
|
|
573
370
|
const spinner = ora("Creating TypeScript migration...").start();
|
|
574
|
-
|
|
575
371
|
try {
|
|
372
|
+
const { migrations } = this.getPaths(true);
|
|
373
|
+
const migrationsDir = this.extractMigrationDirectory(migrations[0], true);
|
|
576
374
|
const timestamp = Date.now();
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
// Get migration directory from config
|
|
580
|
-
const migrationPaths = this.getResolvedMigrationPaths(useTypeScript);
|
|
581
|
-
const migrationsDir = path.dirname(
|
|
582
|
-
migrationPaths[0].replace("/**/*", ""),
|
|
583
|
-
);
|
|
584
|
-
const fileName = `${timestamp}-${name}${fileExt}`;
|
|
375
|
+
const fileName = `${timestamp}-${name}.ts`;
|
|
585
376
|
const filePath = path.join(migrationsDir, fileName);
|
|
586
377
|
|
|
587
378
|
const content = `import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
@@ -595,16 +386,7 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
595
386
|
|
|
596
387
|
await fs.ensureDir(path.dirname(filePath));
|
|
597
388
|
await fs.writeFile(filePath, content);
|
|
598
|
-
|
|
599
389
|
spinner.succeed(`TypeScript migration created: ${fileName}`);
|
|
600
|
-
console.log(
|
|
601
|
-
chalk.gray(
|
|
602
|
-
`\nš” Note: Migration created as TypeScript file.\n` +
|
|
603
|
-
` To use it, either:\n` +
|
|
604
|
-
` 1. Install ts-node: npm install -D ts-node\n` +
|
|
605
|
-
` 2. Or compile to JavaScript: npm run build`,
|
|
606
|
-
),
|
|
607
|
-
);
|
|
608
390
|
} catch (error) {
|
|
609
391
|
spinner.fail("Failed to create migration");
|
|
610
392
|
console.error(
|
|
@@ -615,25 +397,22 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
615
397
|
}
|
|
616
398
|
|
|
617
399
|
private static async revertMigration(): Promise<void> {
|
|
618
|
-
const useTypeScript = this.
|
|
619
|
-
const spinner = ora(
|
|
400
|
+
const useTypeScript = this.setupEnvironment();
|
|
401
|
+
const spinner = ora(
|
|
402
|
+
`Reverting ${useTypeScript ? "TypeScript" : "JavaScript"} migration...`,
|
|
403
|
+
).start();
|
|
620
404
|
let dataSource: DataSource | null = null;
|
|
621
405
|
|
|
622
406
|
try {
|
|
623
|
-
// Register ts-node if using TypeScript
|
|
624
407
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const configOverride = {
|
|
628
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
629
|
-
migrations: this.getResolvedMigrationPaths(useTypeScript),
|
|
630
|
-
};
|
|
408
|
+
const { entities, migrations } = this.getPaths(useTypeScript);
|
|
409
|
+
const configOverride = { entities, migrations };
|
|
631
410
|
|
|
632
411
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
633
412
|
await TypeORMModule.revertMigration();
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
413
|
+
spinner.succeed(
|
|
414
|
+
`${useTypeScript ? "TypeScript" : "JavaScript"} migration reverted successfully`,
|
|
415
|
+
);
|
|
637
416
|
} catch (error) {
|
|
638
417
|
spinner.fail("Revert failed");
|
|
639
418
|
console.error(
|
|
@@ -648,26 +427,23 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
648
427
|
}
|
|
649
428
|
|
|
650
429
|
private static async refreshMigrations(): Promise<void> {
|
|
651
|
-
const useTypeScript = this.
|
|
652
|
-
const spinner = ora(
|
|
430
|
+
const useTypeScript = this.setupEnvironment();
|
|
431
|
+
const spinner = ora(
|
|
432
|
+
`Refreshing ${useTypeScript ? "TypeScript" : "JavaScript"} migrations...`,
|
|
433
|
+
).start();
|
|
653
434
|
let dataSource: DataSource | null = null;
|
|
654
435
|
|
|
655
436
|
try {
|
|
656
|
-
// Register ts-node if using TypeScript
|
|
657
437
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const configOverride = {
|
|
661
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
662
|
-
migrations: this.getResolvedMigrationPaths(useTypeScript),
|
|
663
|
-
};
|
|
438
|
+
const { entities, migrations } = this.getPaths(useTypeScript);
|
|
439
|
+
const configOverride = { entities, migrations };
|
|
664
440
|
|
|
665
441
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
666
442
|
await TypeORMModule.dropSchema();
|
|
667
443
|
await TypeORMModule.runMigrations();
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
444
|
+
spinner.succeed(
|
|
445
|
+
`${useTypeScript ? "TypeScript" : "JavaScript"} migrations refreshed successfully`,
|
|
446
|
+
);
|
|
671
447
|
} catch (error) {
|
|
672
448
|
spinner.fail("Refresh failed");
|
|
673
449
|
console.error(
|
|
@@ -682,18 +458,13 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
682
458
|
}
|
|
683
459
|
|
|
684
460
|
private static async showStatus(): Promise<void> {
|
|
685
|
-
const useTypeScript = this.
|
|
461
|
+
const useTypeScript = this.setupEnvironment();
|
|
686
462
|
let dataSource: DataSource | null = null;
|
|
687
463
|
|
|
688
464
|
try {
|
|
689
|
-
// Register ts-node if using TypeScript
|
|
690
465
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const configOverride = {
|
|
694
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
695
|
-
migrations: this.getResolvedMigrationPaths(useTypeScript),
|
|
696
|
-
};
|
|
466
|
+
const { entities, migrations } = this.getPaths(useTypeScript);
|
|
467
|
+
const configOverride = { entities, migrations };
|
|
697
468
|
|
|
698
469
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
699
470
|
const executedMigrations = await dataSource.query(
|
|
@@ -718,16 +489,16 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
718
489
|
});
|
|
719
490
|
}
|
|
720
491
|
|
|
721
|
-
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
492
|
+
const migrationsPattern = migrations[0];
|
|
493
|
+
const migrationsDir = this.extractMigrationDirectory(
|
|
494
|
+
migrationsPattern,
|
|
495
|
+
useTypeScript,
|
|
725
496
|
);
|
|
726
497
|
const fileExt = useTypeScript ? ".ts" : ".js";
|
|
727
498
|
if (fs.existsSync(migrationsDir)) {
|
|
728
499
|
const files = fs
|
|
729
500
|
.readdirSync(migrationsDir)
|
|
730
|
-
.filter((f) => f.endsWith(fileExt));
|
|
501
|
+
.filter((f: string) => f.endsWith(fileExt));
|
|
731
502
|
console.log(
|
|
732
503
|
chalk.blue(
|
|
733
504
|
`\n Available ${mode.toLowerCase()} migration files: ${files.length}`,
|
|
@@ -735,11 +506,7 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
735
506
|
);
|
|
736
507
|
}
|
|
737
508
|
|
|
738
|
-
|
|
739
|
-
console.log(chalk.gray(`\n Mode detection:`));
|
|
740
|
-
console.log(
|
|
741
|
-
chalk.gray(` - Environment mode: ${EnvUtils.getEnvironmentMode()}`),
|
|
742
|
-
);
|
|
509
|
+
console.log(chalk.gray(`\n Configuration:`));
|
|
743
510
|
console.log(
|
|
744
511
|
chalk.gray(` - ts-node available: ${this.isTsNodeAvailable()}`),
|
|
745
512
|
);
|
|
@@ -761,18 +528,16 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
761
528
|
}
|
|
762
529
|
|
|
763
530
|
private static async syncSchema(): Promise<void> {
|
|
764
|
-
const useTypeScript = this.
|
|
765
|
-
const spinner = ora(
|
|
531
|
+
const useTypeScript = this.setupEnvironment();
|
|
532
|
+
const spinner = ora(
|
|
533
|
+
`Synchronizing ${useTypeScript ? "TypeScript" : "JavaScript"} schema...`,
|
|
534
|
+
).start();
|
|
766
535
|
let dataSource: DataSource | null = null;
|
|
767
536
|
|
|
768
537
|
try {
|
|
769
|
-
// Register ts-node if using TypeScript
|
|
770
538
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const configOverride = {
|
|
774
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
775
|
-
};
|
|
539
|
+
const { entities } = this.getPaths(useTypeScript);
|
|
540
|
+
const configOverride = { entities };
|
|
776
541
|
|
|
777
542
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
778
543
|
await TypeORMModule.syncSchema();
|
|
@@ -791,18 +556,16 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
791
556
|
}
|
|
792
557
|
|
|
793
558
|
private static async dropSchema(): Promise<void> {
|
|
794
|
-
const useTypeScript = this.
|
|
795
|
-
const spinner = ora(
|
|
559
|
+
const useTypeScript = this.setupEnvironment();
|
|
560
|
+
const spinner = ora(
|
|
561
|
+
`Dropping ${useTypeScript ? "TypeScript" : "JavaScript"} schema...`,
|
|
562
|
+
).start();
|
|
796
563
|
let dataSource: DataSource | null = null;
|
|
797
564
|
|
|
798
565
|
try {
|
|
799
|
-
// Register ts-node if using TypeScript
|
|
800
566
|
this.registerTsNodeIfNeeded(useTypeScript);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
const configOverride = {
|
|
804
|
-
entities: this.getResolvedEntityPaths(useTypeScript),
|
|
805
|
-
};
|
|
567
|
+
const { entities } = this.getPaths(useTypeScript);
|
|
568
|
+
const configOverride = { entities };
|
|
806
569
|
|
|
807
570
|
dataSource = await TypeORMModule.initialize(configOverride);
|
|
808
571
|
await TypeORMModule.dropSchema();
|
|
@@ -821,47 +584,33 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
821
584
|
}
|
|
822
585
|
|
|
823
586
|
private static async runSeeds(): Promise<void> {
|
|
824
|
-
const
|
|
825
|
-
|
|
587
|
+
const useTypeScript = this.setupEnvironment();
|
|
588
|
+
const spinner = ora(
|
|
589
|
+
`Running ${useTypeScript ? "TypeScript" : "JavaScript"} seeds...`,
|
|
590
|
+
).start();
|
|
826
591
|
try {
|
|
827
|
-
|
|
828
|
-
const useTypeScript = this.shouldUseTypeScript();
|
|
829
|
-
|
|
830
|
-
// Get seed directory from config or default
|
|
831
|
-
const dbConfig = ConfigUtils.getDatabaseConfig();
|
|
832
|
-
let seedsDir = "";
|
|
592
|
+
const { seeds: seedsDir } = this.getPaths(useTypeScript);
|
|
833
593
|
|
|
834
|
-
if (
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
seedsDir = path.join(path.dirname(migrationPaths[0]), "..", "seeds");
|
|
838
|
-
} else {
|
|
839
|
-
// seedsDir = useTypeScript
|
|
840
|
-
// ? path.join(process.cwd(), "src", "seeds")
|
|
841
|
-
// : path.join(process.cwd(), "dist", "seeds");
|
|
842
|
-
throw new Error("No seeds directory found");
|
|
594
|
+
if (!fs.existsSync(seedsDir)) {
|
|
595
|
+
spinner.info(`No seeds directory found at ${seedsDir}`);
|
|
596
|
+
return;
|
|
843
597
|
}
|
|
844
598
|
|
|
845
|
-
|
|
846
|
-
|
|
599
|
+
const files = fs.readdirSync(seedsDir);
|
|
600
|
+
for (const file of files) {
|
|
601
|
+
const isTsFile = file.endsWith(".ts") && !file.endsWith(".d.ts");
|
|
602
|
+
const isJsFile = file.endsWith(".js");
|
|
847
603
|
|
|
848
|
-
|
|
849
|
-
if (useTypeScript
|
|
850
|
-
// For TypeScript, we need to register ts-node
|
|
604
|
+
if ((useTypeScript && isTsFile) || (!useTypeScript && isJsFile)) {
|
|
605
|
+
if (useTypeScript) {
|
|
851
606
|
this.registerTsNodeIfNeeded(true);
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
} else if (!useTypeScript && file.endsWith(".js")) {
|
|
857
|
-
const seedModule = require(path.join(seedsDir, file));
|
|
858
|
-
if (seedModule.default?.run) {
|
|
859
|
-
await seedModule.default.run();
|
|
860
|
-
}
|
|
607
|
+
}
|
|
608
|
+
const seedModule = require(path.join(seedsDir, file));
|
|
609
|
+
if (seedModule.default?.run) {
|
|
610
|
+
await seedModule.default.run();
|
|
861
611
|
}
|
|
862
612
|
}
|
|
863
613
|
}
|
|
864
|
-
|
|
865
614
|
spinner.succeed("Seeds completed successfully");
|
|
866
615
|
} catch (error) {
|
|
867
616
|
spinner.fail("Seed failed");
|
|
@@ -873,21 +622,10 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
873
622
|
}
|
|
874
623
|
|
|
875
624
|
private static async createSeed(name: string): Promise<void> {
|
|
876
|
-
|
|
625
|
+
const useTypeScript = this.setupEnvironment();
|
|
877
626
|
const spinner = ora("Creating TypeScript seed...").start();
|
|
878
|
-
|
|
879
627
|
try {
|
|
880
|
-
|
|
881
|
-
const dbConfig = ConfigUtils.getDatabaseConfig();
|
|
882
|
-
let seedsDir = "";
|
|
883
|
-
|
|
884
|
-
if (dbConfig.subscribers && dbConfig.subscribers.length > 0) {
|
|
885
|
-
const migrationPaths = this.getResolvedMigrationPaths(true);
|
|
886
|
-
seedsDir = path.join(path.dirname(migrationPaths[0]), "..", "seeds");
|
|
887
|
-
} else {
|
|
888
|
-
seedsDir = path.join(process.cwd(), "src", "seeds");
|
|
889
|
-
}
|
|
890
|
-
|
|
628
|
+
const { seeds: seedsDir } = this.getPaths(true);
|
|
891
629
|
const fileName = `${name}.seed.ts`;
|
|
892
630
|
const filePath = path.join(seedsDir, fileName);
|
|
893
631
|
|
|
@@ -895,12 +633,10 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
895
633
|
static async run() {
|
|
896
634
|
console.log('Running ${name} seed...');
|
|
897
635
|
}
|
|
898
|
-
}
|
|
899
|
-
`;
|
|
636
|
+
}`;
|
|
900
637
|
|
|
901
638
|
await fs.ensureDir(path.dirname(filePath));
|
|
902
639
|
await fs.writeFile(filePath, content);
|
|
903
|
-
|
|
904
640
|
spinner.succeed(`TypeScript seed created: ${fileName}`);
|
|
905
641
|
} catch (error) {
|
|
906
642
|
spinner.fail("Failed to create seed");
|
|
@@ -911,51 +647,38 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
911
647
|
}
|
|
912
648
|
}
|
|
913
649
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
.
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return tsFiles.length > 0;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
private static hasJavaScriptMigrations(): boolean {
|
|
928
|
-
const distMigrationsDir = path.join(process.cwd(), "dist", "migrations");
|
|
929
|
-
|
|
930
|
-
if (!fs.existsSync(distMigrationsDir)) return false;
|
|
931
|
-
|
|
932
|
-
const jsFiles = fs
|
|
933
|
-
.readdirSync(distMigrationsDir)
|
|
934
|
-
.filter((file) => file.endsWith(".js"));
|
|
935
|
-
|
|
936
|
-
return jsFiles.length > 0;
|
|
650
|
+
private static verifyEntities(dataSource: DataSource): void {
|
|
651
|
+
const entities = dataSource.entityMetadatas;
|
|
652
|
+
if (entities.length === 0) {
|
|
653
|
+
throw new Error("No entities found!");
|
|
654
|
+
}
|
|
655
|
+
console.log(chalk.gray(`\nš¦ Loaded ${entities.length} entity/entities:`));
|
|
656
|
+
entities.forEach((entity) => {
|
|
657
|
+
console.log(chalk.gray(` - ${entity.name} (${entity.tableName})`));
|
|
658
|
+
});
|
|
659
|
+
console.log();
|
|
937
660
|
}
|
|
938
661
|
|
|
939
|
-
private static
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
662
|
+
private static buildQueryStatement(query: {
|
|
663
|
+
query: string;
|
|
664
|
+
parameters?: any[];
|
|
665
|
+
}): string {
|
|
666
|
+
let sql = query.query
|
|
667
|
+
.replace(/\\/g, "\\\\")
|
|
668
|
+
.replace(/`/g, "\\`")
|
|
669
|
+
.replace(/\$/g, "\\$")
|
|
670
|
+
.replace(/\r\n/g, " ")
|
|
671
|
+
.replace(/\n/g, " ")
|
|
672
|
+
.replace(/\s+/g, " ")
|
|
673
|
+
.trim();
|
|
945
674
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
files.push(fullPath.replace(process.cwd(), "."));
|
|
950
|
-
}
|
|
675
|
+
if (query.parameters && query.parameters.length > 0) {
|
|
676
|
+
const params = JSON.stringify(query.parameters);
|
|
677
|
+
return `await queryRunner.query(\`${sql}\`, ${params});`;
|
|
951
678
|
}
|
|
952
|
-
|
|
953
|
-
return files;
|
|
679
|
+
return `await queryRunner.query(\`${sql}\`);`;
|
|
954
680
|
}
|
|
955
681
|
|
|
956
|
-
/**
|
|
957
|
-
* Build the complete migration file template
|
|
958
|
-
*/
|
|
959
682
|
private static buildMigrationTemplate(
|
|
960
683
|
className: string,
|
|
961
684
|
upStatements: string[],
|
|
@@ -966,20 +689,14 @@ export class ${name}${timestamp} implements MigrationInterface {
|
|
|
966
689
|
upStatements.length > 0
|
|
967
690
|
? upStatements.map((s) => ` ${s}`).join("\n")
|
|
968
691
|
: " // No changes";
|
|
969
|
-
|
|
970
692
|
const downBody =
|
|
971
693
|
downStatements.length > 0
|
|
972
694
|
? downStatements.map((s) => ` ${s}`).join("\n")
|
|
973
695
|
: " // No changes";
|
|
974
696
|
|
|
975
|
-
|
|
976
|
-
? " // Initial migration - creates all tables\n"
|
|
977
|
-
: "";
|
|
978
|
-
|
|
979
|
-
return `import { MigrationInterface, QueryRunner } from "typeorm";
|
|
697
|
+
return `import { MigrationInterface, QueryRunner } from "fragment-ts";
|
|
980
698
|
|
|
981
699
|
export class ${className} implements MigrationInterface {
|
|
982
|
-
${comment}
|
|
983
700
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
984
701
|
${upBody}
|
|
985
702
|
}
|
|
@@ -987,7 +704,6 @@ ${upBody}
|
|
|
987
704
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
988
705
|
${downBody}
|
|
989
706
|
}
|
|
990
|
-
}
|
|
991
|
-
`;
|
|
707
|
+
}`;
|
|
992
708
|
}
|
|
993
709
|
}
|