@famgia/omnify-laravel 0.0.17 → 0.0.18
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/{chunk-52JAFQBQ.js → chunk-H37M25AK.js} +174 -14
- package/dist/chunk-H37M25AK.js.map +1 -0
- package/dist/index.cjs +172 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +453 -12
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-52JAFQBQ.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropertyDefinition, SchemaCollection, LoadedSchema } from '@famgia/omnify-types';
|
|
1
|
+
import { CustomTypeDefinition, PropertyDefinition, SchemaCollection, LoadedSchema } from '@famgia/omnify-types';
|
|
2
2
|
import { SchemaChange } from '@famgia/omnify-atlas';
|
|
3
3
|
export { LaravelPluginOptions, laravelPlugin } from './plugin.cjs';
|
|
4
4
|
|
|
@@ -7,6 +7,7 @@ export { LaravelPluginOptions, laravelPlugin } from './plugin.cjs';
|
|
|
7
7
|
*
|
|
8
8
|
* Types for Laravel migration generation.
|
|
9
9
|
*/
|
|
10
|
+
|
|
10
11
|
/**
|
|
11
12
|
* Laravel migration file structure.
|
|
12
13
|
*/
|
|
@@ -34,6 +35,8 @@ interface MigrationOptions {
|
|
|
34
35
|
readonly generateDown?: boolean | undefined;
|
|
35
36
|
/** Database connection name */
|
|
36
37
|
readonly connection?: string | undefined;
|
|
38
|
+
/** Custom types from plugins (for compound type expansion) */
|
|
39
|
+
readonly customTypes?: ReadonlyMap<string, CustomTypeDefinition> | undefined;
|
|
37
40
|
}
|
|
38
41
|
/**
|
|
39
42
|
* Schema Builder column method.
|
|
@@ -161,10 +164,17 @@ declare function generateForeignKey(propertyName: string, property: PropertyDefi
|
|
|
161
164
|
foreignKey: ForeignKeyDefinition;
|
|
162
165
|
index: IndexDefinition;
|
|
163
166
|
} | null;
|
|
167
|
+
/**
|
|
168
|
+
* Options for schema to blueprint conversion.
|
|
169
|
+
*/
|
|
170
|
+
interface SchemaToBlueprintOptions {
|
|
171
|
+
/** Custom types from plugins (for compound type expansion) */
|
|
172
|
+
customTypes?: ReadonlyMap<string, CustomTypeDefinition>;
|
|
173
|
+
}
|
|
164
174
|
/**
|
|
165
175
|
* Generates table blueprint from schema.
|
|
166
176
|
*/
|
|
167
|
-
declare function schemaToBlueprint(schema: LoadedSchema, allSchemas: SchemaCollection): TableBlueprint;
|
|
177
|
+
declare function schemaToBlueprint(schema: LoadedSchema, allSchemas: SchemaCollection, options?: SchemaToBlueprintOptions): TableBlueprint;
|
|
168
178
|
/**
|
|
169
179
|
* Formats a column method to PHP code.
|
|
170
180
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropertyDefinition, SchemaCollection, LoadedSchema } from '@famgia/omnify-types';
|
|
1
|
+
import { CustomTypeDefinition, PropertyDefinition, SchemaCollection, LoadedSchema } from '@famgia/omnify-types';
|
|
2
2
|
import { SchemaChange } from '@famgia/omnify-atlas';
|
|
3
3
|
export { LaravelPluginOptions, laravelPlugin } from './plugin.js';
|
|
4
4
|
|
|
@@ -7,6 +7,7 @@ export { LaravelPluginOptions, laravelPlugin } from './plugin.js';
|
|
|
7
7
|
*
|
|
8
8
|
* Types for Laravel migration generation.
|
|
9
9
|
*/
|
|
10
|
+
|
|
10
11
|
/**
|
|
11
12
|
* Laravel migration file structure.
|
|
12
13
|
*/
|
|
@@ -34,6 +35,8 @@ interface MigrationOptions {
|
|
|
34
35
|
readonly generateDown?: boolean | undefined;
|
|
35
36
|
/** Database connection name */
|
|
36
37
|
readonly connection?: string | undefined;
|
|
38
|
+
/** Custom types from plugins (for compound type expansion) */
|
|
39
|
+
readonly customTypes?: ReadonlyMap<string, CustomTypeDefinition> | undefined;
|
|
37
40
|
}
|
|
38
41
|
/**
|
|
39
42
|
* Schema Builder column method.
|
|
@@ -161,10 +164,17 @@ declare function generateForeignKey(propertyName: string, property: PropertyDefi
|
|
|
161
164
|
foreignKey: ForeignKeyDefinition;
|
|
162
165
|
index: IndexDefinition;
|
|
163
166
|
} | null;
|
|
167
|
+
/**
|
|
168
|
+
* Options for schema to blueprint conversion.
|
|
169
|
+
*/
|
|
170
|
+
interface SchemaToBlueprintOptions {
|
|
171
|
+
/** Custom types from plugins (for compound type expansion) */
|
|
172
|
+
customTypes?: ReadonlyMap<string, CustomTypeDefinition>;
|
|
173
|
+
}
|
|
164
174
|
/**
|
|
165
175
|
* Generates table blueprint from schema.
|
|
166
176
|
*/
|
|
167
|
-
declare function schemaToBlueprint(schema: LoadedSchema, allSchemas: SchemaCollection): TableBlueprint;
|
|
177
|
+
declare function schemaToBlueprint(schema: LoadedSchema, allSchemas: SchemaCollection, options?: SchemaToBlueprintOptions): TableBlueprint;
|
|
168
178
|
/**
|
|
169
179
|
* Formats a column method to PHP code.
|
|
170
180
|
*/
|
package/dist/index.js
CHANGED
package/dist/plugin.cjs
CHANGED
|
@@ -39,6 +39,7 @@ var TYPE_METHOD_MAP = {
|
|
|
39
39
|
LongText: "longText",
|
|
40
40
|
Date: "date",
|
|
41
41
|
Time: "time",
|
|
42
|
+
DateTime: "dateTime",
|
|
42
43
|
Timestamp: "timestamp",
|
|
43
44
|
Json: "json",
|
|
44
45
|
Email: "string",
|
|
@@ -111,6 +112,10 @@ function propertyToColumnMethod(propertyName, property) {
|
|
|
111
112
|
if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
|
|
112
113
|
modifiers.push({ method: "unsigned" });
|
|
113
114
|
}
|
|
115
|
+
const displayName = property.displayName;
|
|
116
|
+
if (displayName) {
|
|
117
|
+
modifiers.push({ method: "comment", args: [displayName] });
|
|
118
|
+
}
|
|
114
119
|
return {
|
|
115
120
|
name: columnName,
|
|
116
121
|
method,
|
|
@@ -245,6 +250,9 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
245
250
|
if (assocProp.default !== void 0 && assocProp.default !== null) {
|
|
246
251
|
modifiers.push({ method: "default", args: [assocProp.default] });
|
|
247
252
|
}
|
|
253
|
+
if (assocProp.displayName) {
|
|
254
|
+
modifiers.push({ method: "comment", args: [assocProp.displayName] });
|
|
255
|
+
}
|
|
248
256
|
const column = {
|
|
249
257
|
name: columnName,
|
|
250
258
|
method,
|
|
@@ -264,7 +272,65 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
264
272
|
};
|
|
265
273
|
return { column, foreignKey, index };
|
|
266
274
|
}
|
|
267
|
-
function
|
|
275
|
+
function expandCompoundType(propName, property, customTypes) {
|
|
276
|
+
const typeDef = customTypes.get(property.type);
|
|
277
|
+
if (!typeDef || !typeDef.compound || !typeDef.expand) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const expanded = [];
|
|
281
|
+
const baseProp = property;
|
|
282
|
+
for (const field of typeDef.expand) {
|
|
283
|
+
const suffixSnake = toColumnName(field.suffix);
|
|
284
|
+
const columnName = `${propName}_${suffixSnake}`;
|
|
285
|
+
const expandedProp = {
|
|
286
|
+
type: "String"
|
|
287
|
+
// Default type, will be overridden by sql definition
|
|
288
|
+
};
|
|
289
|
+
if (field.sql) {
|
|
290
|
+
const sqlType = field.sql.sqlType.toUpperCase();
|
|
291
|
+
if (sqlType === "VARCHAR" || sqlType === "CHAR" || sqlType === "STRING") {
|
|
292
|
+
expandedProp.type = "String";
|
|
293
|
+
if (field.sql.length) {
|
|
294
|
+
expandedProp.length = field.sql.length;
|
|
295
|
+
}
|
|
296
|
+
} else if (sqlType === "INT" || sqlType === "INTEGER") {
|
|
297
|
+
expandedProp.type = "Int";
|
|
298
|
+
} else if (sqlType === "BIGINT") {
|
|
299
|
+
expandedProp.type = "BigInt";
|
|
300
|
+
} else if (sqlType === "TEXT") {
|
|
301
|
+
expandedProp.type = "Text";
|
|
302
|
+
} else if (sqlType === "BOOLEAN" || sqlType === "BOOL") {
|
|
303
|
+
expandedProp.type = "Boolean";
|
|
304
|
+
} else if (sqlType === "DECIMAL") {
|
|
305
|
+
expandedProp.type = "Decimal";
|
|
306
|
+
if (field.sql.precision) expandedProp.precision = field.sql.precision;
|
|
307
|
+
if (field.sql.scale) expandedProp.scale = field.sql.scale;
|
|
308
|
+
} else if (sqlType === "DATE") {
|
|
309
|
+
expandedProp.type = "Date";
|
|
310
|
+
} else if (sqlType === "TIMESTAMP" || sqlType === "DATETIME") {
|
|
311
|
+
expandedProp.type = "Timestamp";
|
|
312
|
+
}
|
|
313
|
+
if (field.sql.nullable !== void 0) {
|
|
314
|
+
expandedProp.nullable = field.sql.nullable;
|
|
315
|
+
} else if (baseProp.nullable !== void 0) {
|
|
316
|
+
expandedProp.nullable = baseProp.nullable;
|
|
317
|
+
}
|
|
318
|
+
if (field.sql.default !== void 0) {
|
|
319
|
+
expandedProp.default = field.sql.default;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (baseProp.displayName) {
|
|
323
|
+
expandedProp.displayName = `${baseProp.displayName} (${field.suffix})`;
|
|
324
|
+
}
|
|
325
|
+
expanded.push({
|
|
326
|
+
name: columnName,
|
|
327
|
+
property: expandedProp
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return expanded;
|
|
331
|
+
}
|
|
332
|
+
function schemaToBlueprint(schema, allSchemas, options = {}) {
|
|
333
|
+
const { customTypes = /* @__PURE__ */ new Map() } = options;
|
|
268
334
|
const tableName = toTableName(schema.name);
|
|
269
335
|
const columns = [];
|
|
270
336
|
const foreignKeys = [];
|
|
@@ -275,6 +341,16 @@ function schemaToBlueprint(schema, allSchemas) {
|
|
|
275
341
|
}
|
|
276
342
|
if (schema.properties) {
|
|
277
343
|
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
344
|
+
const expandedProps = expandCompoundType(propName, property, customTypes);
|
|
345
|
+
if (expandedProps) {
|
|
346
|
+
for (const { name: expandedName, property: expandedProp } of expandedProps) {
|
|
347
|
+
const columnMethod2 = propertyToColumnMethod(expandedName, expandedProp);
|
|
348
|
+
if (columnMethod2) {
|
|
349
|
+
columns.push(columnMethod2);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
278
354
|
const columnMethod = propertyToColumnMethod(propName, property);
|
|
279
355
|
if (columnMethod) {
|
|
280
356
|
columns.push(columnMethod);
|
|
@@ -681,7 +757,9 @@ function generateMigrations(schemas, options = {}) {
|
|
|
681
757
|
const timestamp = options.timestamp ?? generateTimestamp();
|
|
682
758
|
const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
|
|
683
759
|
timestampOffset++;
|
|
684
|
-
const blueprint = schemaToBlueprint(schema, schemas
|
|
760
|
+
const blueprint = schemaToBlueprint(schema, schemas, {
|
|
761
|
+
customTypes: options.customTypes
|
|
762
|
+
});
|
|
685
763
|
const migration = generateCreateMigration(blueprint, {
|
|
686
764
|
...options,
|
|
687
765
|
timestamp: offsetTimestamp
|
|
@@ -737,6 +815,288 @@ function getMigrationPath(migration, outputDir = "database/migrations") {
|
|
|
737
815
|
return `${outputDir}/${migration.fileName}`;
|
|
738
816
|
}
|
|
739
817
|
|
|
818
|
+
// src/migration/alter-generator.ts
|
|
819
|
+
var TYPE_METHOD_MAP2 = {
|
|
820
|
+
String: "string",
|
|
821
|
+
Int: "integer",
|
|
822
|
+
BigInt: "bigInteger",
|
|
823
|
+
Float: "double",
|
|
824
|
+
Decimal: "decimal",
|
|
825
|
+
Boolean: "boolean",
|
|
826
|
+
Text: "text",
|
|
827
|
+
LongText: "longText",
|
|
828
|
+
Date: "date",
|
|
829
|
+
Time: "time",
|
|
830
|
+
DateTime: "dateTime",
|
|
831
|
+
Timestamp: "timestamp",
|
|
832
|
+
Json: "json",
|
|
833
|
+
Email: "string",
|
|
834
|
+
Password: "string",
|
|
835
|
+
File: "string",
|
|
836
|
+
MultiFile: "json",
|
|
837
|
+
Enum: "enum",
|
|
838
|
+
Select: "string",
|
|
839
|
+
Lookup: "unsignedBigInteger"
|
|
840
|
+
};
|
|
841
|
+
function generateTimestamp2() {
|
|
842
|
+
const now = /* @__PURE__ */ new Date();
|
|
843
|
+
const year = now.getFullYear();
|
|
844
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
845
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
846
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
847
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
848
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
849
|
+
return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
|
|
850
|
+
}
|
|
851
|
+
function formatAddColumn(columnName, prop) {
|
|
852
|
+
const snakeColumn = toColumnName(columnName);
|
|
853
|
+
const method = TYPE_METHOD_MAP2[prop.type] ?? "string";
|
|
854
|
+
let code;
|
|
855
|
+
if (prop.type === "Decimal") {
|
|
856
|
+
const precision = prop.precision ?? 8;
|
|
857
|
+
const scale = prop.scale ?? 2;
|
|
858
|
+
code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;
|
|
859
|
+
} else {
|
|
860
|
+
code = `$table->${method}('${snakeColumn}')`;
|
|
861
|
+
}
|
|
862
|
+
if (prop.nullable) code += "->nullable()";
|
|
863
|
+
if (prop.unique) code += "->unique()";
|
|
864
|
+
if (prop.default !== void 0) {
|
|
865
|
+
const defaultValue = typeof prop.default === "string" ? `'${prop.default}'` : JSON.stringify(prop.default);
|
|
866
|
+
code += `->default(${defaultValue})`;
|
|
867
|
+
}
|
|
868
|
+
return code + ";";
|
|
869
|
+
}
|
|
870
|
+
function formatDropColumn(columnName) {
|
|
871
|
+
const snakeColumn = toColumnName(columnName);
|
|
872
|
+
return `$table->dropColumn('${snakeColumn}');`;
|
|
873
|
+
}
|
|
874
|
+
function formatRenameColumn(oldName, newName) {
|
|
875
|
+
const oldSnake = toColumnName(oldName);
|
|
876
|
+
const newSnake = toColumnName(newName);
|
|
877
|
+
return `$table->renameColumn('${oldSnake}', '${newSnake}');`;
|
|
878
|
+
}
|
|
879
|
+
function formatModifyColumn(columnName, _prevProp, currProp) {
|
|
880
|
+
const snakeColumn = toColumnName(columnName);
|
|
881
|
+
const method = TYPE_METHOD_MAP2[currProp.type] ?? "string";
|
|
882
|
+
let code;
|
|
883
|
+
if (currProp.type === "Decimal") {
|
|
884
|
+
const precision = currProp.precision ?? 8;
|
|
885
|
+
const scale = currProp.scale ?? 2;
|
|
886
|
+
code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;
|
|
887
|
+
} else {
|
|
888
|
+
code = `$table->${method}('${snakeColumn}')`;
|
|
889
|
+
}
|
|
890
|
+
if (currProp.nullable) code += "->nullable()";
|
|
891
|
+
if (currProp.unique) code += "->unique()";
|
|
892
|
+
if (currProp.default !== void 0) {
|
|
893
|
+
const defaultValue = typeof currProp.default === "string" ? `'${currProp.default}'` : JSON.stringify(currProp.default);
|
|
894
|
+
code += `->default(${defaultValue})`;
|
|
895
|
+
}
|
|
896
|
+
return code + "->change();";
|
|
897
|
+
}
|
|
898
|
+
function formatAddIndex(columns, unique) {
|
|
899
|
+
const snakeColumns = columns.map(toColumnName);
|
|
900
|
+
const method = unique ? "unique" : "index";
|
|
901
|
+
const colsArg = snakeColumns.length === 1 ? `'${snakeColumns[0]}'` : `[${snakeColumns.map((c) => `'${c}'`).join(", ")}]`;
|
|
902
|
+
return `$table->${method}(${colsArg});`;
|
|
903
|
+
}
|
|
904
|
+
function formatDropIndex(tableName, columns, unique) {
|
|
905
|
+
const snakeColumns = columns.map(toColumnName);
|
|
906
|
+
const method = unique ? "dropUnique" : "dropIndex";
|
|
907
|
+
const suffix = unique ? "unique" : "index";
|
|
908
|
+
const indexName = `${tableName}_${snakeColumns.join("_")}_${suffix}`;
|
|
909
|
+
return `$table->${method}('${indexName}');`;
|
|
910
|
+
}
|
|
911
|
+
function generateAlterMigrationContent(tableName, change, options = {}) {
|
|
912
|
+
const upLines = [];
|
|
913
|
+
const downLines = [];
|
|
914
|
+
if (change.columnChanges) {
|
|
915
|
+
for (const col of change.columnChanges) {
|
|
916
|
+
if (col.changeType === "added" && col.currentDef) {
|
|
917
|
+
upLines.push(` ${formatAddColumn(col.column, col.currentDef)}`);
|
|
918
|
+
downLines.push(` ${formatDropColumn(col.column)}`);
|
|
919
|
+
} else if (col.changeType === "removed" && col.previousDef) {
|
|
920
|
+
upLines.push(` ${formatDropColumn(col.column)}`);
|
|
921
|
+
downLines.push(` ${formatAddColumn(col.column, col.previousDef)}`);
|
|
922
|
+
} else if (col.changeType === "modified" && col.previousDef && col.currentDef) {
|
|
923
|
+
upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
|
|
924
|
+
downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
|
|
925
|
+
} else if (col.changeType === "renamed" && col.previousColumn) {
|
|
926
|
+
upLines.push(` ${formatRenameColumn(col.previousColumn, col.column)}`);
|
|
927
|
+
downLines.push(` ${formatRenameColumn(col.column, col.previousColumn)}`);
|
|
928
|
+
if (col.modifications && col.modifications.length > 0 && col.previousDef && col.currentDef) {
|
|
929
|
+
upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
|
|
930
|
+
downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (change.indexChanges) {
|
|
936
|
+
for (const idx of change.indexChanges) {
|
|
937
|
+
if (idx.changeType === "added") {
|
|
938
|
+
upLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
|
|
939
|
+
downLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
|
|
940
|
+
} else {
|
|
941
|
+
upLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
|
|
942
|
+
downLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (change.optionChanges) {
|
|
947
|
+
if (change.optionChanges.timestamps) {
|
|
948
|
+
const { from, to } = change.optionChanges.timestamps;
|
|
949
|
+
if (to && !from) {
|
|
950
|
+
upLines.push(` $table->timestamps();`);
|
|
951
|
+
downLines.push(` $table->dropTimestamps();`);
|
|
952
|
+
} else if (from && !to) {
|
|
953
|
+
upLines.push(` $table->dropTimestamps();`);
|
|
954
|
+
downLines.push(` $table->timestamps();`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (change.optionChanges.softDelete) {
|
|
958
|
+
const { from, to } = change.optionChanges.softDelete;
|
|
959
|
+
if (to && !from) {
|
|
960
|
+
upLines.push(` $table->softDeletes();`);
|
|
961
|
+
downLines.push(` $table->dropSoftDeletes();`);
|
|
962
|
+
} else if (from && !to) {
|
|
963
|
+
upLines.push(` $table->dropSoftDeletes();`);
|
|
964
|
+
downLines.push(` $table->softDeletes();`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const connection = options.connection ? `
|
|
969
|
+
protected $connection = '${options.connection}';
|
|
970
|
+
` : "";
|
|
971
|
+
return `<?php
|
|
972
|
+
|
|
973
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
974
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
975
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
976
|
+
|
|
977
|
+
return new class extends Migration
|
|
978
|
+
{${connection}
|
|
979
|
+
/**
|
|
980
|
+
* Run the migrations.
|
|
981
|
+
*/
|
|
982
|
+
public function up(): void
|
|
983
|
+
{
|
|
984
|
+
Schema::table('${tableName}', function (Blueprint $table) {
|
|
985
|
+
${upLines.join("\n")}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Reverse the migrations.
|
|
991
|
+
*/
|
|
992
|
+
public function down(): void
|
|
993
|
+
{
|
|
994
|
+
Schema::table('${tableName}', function (Blueprint $table) {
|
|
995
|
+
${downLines.join("\n")}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
`;
|
|
1000
|
+
}
|
|
1001
|
+
function generateAlterMigration(change, options = {}) {
|
|
1002
|
+
if (change.changeType !== "modified") {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
const hasChanges = change.columnChanges && change.columnChanges.length > 0 || change.indexChanges && change.indexChanges.length > 0 || change.optionChanges && (change.optionChanges.timestamps || change.optionChanges.softDelete);
|
|
1006
|
+
if (!hasChanges) {
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
const tableName = toTableName(change.schemaName);
|
|
1010
|
+
const timestamp = options.timestamp ?? generateTimestamp2();
|
|
1011
|
+
const fileName = `${timestamp}_update_${tableName}_table.php`;
|
|
1012
|
+
const content = generateAlterMigrationContent(tableName, change, options);
|
|
1013
|
+
return {
|
|
1014
|
+
fileName,
|
|
1015
|
+
className: `Update${change.schemaName}Table`,
|
|
1016
|
+
content,
|
|
1017
|
+
tables: [tableName],
|
|
1018
|
+
type: "alter"
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function generateDropTableMigration(schemaName, options = {}) {
|
|
1022
|
+
const tableName = toTableName(schemaName);
|
|
1023
|
+
const timestamp = options.timestamp ?? generateTimestamp2();
|
|
1024
|
+
const fileName = `${timestamp}_drop_${tableName}_table.php`;
|
|
1025
|
+
const connection = options.connection ? `
|
|
1026
|
+
protected $connection = '${options.connection}';
|
|
1027
|
+
` : "";
|
|
1028
|
+
const content = `<?php
|
|
1029
|
+
|
|
1030
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
1031
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
1032
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
1033
|
+
|
|
1034
|
+
return new class extends Migration
|
|
1035
|
+
{${connection}
|
|
1036
|
+
/**
|
|
1037
|
+
* Run the migrations.
|
|
1038
|
+
*/
|
|
1039
|
+
public function up(): void
|
|
1040
|
+
{
|
|
1041
|
+
Schema::dropIfExists('${tableName}');
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Reverse the migrations.
|
|
1046
|
+
*/
|
|
1047
|
+
public function down(): void
|
|
1048
|
+
{
|
|
1049
|
+
// Cannot recreate table without full schema
|
|
1050
|
+
// Consider restoring from backup if needed
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
`;
|
|
1054
|
+
return {
|
|
1055
|
+
fileName,
|
|
1056
|
+
className: `Drop${schemaName}Table`,
|
|
1057
|
+
content,
|
|
1058
|
+
tables: [tableName],
|
|
1059
|
+
type: "drop"
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function generateMigrationsFromChanges(changes, options = {}) {
|
|
1063
|
+
const migrations = [];
|
|
1064
|
+
let timestampOffset = 0;
|
|
1065
|
+
const getNextTimestamp = () => {
|
|
1066
|
+
const ts = options.timestamp ?? generateTimestamp2();
|
|
1067
|
+
const offset = timestampOffset++;
|
|
1068
|
+
if (offset === 0) return ts;
|
|
1069
|
+
const parts = ts.split("_");
|
|
1070
|
+
if (parts.length >= 4) {
|
|
1071
|
+
const timePart = parts[3] ?? "000000";
|
|
1072
|
+
const secs = parseInt(timePart.substring(4, 6), 10) + offset;
|
|
1073
|
+
const newSecs = String(secs % 60).padStart(2, "0");
|
|
1074
|
+
parts[3] = timePart.substring(0, 4) + newSecs;
|
|
1075
|
+
return parts.join("_");
|
|
1076
|
+
}
|
|
1077
|
+
return ts;
|
|
1078
|
+
};
|
|
1079
|
+
for (const change of changes) {
|
|
1080
|
+
if (change.changeType === "modified") {
|
|
1081
|
+
const migration = generateAlterMigration(change, {
|
|
1082
|
+
...options,
|
|
1083
|
+
timestamp: getNextTimestamp()
|
|
1084
|
+
});
|
|
1085
|
+
if (migration) {
|
|
1086
|
+
migrations.push(migration);
|
|
1087
|
+
}
|
|
1088
|
+
} else if (change.changeType === "removed") {
|
|
1089
|
+
migrations.push(
|
|
1090
|
+
generateDropTableMigration(change.schemaName, {
|
|
1091
|
+
...options,
|
|
1092
|
+
timestamp: getNextTimestamp()
|
|
1093
|
+
})
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return migrations;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
740
1100
|
// src/utils.ts
|
|
741
1101
|
function toSnakeCase(str) {
|
|
742
1102
|
return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
|
|
@@ -1748,6 +2108,24 @@ function getFactoryPath(factory) {
|
|
|
1748
2108
|
}
|
|
1749
2109
|
|
|
1750
2110
|
// src/plugin.ts
|
|
2111
|
+
function getExistingMigrationTables(migrationsDir) {
|
|
2112
|
+
const existingTables = /* @__PURE__ */ new Set();
|
|
2113
|
+
if (!(0, import_node_fs.existsSync)(migrationsDir)) {
|
|
2114
|
+
return existingTables;
|
|
2115
|
+
}
|
|
2116
|
+
try {
|
|
2117
|
+
const files = (0, import_node_fs.readdirSync)(migrationsDir);
|
|
2118
|
+
const createMigrationPattern = /^\d{4}_\d{2}_\d{2}_\d{6}_create_(.+)_table\.php$/;
|
|
2119
|
+
for (const file of files) {
|
|
2120
|
+
const match = file.match(createMigrationPattern);
|
|
2121
|
+
if (match) {
|
|
2122
|
+
existingTables.add(match[1]);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
} catch {
|
|
2126
|
+
}
|
|
2127
|
+
return existingTables;
|
|
2128
|
+
}
|
|
1751
2129
|
var LARAVEL_CONFIG_SCHEMA = {
|
|
1752
2130
|
fields: [
|
|
1753
2131
|
{
|
|
@@ -1831,18 +2209,81 @@ function laravelPlugin(options) {
|
|
|
1831
2209
|
generate: async (ctx) => {
|
|
1832
2210
|
const migrationOptions = {
|
|
1833
2211
|
connection: resolved.connection,
|
|
1834
|
-
timestamp: resolved.timestamp
|
|
2212
|
+
timestamp: resolved.timestamp,
|
|
2213
|
+
customTypes: ctx.customTypes
|
|
1835
2214
|
};
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
tableName: migration.tables[0],
|
|
1843
|
-
migrationType: migration.type
|
|
2215
|
+
const outputs = [];
|
|
2216
|
+
const migrationsDir = (0, import_node_path.join)(ctx.cwd, resolved.migrationsPath);
|
|
2217
|
+
const existingTables = getExistingMigrationTables(migrationsDir);
|
|
2218
|
+
if (ctx.changes !== void 0) {
|
|
2219
|
+
if (ctx.changes.length === 0) {
|
|
2220
|
+
return outputs;
|
|
1844
2221
|
}
|
|
1845
|
-
|
|
2222
|
+
const addedSchemaNames = new Set(
|
|
2223
|
+
ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
|
|
2224
|
+
);
|
|
2225
|
+
if (addedSchemaNames.size > 0) {
|
|
2226
|
+
const addedSchemas = Object.fromEntries(
|
|
2227
|
+
Object.entries(ctx.schemas).filter(([name]) => addedSchemaNames.has(name))
|
|
2228
|
+
);
|
|
2229
|
+
const createMigrations = generateMigrations(addedSchemas, migrationOptions);
|
|
2230
|
+
for (const migration of createMigrations) {
|
|
2231
|
+
const tableName = migration.tables[0];
|
|
2232
|
+
if (existingTables.has(tableName)) {
|
|
2233
|
+
ctx.logger.debug(`Skipping CREATE for ${tableName} (already exists)`);
|
|
2234
|
+
continue;
|
|
2235
|
+
}
|
|
2236
|
+
outputs.push({
|
|
2237
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2238
|
+
content: migration.content,
|
|
2239
|
+
type: "migration",
|
|
2240
|
+
metadata: {
|
|
2241
|
+
tableName,
|
|
2242
|
+
migrationType: "create"
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
const alterChanges = ctx.changes.filter(
|
|
2248
|
+
(c) => c.changeType === "modified" || c.changeType === "removed"
|
|
2249
|
+
);
|
|
2250
|
+
if (alterChanges.length > 0) {
|
|
2251
|
+
const alterMigrations = generateMigrationsFromChanges(
|
|
2252
|
+
alterChanges,
|
|
2253
|
+
migrationOptions
|
|
2254
|
+
);
|
|
2255
|
+
for (const migration of alterMigrations) {
|
|
2256
|
+
outputs.push({
|
|
2257
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2258
|
+
content: migration.content,
|
|
2259
|
+
type: "migration",
|
|
2260
|
+
metadata: {
|
|
2261
|
+
tableName: migration.tables[0],
|
|
2262
|
+
migrationType: migration.type
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
} else {
|
|
2268
|
+
const migrations = generateMigrations(ctx.schemas, migrationOptions);
|
|
2269
|
+
for (const migration of migrations) {
|
|
2270
|
+
const tableName = migration.tables[0];
|
|
2271
|
+
if (migration.type === "create" && existingTables.has(tableName)) {
|
|
2272
|
+
ctx.logger.debug(`Skipping migration for ${tableName} (already exists)`);
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
outputs.push({
|
|
2276
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2277
|
+
content: migration.content,
|
|
2278
|
+
type: "migration",
|
|
2279
|
+
metadata: {
|
|
2280
|
+
tableName,
|
|
2281
|
+
migrationType: migration.type
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
return outputs;
|
|
1846
2287
|
}
|
|
1847
2288
|
};
|
|
1848
2289
|
const modelGenerator = {
|