fix(dbml): fix dbml output format (#815)

This commit is contained in:
Guy Ben-Aharon
2025-07-30 14:31:56 +03:00
committed by GitHub
parent 00bd535b3c
commit eed104be5b
4 changed files with 62 additions and 45 deletions

View File

@@ -44,6 +44,7 @@ export interface CodeSnippetProps {
editorProps?: React.ComponentProps<EditorType>; editorProps?: React.ComponentProps<EditorType>;
actions?: CodeSnippetAction[]; actions?: CodeSnippetAction[];
actionsTooltipSide?: 'top' | 'right' | 'bottom' | 'left'; actionsTooltipSide?: 'top' | 'right' | 'bottom' | 'left';
allowCopy?: boolean;
} }
export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo( export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
@@ -58,6 +59,7 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
editorProps, editorProps,
actions, actions,
actionsTooltipSide, actionsTooltipSide,
allowCopy = true,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const monaco = useMonaco(); const monaco = useMonaco();
@@ -131,6 +133,7 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
<Suspense fallback={<Spinner />}> <Suspense fallback={<Spinner />}>
{isComplete ? ( {isComplete ? (
<div className="absolute right-1 top-1 z-10 flex flex-col gap-1"> <div className="absolute right-1 top-1 z-10 flex flex-col gap-1">
{allowCopy ? (
<Tooltip <Tooltip
onOpenChange={setTooltipOpen} onOpenChange={setTooltipOpen}
open={isCopied || tooltipOpen} open={isCopied || tooltipOpen}
@@ -150,7 +153,9 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
</Button> </Button>
</span> </span>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side={actionsTooltipSide}> <TooltipContent
side={actionsTooltipSide}
>
{t( {t(
isCopied isCopied
? 'copied' ? 'copied'
@@ -158,6 +163,7 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
)} )}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
) : null}
{actions && {actions &&
actions.length > 0 && actions.length > 0 &&

View File

@@ -362,7 +362,8 @@ export const exportBaseSQL = ({
.join(', '); .join(', ');
if (fieldNames) { if (fieldNames) {
const indexName = table.schema const indexName =
table.schema && !isDBMLFlow
? `${table.schema}_${index.name}` ? `${table.schema}_${index.name}`
: index.name; : index.name;
sqlScript += `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName} (${fieldNames});\n`; sqlScript += `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${indexName} ON ${tableName} (${fieldNames});\n`;

View File

@@ -937,26 +937,24 @@ describe('DBML Export - Issue Fixes', () => {
// Check that indexes are properly formatted with names // Check that indexes are properly formatted with names
// Note: When a table has a schema, index names are prefixed with the schema // Note: When a table has a schema, index names are prefixed with the schema
expect(result.standardDbml).toContain( expect(result.standardDbml).toContain(
'email [unique, name: "public_idx_email"]' 'email [unique, name: "idx_email"]'
); );
expect(result.standardDbml).toContain( expect(result.standardDbml).toContain(
'created_at [name: "public_idx_created_at"]' 'created_at [name: "idx_created_at"]'
); );
expect(result.standardDbml).toContain( expect(result.standardDbml).toContain(
'(email, created_at) [name: "public_idx_email_created"]' '(email, created_at) [name: "idx_email_created"]'
); );
// Verify proper index syntax in the table // Verify proper index syntax in the table
const indexSection = result.standardDbml.match(/Indexes \{[\s\S]*?\}/); const indexSection = result.standardDbml.match(/Indexes \{[\s\S]*?\}/);
expect(indexSection).toBeTruthy(); expect(indexSection).toBeTruthy();
expect(indexSection![0]).toContain('email [unique, name: "idx_email"]');
expect(indexSection![0]).toContain( expect(indexSection![0]).toContain(
'email [unique, name: "public_idx_email"]' 'created_at [name: "idx_created_at"]'
); );
expect(indexSection![0]).toContain( expect(indexSection![0]).toContain(
'created_at [name: "public_idx_created_at"]' '(email, created_at) [name: "idx_email_created"]'
);
expect(indexSection![0]).toContain(
'(email, created_at) [name: "public_idx_email_created"]'
); );
}); });
}); });

View File

@@ -350,7 +350,10 @@ const convertToInlineRefs = (dbml: string): string => {
// Combine all attributes into a single bracket // Combine all attributes into a single bracket
const combinedAttributes = allAttributes.join(', '); const combinedAttributes = allAttributes.join(', ');
return `${fieldPart.trim()} [${combinedAttributes}]${commentPart || ''}`; // Preserve original spacing from fieldPart
const leadingSpaces = fieldPart.match(/^(\s*)/)?.[1] || '';
const fieldDefWithoutSpaces = fieldPart.trim();
return `${leadingSpaces}${fieldDefWithoutSpaces} [${combinedAttributes}]${commentPart || ''}`;
} }
); );
@@ -388,7 +391,10 @@ const convertToInlineRefs = (dbml: string): string => {
.filter((line) => !line.trim().startsWith('Ref ')); .filter((line) => !line.trim().startsWith('Ref '));
const finalDbml = finalLines.join('\n').trim(); const finalDbml = finalLines.join('\n').trim();
return finalDbml; // Clean up excessive empty lines - replace multiple consecutive empty lines with just one
const cleanedDbml = finalDbml.replace(/\n\s*\n\s*\n/g, '\n\n');
return cleanedDbml;
}; };
// Function to check for SQL keywords (add more if needed) // Function to check for SQL keywords (add more if needed)
@@ -804,9 +810,15 @@ export function generateDBMLFromDiagram(diagram: Diagram): DBMLExportResult {
standard = restoreTableSchemas(standard, diagram); standard = restoreTableSchemas(standard, diagram);
// Prepend Enum DBML to the standard output // Prepend Enum DBML to the standard output
standard = enumsDBML + '\n' + standard; if (enumsDBML) {
standard = enumsDBML + '\n\n' + standard;
}
inline = normalizeCharTypeFormat(convertToInlineRefs(standard)); inline = normalizeCharTypeFormat(convertToInlineRefs(standard));
// Clean up excessive empty lines in both outputs
standard = standard.replace(/\n\s*\n\s*\n/g, '\n\n');
inline = inline.replace(/\n\s*\n\s*\n/g, '\n\n');
} catch (error: unknown) { } catch (error: unknown) {
console.error( console.error(
'Error during DBML generation process:', 'Error during DBML generation process:',
@@ -822,11 +834,11 @@ export function generateDBMLFromDiagram(diagram: Diagram): DBMLExportResult {
// If an error occurred, still prepend enums if they exist, or they'll be lost. // If an error occurred, still prepend enums if they exist, or they'll be lost.
// The error message will then follow. // The error message will then follow.
if (standard.startsWith('// Error generating DBML:')) { if (standard.startsWith('// Error generating DBML:') && enumsDBML) {
standard = enumsDBML + standard; standard = enumsDBML + '\n\n' + standard;
} }
if (inline.startsWith('// Error generating DBML:')) { if (inline.startsWith('// Error generating DBML:') && enumsDBML) {
inline = enumsDBML + inline; inline = enumsDBML + '\n\n' + inline;
} }
} }