diff --git a/src/lib/dbml/dbml-export/__tests__/cases/3.dbml b/src/lib/dbml/dbml-export/__tests__/cases/3.dbml new file mode 100644 index 00000000..d5fe9e9e --- /dev/null +++ b/src/lib/dbml/dbml-export/__tests__/cases/3.dbml @@ -0,0 +1,8 @@ +Table "public"."guy_table" { + "id" integer [pk, not null] + "created_at" timestamp [not null] + "column3" text + "arrayfield" text[] + "field_5" "character varying" + "field_6" "character varying(100)" +} diff --git a/src/lib/dbml/dbml-export/__tests__/cases/3.json b/src/lib/dbml/dbml-export/__tests__/cases/3.json new file mode 100644 index 00000000..09b57ccc --- /dev/null +++ b/src/lib/dbml/dbml-export/__tests__/cases/3.json @@ -0,0 +1 @@ +{"id":"mqqwkkod7trl","name":"guy-db","databaseType":"postgresql","createdAt":"2025-09-10T18:45:32.817Z","updatedAt":"2025-09-10T19:15:21.682Z","tables":[{"id":"g2hv9mlo3qbyjnxdc44j1zxl2","name":"guy_table","schema":"public","x":100,"y":300,"fields":[{"id":"qdqgzmtxsi84ujfuktsvjuop8","name":"id","type":{"id":"integer","name":"integer"},"primaryKey":true,"unique":true,"nullable":false,"createdAt":1757529932816},{"id":"wsys99f86679ch6fbjryw0egr","name":"created_at","type":{"id":"timestamp_without_time_zone","name":"timestamp without time zone"},"primaryKey":false,"unique":false,"nullable":false,"createdAt":1757529932816},{"id":"ro39cba7sd290k90qjgzib8pi","name":"column3","type":{"id":"text","name":"text"},"primaryKey":false,"unique":false,"nullable":true,"createdAt":1757529932816},{"id":"6cntbu2orwk7kxlg0rcduqgbo","name":"arrayfield","type":{"id":"array","name":"array"},"primaryKey":false,"unique":false,"nullable":true,"createdAt":1757529932816},{"id":"7cz0ybdoov2m3wbgm9tlzatz0","name":"field_5","type":{"id":"character_varying","name":"character varying"},"unique":false,"nullable":true,"primaryKey":false,"createdAt":1757531685981},{"id":"zzwlyvqzz93oh0vv8f8qob103","name":"field_6","type":{"id":"character_varying","name":"character varying"},"unique":false,"nullable":true,"primaryKey":false,"createdAt":1757531713961,"characterMaximumLength":"100"}],"indexes":[{"id":"r0w71lnbnje2j9cz1t9j64rya","name":"guy_table_pkey","unique":true,"fieldIds":["qdqgzmtxsi84ujfuktsvjuop8"],"createdAt":1757529932816,"isPrimaryKey":true}],"color":"#8eb7ff","isView":false,"isMaterializedView":false,"createdAt":1757529932816,"diagramId":"mqqwkkod7trl"}],"relationships":[],"dependencies":[],"areas":[],"customTypes":[]} \ No newline at end of file diff --git a/src/lib/dbml/dbml-export/__tests__/export-sql-dbml-cases.test.ts b/src/lib/dbml/dbml-export/__tests__/export-sql-dbml-cases.test.ts index 356a032e..b4f89317 100644 --- a/src/lib/dbml/dbml-export/__tests__/export-sql-dbml-cases.test.ts +++ b/src/lib/dbml/dbml-export/__tests__/export-sql-dbml-cases.test.ts @@ -44,4 +44,24 @@ describe('DBML Export - Diagram Case 1 Tests', () => { // Compare the generated DBML with the expected DBML expect(generatedDBML).toBe(expectedDBML); }); + + it('should handle case 3 diagram', { timeout: 30000 }, async () => { + // Read the JSON file + const jsonPath = path.join(__dirname, 'cases', '3.json'); + const jsonContent = fs.readFileSync(jsonPath, 'utf-8'); + + // Parse the JSON and convert to diagram + const diagram = diagramFromJSONInput(jsonContent); + + // Generate DBML from the diagram + const result = generateDBMLFromDiagram(diagram); + const generatedDBML = result.standardDbml; + + // Read the expected DBML file + const dbmlPath = path.join(__dirname, 'cases', '3.dbml'); + const expectedDBML = fs.readFileSync(dbmlPath, 'utf-8'); + + // Compare the generated DBML with the expected DBML + expect(generatedDBML).toBe(expectedDBML); + }); }); diff --git a/src/lib/dbml/dbml-export/dbml-export.ts b/src/lib/dbml/dbml-export/dbml-export.ts index b0049583..0c0a8935 100644 --- a/src/lib/dbml/dbml-export/dbml-export.ts +++ b/src/lib/dbml/dbml-export/dbml-export.ts @@ -596,6 +596,13 @@ const normalizeCharTypeFormat = (dbml: string): string => { .replace(/character \(([0-9]+)\)/g, 'character($1)'); }; +// Fix array types that are incorrectly quoted by DBML importer +const fixArrayTypes = (dbml: string): string => { + // Remove quotes around array types like "text[]" -> text[] + // Matches patterns like: "fieldname" "type[]" and replaces with "fieldname" type[] + return dbml.replace(/(\s+"[^"]+"\s+)"([^"\s]+\[\])"/g, '$1$2'); +}; + // Fix table definitions with incorrect bracket syntax const fixTableBracketSyntax = (dbml: string): string => { // Fix patterns like Table [schema].[table] to Table "schema"."table" @@ -985,12 +992,14 @@ export function generateDBMLFromDiagram(diagram: Diagram): DBMLExportResult { ); } - standard = normalizeCharTypeFormat( - fixMultilineTableNames( - fixTableBracketSyntax( - importer.import( - baseScript, - databaseTypeToImportFormat(diagram.databaseType) + standard = fixArrayTypes( + normalizeCharTypeFormat( + fixMultilineTableNames( + fixTableBracketSyntax( + importer.import( + baseScript, + databaseTypeToImportFormat(diagram.databaseType) + ) ) ) ) @@ -1007,7 +1016,9 @@ export function generateDBMLFromDiagram(diagram: Diagram): DBMLExportResult { standard = enumsDBML + '\n\n' + standard; } - inline = normalizeCharTypeFormat(convertToInlineRefs(standard)); + inline = fixArrayTypes( + normalizeCharTypeFormat(convertToInlineRefs(standard)) + ); // Clean up excessive empty lines in both outputs standard = standard.replace(/\n\s*\n\s*\n/g, '\n\n');