@cuongph.dev/dbdocgen 0.1.0 → 0.1.1

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
@@ -62,12 +62,17 @@ var reviewTodoSchema = import_zod.z.object({
62
62
  var columnDocSchema = import_zod.z.object({
63
63
  name: import_zod.z.string().min(1),
64
64
  type: import_zod.z.string().min(1),
65
+ size: import_zod.z.string().optional(),
65
66
  nullable: import_zod.z.boolean(),
66
67
  defaultValue: import_zod.z.string().optional(),
68
+ minValue: import_zod.z.string().optional(),
69
+ maxValue: import_zod.z.string().optional(),
70
+ isUnique: import_zod.z.boolean(),
67
71
  isPrimaryKey: import_zod.z.boolean(),
68
72
  isForeignKey: import_zod.z.boolean(),
69
73
  comment: import_zod.z.string().optional(),
70
- description: enrichedTextSchema.optional()
74
+ description: enrichedTextSchema.optional(),
75
+ constraintNotes: import_zod.z.array(import_zod.z.string()).optional()
71
76
  });
72
77
  var foreignKeyDocSchema = import_zod.z.object({
73
78
  name: import_zod.z.string().optional(),
@@ -146,7 +151,7 @@ var outputLanguageSchema = import_zod2.z.enum(["en", "jp"]);
146
151
  var dbdocgenConfigSchema = import_zod2.z.object({
147
152
  schema: import_zod2.z.string().default("./schema.sql"),
148
153
  dialect: dialectSchema.optional(),
149
- outDir: import_zod2.z.string().default("./docs/db"),
154
+ outDir: import_zod2.z.string().default("./output"),
150
155
  output: import_zod2.z.object({
151
156
  formats: import_zod2.z.array(outputFormatSchema).default(["excel", "markdown", "html", "diagram", "word"]),
152
157
  language: outputLanguageSchema.default("en")
@@ -189,6 +194,221 @@ function createWarning(code, message, target) {
189
194
  };
190
195
  }
191
196
 
197
+ // src/parsers/sql/column-meta.ts
198
+ function normalizeColumnType(definition) {
199
+ if (typeof definition !== "object" || definition === null) {
200
+ return { type: String(definition ?? "unknown").toLowerCase() };
201
+ }
202
+ const def = definition;
203
+ const base = String(def.dataType ?? def.type ?? def.name ?? "unknown").toLowerCase();
204
+ if (String(def.dataType ?? "").toUpperCase() === "ENUM") {
205
+ const values = extractEnumValues(def.expr);
206
+ if (values.length > 0) {
207
+ const joined = values.join(", ");
208
+ return {
209
+ type: `${base}(${values.map((v) => `'${v}'`).join(",")})`,
210
+ size: String(values.length)
211
+ };
212
+ }
213
+ }
214
+ if (def.length !== void 0 && def.length !== null) {
215
+ const length = formatAstValue(def.length);
216
+ if (def.scale !== void 0 && def.scale !== null) {
217
+ const scale = formatAstValue(def.scale);
218
+ return {
219
+ type: `${base}(${length},${scale})`,
220
+ size: `${length},${scale}`
221
+ };
222
+ }
223
+ return {
224
+ type: `${base}(${length})`,
225
+ size: length
226
+ };
227
+ }
228
+ const suffix = Array.isArray(def.suffix) ? def.suffix.map((item) => formatAstValue(item)).filter(Boolean).join(" ") : def.suffix ? formatAstValue(def.suffix) : "";
229
+ return {
230
+ type: suffix ? `${base} ${suffix}`.trim() : base
231
+ };
232
+ }
233
+ function extractColumnComment(definition) {
234
+ const comment = definition.comment;
235
+ if (!comment) return void 0;
236
+ const value = comment.value;
237
+ if (value?.value !== void 0) return String(value.value);
238
+ if (comment.value !== void 0 && typeof comment.value === "string") {
239
+ return comment.value;
240
+ }
241
+ return void 0;
242
+ }
243
+ function hasColumnUnique(definition) {
244
+ return definition.unique === "unique" || definition.unique === true;
245
+ }
246
+ function extractColumnConstraintNotes(definition) {
247
+ const notes = [];
248
+ if (definition.auto_increment === "auto_increment" || definition.auto_increment === true) {
249
+ notes.push("AUTO_INCREMENT");
250
+ }
251
+ if (definition.on_update) {
252
+ notes.push(`ON UPDATE ${formatOnUpdate(definition.on_update)}`);
253
+ }
254
+ const generated = definition.generated;
255
+ if (generated) {
256
+ const storage = String(generated.storage_type ?? "virtual").toUpperCase();
257
+ const expression = stringifyGeneratedExpression(generated.expr);
258
+ notes.push(
259
+ expression ? `GENERATED ALWAYS ${storage}: ${expression}` : `GENERATED ALWAYS ${storage}`
260
+ );
261
+ }
262
+ const enumValues = extractEnumValues(definition.definition?.expr);
263
+ if (enumValues.length > 0) {
264
+ notes.push(`ENUM: ${enumValues.join(", ")}`);
265
+ }
266
+ return notes;
267
+ }
268
+ function extractEnumValues(expr) {
269
+ if (!expr || typeof expr !== "object") return [];
270
+ const object = expr;
271
+ if (object.type !== "expr_list" || !Array.isArray(object.value)) return [];
272
+ return object.value.map((item) => {
273
+ if (!item || typeof item !== "object") return "";
274
+ const entry = item;
275
+ return entry.value !== void 0 ? String(entry.value) : "";
276
+ }).filter(Boolean);
277
+ }
278
+ function formatOnUpdate(value) {
279
+ if (!value || typeof value !== "object") return String(value ?? "");
280
+ const object = value;
281
+ if (object.type === "function" && object.name) {
282
+ const name = object.name;
283
+ const parts = Array.isArray(name.name) ? name.name : [];
284
+ return parts.map((part) => String(part.value ?? "")).join("") || "CURRENT_TIMESTAMP";
285
+ }
286
+ return formatAstValue(value);
287
+ }
288
+ function stringifyGeneratedExpression(expr) {
289
+ if (!expr || typeof expr !== "object") return void 0;
290
+ const text = stringifyExpression(expr);
291
+ return text === "check" ? void 0 : text;
292
+ }
293
+ function extractCheckBounds(expression, columnName) {
294
+ const result = {};
295
+ walkCheckExpression(expression, columnName, result);
296
+ return result;
297
+ }
298
+ function walkCheckExpression(expression, columnName, result) {
299
+ if (!expression || typeof expression !== "object") return;
300
+ const expr = expression;
301
+ if (expr.type === "binary_expr") {
302
+ const operator = String(expr.operator ?? "").toUpperCase();
303
+ if (operator === "AND" || operator === "OR") {
304
+ walkCheckExpression(expr.left, columnName, result);
305
+ walkCheckExpression(expr.right, columnName, result);
306
+ return;
307
+ }
308
+ const columnRef = findColumnRef(expr.left) ?? findColumnRef(expr.right);
309
+ if (columnRef !== columnName) return;
310
+ const bound = readBound(expr, columnName);
311
+ if (!bound) return;
312
+ if (bound.kind === "min") {
313
+ result.minValue = mergeBound(result.minValue, bound.value, "max");
314
+ } else {
315
+ result.maxValue = mergeBound(result.maxValue, bound.value, "min");
316
+ }
317
+ return;
318
+ }
319
+ result.expression ??= stringifyExpression(expr);
320
+ }
321
+ function readBound(expr, columnName) {
322
+ const operator = String(expr.operator ?? "");
323
+ const left = expr.left;
324
+ const right = expr.right;
325
+ if (findColumnRef(left) === columnName) {
326
+ if (operator === ">=" || operator === ">") {
327
+ return { kind: "min", value: formatAstValue(right) };
328
+ }
329
+ if (operator === "<=" || operator === "<") {
330
+ return { kind: "max", value: formatAstValue(right) };
331
+ }
332
+ }
333
+ if (findColumnRef(right) === columnName) {
334
+ if (operator === ">=" || operator === ">") {
335
+ return { kind: "max", value: formatAstValue(left) };
336
+ }
337
+ if (operator === "<=" || operator === "<") {
338
+ return { kind: "min", value: formatAstValue(left) };
339
+ }
340
+ }
341
+ return void 0;
342
+ }
343
+ function mergeBound(current, next, pick) {
344
+ if (!current) return next;
345
+ const currentNum = Number(current);
346
+ const nextNum = Number(next);
347
+ if (!Number.isNaN(currentNum) && !Number.isNaN(nextNum)) {
348
+ return pick === "min" ? String(Math.max(currentNum, nextNum)) : String(Math.min(currentNum, nextNum));
349
+ }
350
+ return next;
351
+ }
352
+ function findColumnRef(value) {
353
+ if (!value || typeof value !== "object") return void 0;
354
+ const expr = value;
355
+ if (expr.type === "column_ref" && expr.column) {
356
+ return String(expr.column);
357
+ }
358
+ return void 0;
359
+ }
360
+ function formatAstValue(value) {
361
+ if (value === null || value === void 0) return "";
362
+ if (typeof value === "object") {
363
+ const object = value;
364
+ if (object.value !== void 0) return String(object.value);
365
+ if (object.dataType) return normalizeColumnType(object).type;
366
+ }
367
+ return String(value);
368
+ }
369
+ function stringifyExpression(expr) {
370
+ if (expr.type === "binary_expr") {
371
+ const left = stringifyExpression(expr.left ?? {});
372
+ const right = stringifyExpression(expr.right ?? {});
373
+ return `${left} ${expr.operator} ${right}`.trim();
374
+ }
375
+ if (expr.type === "column_ref") return String(expr.column ?? "");
376
+ if (expr.value !== void 0) return formatAstValue(expr);
377
+ return "check";
378
+ }
379
+ function extractConstraintColumnNames(definition) {
380
+ return extractDeepColumnNames(definition);
381
+ }
382
+ function extractDeepColumnNames(value) {
383
+ if (!Array.isArray(value)) return [];
384
+ return value.map((item) => {
385
+ if (typeof item !== "object" || item === null) return String(item ?? "unknown");
386
+ const object = item;
387
+ if (object.column !== void 0) return String(object.column);
388
+ if (object.expr) return extractDeepColumnName(object.expr);
389
+ return String(object.name ?? "unknown");
390
+ });
391
+ }
392
+ function extractDeepColumnName(value) {
393
+ if (typeof value !== "object" || value === null) {
394
+ return String(value ?? "unknown");
395
+ }
396
+ const object = value;
397
+ if (object.expr) return extractDeepColumnName(object.expr);
398
+ if (object.column && typeof object.column === "object") {
399
+ return extractDeepColumnName(object.column);
400
+ }
401
+ if (object.column !== void 0) return String(object.column);
402
+ return String(object.name ?? "unknown");
403
+ }
404
+ function stringifyCheckDefinition(definition) {
405
+ if (!Array.isArray(definition) || definition.length === 0) return void 0;
406
+ if (definition.length === 1) {
407
+ return stringifyExpression(definition[0]);
408
+ }
409
+ return definition.map((item) => stringifyExpression(item)).filter(Boolean).join("; ");
410
+ }
411
+
192
412
  // src/parsers/sql/sql-normalizer.ts
193
413
  function normalizeSqlAst(ast, dialect) {
194
414
  const statements = Array.isArray(ast) ? ast : [ast];
@@ -210,7 +430,11 @@ function normalizeSqlAst(ast, dialect) {
210
430
  }
211
431
  for (const index of indexes) {
212
432
  const table = tables.find((candidate) => candidate.name === index.table);
213
- table?.indexes.push(index);
433
+ if (!table) continue;
434
+ table.indexes.push(index);
435
+ if (index.unique) {
436
+ markColumnsUnique(table, index.columns, `INDEX ${index.name}`);
437
+ }
214
438
  }
215
439
  return {
216
440
  dialect,
@@ -232,31 +456,49 @@ function normalizeCreateTable(statement) {
232
456
  reviewTodos: []
233
457
  };
234
458
  for (const definition of createDefinitions) {
235
- if (definition.resource === "column") {
236
- const columnName = extractDeepColumnName(definition.column);
237
- const isPrimaryKey = hasPrimaryKey(definition);
238
- const isNotNull = hasNotNull(definition);
239
- table.columns.push({
240
- name: columnName,
241
- type: normalizeType(definition.definition),
242
- nullable: !isNotNull && !isPrimaryKey,
243
- defaultValue: extractDefaultFromDef(definition),
244
- isPrimaryKey,
245
- isForeignKey: false
246
- });
247
- if (isPrimaryKey) table.primaryKeys.push(columnName);
459
+ if (definition.resource !== "column") continue;
460
+ const columnName = extractDeepColumnName2(definition.column);
461
+ const isPrimaryKey = hasPrimaryKey(definition);
462
+ const isNotNull = hasNotNull(definition);
463
+ const { type, size } = normalizeColumnType(definition.definition);
464
+ const check = definition.check;
465
+ const bounds = check?.definition ? extractCheckBounds(check.definition, columnName) : {};
466
+ const constraintNotes = extractColumnConstraintNotes(definition);
467
+ if (check?.definition) {
468
+ const expression = stringifyCheckDefinition(check.definition);
469
+ if (expression && (!bounds.minValue || !bounds.maxValue)) {
470
+ constraintNotes.push(`CHECK: ${expression}`);
471
+ }
248
472
  }
249
- if (definition.resource === "constraint" && isConstraintType(definition.constraint_type, "PRIMARY KEY")) {
250
- table.primaryKeys = extractDeepColumnNames(definition.definition);
473
+ table.columns.push({
474
+ name: columnName,
475
+ type,
476
+ size,
477
+ nullable: !isNotNull && !isPrimaryKey,
478
+ defaultValue: extractDefaultFromDef(definition),
479
+ minValue: bounds.minValue,
480
+ maxValue: bounds.maxValue,
481
+ isUnique: hasColumnUnique(definition),
482
+ isPrimaryKey,
483
+ isForeignKey: false,
484
+ comment: extractColumnComment(definition),
485
+ constraintNotes: constraintNotes.length > 0 ? constraintNotes : void 0
486
+ });
487
+ if (isPrimaryKey) table.primaryKeys.push(columnName);
488
+ }
489
+ for (const definition of createDefinitions) {
490
+ if (definition.resource !== "constraint") continue;
491
+ if (isConstraintType(definition.constraint_type, "PRIMARY KEY")) {
492
+ table.primaryKeys = extractDeepColumnNames2(definition.definition);
251
493
  for (const column of table.columns) {
252
494
  if (table.primaryKeys.includes(column.name)) column.isPrimaryKey = true;
253
495
  }
254
496
  }
255
- if (definition.resource === "constraint" && isConstraintType(definition.constraint_type, "FOREIGN KEY")) {
256
- const columns = extractDeepColumnNames(definition.definition);
497
+ if (isConstraintType(definition.constraint_type, "FOREIGN KEY")) {
498
+ const columns = extractDeepColumnNames2(definition.definition);
257
499
  const refDef = definition.reference_definition;
258
500
  const referencedTable = extractTableName(refDef?.table);
259
- const referencedColumns = extractDeepColumnNames(refDef?.definition);
501
+ const referencedColumns = extractDeepColumnNames2(refDef?.definition);
260
502
  table.foreignKeys.push({
261
503
  name: typeof definition.constraint === "string" ? definition.constraint : void 0,
262
504
  columns,
@@ -267,14 +509,71 @@ function normalizeCreateTable(statement) {
267
509
  if (columns.includes(column.name)) column.isForeignKey = true;
268
510
  }
269
511
  }
512
+ if (isConstraintType(definition.constraint_type, "UNIQUE")) {
513
+ const columns = extractConstraintColumnNames(definition.definition);
514
+ const label = typeof definition.constraint === "string" ? definition.constraint : "UNIQUE";
515
+ markColumnsUnique(table, columns, label);
516
+ }
517
+ if (isConstraintType(definition.constraint_type, "CHECK")) {
518
+ applyTableCheckConstraint(table, definition);
519
+ }
270
520
  }
271
521
  return table;
272
522
  }
523
+ function markColumnsUnique(table, columns, label) {
524
+ const composite = columns.length > 1;
525
+ for (const columnName of columns) {
526
+ const column = table.columns.find((item) => item.name === columnName);
527
+ if (!column) continue;
528
+ column.isUnique = true;
529
+ if (composite) {
530
+ addConstraintNote(
531
+ column,
532
+ `UNIQUE (${label}: ${columns.join(", ")})`
533
+ );
534
+ }
535
+ }
536
+ }
537
+ function applyTableCheckConstraint(table, definition) {
538
+ const expression = stringifyCheckDefinition(definition.definition);
539
+ if (!expression) return;
540
+ const referencedColumns = /* @__PURE__ */ new Set();
541
+ for (const column of table.columns) {
542
+ const bounds = extractCheckBounds(definition.definition, column.name);
543
+ if (bounds.minValue) column.minValue = bounds.minValue;
544
+ if (bounds.maxValue) column.maxValue = bounds.maxValue;
545
+ if (bounds.minValue || bounds.maxValue) {
546
+ referencedColumns.add(column.name);
547
+ continue;
548
+ }
549
+ if (expression.includes(column.name)) {
550
+ referencedColumns.add(column.name);
551
+ }
552
+ }
553
+ if (referencedColumns.size === 0) {
554
+ for (const column of table.columns) {
555
+ addConstraintNote(column, `CHECK: ${expression}`);
556
+ }
557
+ return;
558
+ }
559
+ for (const columnName of referencedColumns) {
560
+ const column = table.columns.find((item) => item.name === columnName);
561
+ if (!column) continue;
562
+ if (!column.minValue && !column.maxValue) {
563
+ addConstraintNote(column, `CHECK: ${expression}`);
564
+ }
565
+ }
566
+ }
567
+ function addConstraintNote(column, note) {
568
+ const notes = column.constraintNotes ?? [];
569
+ if (!notes.includes(note)) notes.push(note);
570
+ column.constraintNotes = notes;
571
+ }
273
572
  function normalizeCreateIndex(statement) {
274
573
  return {
275
574
  name: String(statement.index ?? statement.index_name ?? "unnamed_index"),
276
575
  table: extractTableName(statement.table),
277
- columns: extractDeepColumnNames(
576
+ columns: extractDeepColumnNames2(
278
577
  statement.index_columns ?? statement.columns
279
578
  ),
280
579
  unique: Boolean(statement.unique)
@@ -328,15 +627,15 @@ function extractTableName(value) {
328
627
  }
329
628
  return String(value ?? "unknown");
330
629
  }
331
- function extractDeepColumnName(value) {
630
+ function extractDeepColumnName2(value) {
332
631
  if (typeof value !== "object" || value === null)
333
632
  return String(value ?? "unknown");
334
633
  const object = value;
335
634
  if (object.expr && typeof object.expr === "object") {
336
- return extractDeepColumnName(object.expr);
635
+ return extractDeepColumnName2(object.expr);
337
636
  }
338
637
  if (object.column && typeof object.column === "object") {
339
- return extractDeepColumnName(object.column);
638
+ return extractDeepColumnName2(object.column);
340
639
  }
341
640
  if (object.value !== void 0) {
342
641
  return String(object.value);
@@ -346,18 +645,9 @@ function extractDeepColumnName(value) {
346
645
  }
347
646
  return String(object.name ?? object.tableName ?? "unknown");
348
647
  }
349
- function extractDeepColumnNames(value) {
648
+ function extractDeepColumnNames2(value) {
350
649
  if (!Array.isArray(value)) return [];
351
- return value.map((item) => extractDeepColumnName(item));
352
- }
353
- function normalizeType(value) {
354
- if (typeof value === "object" && value !== null) {
355
- const object = value;
356
- return String(
357
- object.dataType ?? object.type ?? object.name ?? "unknown"
358
- ).toLowerCase();
359
- }
360
- return String(value ?? "unknown").toLowerCase();
650
+ return value.map((item) => extractDeepColumnName2(item));
361
651
  }
362
652
  function hasPrimaryKey(def) {
363
653
  if (def.primary_key) return true;
@@ -552,8 +842,8 @@ function dialectBias(dialect, requestedDialect, detectedDialect) {
552
842
  }
553
843
 
554
844
  // src/exporters/excel/excel-exporter.ts
555
- var import_promises = require("fs/promises");
556
- var import_node_path = require("path");
845
+ var import_promises2 = require("fs/promises");
846
+ var import_node_path2 = require("path");
557
847
  var import_exceljs = __toESM(require("exceljs"), 1);
558
848
 
559
849
  // src/exporters/shared/output-labels.ts
@@ -576,6 +866,10 @@ var LABELS = {
576
866
  type: "Type",
577
867
  required: "Required",
578
868
  defaultValue: "Default Value",
869
+ size: "Size",
870
+ minValue: "Min",
871
+ maxValue: "Max",
872
+ unique: "Unique",
579
873
  notes: "Notes",
580
874
  yes: "Yes",
581
875
  no: "No",
@@ -604,7 +898,15 @@ var LABELS = {
604
898
  rowNo: "#",
605
899
  backToOverview: "\u2190 Overview",
606
900
  pkMarker: "PK",
607
- fkMarker: "FK"
901
+ fkMarker: "FK",
902
+ erDiagramHeading: "ER Diagram",
903
+ erDiagramSheet: "ER Diagram",
904
+ viewErDiagram: "View interactive ER diagram (html/er-diagram.html)",
905
+ zoomIn: "Zoom in",
906
+ zoomOut: "Zoom out",
907
+ zoomReset: "Reset",
908
+ zoomFit: "Fit",
909
+ panZoomHint: "Drag to pan \xB7 Scroll to zoom"
608
910
  },
609
911
  jp: {
610
912
  docTitle: "Database Documentation",
@@ -624,6 +926,10 @@ var LABELS = {
624
926
  type: "\u578B",
625
927
  required: "\u5FC5\u9808",
626
928
  defaultValue: "\u30C7\u30D5\u30A9\u30EB\u30C8\u5024",
929
+ size: "\u6841\u6570",
930
+ minValue: "\u6700\u5C0F\u5024",
931
+ maxValue: "\u6700\u5927\u5024",
932
+ unique: "\u4E00\u610F",
627
933
  notes: "\u5099\u8003",
628
934
  yes: "Yes",
629
935
  no: "No",
@@ -652,13 +958,582 @@ var LABELS = {
652
958
  rowNo: "No.",
653
959
  backToOverview: "\u2190 \u4E00\u89A7",
654
960
  pkMarker: "PK",
655
- fkMarker: "FK"
961
+ fkMarker: "FK",
962
+ erDiagramHeading: "ER Diagram",
963
+ erDiagramSheet: "ER Diagram",
964
+ viewErDiagram: "\u30A4\u30F3\u30BF\u30E9\u30AF\u30C6\u30A3\u30D6ER\u56F3 (html/er-diagram.html)",
965
+ zoomIn: "\u62E1\u5927",
966
+ zoomOut: "\u7E2E\u5C0F",
967
+ zoomReset: "\u30EA\u30BB\u30C3\u30C8",
968
+ zoomFit: "\u5168\u4F53\u8868\u793A",
969
+ panZoomHint: "\u30C9\u30E9\u30C3\u30B0\u3067\u79FB\u52D5 \xB7 \u30B9\u30AF\u30ED\u30FC\u30EB\u3067\u62E1\u5927\u7E2E\u5C0F"
656
970
  }
657
971
  };
658
972
  function getOutputLabels(language = "en") {
659
973
  return LABELS[language];
660
974
  }
661
975
 
976
+ // src/exporters/shared/column-definition.ts
977
+ var A5_COLUMN_COUNT = 10;
978
+ function columnDefinitionHeaders(labels) {
979
+ return [
980
+ labels.physicalName,
981
+ labels.logicalName,
982
+ labels.type,
983
+ labels.size,
984
+ labels.required,
985
+ labels.defaultValue,
986
+ labels.minValue,
987
+ labels.maxValue,
988
+ labels.unique,
989
+ labels.notes
990
+ ];
991
+ }
992
+ function formatColumnNotes(column, labels) {
993
+ const parts = [];
994
+ if (column.isPrimaryKey) parts.push(labels.pkMarker);
995
+ if (column.isForeignKey) parts.push(labels.fkMarker);
996
+ if (column.constraintNotes?.length) parts.push(...column.constraintNotes);
997
+ if (column.description?.value) parts.push(column.description.value);
998
+ return parts.join(", ") || labels.none;
999
+ }
1000
+ function columnDefinitionRow(column, labels) {
1001
+ return [
1002
+ column.name,
1003
+ column.comment ?? "",
1004
+ column.type,
1005
+ column.size ?? labels.none,
1006
+ column.nullable ? labels.no : labels.yes,
1007
+ column.defaultValue ?? labels.none,
1008
+ column.minValue ?? labels.none,
1009
+ column.maxValue ?? labels.none,
1010
+ column.isUnique ? labels.yes : labels.no,
1011
+ formatColumnNotes(column, labels)
1012
+ ];
1013
+ }
1014
+
1015
+ // src/exporters/diagram/mermaid-exporter.ts
1016
+ var import_promises = require("fs/promises");
1017
+ var import_node_path = require("path");
1018
+ async function exportMermaidDiagram(doc, options) {
1019
+ await (0, import_promises.mkdir)(options.outDir, { recursive: true });
1020
+ await (0, import_promises.writeFile)(
1021
+ (0, import_node_path.join)(options.outDir, "er_diagram.mmd"),
1022
+ renderMermaid(doc),
1023
+ "utf8"
1024
+ );
1025
+ }
1026
+ function renderMermaid(doc) {
1027
+ const lines = ["erDiagram"];
1028
+ for (const warning of doc.warnings) {
1029
+ const target = warning.target ? ` (${warning.target})` : "";
1030
+ lines.push(
1031
+ ` %% WARNING [${warning.severity}] ${warning.code}${target}: ${warning.message}`
1032
+ );
1033
+ }
1034
+ for (const table of doc.tables) {
1035
+ for (const todo of table.reviewTodos) {
1036
+ lines.push(` %% TODO [${todo.type}] ${todo.target}: ${todo.issue}`);
1037
+ }
1038
+ lines.push(` ${table.name} {`);
1039
+ for (const column of table.columns) {
1040
+ const markers = [
1041
+ column.isPrimaryKey ? "PK" : "",
1042
+ column.isForeignKey ? "FK" : ""
1043
+ ].filter(Boolean).join(" ");
1044
+ lines.push(
1045
+ ` ${sanitizeType(column.type)} ${column.name}${markers ? ` "${markers}"` : ""}`
1046
+ );
1047
+ }
1048
+ lines.push(" }");
1049
+ }
1050
+ for (const relationship of doc.relationships.filter(
1051
+ (item) => item.source === "schema"
1052
+ )) {
1053
+ lines.push(
1054
+ ` ${relationship.toTable} ||--o{ ${relationship.fromTable} : "${relationship.constraintName ?? relationship.fromColumn}"`
1055
+ );
1056
+ }
1057
+ return `${lines.join("\n")}
1058
+ `;
1059
+ }
1060
+ function sanitizeType(type) {
1061
+ return type.replace(/[^a-zA-Z0-9_]/g, "_");
1062
+ }
1063
+
1064
+ // src/exporters/diagram/er-diagram-embed.ts
1065
+ function getErDiagramMermaid(doc) {
1066
+ return renderMermaid(doc);
1067
+ }
1068
+ function renderErDiagramHtmlPage(mermaidSource, labels) {
1069
+ const escaped = mermaidSource.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1070
+ return `<!DOCTYPE html>
1071
+ <html lang="en">
1072
+ <head>
1073
+ <meta charset="UTF-8">
1074
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1075
+ <title>${esc(labels.erDiagramHeading)}</title>
1076
+ <style>
1077
+ body { margin: 0; font-family: "Yu Gothic UI", "Meiryo", Arial, sans-serif; background: #f3f4f6; }
1078
+ .toolbar {
1079
+ background: #4472c4; color: #fff; padding: 10px 16px;
1080
+ display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
1081
+ }
1082
+ .toolbar a { color: #fff; text-decoration: underline; }
1083
+ .toolbar .spacer { flex: 1; }
1084
+ .toolbar .hint { opacity: 0.9; font-size: 13px; }
1085
+ .toolbar button {
1086
+ background: #fff; color: #2f5597; border: none; border-radius: 4px;
1087
+ padding: 6px 12px; font-size: 13px; cursor: pointer; font-weight: 600;
1088
+ }
1089
+ .toolbar button:hover { background: #e8eef8; }
1090
+ .viewport {
1091
+ position: relative; height: calc(100vh - 52px); margin: 12px;
1092
+ background: #fff; border: 1px solid #bfc7d4; border-radius: 4px;
1093
+ overflow: hidden; cursor: grab; touch-action: none;
1094
+ }
1095
+ .viewport.dragging { cursor: grabbing; }
1096
+ .canvas {
1097
+ position: absolute; left: 0; top: 0; transform-origin: 0 0;
1098
+ padding: 24px;
1099
+ }
1100
+ .mermaid { min-width: 320px; }
1101
+ .mermaid svg { max-width: none !important; height: auto !important; }
1102
+ </style>
1103
+ </head>
1104
+ <body>
1105
+ <div class="toolbar">
1106
+ <strong>${esc(labels.erDiagramHeading)}</strong>
1107
+ <a href="index.html">\u2190 ${esc(labels.tableListHeading)}</a>
1108
+ <span class="spacer"></span>
1109
+ <span class="hint">${esc(labels.panZoomHint)}</span>
1110
+ <button type="button" id="zoom-out" title="${esc(labels.zoomOut)}">\u2212</button>
1111
+ <button type="button" id="zoom-reset" title="${esc(labels.zoomReset)}">${esc(labels.zoomReset)}</button>
1112
+ <button type="button" id="zoom-in" title="${esc(labels.zoomIn)}">+</button>
1113
+ <button type="button" id="zoom-fit" title="${esc(labels.zoomFit)}">${esc(labels.zoomFit)}</button>
1114
+ </div>
1115
+ <div class="viewport" id="viewport">
1116
+ <div class="canvas" id="canvas">
1117
+ <pre class="mermaid">${escaped}</pre>
1118
+ </div>
1119
+ </div>
1120
+ <script type="module">
1121
+ import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
1122
+
1123
+ mermaid.initialize({
1124
+ startOnLoad: false,
1125
+ theme: "default",
1126
+ er: { useMaxWidth: false }
1127
+ });
1128
+
1129
+ await mermaid.run({ querySelector: ".mermaid" });
1130
+ setupPanZoom(
1131
+ document.getElementById("viewport"),
1132
+ document.getElementById("canvas")
1133
+ );
1134
+
1135
+ function setupPanZoom(viewport, canvas) {
1136
+ let scale = 1;
1137
+ let tx = 40;
1138
+ let ty = 40;
1139
+ let dragging = false;
1140
+ let lastX = 0;
1141
+ let lastY = 0;
1142
+
1143
+ function apply() {
1144
+ canvas.style.transform = "translate(" + tx + "px," + ty + "px) scale(" + scale + ")";
1145
+ }
1146
+
1147
+ function zoomAt(factor, cx, cy) {
1148
+ const next = Math.min(4, Math.max(0.15, scale * factor));
1149
+ const ratio = next / scale;
1150
+ tx = cx - (cx - tx) * ratio;
1151
+ ty = cy - (cy - ty) * ratio;
1152
+ scale = next;
1153
+ apply();
1154
+ }
1155
+
1156
+ function fitToView() {
1157
+ const svg = canvas.querySelector("svg");
1158
+ if (!svg) return;
1159
+ const box = svg.getBBox();
1160
+ const pad = 32;
1161
+ const vw = viewport.clientWidth;
1162
+ const vh = viewport.clientHeight;
1163
+ scale = Math.min(
1164
+ (vw - pad * 2) / Math.max(box.width, 1),
1165
+ (vh - pad * 2) / Math.max(box.height, 1),
1166
+ 1.5
1167
+ );
1168
+ tx = (vw - box.width * scale) / 2 - box.x * scale;
1169
+ ty = (vh - box.height * scale) / 2 - box.y * scale;
1170
+ apply();
1171
+ }
1172
+
1173
+ viewport.addEventListener("wheel", (e) => {
1174
+ e.preventDefault();
1175
+ const rect = viewport.getBoundingClientRect();
1176
+ const cx = e.clientX - rect.left;
1177
+ const cy = e.clientY - rect.top;
1178
+ zoomAt(e.deltaY < 0 ? 1.12 : 0.89, cx, cy);
1179
+ }, { passive: false });
1180
+
1181
+ viewport.addEventListener("mousedown", (e) => {
1182
+ if (e.button !== 0) return;
1183
+ dragging = true;
1184
+ lastX = e.clientX;
1185
+ lastY = e.clientY;
1186
+ viewport.classList.add("dragging");
1187
+ });
1188
+
1189
+ window.addEventListener("mousemove", (e) => {
1190
+ if (!dragging) return;
1191
+ tx += e.clientX - lastX;
1192
+ ty += e.clientY - lastY;
1193
+ lastX = e.clientX;
1194
+ lastY = e.clientY;
1195
+ apply();
1196
+ });
1197
+
1198
+ window.addEventListener("mouseup", () => {
1199
+ dragging = false;
1200
+ viewport.classList.remove("dragging");
1201
+ });
1202
+
1203
+ document.getElementById("zoom-in").addEventListener("click", () => {
1204
+ zoomAt(1.2, viewport.clientWidth / 2, viewport.clientHeight / 2);
1205
+ });
1206
+ document.getElementById("zoom-out").addEventListener("click", () => {
1207
+ zoomAt(1 / 1.2, viewport.clientWidth / 2, viewport.clientHeight / 2);
1208
+ });
1209
+ document.getElementById("zoom-reset").addEventListener("click", () => {
1210
+ scale = 1;
1211
+ tx = 40;
1212
+ ty = 40;
1213
+ apply();
1214
+ });
1215
+ document.getElementById("zoom-fit").addEventListener("click", fitToView);
1216
+
1217
+ fitToView();
1218
+ }
1219
+ </script>
1220
+ </body>
1221
+ </html>`;
1222
+ }
1223
+ function renderErDiagramMarkdown(mermaidSource, labels) {
1224
+ return [
1225
+ `# ${labels.erDiagramHeading}`,
1226
+ "",
1227
+ "```mermaid",
1228
+ mermaidSource.trimEnd(),
1229
+ "```",
1230
+ ""
1231
+ ].join("\n");
1232
+ }
1233
+ function esc(text) {
1234
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1235
+ }
1236
+
1237
+ // src/exporters/diagram/er-diagram-layout.ts
1238
+ var import_elk_bundled = __toESM(require("elkjs/lib/elk.bundled.js"), 1);
1239
+ var BOX_W = 200;
1240
+ var HEADER_H = 28;
1241
+ var LINE_H = 14;
1242
+ var MAX_COLS_SHOWN = 6;
1243
+ var COMPACT_THRESHOLD = 18;
1244
+ var PAD = 24;
1245
+ var CLUSTER_GAP = 56;
1246
+ function isCompactLayout(tableCount) {
1247
+ return tableCount >= COMPACT_THRESHOLD;
1248
+ }
1249
+ function getVisibleErColumns(table) {
1250
+ const prioritized = [
1251
+ ...table.columns.filter((column) => column.isPrimaryKey),
1252
+ ...table.columns.filter(
1253
+ (column) => column.isForeignKey && !column.isPrimaryKey
1254
+ ),
1255
+ ...table.columns.filter(
1256
+ (column) => !column.isPrimaryKey && !column.isForeignKey
1257
+ )
1258
+ ];
1259
+ const unique = prioritized.filter(
1260
+ (column, index, columns) => columns.findIndex((item) => item.name === column.name) === index
1261
+ );
1262
+ return unique.slice(0, MAX_COLS_SHOWN);
1263
+ }
1264
+ function measureTableBox(table, _compact = false) {
1265
+ const visible = getVisibleErColumns(table);
1266
+ const extra = table.columns.length > visible.length ? 1 : 0;
1267
+ return {
1268
+ w: BOX_W,
1269
+ h: HEADER_H + (visible.length + extra) * LINE_H + 8
1270
+ };
1271
+ }
1272
+ function buildAdjacency(doc) {
1273
+ const names = new Set(doc.tables.map((t) => t.name));
1274
+ const adj = /* @__PURE__ */ new Map();
1275
+ for (const name of names) adj.set(name, /* @__PURE__ */ new Set());
1276
+ for (const rel of doc.relationships.filter((r) => r.source === "schema")) {
1277
+ if (!names.has(rel.fromTable) || !names.has(rel.toTable)) continue;
1278
+ adj.get(rel.fromTable).add(rel.toTable);
1279
+ adj.get(rel.toTable).add(rel.fromTable);
1280
+ }
1281
+ return adj;
1282
+ }
1283
+ function connectedComponents(tableNames, adj) {
1284
+ const visited = /* @__PURE__ */ new Set();
1285
+ const components = [];
1286
+ for (const name of tableNames) {
1287
+ if (visited.has(name)) continue;
1288
+ const stack = [name];
1289
+ const component = [];
1290
+ visited.add(name);
1291
+ while (stack.length > 0) {
1292
+ const current = stack.pop();
1293
+ component.push(current);
1294
+ for (const next of adj.get(current) ?? []) {
1295
+ if (!visited.has(next)) {
1296
+ visited.add(next);
1297
+ stack.push(next);
1298
+ }
1299
+ }
1300
+ }
1301
+ component.sort();
1302
+ components.push(component);
1303
+ }
1304
+ return components.sort((a, b) => b.length - a.length);
1305
+ }
1306
+ function sectionToPoints(section) {
1307
+ return [section.startPoint, ...section.bendPoints ?? [], section.endPoint];
1308
+ }
1309
+ function extractEdges(layouted) {
1310
+ const edges = [];
1311
+ for (const edge of layouted.edges ?? []) {
1312
+ for (const section of edge.sections ?? []) {
1313
+ edges.push({
1314
+ id: edge.id,
1315
+ points: sectionToPoints(section)
1316
+ });
1317
+ }
1318
+ }
1319
+ return edges;
1320
+ }
1321
+ async function layoutComponent(doc, tableNames, compact) {
1322
+ const elk = new import_elk_bundled.default();
1323
+ const nameSet = new Set(tableNames);
1324
+ const tables = doc.tables.filter((t) => nameSet.has(t.name));
1325
+ const direction = tables.length >= 8 ? "DOWN" : "RIGHT";
1326
+ const children = tables.map((table) => {
1327
+ const { w, h } = measureTableBox(table, compact);
1328
+ return { id: table.name, width: w, height: h };
1329
+ });
1330
+ const edges = [];
1331
+ const seen = /* @__PURE__ */ new Set();
1332
+ for (const rel of doc.relationships.filter((r) => r.source === "schema")) {
1333
+ if (!nameSet.has(rel.fromTable) || !nameSet.has(rel.toTable)) continue;
1334
+ const key = `${rel.fromTable}->${rel.toTable}`;
1335
+ if (seen.has(key)) continue;
1336
+ seen.add(key);
1337
+ edges.push({
1338
+ id: key,
1339
+ sources: [rel.fromTable],
1340
+ targets: [rel.toTable]
1341
+ });
1342
+ }
1343
+ const graph = {
1344
+ id: "root",
1345
+ layoutOptions: {
1346
+ "elk.algorithm": "layered",
1347
+ "elk.direction": direction,
1348
+ "elk.edgeRouting": "ORTHOGONAL",
1349
+ "elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX",
1350
+ "elk.layered.crossingMinimization.strategy": "LAYER_SWEEP",
1351
+ "elk.layered.spacing.nodeNodeBetweenLayers": compact ? "56" : "80",
1352
+ "elk.spacing.nodeNode": compact ? "32" : "48",
1353
+ "elk.spacing.edgeNode": "24",
1354
+ "elk.padding": `[top=${PAD},left=${PAD},bottom=${PAD},right=${PAD}]`
1355
+ },
1356
+ children,
1357
+ edges
1358
+ };
1359
+ const layouted = await elk.layout(graph);
1360
+ const boxes = /* @__PURE__ */ new Map();
1361
+ let minX = Infinity;
1362
+ let minY = Infinity;
1363
+ let maxX = -Infinity;
1364
+ let maxY = -Infinity;
1365
+ for (const child of layouted.children ?? []) {
1366
+ const table = tables.find((t) => t.name === child.id);
1367
+ if (!table) continue;
1368
+ const { w, h } = measureTableBox(table, compact);
1369
+ const x = child.x ?? 0;
1370
+ const y = child.y ?? 0;
1371
+ const box = { x, y, w, h };
1372
+ boxes.set(child.id, box);
1373
+ minX = Math.min(minX, x);
1374
+ minY = Math.min(minY, y);
1375
+ maxX = Math.max(maxX, x + w);
1376
+ maxY = Math.max(maxY, y + h);
1377
+ }
1378
+ const width = Math.ceil(maxX - minX + PAD * 2);
1379
+ const height = Math.ceil(maxY - minY + PAD * 2);
1380
+ const dx = PAD - minX;
1381
+ const dy = PAD - minY;
1382
+ for (const [name, box] of boxes) {
1383
+ boxes.set(name, { x: box.x + dx, y: box.y + dy, w: box.w, h: box.h });
1384
+ }
1385
+ const shiftedEdges = extractEdges(layouted).map((edge) => ({
1386
+ ...edge,
1387
+ points: edge.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
1388
+ }));
1389
+ return { boxes, edges: shiftedEdges, width, height };
1390
+ }
1391
+ function shiftLayout(boxes, edges, offsetX, offsetY) {
1392
+ for (const [name, box] of boxes) {
1393
+ boxes.set(name, { ...box, x: box.x + offsetX, y: box.y + offsetY });
1394
+ }
1395
+ for (const edge of edges) {
1396
+ for (const p of edge.points) {
1397
+ p.x += offsetX;
1398
+ p.y += offsetY;
1399
+ }
1400
+ }
1401
+ }
1402
+ async function layoutErDiagram(doc) {
1403
+ const tables = doc.tables;
1404
+ if (tables.length === 0) {
1405
+ return {
1406
+ boxes: /* @__PURE__ */ new Map(),
1407
+ edges: [],
1408
+ compact: false,
1409
+ width: 400,
1410
+ height: 80
1411
+ };
1412
+ }
1413
+ const compact = isCompactLayout(tables.length);
1414
+ const adj = buildAdjacency(doc);
1415
+ const components = connectedComponents(
1416
+ tables.map((t) => t.name),
1417
+ adj
1418
+ );
1419
+ const mergedBoxes = /* @__PURE__ */ new Map();
1420
+ const mergedEdges = [];
1421
+ const clusterCols = components.length <= 1 ? 1 : components.length <= 4 ? 2 : 3;
1422
+ let tileX = 0;
1423
+ let tileY = 0;
1424
+ let rowHeight = 0;
1425
+ let maxWidth = PAD;
1426
+ let maxHeight = PAD;
1427
+ for (const [i, component] of components.entries()) {
1428
+ const laid = await layoutComponent(doc, component, compact);
1429
+ shiftLayout(laid.boxes, laid.edges, tileX, tileY);
1430
+ for (const [name, box] of laid.boxes) mergedBoxes.set(name, box);
1431
+ mergedEdges.push(...laid.edges);
1432
+ rowHeight = Math.max(rowHeight, laid.height);
1433
+ tileX += laid.width + CLUSTER_GAP;
1434
+ maxWidth = Math.max(maxWidth, tileX);
1435
+ maxHeight = Math.max(maxHeight, tileY + laid.height);
1436
+ if ((i + 1) % clusterCols === 0) {
1437
+ tileX = 0;
1438
+ tileY += rowHeight + CLUSTER_GAP;
1439
+ rowHeight = 0;
1440
+ }
1441
+ }
1442
+ return {
1443
+ boxes: mergedBoxes,
1444
+ edges: mergedEdges,
1445
+ compact,
1446
+ width: Math.ceil(maxWidth),
1447
+ height: Math.ceil(maxHeight + PAD)
1448
+ };
1449
+ }
1450
+
1451
+ // src/exporters/diagram/er-diagram-svg.ts
1452
+ async function renderErDiagramSvg(doc) {
1453
+ const tables = doc.tables;
1454
+ if (tables.length === 0) {
1455
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="400" height="80"><text x="10" y="40" font-family="Arial,sans-serif" font-size="14">No tables</text></svg>`;
1456
+ }
1457
+ const layout = await layoutErDiagram(doc);
1458
+ return buildErDiagramSvg(doc, layout);
1459
+ }
1460
+ async function renderErDiagramPng(doc) {
1461
+ const tables = doc.tables;
1462
+ if (tables.length === 0) {
1463
+ const svg2 = await renderErDiagramSvg(doc);
1464
+ const sharp2 = (await import("sharp")).default;
1465
+ const buffer2 = await sharp2(Buffer.from(svg2)).png().toBuffer();
1466
+ return { buffer: buffer2, width: 400, height: 80 };
1467
+ }
1468
+ const layout = await layoutErDiagram(doc);
1469
+ const svg = buildErDiagramSvg(doc, layout);
1470
+ const sharp = (await import("sharp")).default;
1471
+ const buffer = await sharp(Buffer.from(svg)).png().toBuffer();
1472
+ return { buffer, width: layout.width, height: layout.height };
1473
+ }
1474
+ function buildErDiagramSvg(doc, layout) {
1475
+ const { boxes, edges, width, height } = layout;
1476
+ const parts = [
1477
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" font-family="Arial,sans-serif" font-size="11">`,
1478
+ `<rect width="100%" height="100%" fill="#ffffff"/>`,
1479
+ `<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L0,6 L8,3 z" fill="#5b7aa6"/></marker></defs>`,
1480
+ `<g class="edges">`
1481
+ ];
1482
+ for (const edge of edges) {
1483
+ if (edge.points.length < 2) continue;
1484
+ const d = pointsToPath(edge.points);
1485
+ parts.push(
1486
+ `<path d="${d}" fill="none" stroke="#7d96b8" stroke-width="1.25" marker-end="url(#arrow)"/>`
1487
+ );
1488
+ }
1489
+ parts.push(`</g><g class="nodes">`);
1490
+ for (const table of doc.tables) {
1491
+ const box = boxes.get(table.name);
1492
+ parts.push(...renderTableBox(table, box));
1493
+ }
1494
+ parts.push("</g></svg>");
1495
+ return parts.join("");
1496
+ }
1497
+ function pointsToPath(points) {
1498
+ return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
1499
+ }
1500
+ function renderTableBox(table, box) {
1501
+ const parts = [
1502
+ `<rect x="${box.x}" y="${box.y}" width="${box.w}" height="${box.h}" fill="#f8fafc" stroke="#4472c4" stroke-width="1.5" rx="4"/>`,
1503
+ `<rect x="${box.x}" y="${box.y}" width="${box.w}" height="${HEADER_H}" fill="#4472c4" rx="4"/>`,
1504
+ `<rect x="${box.x}" y="${box.y + HEADER_H - 4}" width="${box.w}" height="4" fill="#4472c4"/>`,
1505
+ `<text x="${box.x + 8}" y="${box.y + 18}" fill="#ffffff" font-weight="bold">${escapeXml(table.name)}</text>`
1506
+ ];
1507
+ let cy = box.y + HEADER_H + 14;
1508
+ const visible = getVisibleErColumns(table);
1509
+ for (const col of visible) {
1510
+ const marker = col.isPrimaryKey ? " PK" : col.isForeignKey ? " FK" : "";
1511
+ parts.push(
1512
+ `<text x="${box.x + 8}" y="${cy}" fill="#333333">${escapeXml(col.name)} : ${escapeXml(shortType(col.type))}${marker}</text>`
1513
+ );
1514
+ cy += LINE_H;
1515
+ }
1516
+ if (table.columns.length > visible.length) {
1517
+ parts.push(
1518
+ `<text x="${box.x + 8}" y="${cy}" fill="#666666">... +${table.columns.length - visible.length} more</text>`
1519
+ );
1520
+ }
1521
+ return parts;
1522
+ }
1523
+ function shortType(type) {
1524
+ return type.length > 18 ? `${type.slice(0, 15)}...` : type;
1525
+ }
1526
+ function escapeXml(text) {
1527
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1528
+ }
1529
+ function fitErDiagramToBox(width, height, maxWidth, maxHeight) {
1530
+ const scale = Math.min(maxWidth / width, maxHeight / height, 1);
1531
+ return {
1532
+ width: Math.max(1, Math.round(width * scale)),
1533
+ height: Math.max(1, Math.round(height * scale))
1534
+ };
1535
+ }
1536
+
662
1537
  // src/exporters/excel/excel-exporter.ts
663
1538
  var COLOR = {
664
1539
  headerBg: "FF4472C4",
@@ -676,7 +1551,7 @@ var COLOR = {
676
1551
  };
677
1552
  var COL_COUNT = 7;
678
1553
  async function exportExcelDictionary(doc, options) {
679
- await (0, import_promises.mkdir)(options.outDir, { recursive: true });
1554
+ await (0, import_promises2.mkdir)(options.outDir, { recursive: true });
680
1555
  const workbook = new import_exceljs.default.Workbook();
681
1556
  const labels = getOutputLabels(options.language);
682
1557
  const sheetNames = /* @__PURE__ */ new Map();
@@ -684,13 +1559,16 @@ async function exportExcelDictionary(doc, options) {
684
1559
  sheetNames.set(table.name, buildSheetName(table.name, sheetNames));
685
1560
  }
686
1561
  addOverviewSheet(workbook, doc, labels, sheetNames);
1562
+ if (doc.tables.length > 0) {
1563
+ await addErDiagramSheet(workbook, doc, labels, options.outDir);
1564
+ }
687
1565
  for (const table of doc.tables) {
688
1566
  const sheetName = sheetNames.get(table.name);
689
1567
  const sheet = workbook.addWorksheet(sheetName);
690
1568
  populateTableSheet(sheet, table, doc, labels);
691
1569
  }
692
1570
  await workbook.xlsx.writeFile(
693
- (0, import_node_path.join)(options.outDir, "database_dictionary.xlsx")
1571
+ (0, import_node_path2.join)(options.outDir, "database_dictionary.xlsx")
694
1572
  );
695
1573
  }
696
1574
  function addOverviewSheet(workbook, doc, labels, sheetNames) {
@@ -771,6 +1649,51 @@ function addOverviewSheet(workbook, doc, labels, sheetNames) {
771
1649
  };
772
1650
  sheet.views = [{ state: "frozen", ySplit: headerRowNum }];
773
1651
  }
1652
+ async function addErDiagramSheet(workbook, doc, labels, outDir) {
1653
+ const sheet = workbook.addWorksheet(labels.erDiagramSheet);
1654
+ sheet.mergeCells(1, 1, 1, 6);
1655
+ const titleCell = sheet.getCell(1, 1);
1656
+ titleCell.value = labels.erDiagramHeading;
1657
+ titleCell.font = { bold: true, size: 14, color: { argb: COLOR.overviewFg } };
1658
+ titleCell.fill = solidFill(COLOR.overviewBg);
1659
+ titleCell.alignment = { horizontal: "center", vertical: "middle" };
1660
+ sheet.getRow(1).height = 28;
1661
+ let nextRow = 3;
1662
+ try {
1663
+ const { buffer: png, width, height } = await renderErDiagramPng(doc);
1664
+ await (0, import_promises2.writeFile)((0, import_node_path2.join)(outDir, "er_diagram.png"), png);
1665
+ const imageId = workbook.addImage({
1666
+ base64: png.toString("base64"),
1667
+ extension: "png"
1668
+ });
1669
+ const fitted = fitErDiagramToBox(width, height, 1100, 1200);
1670
+ sheet.addImage(imageId, {
1671
+ tl: { col: 0, row: 2 },
1672
+ ext: fitted
1673
+ });
1674
+ nextRow = Math.max(28, Math.ceil(fitted.height / 18) + 4);
1675
+ } catch {
1676
+ sheet.getCell(3, 1).value = labels.viewErDiagram;
1677
+ nextRow = 5;
1678
+ }
1679
+ const mermaid = getErDiagramMermaid(doc);
1680
+ sheet.getCell(nextRow, 1).value = "Mermaid source";
1681
+ sheet.getCell(nextRow, 1).font = { bold: true, color: { argb: COLOR.metaFg } };
1682
+ nextRow += 1;
1683
+ sheet.mergeCells(nextRow, 1, nextRow + 20, 6);
1684
+ const sourceCell = sheet.getCell(nextRow, 1);
1685
+ sourceCell.value = mermaid;
1686
+ sourceCell.alignment = { wrapText: true, vertical: "top" };
1687
+ sourceCell.font = { name: "Courier New", size: 9 };
1688
+ sheet.columns = [
1689
+ { width: 24 },
1690
+ { width: 24 },
1691
+ { width: 24 },
1692
+ { width: 24 },
1693
+ { width: 24 },
1694
+ { width: 24 }
1695
+ ];
1696
+ }
774
1697
  function populateTableSheet(sheet, table, doc, labels) {
775
1698
  const indexes = collectTableIndexes(table, doc);
776
1699
  sheet.mergeCells(1, 1, 1, 6);
@@ -811,48 +1734,39 @@ function populateTableSheet(sheet, table, doc, labels) {
811
1734
  row.getCell(2).alignment = { wrapText: true, vertical: "top" };
812
1735
  }
813
1736
  sheet.addRow([]);
814
- const headerRow = sheet.addRow([
815
- labels.physicalName,
816
- labels.logicalName,
817
- labels.type,
818
- labels.required,
819
- labels.defaultValue,
820
- labels.notes
821
- ]);
1737
+ const headerRow = sheet.addRow(columnDefinitionHeaders(labels));
822
1738
  styleColorRow(headerRow, COLOR.headerBg, COLOR.headerFg);
823
- applyBorderToRow(headerRow, 6);
1739
+ applyBorderToRow(headerRow, A5_COLUMN_COUNT);
824
1740
  const headerRowNum = headerRow.number;
825
1741
  for (const [i, column] of table.columns.entries()) {
826
- const markers = [];
827
- if (column.isPrimaryKey) markers.push(labels.pkMarker);
828
- if (column.isForeignKey) markers.push(labels.fkMarker);
829
- const notes = [markers.join(", "), column.description?.value ?? ""].filter(Boolean).join(" | ");
830
- const row = sheet.addRow([
831
- column.name,
832
- displayValue(column.comment, labels),
833
- column.type,
834
- column.nullable ? labels.no : labels.yes,
835
- column.defaultValue ?? "-",
836
- notes || "-"
837
- ]);
1742
+ const row = sheet.addRow(
1743
+ columnDefinitionRow(column, labels).map(
1744
+ (value, index) => index === 1 ? displayValue(value, labels) : value
1745
+ )
1746
+ );
838
1747
  if (column.isPrimaryKey) {
839
- shadeRow(row, 6, COLOR.pkBg);
1748
+ shadeRow(row, A5_COLUMN_COUNT, COLOR.pkBg);
840
1749
  row.getCell(1).font = { bold: true };
841
1750
  } else if (column.isForeignKey) {
842
- shadeRow(row, 6, COLOR.fkBg);
1751
+ shadeRow(row, A5_COLUMN_COUNT, COLOR.fkBg);
843
1752
  } else if (i % 2 === 1) {
844
- shadeRow(row, 6, COLOR.altRow);
1753
+ shadeRow(row, A5_COLUMN_COUNT, COLOR.altRow);
845
1754
  }
846
- row.getCell(4).alignment = { horizontal: "center" };
847
- applyBorderToRow(row, 6);
1755
+ row.getCell(5).alignment = { horizontal: "center" };
1756
+ row.getCell(9).alignment = { horizontal: "center" };
1757
+ applyBorderToRow(row, A5_COLUMN_COUNT);
848
1758
  }
849
1759
  sheet.columns = [
1760
+ { width: 22 },
850
1761
  { width: 24 },
851
- { width: 28 },
852
- { width: 18 },
1762
+ { width: 16 },
853
1763
  { width: 10 },
854
- { width: 18 },
855
- { width: 36 }
1764
+ { width: 8 },
1765
+ { width: 14 },
1766
+ { width: 8 },
1767
+ { width: 8 },
1768
+ { width: 8 },
1769
+ { width: 28 }
856
1770
  ];
857
1771
  sheet.views = [{ state: "frozen", ySplit: headerRowNum }];
858
1772
  }
@@ -922,55 +1836,6 @@ function collectTableIndexes(table, doc) {
922
1836
  ];
923
1837
  }
924
1838
 
925
- // src/exporters/diagram/mermaid-exporter.ts
926
- var import_promises2 = require("fs/promises");
927
- var import_node_path2 = require("path");
928
- async function exportMermaidDiagram(doc, options) {
929
- await (0, import_promises2.mkdir)(options.outDir, { recursive: true });
930
- await (0, import_promises2.writeFile)(
931
- (0, import_node_path2.join)(options.outDir, "er_diagram.mmd"),
932
- renderMermaid(doc),
933
- "utf8"
934
- );
935
- }
936
- function renderMermaid(doc) {
937
- const lines = ["erDiagram"];
938
- for (const warning of doc.warnings) {
939
- const target = warning.target ? ` (${warning.target})` : "";
940
- lines.push(
941
- ` %% WARNING [${warning.severity}] ${warning.code}${target}: ${warning.message}`
942
- );
943
- }
944
- for (const table of doc.tables) {
945
- for (const todo of table.reviewTodos) {
946
- lines.push(` %% TODO [${todo.type}] ${todo.target}: ${todo.issue}`);
947
- }
948
- lines.push(` ${table.name} {`);
949
- for (const column of table.columns) {
950
- const markers = [
951
- column.isPrimaryKey ? "PK" : "",
952
- column.isForeignKey ? "FK" : ""
953
- ].filter(Boolean).join(" ");
954
- lines.push(
955
- ` ${sanitizeType(column.type)} ${column.name}${markers ? ` "${markers}"` : ""}`
956
- );
957
- }
958
- lines.push(" }");
959
- }
960
- for (const relationship of doc.relationships.filter(
961
- (item) => item.source === "schema"
962
- )) {
963
- lines.push(
964
- ` ${relationship.toTable} ||--o{ ${relationship.fromTable} : "${relationship.constraintName ?? relationship.fromColumn}"`
965
- );
966
- }
967
- return `${lines.join("\n")}
968
- `;
969
- }
970
- function sanitizeType(type) {
971
- return type.replace(/[^a-zA-Z0-9_]/g, "_");
972
- }
973
-
974
1839
  // src/exporters/markdown/markdown-exporter.ts
975
1840
  var import_promises3 = require("fs/promises");
976
1841
  var import_node_path3 = require("path");
@@ -986,6 +1851,14 @@ async function exportMarkdownDocs(doc, options) {
986
1851
  const tablesDir = (0, import_node_path3.join)(options.outDir, "tables");
987
1852
  await (0, import_promises3.mkdir)(tablesDir, { recursive: true });
988
1853
  const labels = getOutputLabels(options.language);
1854
+ if (doc.tables.length > 0) {
1855
+ const mermaid = getErDiagramMermaid(doc);
1856
+ await (0, import_promises3.writeFile)(
1857
+ (0, import_node_path3.join)(options.outDir, "ER_DIAGRAM.md"),
1858
+ renderErDiagramMarkdown(mermaid, labels),
1859
+ "utf8"
1860
+ );
1861
+ }
989
1862
  for (const table of doc.tables) {
990
1863
  await (0, import_promises3.writeFile)(
991
1864
  (0, import_node_path3.join)(tablesDir, `${sanitizeFilename(table.name)}.md`),
@@ -1034,12 +1907,12 @@ function renderTableDoc(table, doc, labels) {
1034
1907
  lines.push(`## ${labels.columnsHeading}`);
1035
1908
  lines.push("");
1036
1909
  lines.push(
1037
- `| ${labels.physicalName} | ${labels.logicalName} | ${labels.type} | ${labels.required} | ${labels.defaultValue} | ${labels.notes} |`
1910
+ `| ${columnDefinitionHeaders(labels).join(" | ")} |`
1038
1911
  );
1039
- lines.push("|--------|--------|----|------|--------------|------|");
1912
+ lines.push(`|${columnDefinitionHeaders(labels).map(() => "--------").join("|")}|`);
1040
1913
  for (const col of table.columns) {
1041
1914
  lines.push(
1042
- `| ${escapeMd(col.name)} | ${escapeMd(col.comment ?? "")} | ${escapeMd(col.type)} | ${col.nullable ? labels.no : labels.yes} | ${escapeMd(col.defaultValue ?? "-")} | ${escapeMd(col.description?.value ?? "")} |`
1915
+ `| ${columnDefinitionRow(col, labels).map((value) => escapeMd(value)).join(" | ")} |`
1043
1916
  );
1044
1917
  }
1045
1918
  lines.push("");
@@ -1071,6 +1944,14 @@ async function exportHtmlDocs(doc, options) {
1071
1944
  renderIndexPage(doc, labels),
1072
1945
  "utf8"
1073
1946
  );
1947
+ if (doc.tables.length > 0) {
1948
+ const mermaid = getErDiagramMermaid(doc);
1949
+ await (0, import_promises4.writeFile)(
1950
+ (0, import_node_path4.join)(htmlDir, "er-diagram.html"),
1951
+ renderErDiagramHtmlPage(mermaid, labels),
1952
+ "utf8"
1953
+ );
1954
+ }
1074
1955
  for (const table of doc.tables) {
1075
1956
  await (0, import_promises4.writeFile)(
1076
1957
  (0, import_node_path4.join)(tablesDir, `${sanitizeFilename(table.name)}.html`),
@@ -1154,7 +2035,7 @@ function pageShell(title, body, fromSubdir = false) {
1154
2035
  <head>
1155
2036
  <meta charset="UTF-8">
1156
2037
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1157
- <title>${esc(title)}</title>
2038
+ <title>${esc2(title)}</title>
1158
2039
  <style>${CSS} </style>
1159
2040
  </head>
1160
2041
  <body>
@@ -1171,34 +2052,35 @@ function renderIndexPage(doc, labels) {
1171
2052
  const fkCount = table.foreignKeys.length;
1172
2053
  const fileName = sanitizeFilename(table.name);
1173
2054
  tableRows += ` <tr>
1174
- <td><a href="tables/${fileName}.html">${esc(table.name)}</a></td>
1175
- <td>${esc(table.comment ?? "")}</td>
2055
+ <td><a href="tables/${fileName}.html">${esc2(table.name)}</a></td>
2056
+ <td>${esc2(table.comment ?? "")}</td>
1176
2057
  <td style="text-align:center">${table.columns.length}</td>
1177
- <td>${esc(pkCols)}</td>
2058
+ <td>${esc2(pkCols)}</td>
1178
2059
  <td style="text-align:center">${fkCount}</td>
1179
2060
  </tr>
1180
2061
  `;
1181
2062
  }
1182
2063
  const body = `
1183
- <h1>${esc(labels.docTitle)}</h1>
2064
+ <h1>${esc2(labels.docTitle)}</h1>
1184
2065
  <div class="summary">
1185
- <div class="summary-item"><div class="num">${doc.tables.length}</div><div class="lbl">${esc(labels.tablesLabel)}</div></div>
1186
- <div class="summary-item"><div class="num">${doc.relationships.length}</div><div class="lbl">${esc(labels.relationshipsLabel)}</div></div>
1187
- <div class="summary-item"><div class="num">${doc.dialect}</div><div class="lbl">${esc(labels.dialectLabel)}</div></div>
2066
+ <div class="summary-item"><div class="num">${doc.tables.length}</div><div class="lbl">${esc2(labels.tablesLabel)}</div></div>
2067
+ <div class="summary-item"><div class="num">${doc.relationships.length}</div><div class="lbl">${esc2(labels.relationshipsLabel)}</div></div>
2068
+ <div class="summary-item"><div class="num">${doc.dialect}</div><div class="lbl">${esc2(labels.dialectLabel)}</div></div>
1188
2069
  </div>
1189
- <h2>${esc(labels.tableListHeading)}</h2>
2070
+ <p class="back"><a href="er-diagram.html">${esc2(labels.erDiagramHeading)} \u2192</a></p>
2071
+ <h2>${esc2(labels.tableListHeading)}</h2>
1190
2072
  <table class="table-list">
1191
2073
  <thead><tr>
1192
- <th>${esc(labels.tableLabel)}</th>
1193
- <th>${esc(labels.tableLogicalName)}</th>
2074
+ <th>${esc2(labels.tableLabel)}</th>
2075
+ <th>${esc2(labels.tableLogicalName)}</th>
1194
2076
  <th style="width:70px;text-align:center">Cols</th>
1195
- <th>${esc(labels.primaryKey)}</th>
2077
+ <th>${esc2(labels.primaryKey)}</th>
1196
2078
  <th style="width:50px;text-align:center">FK</th>
1197
2079
  </tr></thead>
1198
2080
  <tbody>
1199
2081
  ${tableRows} </tbody>
1200
2082
  </table>
1201
- <p class="note">${esc(labels.generatedNote)}</p>
2083
+ <p class="note">${esc2(labels.generatedNote)}</p>
1202
2084
  `;
1203
2085
  return pageShell(labels.docTitle, body);
1204
2086
  }
@@ -1216,39 +2098,34 @@ function renderTablePage(table, doc, labels) {
1216
2098
  const pkBadge = col.isPrimaryKey ? `<span class="badge badge-pk">PK</span>` : "";
1217
2099
  const fkBadge = col.isForeignKey ? `<span class="badge badge-fk">FK</span>` : "";
1218
2100
  const rowClass = col.isPrimaryKey ? "pk" : col.isForeignKey ? "fk" : "";
1219
- const required = col.nullable ? labels.no : labels.yes;
1220
- colRows += ` <tr${rowClass ? ` class="${rowClass}"` : ""}><td>${esc(col.name)}${pkBadge}${fkBadge}</td><td>${esc(col.comment ?? "")}</td><td>${esc(col.type)}</td><td>${required}</td><td>${esc(col.defaultValue ?? "-")}</td><td>${esc(col.description?.value ?? "")}</td></tr>
2101
+ const cells = columnDefinitionRow(col, labels);
2102
+ colRows += ` <tr${rowClass ? ` class="${rowClass}"` : ""}><td>${esc2(cells[0] ?? "")}${pkBadge}${fkBadge}</td>` + cells.slice(1).map((cell) => `<td>${esc2(cell)}</td>`).join("") + `</tr>
1221
2103
  `;
1222
2104
  }
1223
2105
  const body = `
1224
- <p class="back"><a href="../index.html">\u2190 ${esc(labels.tableListHeading)}</a></p>
1225
- <h1>${esc(table.name)}</h1>
1226
- <h2>${esc(labels.tableInfoHeading)}</h2>
2106
+ <p class="back"><a href="../index.html">\u2190 ${esc2(labels.tableListHeading)}</a></p>
2107
+ <h1>${esc2(table.name)}</h1>
2108
+ <h2>${esc2(labels.tableInfoHeading)}</h2>
1227
2109
  <table class="meta">
1228
2110
  <tbody>
1229
- <tr><th>${esc(labels.tablePhysicalName)}</th><td>${esc(table.name)}</td></tr>
1230
- <tr><th>${esc(labels.tableLogicalName)}</th><td>${esc(table.comment ?? "")}</td></tr>
1231
- <tr><th>${esc(labels.schema)}</th><td>${esc(table.schema ?? "")}</td></tr>
1232
- <tr><th>${esc(labels.primaryKey)}</th><td>${esc(table.primaryKeys.join(", ") || labels.none)}</td></tr>
1233
- <tr><th>${esc(labels.foreignKeys)}</th><td>${foreignKeys}</td></tr>
1234
- <tr><th>${esc(labels.indexes)}</th><td>${indexText}</td></tr>
2111
+ <tr><th>${esc2(labels.tablePhysicalName)}</th><td>${esc2(table.name)}</td></tr>
2112
+ <tr><th>${esc2(labels.tableLogicalName)}</th><td>${esc2(table.comment ?? "")}</td></tr>
2113
+ <tr><th>${esc2(labels.schema)}</th><td>${esc2(table.schema ?? "")}</td></tr>
2114
+ <tr><th>${esc2(labels.primaryKey)}</th><td>${esc2(table.primaryKeys.join(", ") || labels.none)}</td></tr>
2115
+ <tr><th>${esc2(labels.foreignKeys)}</th><td>${foreignKeys}</td></tr>
2116
+ <tr><th>${esc2(labels.indexes)}</th><td>${indexText}</td></tr>
1235
2117
  </tbody>
1236
2118
  </table>
1237
2119
 
1238
- <h2>${esc(labels.columnsHeading)}</h2>
2120
+ <h2>${esc2(labels.columnsHeading)}</h2>
1239
2121
  <table class="columns">
1240
2122
  <thead><tr>
1241
- <th>${esc(labels.physicalName)}</th>
1242
- <th>${esc(labels.logicalName)}</th>
1243
- <th>${esc(labels.type)}</th>
1244
- <th>${esc(labels.required)}</th>
1245
- <th>${esc(labels.defaultValue)}</th>
1246
- <th>${esc(labels.notes)}</th>
2123
+ ${columnDefinitionHeaders(labels).map((header) => `<th>${esc2(header)}</th>`).join("\n ")}
1247
2124
  </tr></thead>
1248
2125
  <tbody>
1249
2126
  ${colRows} </tbody>
1250
2127
  </table>
1251
- <p class="note">${esc(labels.generatedNote)}</p>
2128
+ <p class="note">${esc2(labels.generatedNote)}</p>
1252
2129
  `;
1253
2130
  return pageShell(table.name, body);
1254
2131
  }
@@ -1260,7 +2137,7 @@ function collectTableIndexes3(table, doc) {
1260
2137
  )
1261
2138
  ];
1262
2139
  }
1263
- function esc(text) {
2140
+ function esc2(text) {
1264
2141
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1265
2142
  }
1266
2143
 
@@ -1300,6 +2177,53 @@ async function exportWordDocument(doc, options) {
1300
2177
  children: [new import_docx.TextRun(`${labels.relationshipsLabel}: ${doc.relationships.length}`)]
1301
2178
  })
1302
2179
  );
2180
+ if (doc.tables.length > 0) {
2181
+ children.push(
2182
+ new import_docx.Paragraph({
2183
+ heading: import_docx.HeadingLevel.HEADING_2,
2184
+ children: [new import_docx.TextRun(labels.erDiagramHeading)]
2185
+ })
2186
+ );
2187
+ try {
2188
+ const { buffer: png, width, height } = await renderErDiagramPng(doc);
2189
+ await (0, import_promises5.writeFile)((0, import_node_path5.join)(options.outDir, "er_diagram.png"), png);
2190
+ const fitted = fitErDiagramToBox(width, height, 620, 900);
2191
+ children.push(
2192
+ new import_docx.Paragraph({
2193
+ children: [
2194
+ new import_docx.ImageRun({
2195
+ data: png,
2196
+ transformation: fitted,
2197
+ type: "png"
2198
+ })
2199
+ ]
2200
+ })
2201
+ );
2202
+ } catch {
2203
+ children.push(
2204
+ new import_docx.Paragraph({
2205
+ children: [new import_docx.TextRun(labels.viewErDiagram)]
2206
+ })
2207
+ );
2208
+ }
2209
+ const mermaid = getErDiagramMermaid(doc);
2210
+ children.push(
2211
+ new import_docx.Paragraph({
2212
+ children: [new import_docx.TextRun({ text: "Mermaid source", bold: true })]
2213
+ })
2214
+ );
2215
+ children.push(
2216
+ new import_docx.Paragraph({
2217
+ children: [
2218
+ new import_docx.TextRun({
2219
+ text: mermaid,
2220
+ font: "Courier New",
2221
+ size: 18
2222
+ })
2223
+ ]
2224
+ })
2225
+ );
2226
+ }
1303
2227
  children.push(
1304
2228
  new import_docx.Paragraph({
1305
2229
  heading: import_docx.HeadingLevel.HEADING_2,
@@ -1559,14 +2483,7 @@ function renderTableDetail(table, doc, labels) {
1559
2483
  return items;
1560
2484
  }
1561
2485
  function renderColumnsTable(table, labels) {
1562
- const headerCells = [
1563
- labels.physicalName,
1564
- labels.logicalName,
1565
- labels.type,
1566
- labels.required,
1567
- labels.defaultValue,
1568
- labels.notes
1569
- ].map(
2486
+ const headerCells = columnDefinitionHeaders(labels).map(
1570
2487
  (h) => new import_docx.TableCell({
1571
2488
  children: [
1572
2489
  new import_docx.Paragraph({ children: [new import_docx.TextRun({ text: h, bold: true })] })
@@ -1577,40 +2494,11 @@ function renderColumnsTable(table, labels) {
1577
2494
  for (const col of table.columns) {
1578
2495
  colRows.push(
1579
2496
  new import_docx.TableRow({
1580
- children: [
1581
- new import_docx.TableCell({
1582
- children: [new import_docx.Paragraph({ children: [new import_docx.TextRun(col.name)] })]
1583
- }),
1584
- new import_docx.TableCell({
1585
- children: [
1586
- new import_docx.Paragraph({ children: [new import_docx.TextRun(col.comment ?? "")] })
1587
- ]
1588
- }),
1589
- new import_docx.TableCell({
1590
- children: [new import_docx.Paragraph({ children: [new import_docx.TextRun(col.type)] })]
1591
- }),
1592
- new import_docx.TableCell({
1593
- children: [
1594
- new import_docx.Paragraph({
1595
- children: [new import_docx.TextRun(col.nullable ? labels.no : labels.yes)]
1596
- })
1597
- ]
1598
- }),
1599
- new import_docx.TableCell({
1600
- children: [
1601
- new import_docx.Paragraph({
1602
- children: [new import_docx.TextRun(col.defaultValue ?? "-")]
1603
- })
1604
- ]
1605
- }),
1606
- new import_docx.TableCell({
1607
- children: [
1608
- new import_docx.Paragraph({
1609
- children: [new import_docx.TextRun(col.description?.value ?? "")]
1610
- })
1611
- ]
2497
+ children: columnDefinitionRow(col, labels).map(
2498
+ (value) => new import_docx.TableCell({
2499
+ children: [new import_docx.Paragraph({ children: [new import_docx.TextRun(value)] })]
1612
2500
  })
1613
- ]
2501
+ )
1614
2502
  })
1615
2503
  );
1616
2504
  }