@famgia/omnify-laravel 0.0.16 → 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/index.cjs CHANGED
@@ -55,6 +55,7 @@ var TYPE_METHOD_MAP = {
55
55
  LongText: "longText",
56
56
  Date: "date",
57
57
  Time: "time",
58
+ DateTime: "dateTime",
58
59
  Timestamp: "timestamp",
59
60
  Json: "json",
60
61
  Email: "string",
@@ -127,6 +128,10 @@ function propertyToColumnMethod(propertyName, property) {
127
128
  if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
128
129
  modifiers.push({ method: "unsigned" });
129
130
  }
131
+ const displayName = property.displayName;
132
+ if (displayName) {
133
+ modifiers.push({ method: "comment", args: [displayName] });
134
+ }
130
135
  return {
131
136
  name: columnName,
132
137
  method,
@@ -255,12 +260,15 @@ function generateForeignKey(propertyName, property, allSchemas) {
255
260
  method = "string";
256
261
  }
257
262
  const modifiers = [];
258
- if (assocProp.nullable || assocProp.relation === "ManyToOne") {
263
+ if (assocProp.nullable === true) {
259
264
  modifiers.push({ method: "nullable" });
260
265
  }
261
266
  if (assocProp.default !== void 0 && assocProp.default !== null) {
262
267
  modifiers.push({ method: "default", args: [assocProp.default] });
263
268
  }
269
+ if (assocProp.displayName) {
270
+ modifiers.push({ method: "comment", args: [assocProp.displayName] });
271
+ }
264
272
  const column = {
265
273
  name: columnName,
266
274
  method,
@@ -280,7 +288,65 @@ function generateForeignKey(propertyName, property, allSchemas) {
280
288
  };
281
289
  return { column, foreignKey, index };
282
290
  }
283
- function schemaToBlueprint(schema, allSchemas) {
291
+ function expandCompoundType(propName, property, customTypes) {
292
+ const typeDef = customTypes.get(property.type);
293
+ if (!typeDef || !typeDef.compound || !typeDef.expand) {
294
+ return null;
295
+ }
296
+ const expanded = [];
297
+ const baseProp = property;
298
+ for (const field of typeDef.expand) {
299
+ const suffixSnake = toColumnName(field.suffix);
300
+ const columnName = `${propName}_${suffixSnake}`;
301
+ const expandedProp = {
302
+ type: "String"
303
+ // Default type, will be overridden by sql definition
304
+ };
305
+ if (field.sql) {
306
+ const sqlType = field.sql.sqlType.toUpperCase();
307
+ if (sqlType === "VARCHAR" || sqlType === "CHAR" || sqlType === "STRING") {
308
+ expandedProp.type = "String";
309
+ if (field.sql.length) {
310
+ expandedProp.length = field.sql.length;
311
+ }
312
+ } else if (sqlType === "INT" || sqlType === "INTEGER") {
313
+ expandedProp.type = "Int";
314
+ } else if (sqlType === "BIGINT") {
315
+ expandedProp.type = "BigInt";
316
+ } else if (sqlType === "TEXT") {
317
+ expandedProp.type = "Text";
318
+ } else if (sqlType === "BOOLEAN" || sqlType === "BOOL") {
319
+ expandedProp.type = "Boolean";
320
+ } else if (sqlType === "DECIMAL") {
321
+ expandedProp.type = "Decimal";
322
+ if (field.sql.precision) expandedProp.precision = field.sql.precision;
323
+ if (field.sql.scale) expandedProp.scale = field.sql.scale;
324
+ } else if (sqlType === "DATE") {
325
+ expandedProp.type = "Date";
326
+ } else if (sqlType === "TIMESTAMP" || sqlType === "DATETIME") {
327
+ expandedProp.type = "Timestamp";
328
+ }
329
+ if (field.sql.nullable !== void 0) {
330
+ expandedProp.nullable = field.sql.nullable;
331
+ } else if (baseProp.nullable !== void 0) {
332
+ expandedProp.nullable = baseProp.nullable;
333
+ }
334
+ if (field.sql.default !== void 0) {
335
+ expandedProp.default = field.sql.default;
336
+ }
337
+ }
338
+ if (baseProp.displayName) {
339
+ expandedProp.displayName = `${baseProp.displayName} (${field.suffix})`;
340
+ }
341
+ expanded.push({
342
+ name: columnName,
343
+ property: expandedProp
344
+ });
345
+ }
346
+ return expanded;
347
+ }
348
+ function schemaToBlueprint(schema, allSchemas, options = {}) {
349
+ const { customTypes = /* @__PURE__ */ new Map() } = options;
284
350
  const tableName = toTableName(schema.name);
285
351
  const columns = [];
286
352
  const foreignKeys = [];
@@ -291,6 +357,16 @@ function schemaToBlueprint(schema, allSchemas) {
291
357
  }
292
358
  if (schema.properties) {
293
359
  for (const [propName, property] of Object.entries(schema.properties)) {
360
+ const expandedProps = expandCompoundType(propName, property, customTypes);
361
+ if (expandedProps) {
362
+ for (const { name: expandedName, property: expandedProp } of expandedProps) {
363
+ const columnMethod2 = propertyToColumnMethod(expandedName, expandedProp);
364
+ if (columnMethod2) {
365
+ columns.push(columnMethod2);
366
+ }
367
+ }
368
+ continue;
369
+ }
294
370
  const columnMethod = propertyToColumnMethod(propName, property);
295
371
  if (columnMethod) {
296
372
  columns.push(columnMethod);
@@ -316,36 +392,67 @@ function schemaToBlueprint(schema, allSchemas) {
316
392
  columns.push(generateSoftDeleteColumn());
317
393
  }
318
394
  if (schema.options?.indexes) {
395
+ const propToColName = (propName) => {
396
+ const colName = toColumnName(propName);
397
+ const prop = schema.properties?.[propName];
398
+ if (prop?.type === "Association") {
399
+ const assoc = prop;
400
+ if ((assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") && !assoc.mappedBy) {
401
+ return colName + "_id";
402
+ }
403
+ }
404
+ return colName;
405
+ };
319
406
  for (const index of schema.options.indexes) {
320
407
  if (typeof index === "string") {
321
408
  indexes.push({
322
- columns: [toColumnName(index)],
409
+ columns: [propToColName(index)],
323
410
  unique: false
324
411
  });
325
412
  } else {
326
413
  indexes.push({
327
414
  name: index.name,
328
- columns: index.columns.map(toColumnName),
415
+ columns: index.columns.map(propToColName),
329
416
  unique: index.unique ?? false
330
417
  });
331
418
  }
332
419
  }
333
420
  }
334
421
  if (schema.options?.unique) {
422
+ const propToColName = (propName) => {
423
+ const colName = toColumnName(propName);
424
+ const prop = schema.properties?.[propName];
425
+ if (prop?.type === "Association") {
426
+ const assoc = prop;
427
+ if ((assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") && !assoc.mappedBy) {
428
+ return colName + "_id";
429
+ }
430
+ }
431
+ return colName;
432
+ };
335
433
  const uniqueConstraints = Array.isArray(schema.options.unique[0]) ? schema.options.unique : [schema.options.unique];
336
434
  for (const constraint of uniqueConstraints) {
337
435
  indexes.push({
338
- columns: constraint.map(toColumnName),
436
+ columns: constraint.map(propToColName),
339
437
  unique: true
340
438
  });
341
439
  }
342
440
  }
441
+ const seenIndexes = /* @__PURE__ */ new Set();
442
+ const uniqueIndexes = indexes.filter((idx) => {
443
+ const key = idx.columns.join(",") + (idx.unique ? ":unique" : "");
444
+ if (seenIndexes.has(key)) {
445
+ return false;
446
+ }
447
+ seenIndexes.add(key);
448
+ return true;
449
+ });
343
450
  return {
344
451
  tableName,
345
452
  columns,
346
453
  primaryKey: ["id"],
347
454
  foreignKeys,
348
- indexes
455
+ indexes: uniqueIndexes
349
456
  };
350
457
  }
351
458
  function formatColumnMethod(column) {
@@ -706,7 +813,9 @@ function generateMigrations(schemas, options = {}) {
706
813
  const timestamp = options.timestamp ?? generateTimestamp();
707
814
  const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
708
815
  timestampOffset++;
709
- const blueprint = schemaToBlueprint(schema, schemas);
816
+ const blueprint = schemaToBlueprint(schema, schemas, {
817
+ customTypes: options.customTypes
818
+ });
710
819
  const migration = generateCreateMigration(blueprint, {
711
820
  ...options,
712
821
  timestamp: offsetTimestamp
@@ -784,6 +893,7 @@ var TYPE_METHOD_MAP2 = {
784
893
  LongText: "longText",
785
894
  Date: "date",
786
895
  Time: "time",
896
+ DateTime: "dateTime",
787
897
  Timestamp: "timestamp",
788
898
  Json: "json",
789
899
  Email: "string",
@@ -2068,6 +2178,24 @@ function getFactoryPath(factory) {
2068
2178
  }
2069
2179
 
2070
2180
  // src/plugin.ts
2181
+ function getExistingMigrationTables(migrationsDir) {
2182
+ const existingTables = /* @__PURE__ */ new Set();
2183
+ if (!(0, import_node_fs.existsSync)(migrationsDir)) {
2184
+ return existingTables;
2185
+ }
2186
+ try {
2187
+ const files = (0, import_node_fs.readdirSync)(migrationsDir);
2188
+ const createMigrationPattern = /^\d{4}_\d{2}_\d{2}_\d{6}_create_(.+)_table\.php$/;
2189
+ for (const file of files) {
2190
+ const match = file.match(createMigrationPattern);
2191
+ if (match) {
2192
+ existingTables.add(match[1]);
2193
+ }
2194
+ }
2195
+ } catch {
2196
+ }
2197
+ return existingTables;
2198
+ }
2071
2199
  var LARAVEL_CONFIG_SCHEMA = {
2072
2200
  fields: [
2073
2201
  {
@@ -2151,18 +2279,81 @@ function laravelPlugin(options) {
2151
2279
  generate: async (ctx) => {
2152
2280
  const migrationOptions = {
2153
2281
  connection: resolved.connection,
2154
- timestamp: resolved.timestamp
2282
+ timestamp: resolved.timestamp,
2283
+ customTypes: ctx.customTypes
2155
2284
  };
2156
- const migrations = generateMigrations(ctx.schemas, migrationOptions);
2157
- return migrations.map((migration) => ({
2158
- path: getMigrationPath(migration, resolved.migrationsPath),
2159
- content: migration.content,
2160
- type: "migration",
2161
- metadata: {
2162
- tableName: migration.tables[0],
2163
- migrationType: migration.type
2285
+ const outputs = [];
2286
+ const migrationsDir = (0, import_node_path.join)(ctx.cwd, resolved.migrationsPath);
2287
+ const existingTables = getExistingMigrationTables(migrationsDir);
2288
+ if (ctx.changes !== void 0) {
2289
+ if (ctx.changes.length === 0) {
2290
+ return outputs;
2164
2291
  }
2165
- }));
2292
+ const addedSchemaNames = new Set(
2293
+ ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
2294
+ );
2295
+ if (addedSchemaNames.size > 0) {
2296
+ const addedSchemas = Object.fromEntries(
2297
+ Object.entries(ctx.schemas).filter(([name]) => addedSchemaNames.has(name))
2298
+ );
2299
+ const createMigrations = generateMigrations(addedSchemas, migrationOptions);
2300
+ for (const migration of createMigrations) {
2301
+ const tableName = migration.tables[0];
2302
+ if (existingTables.has(tableName)) {
2303
+ ctx.logger.debug(`Skipping CREATE for ${tableName} (already exists)`);
2304
+ continue;
2305
+ }
2306
+ outputs.push({
2307
+ path: getMigrationPath(migration, resolved.migrationsPath),
2308
+ content: migration.content,
2309
+ type: "migration",
2310
+ metadata: {
2311
+ tableName,
2312
+ migrationType: "create"
2313
+ }
2314
+ });
2315
+ }
2316
+ }
2317
+ const alterChanges = ctx.changes.filter(
2318
+ (c) => c.changeType === "modified" || c.changeType === "removed"
2319
+ );
2320
+ if (alterChanges.length > 0) {
2321
+ const alterMigrations = generateMigrationsFromChanges(
2322
+ alterChanges,
2323
+ migrationOptions
2324
+ );
2325
+ for (const migration of alterMigrations) {
2326
+ outputs.push({
2327
+ path: getMigrationPath(migration, resolved.migrationsPath),
2328
+ content: migration.content,
2329
+ type: "migration",
2330
+ metadata: {
2331
+ tableName: migration.tables[0],
2332
+ migrationType: migration.type
2333
+ }
2334
+ });
2335
+ }
2336
+ }
2337
+ } else {
2338
+ const migrations = generateMigrations(ctx.schemas, migrationOptions);
2339
+ for (const migration of migrations) {
2340
+ const tableName = migration.tables[0];
2341
+ if (migration.type === "create" && existingTables.has(tableName)) {
2342
+ ctx.logger.debug(`Skipping migration for ${tableName} (already exists)`);
2343
+ continue;
2344
+ }
2345
+ outputs.push({
2346
+ path: getMigrationPath(migration, resolved.migrationsPath),
2347
+ content: migration.content,
2348
+ type: "migration",
2349
+ metadata: {
2350
+ tableName,
2351
+ migrationType: migration.type
2352
+ }
2353
+ });
2354
+ }
2355
+ }
2356
+ return outputs;
2166
2357
  }
2167
2358
  };
2168
2359
  const modelGenerator = {