@dbml/cli 8.0.0-alpha.9 → 8.0.0-prerelease.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.
Files changed (94) hide show
  1. package/__tests__/db2dbml/mssql/dbml-error.log +110 -0
  2. package/__tests__/db2dbml/mysql/dbml-error.log +143 -0
  3. package/__tests__/db2dbml/oracle/dbml-error.log +154 -0
  4. package/__tests__/db2dbml/postgres/dbml-error.log +77 -0
  5. package/__tests__/dbml2sql/filename --mysql --out-file/out-files/schema.sql +1 -1
  6. package/__tests__/dbml2sql/filename --oracle --out-file/out-files/schema.sql +1 -1
  7. package/__tests__/dbml2sql/filename --out-file/out-files/schema.sql +1 -1
  8. package/__tests__/dbml2sql/filename --postgres --out-file/out-files/schema.sql +1 -1
  9. package/__tests__/dbml2sql/filenames --mysql --out-file/out-files/schema.sql +1 -1
  10. package/__tests__/dbml2sql/filenames --oracle --out-file/out-files/schema.sql +1 -1
  11. package/__tests__/dbml2sql/filenames --out-file/out-files/schema.sql +1 -1
  12. package/__tests__/dbml2sql/filenames --postgres --out-file/out-files/schema.sql +1 -1
  13. package/__tests__/dbml2sql/multifile aliased import stdout/dbml-error.log +0 -0
  14. package/__tests__/dbml2sql/multifile aliased import stdout/in-files/auth.dbml +4 -0
  15. package/__tests__/dbml2sql/multifile aliased import stdout/in-files/main.dbml +6 -0
  16. package/__tests__/dbml2sql/multifile aliased import stdout/options.json +3 -0
  17. package/__tests__/dbml2sql/multifile aliased import stdout/stdout.txt +12 -0
  18. package/__tests__/dbml2sql/multifile circular import stdout/dbml-error.log +0 -0
  19. package/__tests__/dbml2sql/multifile circular import stdout/in-files/a.dbml +6 -0
  20. package/__tests__/dbml2sql/multifile circular import stdout/in-files/b.dbml +6 -0
  21. package/__tests__/dbml2sql/multifile circular import stdout/options.json +3 -0
  22. package/__tests__/dbml2sql/multifile circular import stdout/stdout.txt +14 -0
  23. package/__tests__/dbml2sql/multifile error entry-syntax-error/dbml-error.log +97 -0
  24. package/__tests__/dbml2sql/multifile error entry-syntax-error/in-files/lib.dbml +4 -0
  25. package/__tests__/dbml2sql/multifile error entry-syntax-error/in-files/main.dbml +6 -0
  26. package/__tests__/dbml2sql/multifile error entry-syntax-error/options.json +3 -0
  27. package/__tests__/dbml2sql/multifile error entry-syntax-error/stdout.txt +5 -0
  28. package/__tests__/dbml2sql/multifile error import-not-found/dbml-error.log +7182 -0
  29. package/__tests__/dbml2sql/multifile error import-not-found/in-files/main.dbml +5 -0
  30. package/__tests__/dbml2sql/multifile error import-not-found/options.json +3 -0
  31. package/__tests__/dbml2sql/multifile error import-not-found/stdout.txt +4 -0
  32. package/__tests__/dbml2sql/multifile error imported-syntax-error/dbml-error.log +28 -0
  33. package/__tests__/dbml2sql/multifile error imported-syntax-error/in-files/broken.dbml +4 -0
  34. package/__tests__/dbml2sql/multifile error imported-syntax-error/in-files/main.dbml +6 -0
  35. package/__tests__/dbml2sql/multifile error imported-syntax-error/options.json +3 -0
  36. package/__tests__/dbml2sql/multifile error imported-syntax-error/stdout.txt +5 -0
  37. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/dbml-error.log +28 -0
  38. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/in-files/base.dbml +6 -0
  39. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/in-files/main.dbml +3 -0
  40. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/in-files/types.dbml +4 -0
  41. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/options.json +3 -0
  42. package/__tests__/dbml2sql/multifile error transitive-use-not-visible/stdout.txt +4 -0
  43. package/__tests__/dbml2sql/multifile records pulling stdout/dbml-error.log +0 -0
  44. package/__tests__/dbml2sql/multifile records pulling stdout/in-files/base.dbml +9 -0
  45. package/__tests__/dbml2sql/multifile records pulling stdout/in-files/main.dbml +6 -0
  46. package/__tests__/dbml2sql/multifile records pulling stdout/options.json +3 -0
  47. package/__tests__/dbml2sql/multifile records pulling stdout/stdout.txt +23 -0
  48. package/__tests__/dbml2sql/multifile refs across files stdout/dbml-error.log +0 -0
  49. package/__tests__/dbml2sql/multifile refs across files stdout/in-files/main.dbml +2 -0
  50. package/__tests__/dbml2sql/multifile refs across files stdout/in-files/orders.dbml +8 -0
  51. package/__tests__/dbml2sql/multifile refs across files stdout/in-files/users.dbml +4 -0
  52. package/__tests__/dbml2sql/multifile refs across files stdout/options.json +3 -0
  53. package/__tests__/dbml2sql/multifile refs across files stdout/stdout.txt +12 -0
  54. package/__tests__/dbml2sql/multifile schema pulling stdout/dbml-error.log +0 -0
  55. package/__tests__/dbml2sql/multifile schema pulling stdout/in-files/auth.dbml +9 -0
  56. package/__tests__/dbml2sql/multifile schema pulling stdout/in-files/main.dbml +3 -0
  57. package/__tests__/dbml2sql/multifile schema pulling stdout/options.json +3 -0
  58. package/__tests__/dbml2sql/multifile schema pulling stdout/stdout.txt +14 -0
  59. package/__tests__/dbml2sql/multifile tablegroup pulling stdout/dbml-error.log +0 -0
  60. package/__tests__/dbml2sql/multifile tablegroup pulling stdout/in-files/base.dbml +14 -0
  61. package/__tests__/dbml2sql/multifile tablegroup pulling stdout/in-files/main.dbml +1 -0
  62. package/__tests__/dbml2sql/multifile tablegroup pulling stdout/options.json +3 -0
  63. package/__tests__/dbml2sql/multifile tablegroup pulling stdout/stdout.txt +12 -0
  64. package/__tests__/dbml2sql/multifile transitive reuse stdout/dbml-error.log +0 -0
  65. package/__tests__/dbml2sql/multifile transitive reuse stdout/in-files/base.dbml +6 -0
  66. package/__tests__/dbml2sql/multifile transitive reuse stdout/in-files/main.dbml +7 -0
  67. package/__tests__/dbml2sql/multifile transitive reuse stdout/in-files/types.dbml +4 -0
  68. package/__tests__/dbml2sql/multifile transitive reuse stdout/options.json +3 -0
  69. package/__tests__/dbml2sql/multifile transitive reuse stdout/stdout.txt +20 -0
  70. package/__tests__/dbml2sql/multiple_schema_mssql/out-files/multiple_schema.out.sql +1 -1
  71. package/__tests__/dbml2sql/multiple_schema_mysql/out-files/multiple_schema.out.sql +1 -1
  72. package/__tests__/dbml2sql/multiple_schema_oracle/out-files/multiple_schema.out.sql +1 -1
  73. package/__tests__/dbml2sql/multiple_schema_pg/out-files/multiple_schema.out.sql +1 -1
  74. package/__tests__/dbml2sql/single file stdout/dbml-error.log +0 -0
  75. package/__tests__/dbml2sql/single file stdout/in-files/schema.dbml +11 -0
  76. package/__tests__/dbml2sql/single file stdout/options.json +3 -0
  77. package/__tests__/dbml2sql/single file stdout/stdout.txt +14 -0
  78. package/__tests__/dbml2sql/syntax-error/dbml-error.log +116 -0
  79. package/__tests__/sql2dbml/custom-error-alter-table-column-not-found --oracle/dbml-error.log +44 -0
  80. package/__tests__/sql2dbml/custom-error-alter-table-table-not-found --oracle/dbml-error.log +44 -0
  81. package/__tests__/sql2dbml/custom-error-column-comment-column-not-found --oracle/dbml-error.log +44 -0
  82. package/__tests__/sql2dbml/custom-error-column-comment-table-not-found --oracle/dbml-error.log +44 -0
  83. package/__tests__/sql2dbml/custom-error-create-index-table-not-found --oracle/dbml-error.log +44 -0
  84. package/__tests__/sql2dbml/custom-error-table-comment-table-not-found --oracle/dbml-error.log +44 -0
  85. package/__tests__/sql2dbml/syntax-error/dbml-error.log +44 -0
  86. package/__tests__/sql2dbml/syntax-error-duplicate-endpoints --mssql/dbml-error.log +44 -0
  87. package/__tests__/sql2dbml/syntax-error-duplicate-endpoints --mysql/dbml-error.log +44 -0
  88. package/lib/index.js +44 -91
  89. package/package.json +5 -5
  90. package/src/NodeProjectLayout.ts +31 -89
  91. package/src/cli/connector.ts +5 -2
  92. package/src/cli/export.ts +19 -18
  93. package/src/cli/import.ts +7 -3
  94. package/src/helpers/logger.ts +4 -23
@@ -1,52 +1,25 @@
1
1
  import {
2
- existsSync, readFileSync, readdirSync, statSync,
2
+ readFileSync, readdirSync, statSync,
3
3
  } from 'node:fs';
4
4
  import {
5
5
  type DbmlProjectLayout, Filepath,
6
6
  } from '@dbml/parse';
7
+ import {
8
+ fileLogger,
9
+ } from './helpers/logger';
7
10
 
8
11
  /**
9
- * A DbmlProjectLayout backed by the Node.js filesystem.
10
- * Reads source files on demand from disk; setSource/deleteSource maintain an
11
- * in-memory overlay (e.g. for unsaved edits) without touching the real FS.
12
+ * A read-only DbmlProjectLayout backed by the Node.js filesystem.
12
13
  */
13
14
  export class NodeProjectLayout implements DbmlProjectLayout {
14
- // Overlay is an overriding map of file absolute paths to file content
15
- // `null`: The file is deleted
16
- private overlay = new Map<string, string | null>();
17
- private entryPoints: Filepath[];
15
+ private readonly entryPoints: Filepath[];
18
16
 
19
17
  constructor (entryPoints: Filepath[]) {
20
18
  this.entryPoints = [...entryPoints];
21
19
  }
22
20
 
23
- setSource (filePath: Filepath, content: string): void {
24
- this.overlay.set(filePath.absolute, content);
25
- }
26
-
27
21
  getSource (filePath: Filepath): string | undefined {
28
- const absolutePath = filePath.absolute;
29
-
30
- // If overlay already has the file,
31
- // return its content
32
- if (this.overlay.has(absolutePath)) {
33
- const value = this.overlay.get(absolutePath);
34
- return value ?? undefined; // null (deleted) -> undefined
35
- }
36
- try {
37
- return readFileSync(absolutePath, 'utf-8');
38
- } catch {
39
- return undefined;
40
- }
41
- }
42
-
43
- deleteSource (filePath: Filepath): void {
44
- this.overlay.set(filePath.absolute, null);
45
- }
46
-
47
- clearSource (): void {
48
- this.overlay.clear();
49
- this.entryPoints = [];
22
+ return safeReadFileSync(filePath.absolute, 'utf-8');
50
23
  }
51
24
 
52
25
  exists (filePath: Filepath): boolean {
@@ -54,72 +27,41 @@ export class NodeProjectLayout implements DbmlProjectLayout {
54
27
  }
55
28
 
56
29
  isFile (filePath: Filepath): boolean {
57
- const absolutePath = filePath.absolute;
58
-
59
- if (this.overlay.has(absolutePath)) return this.overlay.get(absolutePath) !== null;
60
-
61
- try {
62
- // `statSync` (not lstatSync) so symlinks resolve to their target, matching
63
- // how node reads the file on disk.
64
- return statSync(absolutePath).isFile();
65
- } catch {
66
- return false;
67
- }
30
+ return safeStatSync(filePath.absolute, {
31
+ throwIfNoEntry: false,
32
+ })?.isFile() ?? false;
68
33
  }
69
34
 
70
35
  isDirectory (filePath: Filepath): boolean {
71
- const absolutePath = filePath.absolute;
72
-
73
- try {
74
- // Exist in filesystem
75
- if (statSync(absolutePath).isDirectory()) return true;
76
- } catch {
77
- // fall through: the path may exist only in the overlay
78
- }
79
- for (const [overlayPath, content] of this.overlay) {
80
- if (content === null) continue;
81
- if (overlayPath.startsWith(absolutePath)) return true;
82
- }
83
- return false;
36
+ return safeStatSync(filePath.absolute, {
37
+ throwIfNoEntry: false,
38
+ })?.isDirectory() ?? false;
84
39
  }
85
40
 
86
41
  listDirectory (dirPath?: Filepath): Filepath[] {
87
42
  const basePath = dirPath?.absolute ?? '/';
88
- const results = new Set<string>();
89
-
90
- try {
91
- // Filesystem entries
92
- for (const entry of readdirSync(basePath)) {
93
- results.add(basePath + entry);
94
- }
95
- } catch {
96
- // Not a readable directory - ignore
97
- }
98
-
99
- // Overlay additions: non-deleted files directly under base
100
- for (const [abs, content] of this.overlay) {
101
- if (content === null) continue;
102
- if (!abs.startsWith(basePath)) continue;
103
- if (!abs.slice(basePath.length).includes('/')) results.add(abs);
104
- }
105
-
106
- // Remove overlay-deleted paths
107
- for (const [overlayPath, content] of this.overlay) {
108
- if (content === null) results.delete(overlayPath);
109
- }
110
-
111
- return [...results].sort().map(Filepath.from);
43
+ return safeReaddirSync(basePath)?.map((entry) => basePath + entry).sort().map(Filepath.from) ?? [];
112
44
  }
113
45
 
114
- getEntryPoints (): Filepath[] {
46
+ getEntrypoints (): Filepath[] {
115
47
  return this.entryPoints.filter((fp) => this.exists(fp));
116
48
  }
49
+ }
117
50
 
118
- clone (): NodeProjectLayout {
119
- const copy = new NodeProjectLayout([...this.entryPoints]);
120
- for (const [overlayPath, content] of this.overlay) {
121
- copy.overlay.set(overlayPath, content);
51
+ // Wrap a function so that errors are logged to file and undefined is returned
52
+ function logOnError<Args extends unknown[], R> (fn: (...args: Args) => R): (...args: Args) => R | undefined {
53
+ return (...args) => {
54
+ try {
55
+ return fn(...args);
56
+ } catch (e) {
57
+ fileLogger.error(e instanceof Error ? e : new Error(String(e)));
58
+ return undefined;
122
59
  }
123
- return copy;
124
- }
60
+ };
125
61
  }
62
+
63
+ const safeReadFileSync = logOnError(readFileSync as (path: string, encoding: BufferEncoding) => string);
64
+ const safeReaddirSync = logOnError(readdirSync as (path: string) => string[]);
65
+ const safeStatSync = logOnError(
66
+ (path: string, opts: { throwIfNoEntry: false }) => statSync(path, opts),
67
+ );
@@ -10,7 +10,9 @@ import {
10
10
  Command,
11
11
  } from 'commander';
12
12
  import figures from 'figures';
13
- import logger from '../helpers/logger';
13
+ import {
14
+ consoleLogger, fileLogger,
15
+ } from '../helpers/logger';
14
16
  import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
15
17
  import OutputFilePlugin from './outputPlugins/outputFilePlugin';
16
18
  import {
@@ -37,6 +39,7 @@ export default async function connectionHandler (program: Command) {
37
39
  console.log(` ${chalk.green(figures.main.tick)} Generated DBML file from database's connection: ${path.basename(opts.outFile)}`);
38
40
  }
39
41
  } catch (error) {
40
- logger.error(error as Error | string);
42
+ consoleLogger.error(error as Error | string);
43
+ fileLogger.error(error as Error | string);
41
44
  }
42
45
  }
package/src/cli/export.ts CHANGED
@@ -13,7 +13,9 @@ import figures from 'figures';
13
13
  import {
14
14
  NodeProjectLayout,
15
15
  } from '../NodeProjectLayout';
16
- import logger from '../helpers/logger';
16
+ import {
17
+ consoleLogger, fileLogger,
18
+ } from '../helpers/logger';
17
19
  import config from './config';
18
20
  import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
19
21
  import OutputFilePlugin from './outputPlugins/outputFilePlugin';
@@ -32,9 +34,8 @@ export default async function exportHandler (program: Command): Promise<void> {
32
34
  const opts = program.opts();
33
35
  const format = getFormatOpt(opts);
34
36
 
35
- const entryPoints = inputPaths.map((p: string) => new Filepath(p));
36
- const compiler = new Compiler();
37
- compiler.layout = new NodeProjectLayout(entryPoints);
37
+ const entrypoints = inputPaths.map((p: string) => new Filepath(p));
38
+ const compiler = new Compiler(new NodeProjectLayout(entrypoints));
38
39
 
39
40
  const outputPlugin = opts.outFile
40
41
  ? new OutputFilePlugin(
@@ -49,25 +50,25 @@ export default async function exportHandler (program: Command): Promise<void> {
49
50
 
50
51
  let hasErrors = false;
51
52
 
52
- for (const filepath of entryPoints) {
53
+ for (const filepath of entrypoints) {
53
54
  const result = compiler.interpretFile(filepath);
54
55
  const errors = result.getErrors();
55
56
 
56
57
  if (errors.length > 0) {
57
58
  hasErrors = true;
58
- const isMultifile = compiler.reachableFiles(filepath).length > 1;
59
- logger.error(
60
- `\n ${errors
61
- .map((e) => {
62
- const pos = e.nodeOrToken.startPos;
63
- const line = pos.line + 1;
64
- const col = pos.column + 1;
65
- const location = chalk.cyan(isMultifile ? e.filepath.relativeTo(process.cwd()) : e.filepath.basename);
66
- const position = chalk.yellow(`(${line},${col})`);
67
- return `${location}${position}: ${e.message}`;
68
- })
69
- .join('\n ')}`,
70
- );
59
+ const isMultifile = compiler.reachableFiles(filepath).length > 1; // If this filepath can reach other files, it's mutifile
60
+ const msg = `\n ${errors
61
+ .map((e) => {
62
+ const pos = e.nodeOrToken.startPos;
63
+ const line = pos.line + 1;
64
+ const col = pos.column + 1;
65
+ const location = chalk.cyan(isMultifile ? e.filepath.relativeTo(process.cwd()) : e.filepath.basename);
66
+ const position = chalk.yellow(`(${line},${col})`);
67
+ return `${location}${position}: ${e.message}`;
68
+ })
69
+ .join('\n ')}`;
70
+ consoleLogger.error(msg);
71
+ fileLogger.error(msg);
71
72
  continue;
72
73
  }
73
74
 
package/src/cli/import.ts CHANGED
@@ -13,7 +13,9 @@ import figures from 'figures';
13
13
  import {
14
14
  SyntaxError,
15
15
  } from '../errors';
16
- import logger from '../helpers/logger';
16
+ import {
17
+ consoleLogger, fileLogger,
18
+ } from '../helpers/logger';
17
19
  import config from './config';
18
20
  import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
19
21
  import OutputFilePlugin from './outputPlugins/outputFilePlugin';
@@ -44,9 +46,11 @@ export default async function importHandler (program: Command) {
44
46
  }
45
47
  } catch (error) {
46
48
  if (error instanceof CompilerError) {
47
- logger.error(`\n ${error.diags.map((diag) => new SyntaxError(diag.filepath ?? '', diag)).map(({
49
+ const msg = `\n ${error.diags.map((diag) => new SyntaxError(diag.filepath ?? '', diag)).map(({
48
50
  message,
49
- }) => message).join('\n ')}`);
51
+ }) => message).join('\n ')}`;
52
+ consoleLogger.error(msg);
53
+ fileLogger.error(msg);
50
54
  return;
51
55
  }
52
56
  throw error;
@@ -1,6 +1,5 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
- import stripAnsi from 'strip-ansi';
4
3
  import {
5
4
  createLogger, format, transports,
6
5
  } from 'winston';
@@ -22,7 +21,7 @@ const fileFormat = printf((info) => {
22
21
  const {
23
22
  timestamp: ts, message, stack, rootError,
24
23
  } = info as any;
25
- let logContent = `${ts}\n${stripAnsi(stack ?? message ?? '')}\n`;
24
+ let logContent = `${ts}\n${stack ?? message ?? ''}\n`;
26
25
  if (rootError) {
27
26
  logContent += '\nROOT_ERROR:';
28
27
  logContent += `\n${rootError.stack}`;
@@ -34,7 +33,7 @@ const fileFormat = printf((info) => {
34
33
  return logContent;
35
34
  });
36
35
 
37
- const consoleLogger = createLogger({
36
+ export const consoleLogger = createLogger({
38
37
  format: combine(
39
38
  consoleFormat,
40
39
  ),
@@ -45,9 +44,10 @@ const consoleLogger = createLogger({
45
44
  ],
46
45
  });
47
46
 
48
- const fileLogger = createLogger({
47
+ export const fileLogger = createLogger({
49
48
  format: combine(
50
49
  timestamp(),
50
+ format.uncolorize(),
51
51
  fileFormat,
52
52
  ),
53
53
  transports: [
@@ -57,22 +57,3 @@ const fileLogger = createLogger({
57
57
  }),
58
58
  ],
59
59
  });
60
-
61
- const logger = {
62
- debug (msg: string) {
63
- consoleLogger.debug(msg);
64
- },
65
- info (msg: string) {
66
- consoleLogger.info(msg);
67
- },
68
- warn (msg: string) {
69
- consoleLogger.warn(msg);
70
- },
71
-
72
- error (msg: Error | string) {
73
- consoleLogger.error(msg);
74
- fileLogger.error(msg);
75
- },
76
- };
77
-
78
- export default logger;