forge-sql-orm-cli 2.1.15 → 2.1.17

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/cli.js CHANGED
@@ -11,6 +11,7 @@ const child_process = require("child_process");
11
11
  const mysql = require("mysql2/promise");
12
12
  const forgeSqlOrm = require("forge-sql-orm");
13
13
  const uniqueConstraint = require("drizzle-orm/mysql-core/unique-constraint");
14
+ const uuid = require("uuid");
14
15
  function replaceMySQLTypes(schemaContent) {
15
16
  const imports = `import { forgeDateTimeString, forgeTimeString, forgeDateString, forgeTimestampString } from "forge-sql-orm";
16
17
 
@@ -250,27 +251,152 @@ const createMigration = async (options) => {
250
251
  process.exit(1);
251
252
  }
252
253
  };
254
+ function buildDefault(preMigration) {
255
+ const def = preMigration.defaultValue;
256
+ const type = preMigration.type.toLowerCase();
257
+ if (def === void 0 || def === null) {
258
+ return "";
259
+ }
260
+ if (def === "") {
261
+ return `''`;
262
+ }
263
+ const stringTypes = /* @__PURE__ */ new Set([
264
+ "char",
265
+ "varchar",
266
+ "text",
267
+ "tinytext",
268
+ "mediumtext",
269
+ "longtext",
270
+ "enum",
271
+ "set",
272
+ "binary",
273
+ "varbinary",
274
+ "blob"
275
+ ]);
276
+ const numericTypes = /* @__PURE__ */ new Set([
277
+ "tinyint",
278
+ "smallint",
279
+ "mediumint",
280
+ "int",
281
+ "bigint",
282
+ "decimal",
283
+ "float",
284
+ "double",
285
+ "bit"
286
+ ]);
287
+ const isNumericLiteral = /^[+-]?\d+(\.\d+)?$/.test(def);
288
+ if (numericTypes.has(type) && isNumericLiteral) {
289
+ return `${def}`;
290
+ }
291
+ if (stringTypes.has(type)) {
292
+ const escaped = def.replace(/'/g, "''");
293
+ return `'${escaped}'`;
294
+ }
295
+ return `${def}`;
296
+ }
297
+ function generateWarningMessage(tableName, colName, version) {
298
+ return `⚠️ WARNING: Field \`${tableName}\`.\`${colName}\` requires a default value for existing NULL records.
299
+ Action required in migration file: migrationV${version}.ts
300
+ Find the line with: UPDATE \`${tableName}\` SET \`${colName}\` = ?
301
+ Replace '?' with an actual value (e.g., '' for strings, 0 for numbers, '1970-01-01' for dates)
302
+ OR remove this migration if it's not needed.`;
303
+ }
304
+ function handleMissingDefaultValue(preMigration, version, migrationLineList) {
305
+ const warningMsg = generateWarningMessage(preMigration.tableName, preMigration.colName, version);
306
+ console.warn(warningMsg);
307
+ migrationLineList.push(`console.error(${JSON.stringify(warningMsg)});`);
308
+ }
309
+ function getUpdateDefaultValue(preMigration, defaultValue) {
310
+ return defaultValue === "?" ? defaultValue : buildDefault(preMigration);
311
+ }
312
+ function generateUpdateStatement(preMigration, defaultValue) {
313
+ const updateValue = getUpdateDefaultValue(preMigration, defaultValue);
314
+ return `UPDATE \`${preMigration.tableName}\` SET \`${preMigration.colName}\` = ${updateValue} WHERE \`${preMigration.colName}\` IS NULL`;
315
+ }
253
316
  function generateMigrationFile$1(createStatements, version) {
254
317
  const versionPrefix = `v${version}_MIGRATION`;
255
- const migrationLines = createStatements.map((stmt, index) => ` .enqueue("${versionPrefix}${index}", "${stmt}")`).join("\n");
318
+ const migrationLineList = [];
319
+ createStatements.changes.forEach((change, index) => {
320
+ if (!change.premigrationId) {
321
+ migrationLineList.push(
322
+ `
323
+ migrationRunner.enqueue("${versionPrefix}${index}", "${change.change}")`
324
+ );
325
+ return;
326
+ }
327
+ const preMigration = createStatements.preMigrations[change.premigrationId];
328
+ if (!preMigration) {
329
+ migrationLineList.push(
330
+ `
331
+ migrationRunner.enqueue("${versionPrefix}${index}", "${change.change}")`
332
+ );
333
+ return;
334
+ }
335
+ const defaultValue = preMigration.defaultValue === void 0 || preMigration.defaultValue === null ? "?" : preMigration.defaultValue;
336
+ const needsWarning = defaultValue === "?";
337
+ if (preMigration.migrationType === "NEW_FIELD_NOT_NULL") {
338
+ const addColumnStatement = change.change.replace("NOT NULL", "NULL");
339
+ migrationLineList.push(
340
+ `
341
+ migrationRunner.enqueue("${versionPrefix}${index}_NULLABLE", "${addColumnStatement}");`
342
+ );
343
+ if (needsWarning) {
344
+ handleMissingDefaultValue(preMigration, version, migrationLineList);
345
+ }
346
+ const updateStatement = generateUpdateStatement(preMigration, defaultValue);
347
+ migrationLineList.push(
348
+ `
349
+ migrationRunner.enqueue("${versionPrefix}${index}_UPDATE_EXISTS_RECORDS", "${updateStatement}");`
350
+ );
351
+ const defaultClause = defaultValue === "?" ? "" : ` DEFAULT ${buildDefault(preMigration)}`;
352
+ const modifyStatement = `ALTER TABLE \`${preMigration.tableName}\` MODIFY COLUMN IF EXISTS \`${preMigration.colName}\` ${preMigration.type} NOT NULL${defaultClause};`;
353
+ migrationLineList.push(
354
+ `
355
+ migrationRunner.enqueue("${versionPrefix}${index}", "${modifyStatement}");`
356
+ );
357
+ } else if (preMigration.migrationType === "MODIFY_NOT_NULL") {
358
+ if (needsWarning) {
359
+ handleMissingDefaultValue(preMigration, version, migrationLineList);
360
+ }
361
+ const updateStatement = generateUpdateStatement(preMigration, defaultValue);
362
+ migrationLineList.push(
363
+ `
364
+ migrationRunner.enqueue("${versionPrefix}${index}_UPDATE_EXISTS_RECORDS", "${updateStatement}")`
365
+ );
366
+ migrationLineList.push(
367
+ `
368
+ migrationRunner.enqueue("${versionPrefix}${index}", "${change.change}")`
369
+ );
370
+ }
371
+ });
372
+ const migrationLines = migrationLineList.join("\n");
256
373
  return `import { MigrationRunner } from "@forge/sql/out/migration";
257
374
 
258
- export default (migrationRunner: MigrationRunner): MigrationRunner => {
259
- return migrationRunner
375
+ export default (migrationRunner: MigrationRunner): MigrationRunner => {
260
376
  ${migrationLines};
377
+ return migrationRunner;
261
378
  };`;
262
379
  }
263
380
  function filterWithPreviousMigration(newStatements, prevVersion, outputDir) {
264
381
  const prevMigrationPath = path.join(outputDir, `migrationV${prevVersion}.ts`);
265
382
  if (!fs.existsSync(prevMigrationPath)) {
266
- return newStatements.map((s) => s.replace(/\s+/g, " "));
383
+ return {
384
+ changes: newStatements.changes.map((s) => ({
385
+ change: s.change.replace(/\s+/g, " "),
386
+ premigrationId: s.premigrationId
387
+ })),
388
+ preMigrations: newStatements.preMigrations
389
+ };
267
390
  }
268
391
  const prevContent = fs.readFileSync(prevMigrationPath, "utf-8");
269
392
  const prevStatements = prevContent.split("\n").filter((line) => line.includes(".enqueue(")).map((line) => {
270
393
  const match = line.match(/\.enqueue\([^,]+,\s*"([^"]+)"/);
271
394
  return match ? match[1].replace(/\s+/g, " ").trim() : "";
272
395
  });
273
- return newStatements.filter((s) => !prevStatements.includes(s.replace(/\s+/g, " "))).map((s) => s.replace(/\s+/g, " "));
396
+ return {
397
+ preMigrations: newStatements.preMigrations,
398
+ changes: newStatements.changes.filter((s) => !prevStatements.includes(s.change.replace(/\s+/g, " "))).map((s) => ({ change: s.change.replace(/\s+/g, " "), premigrationId: s.premigrationId }))
399
+ };
274
400
  }
275
401
  function saveMigrationFiles$1(migrationCode, version, outputDir) {
276
402
  if (!fs.existsSync(outputDir)) {
@@ -281,22 +407,23 @@ function saveMigrationFiles$1(migrationCode, version, outputDir) {
281
407
  const indexFilePath = path.join(outputDir, `index.ts`);
282
408
  fs.writeFileSync(migrationFilePath, migrationCode);
283
409
  fs.writeFileSync(migrationCountPath, `export const MIGRATION_VERSION = ${version};`);
410
+ const importLines = [];
411
+ const callLines = [];
412
+ for (let i = 1; i <= version; i++) {
413
+ importLines.push(`import migrationV${i} from "./migrationV${i}";`);
414
+ callLines.push(` migrationV${i}(migrationRunner);`);
415
+ }
284
416
  const indexFileContent = `import { MigrationRunner } from "@forge/sql/out/migration";
285
- import { MIGRATION_VERSION } from "./migrationCount";
417
+ ${importLines.join("\n")}
286
418
 
287
419
  export type MigrationType = (
288
420
  migrationRunner: MigrationRunner,
289
421
  ) => MigrationRunner;
290
422
 
291
- export default async (
423
+ export default (
292
424
  migrationRunner: MigrationRunner,
293
- ): Promise<MigrationRunner> => {
294
- for (let i = 1; i <= MIGRATION_VERSION; i++) {
295
- const migrations = (await import(\`./migrationV\${i}\`)) as {
296
- default: MigrationType;
297
- };
298
- migrations.default(migrationRunner);
299
- }
425
+ ): MigrationRunner => {
426
+ ${callLines.join("\n")}
300
427
  return migrationRunner;
301
428
  };`;
302
429
  fs.writeFileSync(indexFilePath, indexFileContent);
@@ -325,7 +452,7 @@ const loadMigrationVersion = async (migrationPath) => {
325
452
  async function getDatabaseSchema(connection, dbName) {
326
453
  const [columns] = await connection.execute(
327
454
  `
328
- SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA
455
+ SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA, COLUMN_DEFAULT
329
456
  FROM INFORMATION_SCHEMA.COLUMNS
330
457
  WHERE TABLE_SCHEMA = ?
331
458
  `,
@@ -408,6 +535,7 @@ function compareForeignKey(fk, { columns }) {
408
535
  }
409
536
  function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
410
537
  const changes = [];
538
+ const preMigrations = {};
411
539
  for (const [tableName, dbTable] of Object.entries(dbSchema)) {
412
540
  const drizzleColumns = drizzleSchema[tableName];
413
541
  if (!drizzleColumns) {
@@ -417,9 +545,9 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
417
545
  const autoIncrement = col.EXTRA.includes("auto_increment") ? "AUTO_INCREMENT" : "";
418
546
  return `\`${colName}\` ${type} ${nullable} ${autoIncrement}`.trim();
419
547
  }).join(",\n ");
420
- changes.push(`CREATE TABLE if not exists \`${tableName}\` (
548
+ changes.push({ change: `CREATE TABLE if not exists \`${tableName}\` (
421
549
  ${columns}
422
- );`);
550
+ );` });
423
551
  for (const [indexName, dbIndex] of Object.entries(dbTable.indexes)) {
424
552
  if (indexName === "PRIMARY") {
425
553
  continue;
@@ -433,14 +561,14 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
433
561
  }
434
562
  const columns2 = dbIndex.columns.map((col) => `\`${col}\``).join(", ");
435
563
  const unique = dbIndex.unique ? "UNIQUE " : "";
436
- changes.push(
437
- `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns2});`
438
- );
564
+ changes.push({
565
+ change: `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns2});`
566
+ });
439
567
  }
440
568
  for (const [fkName, dbFK] of Object.entries(dbTable.foreignKeys)) {
441
- changes.push(
442
- `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${fkName}\` FOREIGN KEY (\`${dbFK.column}\`) REFERENCES \`${dbFK.referencedTable}\` (\`${dbFK.referencedColumn}\`);`
443
- );
569
+ changes.push({
570
+ change: `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${fkName}\` FOREIGN KEY (\`${dbFK.column}\`) REFERENCES \`${dbFK.referencedTable}\` (\`${dbFK.referencedColumn}\`);`
571
+ });
444
572
  }
445
573
  continue;
446
574
  }
@@ -448,18 +576,67 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
448
576
  const drizzleCol = Object.values(drizzleColumns).find((c) => c.name === colName);
449
577
  if (!drizzleCol) {
450
578
  const type = dbCol.COLUMN_TYPE;
451
- const nullable = dbCol.IS_NULLABLE === "YES" ? "NULL" : "NOT NULL";
452
- changes.push(`ALTER TABLE \`${tableName}\` ADD COLUMN IF NOT EXISTS \`${colName}\` ${type} ${nullable};`);
579
+ const nullable2 = dbCol.IS_NULLABLE === "YES" ? "NULL" : "NOT NULL";
580
+ let premigrationId = nullable2 === "NOT NULL" ? uuid.v4() : void 0;
581
+ const defaultValue = dbCol.COLUMN_DEFAULT;
582
+ if (nullable2 === "NOT NULL") {
583
+ premigrationId = uuid.v4();
584
+ preMigrations[premigrationId] = {
585
+ tableName,
586
+ dbTable,
587
+ colName,
588
+ type,
589
+ migrationType: "NEW_FIELD_NOT_NULL",
590
+ defaultValue
591
+ };
592
+ }
593
+ changes.push({
594
+ change: `ALTER TABLE \`${tableName}\` ADD COLUMN IF NOT EXISTS \`${colName}\` ${type} ${nullable2} ${defaultValue === void 0 || defaultValue === null ? "" : `DEFAULT ${buildDefault({
595
+ type,
596
+ defaultValue
597
+ })}`};`,
598
+ premigrationId
599
+ });
453
600
  continue;
454
601
  }
455
602
  const normalizedDbType = normalizeMySQLType(dbCol.COLUMN_TYPE);
456
603
  const normalizedDrizzleType = normalizeMySQLType(drizzleCol.getSQLType());
457
- if (normalizedDbType !== normalizedDrizzleType) {
604
+ const nullable = dbCol.IS_NULLABLE === "YES" ? "NULL" : "NOT NULL";
605
+ const dbIsNotNull = nullable === "NOT NULL";
606
+ const drizzleIsNotNull = drizzleCol.notNull;
607
+ const typeChanged = normalizedDbType !== normalizedDrizzleType;
608
+ const nullabilityChanged = dbIsNotNull !== drizzleIsNotNull;
609
+ const hasDrizzleDefault = drizzleCol.default !== null && drizzleCol.default !== void 0;
610
+ const hasDbDefault = dbCol.COLUMN_DEFAULT !== null && dbCol.COLUMN_DEFAULT !== void 0;
611
+ const defaultChanged = hasDrizzleDefault && hasDbDefault && drizzleCol.default !== dbCol.COLUMN_DEFAULT;
612
+ if (typeChanged || nullabilityChanged || defaultChanged) {
458
613
  const type = dbCol.COLUMN_TYPE;
459
- const nullable = dbCol.IS_NULLABLE === "YES" ? "NULL" : "NOT NULL";
460
- changes.push(
461
- `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${colName}\` IF EXISTS ${type} ${nullable};`
462
- );
614
+ const defaultValue = dbCol.COLUMN_DEFAULT;
615
+ let premigrationId = void 0;
616
+ if (dbIsNotNull && !drizzleIsNotNull) {
617
+ premigrationId = uuid.v4();
618
+ preMigrations[premigrationId] = {
619
+ tableName,
620
+ dbTable,
621
+ colName,
622
+ type,
623
+ migrationType: "MODIFY_NOT_NULL",
624
+ defaultValue
625
+ };
626
+ }
627
+ let defaultClause = "";
628
+ if (defaultValue !== void 0 && defaultValue !== null) {
629
+ const defaultValueObj = {
630
+ type,
631
+ defaultValue
632
+ };
633
+ defaultClause = ` DEFAULT ${buildDefault(defaultValueObj)}`;
634
+ }
635
+ const modifyStatement = `ALTER TABLE \`${tableName}\` MODIFY COLUMN IF EXISTS \`${colName}\` ${type} ${nullable}${defaultClause};`;
636
+ changes.push({
637
+ change: modifyStatement,
638
+ premigrationId
639
+ });
463
640
  }
464
641
  }
465
642
  const table = Object.values(schemaModule).find((t) => {
@@ -488,20 +665,20 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
488
665
  if (!drizzleIndex) {
489
666
  const columns = dbIndex.columns.map((col) => `\`${col}\``).join(", ");
490
667
  const unique = dbIndex.unique ? "UNIQUE " : "";
491
- changes.push(
492
- `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns});`
493
- );
668
+ changes.push({
669
+ change: `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns});`
670
+ });
494
671
  continue;
495
672
  }
496
673
  const dbColumns = dbIndex.columns.join(", ");
497
674
  const drizzleColumns2 = getIndexColumns(drizzleIndex).join(", ");
498
675
  if (dbColumns !== drizzleColumns2 || dbIndex.unique !== drizzleIndex instanceof uniqueConstraint.UniqueConstraintBuilder) {
499
- changes.push(`DROP INDEX \`${indexName}\` ON \`${tableName}\`;`);
676
+ changes.push({ change: `DROP INDEX \`${indexName}\` ON \`${tableName}\`;` });
500
677
  const columns = dbIndex.columns.map((col) => `\`${col}\``).join(", ");
501
678
  const unique = dbIndex.unique ? "UNIQUE " : "";
502
- changes.push(
503
- `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns});`
504
- );
679
+ changes.push({
680
+ change: `CREATE ${unique}INDEX if not exists \`${indexName}\` ON \`${tableName}\` (${columns});`
681
+ });
505
682
  }
506
683
  }
507
684
  for (const [fkName, dbFK] of Object.entries(dbTable.foreignKeys)) {
@@ -509,9 +686,9 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
509
686
  (fk) => getForeignKeyName(fk) === fkName || compareForeignKey(fk, { columns: [dbFK.column] })
510
687
  );
511
688
  if (!drizzleFK) {
512
- changes.push(
513
- `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${fkName}\` FOREIGN KEY (\`${dbFK.column}\`) REFERENCES \`${dbFK.referencedTable}\` (\`${dbFK.referencedColumn}\`);`
514
- );
689
+ changes.push({
690
+ change: `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${fkName}\` FOREIGN KEY (\`${dbFK.column}\`) REFERENCES \`${dbFK.referencedTable}\` (\`${dbFK.referencedColumn}\`);`
691
+ });
515
692
  continue;
516
693
  }
517
694
  }
@@ -524,7 +701,9 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
524
701
  if (drizzleForeignKey) {
525
702
  const fkName = getForeignKeyName(drizzleForeignKey);
526
703
  if (fkName) {
527
- changes.push(`ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${fkName}\`;`);
704
+ changes.push({
705
+ change: `ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${fkName}\`;`
706
+ });
528
707
  } else {
529
708
  const columns = drizzleForeignKey.columns;
530
709
  const columnNames = columns?.length ? columns.map((c) => c.name).join(", ") : "unknown columns";
@@ -537,7 +716,7 @@ function generateSchemaChanges(drizzleSchema, dbSchema, schemaModule) {
537
716
  }
538
717
  }
539
718
  }
540
- return changes;
719
+ return { changes, preMigrations };
541
720
  }
542
721
  const updateMigration = async (options) => {
543
722
  try {
@@ -580,6 +759,7 @@ const updateMigration = async (options) => {
580
759
  autoincrement: column.autoincrement,
581
760
  columnType: column.columnType,
582
761
  name: column.name,
762
+ default: metadata.columns.email.hasDefault ? String(column.default) : void 0,
583
763
  getSQLType: () => column.getSQLType()
584
764
  };
585
765
  });
@@ -595,7 +775,7 @@ const updateMigration = async (options) => {
595
775
  prevVersion,
596
776
  options.output
597
777
  );
598
- if (createStatements.length) {
778
+ if (createStatements.changes.length) {
599
779
  const migrationFile = generateMigrationFile$1(createStatements, version);
600
780
  if (saveMigrationFiles$1(migrationFile, version, options.output)) {
601
781
  console.log(`✅ Migration successfully updated!`);