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>;
actions?: CodeSnippetAction[];
actionsTooltipSide?: 'top' | 'right' | 'bottom' | 'left';
allowCopy?: boolean;
}
export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
@@ -58,6 +59,7 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
editorProps,
actions,
actionsTooltipSide,
allowCopy = true,
}) => {
const { t } = useTranslation();
const monaco = useMonaco();
@@ -131,33 +133,37 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
<Suspense fallback={<Spinner />}>
{isComplete ? (
<div className="absolute right-1 top-1 z-10 flex flex-col gap-1">
<Tooltip
onOpenChange={setTooltipOpen}
open={isCopied || tooltipOpen}
>
<TooltipTrigger asChild>
<span>
<Button
className="h-fit p-1.5"
variant="outline"
onClick={copyToClipboard}
>
{isCopied ? (
<CopyCheck size={16} />
) : (
<Copy size={16} />
)}
</Button>
</span>
</TooltipTrigger>
<TooltipContent side={actionsTooltipSide}>
{t(
isCopied
? 'copied'
: 'copy_to_clipboard'
)}
</TooltipContent>
</Tooltip>
{allowCopy ? (
<Tooltip
onOpenChange={setTooltipOpen}
open={isCopied || tooltipOpen}
>
<TooltipTrigger asChild>
<span>
<Button
className="h-fit p-1.5"
variant="outline"
onClick={copyToClipboard}
>
{isCopied ? (
<CopyCheck size={16} />
) : (
<Copy size={16} />
)}
</Button>
</span>
</TooltipTrigger>
<TooltipContent
side={actionsTooltipSide}
>
{t(
isCopied
? 'copied'
: 'copy_to_clipboard'
)}
</TooltipContent>
</Tooltip>
) : null}
{actions &&
actions.length > 0 &&

View File

@@ -362,9 +362,10 @@ export const exportBaseSQL = ({
.join(', ');
if (fieldNames) {
const indexName = table.schema
? `${table.schema}_${index.name}`
: index.name;
const indexName =
table.schema && !isDBMLFlow
? `${table.schema}_${index.name}`
: index.name;
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
// Note: When a table has a schema, index names are prefixed with the schema
expect(result.standardDbml).toContain(
'email [unique, name: "public_idx_email"]'
'email [unique, name: "idx_email"]'
);
expect(result.standardDbml).toContain(
'created_at [name: "public_idx_created_at"]'
'created_at [name: "idx_created_at"]'
);
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
const indexSection = result.standardDbml.match(/Indexes \{[\s\S]*?\}/);
expect(indexSection).toBeTruthy();
expect(indexSection![0]).toContain('email [unique, name: "idx_email"]');
expect(indexSection![0]).toContain(
'email [unique, name: "public_idx_email"]'
'created_at [name: "idx_created_at"]'
);
expect(indexSection![0]).toContain(
'created_at [name: "public_idx_created_at"]'
);
expect(indexSection![0]).toContain(
'(email, created_at) [name: "public_idx_email_created"]'
'(email, created_at) [name: "idx_email_created"]'
);
});
});

View File

@@ -350,7 +350,10 @@ const convertToInlineRefs = (dbml: string): string => {
// Combine all attributes into a single bracket
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 '));
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)
@@ -804,9 +810,15 @@ export function generateDBMLFromDiagram(diagram: Diagram): DBMLExportResult {
standard = restoreTableSchemas(standard, diagram);
// Prepend Enum DBML to the standard output
standard = enumsDBML + '\n' + standard;
if (enumsDBML) {
standard = enumsDBML + '\n\n' + 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) {
console.error(
'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.
// The error message will then follow.
if (standard.startsWith('// Error generating DBML:')) {
standard = enumsDBML + standard;
if (standard.startsWith('// Error generating DBML:') && enumsDBML) {
standard = enumsDBML + '\n\n' + standard;
}
if (inline.startsWith('// Error generating DBML:')) {
inline = enumsDBML + inline;
if (inline.startsWith('// Error generating DBML:') && enumsDBML) {
inline = enumsDBML + '\n\n' + inline;
}
}