diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index cb740c62c..83ce26e0f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,14 +1,14 @@
-name: Run unit test and collect coverage
+name: Run unit test & coverage
on:
push:
branches: [master]
paths-ignore:
- - 'dbml-homepage/**'
+ - "dbml-homepage/**"
pull_request:
branches: [master]
types: [opened, synchronize]
paths-ignore:
- - 'dbml-homepage/**'
+ - "dbml-homepage/**"
jobs:
build:
runs-on: ubuntu-22.04
@@ -92,19 +92,38 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- cache: 'yarn'
+ cache: "yarn"
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build packages
run: yarn build
- - name: Run unit test and coverage
+ - name: Run coverage test
run: yarn coverage
- name: Generate coverage report
id: coverage
run: node .github/workflows/scripts/collect-coverage.js
+ - name: Upload coverage test
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report.md
+ path: ./coverage-report.md
+ include-hidden-files: true
+
+ comment-coverage:
+ runs-on: ubuntu-22.04
+ timeout-minutes: 5
+ continue-on-error: true
+ needs: build
+ steps:
+ - name: Download test coverage
+ uses: actions/download-artifact@v4
+ with:
+ name: coverage-report.md
+ path: ./coverage-report.md
- name: Comment PR with coverage report
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
- path: coverage-report.md
+ path: ./coverage-report.md
diff --git a/AGENTS.md b/AGENTS.md
index 4e3299fb7..682990c7e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,7 +1,7 @@
# GitNexus — Code Intelligence
-This project is indexed by GitNexus as **dbml** (7344 symbols, 18714 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
+This project is indexed by GitNexus as **dbml** (7937 symbols, 20808 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
diff --git a/CLAUDE.md b/CLAUDE.md
index 4e3299fb7..682990c7e 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,7 +1,7 @@
# GitNexus — Code Intelligence
-This project is indexed by GitNexus as **dbml** (7344 symbols, 18714 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
+This project is indexed by GitNexus as **dbml** (7937 symbols, 20808 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
diff --git a/dbml-playground/env.d.ts b/dbml-playground/env.d.ts
index a32f75372..971fadbcb 100644
--- a/dbml-playground/env.d.ts
+++ b/dbml-playground/env.d.ts
@@ -1,7 +1,9 @@
///
declare module '*.vue' {
- import type { DefineComponent } from 'vue';
+ import type {
+ DefineComponent,
+ } from 'vue';
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
const component: DefineComponent<{}, {}, any>;
export default component;
@@ -12,7 +14,9 @@ declare module 'monaco-editor' {
}
declare module 'vue-json-viewer' {
- import type { DefineComponent } from 'vue';
+ import type {
+ DefineComponent,
+ } from 'vue';
const component: DefineComponent;
export default component;
}
diff --git a/dbml-playground/eslint.config.ts b/dbml-playground/eslint.config.ts
index f59845cf0..4f333a74b 100644
--- a/dbml-playground/eslint.config.ts
+++ b/dbml-playground/eslint.config.ts
@@ -52,6 +52,14 @@ export default defineConfig(
rules: {
...js.configs.recommended.rules,
...tseslint.configs.recommended.rules,
+ '@stylistic/object-curly-newline': ['error', {
+ ObjectExpression: { multiline: true, minProperties: 1 },
+ ObjectPattern: { multiline: true, minProperties: 1 },
+ ImportDeclaration: { multiline: true, minProperties: 1 },
+ ExportDeclaration: { multiline: true, minProperties: 1 },
+ }],
+ '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: false }],
+
'@stylistic/space-before-function-paren': ['error', 'always'],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
diff --git a/dbml-playground/src/App.vue b/dbml-playground/src/App.vue
index c1ff89771..4e90bf4b1 100644
--- a/dbml-playground/src/App.vue
+++ b/dbml-playground/src/App.vue
@@ -267,24 +267,38 @@
* - Information Hiding: Business logic hidden in composables and services
* - Shallow Module: Simple interface that coordinates deeper modules
*/
-import { ref, provide, watch, onMounted } from 'vue';
-import { useParser } from '@/composables/useParser';
-import { useUserData } from '@/composables/useUserData';
+import {
+ ref, provide, watch, onMounted,
+} from 'vue';
+import {
+ useParser,
+} from '@/composables/useParser';
+import {
+ useUserData,
+} from '@/composables/useUserData';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
import ParserOutputViewer from '@/components/outputs/ParserOutputViewer.vue';
import JsonOutputViewer from '@/components/outputs/JsonOutputViewer.vue';
import * as monaco from 'monaco-editor';
-import { TokenMappingService } from '@/core/token-mapping';
-import { TokenNavigationCoordinator } from '@/core/token-navigation';
+import {
+ TokenMappingService,
+} from '@/core/token-mapping';
+import {
+ TokenNavigationCoordinator,
+} from '@/core/token-navigation';
import packageJson from '../package.json';
-import type { PipelineStage } from '@/types';
+import type {
+ PipelineStage,
+} from '@/types';
import consoleLogger from './utils/logger';
// Initialize parser with clean interface
const parser = useParser();
// Initialize user data management
-const { userData, updateUserData, saveDbml } = useUserData();
+const {
+ userData, updateUserData, saveDbml,
+} = useUserData();
// Reactive state derived from user data
const vimModeEnabled = ref(userData.value.isVim);
@@ -371,7 +385,9 @@ onMounted(() => {
const initialViewMode = userData.value.isRawJson ? 'json' : 'cards';
newViewer.setViewMode?.(initialViewMode);
}
- }, { immediate: true });
+ }, {
+ immediate: true,
+ });
});
/**
@@ -396,7 +412,9 @@ watch(() => parser.tokens.value, (newTokens) => {
if (newTokens && Array.isArray(newTokens)) {
tokenNavigationCoordinator.updateTokenMapping(newTokens);
}
-}, { immediate: true });
+}, {
+ immediate: true,
+});
/**
* Provide services to child components
@@ -421,11 +439,31 @@ const isMainResizing = ref(false);
* Available pipeline stages for visualization
*/
const PIPELINE_STAGES = [
- { id: 'lexer' as const, name: 'Lexer', description: 'Tokenization stage' },
- { id: 'parser' as const, name: 'Parser', description: 'Syntax analysis stage' },
- { id: 'analyzer' as const, name: 'Analyzer', description: 'Semantic analysis stage' },
- { id: 'interpreter' as const, name: 'Interpreter', description: 'Code generation stage' },
- { id: 'errors' as const, name: 'Errors', description: 'Error reports' },
+ {
+ id: 'lexer' as const,
+ name: 'Lexer',
+ description: 'Tokenization stage',
+ },
+ {
+ id: 'parser' as const,
+ name: 'Parser',
+ description: 'Syntax analysis stage',
+ },
+ {
+ id: 'analyzer' as const,
+ name: 'Analyzer',
+ description: 'Semantic analysis stage',
+ },
+ {
+ id: 'interpreter' as const,
+ name: 'Interpreter',
+ description: 'Code generation stage',
+ },
+ {
+ id: 'errors' as const,
+ name: 'Errors',
+ description: 'Error reports',
+ },
] as const;
/**
@@ -491,7 +529,12 @@ const startMainResize = (event: MouseEvent) => {
* Handle navigate to source event from AST viewer
* Highlights the corresponding DBML source code
*/
-const handleNavigateToSource = (position: { start: { line: number; column: number; offset: number }; end: { line: number; column: number; offset: number } }) => {
+const handleNavigateToSource = (position: { start: { line: number;
+ column: number;
+ offset: number; };
+end: { line: number;
+ column: number;
+ offset: number; }; }) => {
// Get reference to the DBML editor (MonacoEditor)
const dbmlEditor = document.querySelector('.dbml-editor');
if (dbmlEditor) {
diff --git a/dbml-playground/src/components/editors/MonacoEditor.vue b/dbml-playground/src/components/editors/MonacoEditor.vue
index 26b16392f..6e72ff96f 100644
--- a/dbml-playground/src/components/editors/MonacoEditor.vue
+++ b/dbml-playground/src/components/editors/MonacoEditor.vue
@@ -37,9 +37,13 @@
* - Information Hiding: Language setup complexity is hidden in services
* - Shallow Module: Simple interface that delegates to deep modules
*/
-import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
+import {
+ ref, onMounted, onBeforeUnmount, watch, nextTick,
+} from 'vue';
import * as monaco from 'monaco-editor';
-import { DBMLLanguageService } from '@/components/monaco/dbml-language';
+import {
+ DBMLLanguageService,
+} from '@/components/monaco/dbml-language';
import consoleLogger from '@/utils/logger';
interface Props {
@@ -72,7 +76,10 @@ let editor: monaco.editor.IStandaloneCodeEditor | null = null;
/**
* Reactive cursor position tracking
*/
-const cursorPosition = ref({ line: 1, column: 1 });
+const cursorPosition = ref({
+ line: 1,
+ column: 1,
+});
/**
* Reactive selection info tracking
@@ -105,7 +112,9 @@ const createEditorConfig = (): monaco.editor.IStandaloneEditorConstructionOption
language: props.language,
theme: getThemeForLanguage(props.language),
readOnly: props.readOnly,
- minimap: { enabled: props.minimap },
+ minimap: {
+ enabled: props.minimap,
+ },
wordWrap: props.wordWrap,
scrollBeyondLastLine: false,
fontSize: 14,
@@ -114,7 +123,10 @@ const createEditorConfig = (): monaco.editor.IStandaloneEditorConstructionOption
lineNumbersMinChars: 3,
lineDecorationsWidth: 10,
columnSelection: false,
- padding: { top: 10, bottom: 10 },
+ padding: {
+ top: 10,
+ bottom: 10,
+ },
renderWhitespace: 'boundary',
renderControlCharacters: true,
smoothScrolling: true,
@@ -139,7 +151,9 @@ const createEditorConfig = (): monaco.editor.IStandaloneEditorConstructionOption
selectOnLineNumbers: false, // Disable line number selection for vim compatibility
cursorWidth: props.vimMode ? 2 : 1, // Slightly wider cursor for vim visibility
quickSuggestions: false, // Disable auto-suggestions for vim mode compatibility
- parameterHints: { enabled: false }, // Disable parameter hints for cleaner vim experience
+ parameterHints: {
+ enabled: false,
+ }, // Disable parameter hints for cleaner vim experience
suggestOnTriggerCharacters: false, // Disable suggestion popup on typing
acceptSuggestionOnEnter: 'off', // Prevent Enter from accepting suggestions
tabCompletion: 'off', // Disable tab completion to avoid conflicts with vim
@@ -157,7 +171,9 @@ const setupVimMode = async (): Promise => {
try {
// Dynamically import monaco-vim
- const { initVimMode } = await import('monaco-vim' as any);
+ const {
+ initVimMode,
+ } = await import('monaco-vim' as any);
// Initialize vim mode with status tracking
vimMode = initVimMode(editor);
@@ -309,7 +325,9 @@ watch(() => props.modelValue, (newValue) => {
watch(() => props.readOnly, (newReadOnly) => {
if (editor) {
- editor.updateOptions({ readOnly: newReadOnly });
+ editor.updateOptions({
+ readOnly: newReadOnly,
+ });
}
});
diff --git a/dbml-playground/src/components/monaco/dbml-language.ts b/dbml-playground/src/components/monaco/dbml-language.ts
index 23102b4b2..779fbae0d 100644
--- a/dbml-playground/src/components/monaco/dbml-language.ts
+++ b/dbml-playground/src/components/monaco/dbml-language.ts
@@ -28,20 +28,56 @@ const DBML_LANGUAGE_CONFIG: monaco.languages.LanguageConfiguration = {
['(', ')'],
],
autoClosingPairs: [
- { open: '{', close: '}' },
- { open: '[', close: ']' },
- { open: '(', close: ')' },
- { open: '"', close: '"' },
- { open: '\'', close: '\'' },
- { open: '`', close: '`' },
+ {
+ open: '{',
+ close: '}',
+ },
+ {
+ open: '[',
+ close: ']',
+ },
+ {
+ open: '(',
+ close: ')',
+ },
+ {
+ open: '"',
+ close: '"',
+ },
+ {
+ open: '\'',
+ close: '\'',
+ },
+ {
+ open: '`',
+ close: '`',
+ },
],
surroundingPairs: [
- { open: '{', close: '}' },
- { open: '[', close: ']' },
- { open: '(', close: ')' },
- { open: '"', close: '"' },
- { open: '\'', close: '\'' },
- { open: '`', close: '`' },
+ {
+ open: '{',
+ close: '}',
+ },
+ {
+ open: '[',
+ close: ']',
+ },
+ {
+ open: '(',
+ close: ')',
+ },
+ {
+ open: '"',
+ close: '"',
+ },
+ {
+ open: '\'',
+ close: '\'',
+ },
+ {
+ open: '`',
+ close: '`',
+ },
],
indentationRules: {
increaseIndentPattern: /^(.*\{[^}]*|\s*[\{\[].*)$/,
@@ -85,7 +121,9 @@ const DBML_TOKEN_PROVIDER: monaco.languages.IMonarchLanguage = {
'@default': 'identifier',
},
}],
- { include: '@whitespace' },
+ {
+ include: '@whitespace',
+ },
[/[{}()\[\]]/, '@brackets'],
[/[<>](?!@symbols)/, '@brackets'],
[/@symbols/, {
@@ -157,23 +195,72 @@ const DBML_THEME: monaco.editor.IStandaloneThemeData = {
inherit: true,
rules: [
// Override default rules to add DBML-specific styling
- { token: 'keyword', foreground: '0000ff', fontStyle: 'bold' },
- { token: 'type', foreground: '008000', fontStyle: 'bold' },
- { token: 'string', foreground: 'a31515' },
- { token: 'string.backtick', foreground: 'a31515', fontStyle: 'italic' },
- { token: 'comment', foreground: '008000', fontStyle: 'italic' },
- { token: 'number', foreground: '098658' },
- { token: 'number.hex', foreground: '3030c0' },
- { token: 'operator', foreground: '000000' },
- { token: 'delimiter', foreground: '000000' },
- { token: 'annotation', foreground: '808080' },
- { token: 'identifier', foreground: '000000' },
+ {
+ token: 'keyword',
+ foreground: '0000ff',
+ fontStyle: 'bold',
+ },
+ {
+ token: 'type',
+ foreground: '008000',
+ fontStyle: 'bold',
+ },
+ {
+ token: 'string',
+ foreground: 'a31515',
+ },
+ {
+ token: 'string.backtick',
+ foreground: 'a31515',
+ fontStyle: 'italic',
+ },
+ {
+ token: 'comment',
+ foreground: '008000',
+ fontStyle: 'italic',
+ },
+ {
+ token: 'number',
+ foreground: '098658',
+ },
+ {
+ token: 'number.hex',
+ foreground: '3030c0',
+ },
+ {
+ token: 'operator',
+ foreground: '000000',
+ },
+ {
+ token: 'delimiter',
+ foreground: '000000',
+ },
+ {
+ token: 'annotation',
+ foreground: '808080',
+ },
+ {
+ token: 'identifier',
+ foreground: '000000',
+ },
// Ensure JSON tokens also use consistent styling
- { token: 'string.key.json', foreground: '0451a5' },
- { token: 'string.value.json', foreground: 'a31515' },
- { token: 'number.json', foreground: '098658' },
- { token: 'keyword.json', foreground: '0000ff' },
+ {
+ token: 'string.key.json',
+ foreground: '0451a5',
+ },
+ {
+ token: 'string.value.json',
+ foreground: 'a31515',
+ },
+ {
+ token: 'number.json',
+ foreground: '098658',
+ },
+ {
+ token: 'keyword.json',
+ foreground: '0000ff',
+ },
],
colors: {},
};
@@ -201,7 +288,9 @@ export class DBMLLanguageService {
try {
// Register the language
- monaco.languages.register({ id: this.LANGUAGE_ID });
+ monaco.languages.register({
+ id: this.LANGUAGE_ID,
+ });
// Set token provider for syntax highlighting
monaco.languages.setMonarchTokensProvider(this.LANGUAGE_ID, DBML_TOKEN_PROVIDER);
diff --git a/dbml-playground/src/components/outputs/InterpreterView.vue b/dbml-playground/src/components/outputs/InterpreterView.vue
index abaebb70c..3d04158f2 100644
--- a/dbml-playground/src/components/outputs/InterpreterView.vue
+++ b/dbml-playground/src/components/outputs/InterpreterView.vue
@@ -157,10 +157,14 @@
* Generic design allows it to handle new features added to dbml-parse
* without breaking when the parser structure evolves.
*/
-import { computed, ref } from 'vue';
+import {
+ computed, ref,
+} from 'vue';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
import InterpreterTreeView from './ast/InterpreterTreeView.vue';
-import type { Database, InterpreterViewProps } from '@/types';
+import type {
+ Database, InterpreterViewProps,
+} from '@/types';
import consoleLogger from '@/utils/logger';
const props = defineProps();
@@ -205,7 +209,8 @@ const handleTreeNodeClick = (node: any) => {
consoleLogger.log('Tree node clicked:', node);
};
-const handleTreePositionClick = (event: { node: any; position: any }) => {
+const handleTreePositionClick = (event: { node: any;
+ position: any; }) => {
consoleLogger.log('Tree position clicked:', event);
};
diff --git a/dbml-playground/src/components/outputs/JsonOutputViewer.vue b/dbml-playground/src/components/outputs/JsonOutputViewer.vue
index e44cc1a45..6611a0492 100644
--- a/dbml-playground/src/components/outputs/JsonOutputViewer.vue
+++ b/dbml-playground/src/components/outputs/JsonOutputViewer.vue
@@ -71,7 +71,9 @@
* - Consistency: Provides uniform appearance across all JSON output stages
* - Deep Module: Rich functionality with simple interface
*/
-import { ref } from 'vue';
+import {
+ ref,
+} from 'vue';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
import consoleLogger from '@/utils/logger';
diff --git a/dbml-playground/src/components/outputs/LexerView.vue b/dbml-playground/src/components/outputs/LexerView.vue
index 44f636bcb..7dee1fc1a 100644
--- a/dbml-playground/src/components/outputs/LexerView.vue
+++ b/dbml-playground/src/components/outputs/LexerView.vue
@@ -174,9 +174,13 @@
* - Information Hiding: Token navigation complexity is encapsulated
* - Deep Module: Rich functionality with simple interface
*/
-import { computed, ref, inject } from 'vue';
+import {
+ computed, ref, inject,
+} from 'vue';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
-import type { TokenNavigationEventBus } from '@/core/token-navigation';
+import type {
+ TokenNavigationEventBus,
+} from '@/core/token-navigation';
import consoleLogger from '@/utils/logger';
interface Props {
diff --git a/dbml-playground/src/components/outputs/ParserASTView.vue b/dbml-playground/src/components/outputs/ParserASTView.vue
index 3212d87c2..df23e3b30 100644
--- a/dbml-playground/src/components/outputs/ParserASTView.vue
+++ b/dbml-playground/src/components/outputs/ParserASTView.vue
@@ -134,10 +134,14 @@
* - Single Responsibility: Only handles AST visualization and navigation
* - Maintainable: Generic structure that adapts to parser evolution
*/
-import { computed, ref, inject } from 'vue';
+import {
+ computed, ref, inject,
+} from 'vue';
import RawASTTreeView from './ast/RawASTTreeView.vue';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
-import type { TokenNavigationEventBus } from '@/core/token-navigation';
+import type {
+ TokenNavigationEventBus,
+} from '@/core/token-navigation';
import consoleLogger from '@/utils/logger';
interface Props {
@@ -148,7 +152,12 @@ const props = defineProps();
// Define emits for parent component communication
const emit = defineEmits<{
- 'navigate-to-source': [position: { start: { line: number; column: number; offset: number }; end: { line: number; column: number; offset: number } }];
+ 'navigate-to-source': [position: { start: { line: number;
+ column: number;
+ offset: number; };
+ end: { line: number;
+ column: number;
+ offset: number; }; }];
}>();
// Inject services
@@ -252,7 +261,8 @@ const handleRawNodeClick = (node: any) => {
}
};
-const handleRawPositionClick = (event: { node: any; position: any }) => {
+const handleRawPositionClick = (event: { node: any;
+ position: any; }) => {
navigateToSourcePosition(event.position);
};
diff --git a/dbml-playground/src/components/outputs/ParserOutputViewer.vue b/dbml-playground/src/components/outputs/ParserOutputViewer.vue
index bc8fa3d40..b22f0b122 100644
--- a/dbml-playground/src/components/outputs/ParserOutputViewer.vue
+++ b/dbml-playground/src/components/outputs/ParserOutputViewer.vue
@@ -70,12 +70,16 @@
* - Information Hiding: Stage detection logic is encapsulated
* - Shallow Module: Simple interface that delegates to specialized components
*/
-import { computed, ref } from 'vue';
+import {
+ computed, ref,
+} from 'vue';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
import LexerView from './LexerView.vue';
import ParserASTView from './ParserASTView.vue';
import InterpreterView from './InterpreterView.vue';
-import type { Token, ParserOutputViewerProps, NavigationPosition } from '@/types';
+import type {
+ Token, ParserOutputViewerProps, NavigationPosition,
+} from '@/types';
const props = defineProps();
@@ -106,11 +110,15 @@ const transformedData = computed(() => {
try {
return JSON.parse(props.data);
} catch {
- return { value: props.data };
+ return {
+ value: props.data,
+ };
}
}
- return { value: props.data };
+ return {
+ value: props.data,
+ };
});
/**
@@ -218,7 +226,12 @@ const setViewMode = (mode: 'cards' | 'json'): void => {
/**
* Handle navigate to source event from AST view
*/
-const handleNavigateToSource = (position: { start: { line: number; column: number; offset: number }; end: { line: number; column: number; offset: number } }) => {
+const handleNavigateToSource = (position: { start: { line: number;
+ column: number;
+ offset: number; };
+end: { line: number;
+ column: number;
+ offset: number; }; }) => {
emit('navigate-to-source', position);
};
diff --git a/dbml-playground/src/components/outputs/ast/ASTDetailsPanel.vue b/dbml-playground/src/components/outputs/ast/ASTDetailsPanel.vue
index 5ced8fe86..c8fefe801 100644
--- a/dbml-playground/src/components/outputs/ast/ASTDetailsPanel.vue
+++ b/dbml-playground/src/components/outputs/ast/ASTDetailsPanel.vue
@@ -311,10 +311,16 @@
* Shows detailed information about selected AST nodes including
* access paths, properties, and navigation options.
*/
-import { computed, ref, inject } from 'vue';
-import type { SemanticASTNode, AccessPath } from '@/core/ast-transformer';
+import {
+ computed, ref, inject,
+} from 'vue';
+import type {
+ SemanticASTNode, AccessPath,
+} from '@/core/ast-transformer';
import MonacoEditor from '@/components/editors/MonacoEditor.vue';
-import type { TokenNavigationEventBus } from '@/core/token-navigation';
+import type {
+ TokenNavigationEventBus,
+} from '@/core/token-navigation';
import consoleLogger from '@/utils/logger';
interface Props {
@@ -514,7 +520,10 @@ const navigateToPosition = (line: number, column: number) => {
if (tokenNavigationCoordinator?.dbmlEditor) {
try {
// Create Monaco position (1-indexed)
- const position = { lineNumber: line, column: column };
+ const position = {
+ lineNumber: line,
+ column: column,
+ };
// Set cursor position
tokenNavigationCoordinator.dbmlEditor.setPosition(position);
@@ -547,16 +556,32 @@ const navigateToPosition = (line: number, column: number) => {
consoleLogger.warn('Direct navigation failed:', error);
// Fallback to emit
const position = {
- start: { line, column, offset: 0 },
- end: { line, column, offset: 0 },
+ start: {
+ line,
+ column,
+ offset: 0,
+ },
+ end: {
+ line,
+ column,
+ offset: 0,
+ },
};
emit('navigate-to-source', position);
}
} else {
// Fallback to old method if coordinator not available
const position = {
- start: { line, column, offset: 0 },
- end: { line, column, offset: 0 },
+ start: {
+ line,
+ column,
+ offset: 0,
+ },
+ end: {
+ line,
+ column,
+ offset: 0,
+ },
};
emit('navigate-to-source', position);
}
@@ -600,16 +625,32 @@ const navigateToRange = (startLine: number, startColumn: number, endLine: number
consoleLogger.warn('Direct range navigation failed:', error);
// Fallback to emit
const position = {
- start: { line: startLine, column: startColumn, offset: 0 },
- end: { line: endLine, column: endColumn, offset: 0 },
+ start: {
+ line: startLine,
+ column: startColumn,
+ offset: 0,
+ },
+ end: {
+ line: endLine,
+ column: endColumn,
+ offset: 0,
+ },
};
emit('navigate-to-source', position);
}
} else {
// Fallback to old method if coordinator not available
const position = {
- start: { line: startLine, column: startColumn, offset: 0 },
- end: { line: endLine, column: endColumn, offset: 0 },
+ start: {
+ line: startLine,
+ column: startColumn,
+ offset: 0,
+ },
+ end: {
+ line: endLine,
+ column: endColumn,
+ offset: 0,
+ },
};
emit('navigate-to-source', position);
}
@@ -618,7 +659,11 @@ const navigateToRange = (startLine: number, startColumn: number, endLine: number
// AST Statistics for debugging
const getASTStats = () => {
if (!props.rawAst?.body) {
- return { totalElements: 0, uniqueTypes: 0, typeBreakdown: [] };
+ return {
+ totalElements: 0,
+ uniqueTypes: 0,
+ typeBreakdown: [],
+ };
}
const typeCounts: Record = {};
diff --git a/dbml-playground/src/components/outputs/ast/InterpreterTreeNode.vue b/dbml-playground/src/components/outputs/ast/InterpreterTreeNode.vue
index 2f4c51dfc..098b1c58f 100644
--- a/dbml-playground/src/components/outputs/ast/InterpreterTreeNode.vue
+++ b/dbml-playground/src/components/outputs/ast/InterpreterTreeNode.vue
@@ -98,18 +98,26 @@
* Displays individual nodes in the interpreter tree structure.
* Shows database objects like tables, fields, enums, etc. with proper icons and formatting.
*/
-import { computed, inject } from 'vue';
+import {
+ computed, inject,
+} from 'vue';
import ASTIcon from './ASTIcon.vue';
-import type { TokenNavigationEventBus } from '@/core/token-navigation';
-import type { InterpreterTreeNode, InterpreterTreeNodeProps } from '@/types';
+import type {
+ TokenNavigationEventBus,
+} from '@/core/token-navigation';
+import type {
+ InterpreterTreeNode, InterpreterTreeNodeProps,
+} from '@/types';
import consoleLogger from '@/utils/logger';
const props = defineProps();
const emit = defineEmits<{
'node-click': [node: InterpreterTreeNode];
- 'node-expand': [{ id: string; expanded: boolean }];
- 'position-click': [{ node: InterpreterTreeNode; position: any }];
+ 'node-expand': [{ id: string;
+ expanded: boolean; }];
+ 'position-click': [{ node: InterpreterTreeNode;
+ position: any; }];
}>();
// Inject navigation services (same as ParserASTView)
@@ -239,11 +247,17 @@ const navigateToSourcePosition = (position: any) => {
}, 2000);
} catch (error) {
// Fallback to emit
- emit('position-click', { node: props.node, position });
+ emit('position-click', {
+ node: props.node,
+ position,
+ });
}
} else {
// Fallback to old method if coordinator not available
- emit('position-click', { node: props.node, position });
+ emit('position-click', {
+ node: props.node,
+ position,
+ });
}
};
diff --git a/dbml-playground/src/components/outputs/ast/InterpreterTreeView.vue b/dbml-playground/src/components/outputs/ast/InterpreterTreeView.vue
index 3e3195bd7..cb1b43d1a 100644
--- a/dbml-playground/src/components/outputs/ast/InterpreterTreeView.vue
+++ b/dbml-playground/src/components/outputs/ast/InterpreterTreeView.vue
@@ -20,15 +20,20 @@
* for debugging purposes. Shows the actual interpreter output hierarchy
* with all properties like tables, fields, refs, etc.
*/
-import { computed, ref } from 'vue';
+import {
+ computed, ref,
+} from 'vue';
import InterpreterTreeNode from './InterpreterTreeNode.vue';
-import type { Database, InterpreterTreeNode as InterpreterNode, InterpreterTreeViewProps } from '@/types';
+import type {
+ Database, InterpreterTreeNode as InterpreterNode, InterpreterTreeViewProps,
+} from '@/types';
const props = defineProps();
const emit = defineEmits<{
'node-click': [node: InterpreterNode];
- 'position-click': [{ node: InterpreterNode; position: any }];
+ 'position-click': [{ node: InterpreterNode;
+ position: any; }];
}>();
const selectedNode = ref(null);
@@ -40,7 +45,8 @@ const rootNode = computed(() => {
});
// Handle node expansion
-const handleNodeExpand = (event: { id: string; expanded: boolean }) => {
+const handleNodeExpand = (event: { id: string;
+ expanded: boolean; }) => {
if (event.expanded) {
expandedNodes.value.add(event.id);
} else {
@@ -55,7 +61,8 @@ const handleNodeClick = (node: InterpreterNode) => {
};
// Handle position click (for future navigation features)
-const handlePositionClick = (event: { node: InterpreterNode; position: any }) => {
+const handlePositionClick = (event: { node: InterpreterNode;
+ position: any; }) => {
emit('position-click', event);
};
diff --git a/dbml-playground/src/components/outputs/ast/RawASTTreeNode.vue b/dbml-playground/src/components/outputs/ast/RawASTTreeNode.vue
index 70ce8076a..199d44edc 100644
--- a/dbml-playground/src/components/outputs/ast/RawASTTreeNode.vue
+++ b/dbml-playground/src/components/outputs/ast/RawASTTreeNode.vue
@@ -99,7 +99,9 @@
* Shows real parser properties like body, callee, args, kind, etc.
* Preserves the original AST hierarchy without artificial semantic grouping.
*/
-import { computed } from 'vue';
+import {
+ computed,
+} from 'vue';
export interface RawASTNode {
id: string;
@@ -121,8 +123,10 @@ const props = defineProps();
const emit = defineEmits<{
'node-click': [node: RawASTNode];
- 'node-expand': [{ id: string; expanded: boolean }];
- 'position-click': [{ node: RawASTNode; position: any }];
+ 'node-expand': [{ id: string;
+ expanded: boolean; }];
+ 'position-click': [{ node: RawASTNode;
+ position: any; }];
}>();
const isSelected = computed(() => props.selectedNode?.id === props.node.id);
@@ -186,13 +190,24 @@ const handlePositionClick = () => {
// For nodes with just offset info, we can't navigate directly
// but we can still emit the event for debugging
position = {
- start: { line: 1, column: 1, offset: data.start },
- end: { line: 1, column: 1, offset: data.end },
+ start: {
+ line: 1,
+ column: 1,
+ offset: data.start,
+ },
+ end: {
+ line: 1,
+ column: 1,
+ offset: data.end,
+ },
};
}
if (position) {
- emit('position-click', { node: props.node, position });
+ emit('position-click', {
+ node: props.node,
+ position,
+ });
}
}
};
diff --git a/dbml-playground/src/components/outputs/ast/RawASTTreeView.vue b/dbml-playground/src/components/outputs/ast/RawASTTreeView.vue
index 90718bb33..0308f18cd 100644
--- a/dbml-playground/src/components/outputs/ast/RawASTTreeView.vue
+++ b/dbml-playground/src/components/outputs/ast/RawASTTreeView.vue
@@ -20,8 +20,12 @@
* that preserves the actual parser hierarchy and shows all properties
* like body, callee, args, etc. for debugging purposes.
*/
-import { computed, ref } from 'vue';
-import RawASTTreeNode, { type RawASTNode } from './RawASTTreeNode.vue';
+import {
+ computed, ref,
+} from 'vue';
+import RawASTTreeNode, {
+ type RawASTNode,
+} from './RawASTTreeNode.vue';
interface Props {
readonly rawAST: any;
@@ -31,7 +35,8 @@ const props = defineProps();
const emit = defineEmits<{
'node-click': [node: RawASTNode];
- 'position-click': [{ node: RawASTNode; position: any }];
+ 'position-click': [{ node: RawASTNode;
+ position: any; }];
}>();
const selectedNode = ref(null);
@@ -43,7 +48,8 @@ const rootNode = computed(() => {
});
// Handle node expansion
-const handleNodeExpand = (event: { id: string; expanded: boolean }) => {
+const handleNodeExpand = (event: { id: string;
+ expanded: boolean; }) => {
if (event.expanded) {
expandedNodes.value.add(event.id);
} else {
diff --git a/dbml-playground/src/composables/useParser.ts b/dbml-playground/src/composables/useParser.ts
index 7bc714c9c..5f2604c9c 100644
--- a/dbml-playground/src/composables/useParser.ts
+++ b/dbml-playground/src/composables/useParser.ts
@@ -10,9 +10,15 @@
* - Single Responsibility: Only handles Vue composable concerns
* - Information Hiding: Complex parsing logic is hidden in services
*/
-import { ReactiveParser } from '@/core/reactive-parser';
-import { DEFAULT_SAMPLE_CONTENT } from '@/core/sample-content';
-import type { ParserError, PipelineStage } from '@/types';
+import {
+ ReactiveParser,
+} from '@/core/reactive-parser';
+import {
+ DEFAULT_SAMPLE_CONTENT,
+} from '@/core/sample-content';
+import type {
+ ParserError, PipelineStage,
+} from '@/types';
/**
* DBML Parser Composable
@@ -48,7 +54,8 @@ export function useParser () {
const formatErrors = (errors: readonly ParserError[]): Array<{
code: number;
message: string;
- location: { line: number; column: number };
+ location: { line: number;
+ column: number; };
}> => {
return errors.map((error) => ({
code: error.code,
diff --git a/dbml-playground/src/composables/useUserData.ts b/dbml-playground/src/composables/useUserData.ts
index 35399fd50..1d38d2650 100644
--- a/dbml-playground/src/composables/useUserData.ts
+++ b/dbml-playground/src/composables/useUserData.ts
@@ -9,8 +9,12 @@
* - Information Hiding: Internal storage format is hidden
* - Deep Module: Complex persistence logic with simple interface
*/
-import { ref, watch, type Ref } from 'vue';
-import type { UserData } from '@/types';
+import {
+ ref, watch, type Ref,
+} from 'vue';
+import type {
+ UserData,
+} from '@/types';
import consoleLogger from '@/utils/logger';
const defaultUserData: UserData = {
@@ -50,12 +54,17 @@ const loadUserData = (): UserData => {
if (stored) {
const parsed = JSON.parse(stored);
// Merge with defaults to handle missing properties
- return { ...defaultUserData, ...parsed };
+ return {
+ ...defaultUserData,
+ ...parsed,
+ };
}
} catch (error) {
consoleLogger.warn('Failed to load user data from localStorage:', error);
}
- return { ...defaultUserData };
+ return {
+ ...defaultUserData,
+ };
};
/**
@@ -79,7 +88,9 @@ export function useUserData () {
// Auto-save when data changes
watch(userData, (newData) => {
saveUserData(newData);
- }, { deep: true });
+ }, {
+ deep: true,
+ });
/**
* Update a specific property
@@ -92,7 +103,9 @@ export function useUserData () {
* Reset to default values
*/
const resetUserData = (): void => {
- userData.value = { ...defaultUserData };
+ userData.value = {
+ ...defaultUserData,
+ };
};
/**
diff --git a/dbml-playground/src/core/ast-transformer.ts b/dbml-playground/src/core/ast-transformer.ts
index fcfef6140..59fc3c134 100644
--- a/dbml-playground/src/core/ast-transformer.ts
+++ b/dbml-playground/src/core/ast-transformer.ts
@@ -57,8 +57,16 @@ export interface SemanticASTNode {
// Source position using parser's Position interface
sourcePosition?: {
- start: { line: number; column: number; offset: number };
- end: { line: number; column: number; offset: number };
+ start: {
+ line: number;
+ column: number;
+ offset: number;
+ };
+ end: {
+ line: number;
+ column: number;
+ offset: number;
+ };
// Raw parser node information - use raw.id for debugging display
raw?: {
startPos: Position;
@@ -404,7 +412,13 @@ export class ASTTransformerService {
const semantic = transformer.transformToSemantic(ast);
const parserInfo = ASTTransformerService.getParserIntegrationInfo();
- return { semantic, tokens, errors, interpretedData, parserInfo };
+ return {
+ semantic,
+ tokens,
+ errors,
+ interpretedData,
+ parserInfo,
+ };
}
/**
diff --git a/dbml-playground/src/core/parser-service.ts b/dbml-playground/src/core/parser-service.ts
index a4826496a..2f8f41f51 100644
--- a/dbml-playground/src/core/parser-service.ts
+++ b/dbml-playground/src/core/parser-service.ts
@@ -10,8 +10,12 @@
* - Information Hiding: Internal parser details are completely hidden
* - Pull Complexity Downwards: Complex logic handled internally
*/
-import { Compiler } from '@dbml/parse';
-import type { ParserError, ParserResult } from '@/types';
+import {
+ Compiler,
+} from '@dbml/parse';
+import type {
+ ParserError, ParserResult,
+} from '@/types';
import consoleLogger from '@/utils/logger';
/**
@@ -142,7 +146,10 @@ export class ParserService {
errors: [{
code: -1,
message,
- location: { line: 1, column: 1 },
+ location: {
+ line: 1,
+ column: 1,
+ },
}],
};
}
diff --git a/dbml-playground/src/core/reactive-parser.ts b/dbml-playground/src/core/reactive-parser.ts
index 0a0984944..0185c40e8 100644
--- a/dbml-playground/src/core/reactive-parser.ts
+++ b/dbml-playground/src/core/reactive-parser.ts
@@ -9,9 +9,15 @@
* - Information Hiding: Hides debouncing and state management complexity
* - General Purpose: Can be used with any reactive framework
*/
-import { ref, computed, watch, type Ref } from 'vue';
-import { ParserService } from './parser-service';
-import type { ReactiveParserOptions, ParserResult } from '@/types';
+import {
+ ref, computed, watch, type Ref,
+} from 'vue';
+import {
+ ParserService,
+} from './parser-service';
+import type {
+ ReactiveParserOptions, ParserResult,
+} from '@/types';
import consoleLogger from '@/utils/logger';
/**
@@ -73,7 +79,9 @@ export class ReactiveParser {
private setupReactiveParsing (): void {
watch(this.input, (newInput) => {
this.debouncedParse(newInput);
- }, { immediate: true });
+ }, {
+ immediate: true,
+ });
}
/**
@@ -142,7 +150,10 @@ export class ReactiveParser {
errors: [{
code: -1,
message,
- location: { line: 1, column: 1 },
+ location: {
+ line: 1,
+ column: 1,
+ },
}],
};
}
diff --git a/dbml-playground/src/core/token-mapping.ts b/dbml-playground/src/core/token-mapping.ts
index dd2e7c088..16418aace 100644
--- a/dbml-playground/src/core/token-mapping.ts
+++ b/dbml-playground/src/core/token-mapping.ts
@@ -10,7 +10,9 @@
* - Single Responsibility: Only handles token-position mapping
*/
import * as monaco from 'monaco-editor';
-import type { Token, TokenMetadata } from '@/types';
+import type {
+ Token, TokenMetadata,
+} from '@/types';
// Implementation-specific map interfaces (different from the centralized types)
interface LexerToDbmlMapInternal {
@@ -106,7 +108,12 @@ export class TokenMappingService {
// Check if token overlaps with the selected range
if (this.rangesOverlap(
- { startLine, startColumn, endLine, endColumn },
+ {
+ startLine,
+ startColumn,
+ endLine,
+ endColumn,
+ },
{
startLine: tokenStart.line,
startColumn: tokenStart.column,
@@ -253,8 +260,14 @@ export class TokenMappingService {
* Check if two ranges overlap
*/
private rangesOverlap (
- range1: { startLine: number; startColumn: number; endLine: number; endColumn: number },
- range2: { startLine: number; startColumn: number; endLine: number; endColumn: number },
+ range1: { startLine: number;
+ startColumn: number;
+ endLine: number;
+ endColumn: number; },
+ range2: { startLine: number;
+ startColumn: number;
+ endLine: number;
+ endColumn: number; },
): boolean {
// Handle single line case
if (range1.startLine === range1.endLine && range2.startLine === range2.endLine) {
diff --git a/dbml-playground/src/core/token-navigation.ts b/dbml-playground/src/core/token-navigation.ts
index a8a045455..965b48766 100644
--- a/dbml-playground/src/core/token-navigation.ts
+++ b/dbml-playground/src/core/token-navigation.ts
@@ -11,8 +11,12 @@
* - Pull Complexity Downwards: Complex event filtering handled internally
*/
import * as monaco from 'monaco-editor';
-import { TokenMappingService } from './token-mapping';
-import type { Token, TokenNavigationEvents } from '@/types';
+import {
+ TokenMappingService,
+} from './token-mapping';
+import type {
+ Token, TokenNavigationEvents,
+} from '@/types';
export type TokenNavigationEventType = keyof TokenNavigationEvents;
@@ -21,7 +25,9 @@ export class TokenNavigationEventBus extends EventTarget {
event: K,
data: TokenNavigationEvents[K],
): void {
- this.dispatchEvent(new CustomEvent(event, { detail: data }));
+ this.dispatchEvent(new CustomEvent(event, {
+ detail: data,
+ }));
}
on(
@@ -103,7 +109,10 @@ export class TokenNavigationCoordinator {
* Navigate from token to DBML (triggered by button click or Cmd+Click in JSON)
*/
public navigateToDbmlFromToken (tokenIndex: number, modifier: 'cmd' | 'ctrl' | 'button' = 'button'): void {
- this.eventBus.emit('navigate:token-to-dbml', { tokenIndex, modifier });
+ this.eventBus.emit('navigate:token-to-dbml', {
+ tokenIndex,
+ modifier,
+ });
}
/**
@@ -120,12 +129,20 @@ export class TokenNavigationCoordinator {
this.switchToCardsMode();
// Wait for view mode transition to complete before navigating
setTimeout(() => {
- this.eventBus.emit('navigate:dbml-to-token', { line, column, modifier });
+ this.eventBus.emit('navigate:dbml-to-token', {
+ line,
+ column,
+ modifier,
+ });
}, 50); // Small delay to ensure view mode switch completes
return;
}
- this.eventBus.emit('navigate:dbml-to-token', { line, column, modifier });
+ this.eventBus.emit('navigate:dbml-to-token', {
+ line,
+ column,
+ modifier,
+ });
}
/**
@@ -142,12 +159,22 @@ export class TokenNavigationCoordinator {
this.switchToCardsMode();
// Wait for view mode transition to complete before navigating
setTimeout(() => {
- this.eventBus.emit('navigate:range-to-tokens', { startLine, startCol, endLine, endCol });
+ this.eventBus.emit('navigate:range-to-tokens', {
+ startLine,
+ startCol,
+ endLine,
+ endCol,
+ });
}, 50); // Small delay to ensure view mode switch completes
return;
}
- this.eventBus.emit('navigate:range-to-tokens', { startLine, startCol, endLine, endCol });
+ this.eventBus.emit('navigate:range-to-tokens', {
+ startLine,
+ startCol,
+ endLine,
+ endCol,
+ });
}
/**
@@ -176,19 +203,25 @@ export class TokenNavigationCoordinator {
*/
private setupEventHandlers (): void {
// Token → DBML navigation
- this.eventBus.on('navigate:token-to-dbml', ({ tokenIndex }) => {
+ this.eventBus.on('navigate:token-to-dbml', ({
+ tokenIndex,
+ }) => {
if (this.isNavigating) return;
this.highlightTokenInDbml(tokenIndex);
});
// DBML → Token navigation
- this.eventBus.on('navigate:dbml-to-token', ({ line, column }) => {
+ this.eventBus.on('navigate:dbml-to-token', ({
+ line, column,
+ }) => {
if (this.isNavigating) return;
this.highlightTokenInLexer(line, column);
});
// DBML Range → Tokens navigation
- this.eventBus.on('navigate:range-to-tokens', ({ startLine, startCol, endLine, endCol }) => {
+ this.eventBus.on('navigate:range-to-tokens', ({
+ startLine, startCol, endLine, endCol,
+ }) => {
if (this.isNavigating) return;
this.highlightTokensInLexer(startLine, startCol, endLine, endCol);
});
diff --git a/dbml-playground/src/main.ts b/dbml-playground/src/main.ts
index 62d54d33a..0580af1e7 100644
--- a/dbml-playground/src/main.ts
+++ b/dbml-playground/src/main.ts
@@ -1,4 +1,6 @@
-import { createApp } from 'vue';
+import {
+ createApp,
+} from 'vue';
import App from './App.vue';
import './styles/main.css';
diff --git a/dbml-playground/src/types/index.ts b/dbml-playground/src/types/index.ts
index 2d8081db0..e3867d10f 100644
--- a/dbml-playground/src/types/index.ts
+++ b/dbml-playground/src/types/index.ts
@@ -12,11 +12,17 @@
*/
// Import types we need to reference
-import type { Database, ElementKind } from '@dbml/parse';
+import type {
+ Database, ElementKind,
+} from '@dbml/parse';
// Re-export for convenience
-export type { Database, ElementKind } from '@dbml/parse';
-export type { Range } from 'monaco-editor';
+export type {
+ Database, ElementKind,
+} from '@dbml/parse';
+export type {
+ Range,
+} from 'monaco-editor';
// ===== PIPELINE TYPES =====
@@ -75,8 +81,14 @@ export interface TokenMetadata {
tokenIndex: number;
kind: string;
value: string;
- startPosition: { line: number; column: number };
- endPosition: { line: number; column: number };
+ startPosition: {
+ line: number;
+ column: number;
+ };
+ endPosition: {
+ line: number;
+ column: number;
+ };
monacoRange: any; // monaco.Range
}
@@ -89,9 +101,21 @@ export interface DbmlToLexerMap {
}
export interface TokenNavigationEvents {
- 'navigate:token-to-dbml': { tokenIndex: number; modifier: 'cmd' | 'ctrl' | 'button' };
- 'navigate:dbml-to-token': { line: number; column: number; modifier: 'cmd' | 'ctrl' };
- 'navigate:range-to-tokens': { startLine: number; startCol: number; endLine: number; endCol: number };
+ 'navigate:token-to-dbml': {
+ tokenIndex: number;
+ modifier: 'cmd' | 'ctrl' | 'button';
+ };
+ 'navigate:dbml-to-token': {
+ line: number;
+ column: number;
+ modifier: 'cmd' | 'ctrl';
+ };
+ 'navigate:range-to-tokens': {
+ startLine: number;
+ startCol: number;
+ endLine: number;
+ endCol: number;
+ };
}
// ===== AST & SEMANTIC TYPES =====
@@ -110,8 +134,16 @@ export interface SemanticASTNode {
// Source position using parser's Position interface
sourcePosition?: {
- start: { line: number; column: number; offset: number };
- end: { line: number; column: number; offset: number };
+ start: {
+ line: number;
+ column: number;
+ offset: number;
+ };
+ end: {
+ line: number;
+ column: number;
+ offset: number;
+ };
raw?: {
startPos: any;
endPos: any;
@@ -196,8 +228,16 @@ export interface InterpreterTreeNodeProps {
// ===== EVENT TYPES =====
export interface NavigationPosition {
- start: { line: number; column: number; offset: number };
- end: { line: number; column: number; offset: number };
+ start: {
+ line: number;
+ column: number;
+ offset: number;
+ };
+ end: {
+ line: number;
+ column: number;
+ offset: number;
+ };
}
export interface NodeClickEvent {
diff --git a/packages/dbml-cli/__tests__/cli.test.ts b/packages/dbml-cli/__tests__/cli.test.ts
index 060199ed4..f3d5dcbdc 100644
--- a/packages/dbml-cli/__tests__/cli.test.ts
+++ b/packages/dbml-cli/__tests__/cli.test.ts
@@ -1,9 +1,11 @@
-import path from 'path';
+import childProcess from 'child_process';
import fs from 'fs';
+import path from 'path';
import util from 'util';
-import childProcess from 'child_process';
import stripAnsi from 'strip-ansi';
-import { scanDirNames } from './testHelpers';
+import {
+ scanDirNames,
+} from './testHelpers';
const exec = util.promisify(childProcess.exec);
@@ -20,7 +22,9 @@ describe('@dbml/cli', () => {
fs.mkdirSync(path.join(dirName, './out-files'));
}
- const { stdout } = await exec(`node ${args.join(' ')}`);
+ const {
+ stdout,
+ } = await exec(`node ${args.join(' ')}`);
const expectStdout = fs.readFileSync(path.join(dirName, './stdout.txt'), 'utf-8');
const actualStdout = stripAnsi(stdout);
diff --git a/packages/dbml-cli/__tests__/db2dbml_bin.js b/packages/dbml-cli/__tests__/db2dbml_bin.js
index dcd50e12b..4d33d0d4f 100644
--- a/packages/dbml-cli/__tests__/db2dbml_bin.js
+++ b/packages/dbml-cli/__tests__/db2dbml_bin.js
@@ -1,4 +1,6 @@
require = require('esm')(module);
-const { db2dbml } = require('../src');
+const {
+ db2dbml,
+} = require('../src');
db2dbml(process.argv);
diff --git a/packages/dbml-cli/__tests__/dbml2sql_bin.js b/packages/dbml-cli/__tests__/dbml2sql_bin.js
index 73a30e356..486c3ee87 100644
--- a/packages/dbml-cli/__tests__/dbml2sql_bin.js
+++ b/packages/dbml-cli/__tests__/dbml2sql_bin.js
@@ -1,4 +1,6 @@
require = require('esm')(module);
-const { dbml2sql } = require('../src');
+const {
+ dbml2sql,
+} = require('../src');
dbml2sql(process.argv);
diff --git a/packages/dbml-cli/__tests__/sql2dbml_bin.js b/packages/dbml-cli/__tests__/sql2dbml_bin.js
index f4ee81df7..8b54e8708 100644
--- a/packages/dbml-cli/__tests__/sql2dbml_bin.js
+++ b/packages/dbml-cli/__tests__/sql2dbml_bin.js
@@ -1,4 +1,6 @@
require = require('esm')(module);
-const { sql2dbml } = require('../src');
+const {
+ sql2dbml,
+} = require('../src');
sql2dbml(process.argv);
diff --git a/packages/dbml-cli/eslint.config.ts b/packages/dbml-cli/eslint.config.ts
index 39be3ff28..5afa873d5 100644
--- a/packages/dbml-cli/eslint.config.ts
+++ b/packages/dbml-cli/eslint.config.ts
@@ -43,6 +43,13 @@ export default defineConfig(
'@stylistic': stylistic,
},
rules: {
+ '@stylistic/object-curly-newline': ['error', {
+ ObjectExpression: { multiline: true, minProperties: 1 },
+ ObjectPattern: { multiline: true, minProperties: 1 },
+ ImportDeclaration: { multiline: true, minProperties: 1 },
+ ExportDeclaration: { multiline: true, minProperties: 1 },
+ }],
+ '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: false }],
'@typescript-eslint/no-explicit-any': 'off',
'no-use-before-define': 'off',
'no-continue': 'off',
@@ -59,18 +66,10 @@ export default defineConfig(
'@typescript-eslint/consistent-return': [
'error',
],
- '@stylistic/quotes': ['error', 'single', { 'avoidEscape': true }],
+ '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/max-statements-per-line': 'off',
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '=': 'after' } }],
},
- settings: {
- 'import/resolver': {
- typescript: {
- alwaysTryTypes: true,
- project: 'packages/*/{ts,js}config.json',
- },
- },
- },
},
{
files: ['**/*.test.js', '**/*.spec.js', '**/*.test.ts', '**/*.spec.ts'],
diff --git a/packages/dbml-cli/package.json b/packages/dbml-cli/package.json
index 90fd52706..ee8ec0eb1 100644
--- a/packages/dbml-cli/package.json
+++ b/packages/dbml-cli/package.json
@@ -34,6 +34,7 @@
"@babel/cli": "^7.21.0",
"@dbml/connector": "^7.0.0",
"@dbml/core": "^7.0.0",
+ "@dbml/parse": "^7.0.0",
"bluebird": "^3.5.5",
"chalk": "^2.4.2",
"commander": "^2.20.0",
diff --git a/packages/dbml-cli/src/cli/config.js b/packages/dbml-cli/src/cli/config.ts
similarity index 79%
rename from packages/dbml-cli/src/cli/config.js
rename to packages/dbml-cli/src/cli/config.ts
index 2bfe5b059..8057a233d 100644
--- a/packages/dbml-cli/src/cli/config.js
+++ b/packages/dbml-cli/src/cli/config.ts
@@ -1,4 +1,4 @@
-export default {
+const config: Record = {
mysql: {
name: 'MySQL',
},
@@ -21,3 +21,5 @@ export default {
name: 'Snowflake',
},
};
+
+export default config;
diff --git a/packages/dbml-cli/src/cli/connector.js b/packages/dbml-cli/src/cli/connector.ts
similarity index 77%
rename from packages/dbml-cli/src/cli/connector.js
rename to packages/dbml-cli/src/cli/connector.ts
index 349c53858..b199c697c 100644
--- a/packages/dbml-cli/src/cli/connector.js
+++ b/packages/dbml-cli/src/cli/connector.ts
@@ -1,19 +1,28 @@
-import { importer } from '@dbml/core';
-import { connector } from '@dbml/connector';
-import figures from 'figures';
-import chalk from 'chalk';
import path from 'path';
import {
- resolvePaths,
- getConnectionOpt,
-} from './utils';
+ connector,
+} from '@dbml/connector';
+import {
+ importer,
+} from '@dbml/core';
+import chalk from 'chalk';
+import {
+ Command,
+} from 'commander';
+import figures from 'figures';
+import logger from '../helpers/logger';
import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
import OutputFilePlugin from './outputPlugins/outputFilePlugin';
-import logger from '../helpers/logger';
+import {
+ getConnectionOpt,
+ resolvePaths,
+} from './utils';
-export default async function connectionHandler (program) {
+export default async function connectionHandler (program: Command) {
try {
- const { connection, databaseType } = getConnectionOpt(program.args);
+ const {
+ connection, databaseType,
+ } = getConnectionOpt(program.args);
const opts = program.opts();
const schemaJson = await connector.fetchSchemaJson(connection, databaseType);
diff --git a/packages/dbml-cli/src/cli/export.js b/packages/dbml-cli/src/cli/export.ts
similarity index 68%
rename from packages/dbml-cli/src/cli/export.js
rename to packages/dbml-cli/src/cli/export.ts
index 4fd211999..ffaaa31bc 100644
--- a/packages/dbml-cli/src/cli/export.js
+++ b/packages/dbml-cli/src/cli/export.ts
@@ -1,27 +1,39 @@
-import figures from 'figures';
-import chalk from 'chalk';
import path from 'path';
-import { exporter } from '@dbml/core';
import {
- validateInputFilePaths,
- resolvePaths,
- getFormatOpt,
- generate,
-} from './utils';
-import { validateFilePlugin } from './validatePlugins/validatePlugins';
+ CompilerError, exporter,
+} from '@dbml/core';
+import type {
+ ExportFormat,
+} from '@dbml/core';
+import chalk from 'chalk';
+import type {
+ Command,
+} from 'commander';
+import figures from 'figures';
+import {
+ SyntaxError,
+} from '../errors';
+import logger from '../helpers/logger';
+import config from './config';
import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
import OutputFilePlugin from './outputPlugins/outputFilePlugin';
-import config from './config';
-import logger from '../helpers/logger';
-import { SyntaxError } from '../errors';
+import {
+ generate,
+ getFormatOpt,
+ resolvePaths,
+ validateInputFilePaths,
+} from './utils';
+import {
+ validateFilePlugin,
+} from './validatePlugins/validatePlugins';
-export default async function exportHandler (program) {
+export default async function exportHandler (program: Command) {
try {
const inputPaths = resolvePaths(program.args);
validateInputFilePaths(inputPaths, validateFilePlugin);
const opts = program.opts();
- const format = getFormatOpt(opts);
+ const format = getFormatOpt(opts) as ExportFormat;
if (!opts.outFile && !opts.outDir) {
generate(inputPaths, (dbml) => exporter.export(dbml, format), OutputConsolePlugin);
@@ -41,6 +53,9 @@ export default async function exportHandler (program) {
console.log(` ${chalk.green(figures.main.tick)} Generated SQL dump file (${config[format].name}): ${path.basename(opts.outFile)}`);
}
} catch (error) {
- logger.error(`\n ${error.diags.map((diag) => new SyntaxError(diag.filepath, diag)).map(({ message }) => message).join('\n ')}`);
+ const e = error as CompilerError;
+ logger.error(`\n ${e.diags.map((diag) => new SyntaxError(diag.filepath ?? '', diag)).map(({
+ message,
+ }) => message).join('\n ')}`);
}
}
diff --git a/packages/dbml-cli/src/cli/import.js b/packages/dbml-cli/src/cli/import.ts
similarity index 67%
rename from packages/dbml-cli/src/cli/import.js
rename to packages/dbml-cli/src/cli/import.ts
index 0af67aca5..7803d5f07 100644
--- a/packages/dbml-cli/src/cli/import.js
+++ b/packages/dbml-cli/src/cli/import.ts
@@ -1,27 +1,39 @@
-import { importer } from '@dbml/core';
-import figures from 'figures';
-import chalk from 'chalk';
import path from 'path';
+import {
+ CompilerError, importer,
+} from '@dbml/core';
+import type {
+ ImportFormat,
+} from '@dbml/core';
+import chalk from 'chalk';
+import {
+ Command,
+} from 'commander';
+import figures from 'figures';
+import {
+ SyntaxError,
+} from '../errors';
+import logger from '../helpers/logger';
import config from './config';
+import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
+import OutputFilePlugin from './outputPlugins/outputFilePlugin';
import {
- validateInputFilePaths,
- resolvePaths,
- getFormatOpt,
generate,
+ getFormatOpt,
+ resolvePaths,
+ validateInputFilePaths,
} from './utils';
-import { validateFilePlugin } from './validatePlugins/validatePlugins';
-import OutputConsolePlugin from './outputPlugins/outputConsolePlugin';
-import OutputFilePlugin from './outputPlugins/outputFilePlugin';
-import logger from '../helpers/logger';
-import { SyntaxError } from '../errors';
+import {
+ validateFilePlugin,
+} from './validatePlugins/validatePlugins';
-export default async function importHandler (program) {
+export default async function importHandler (program: Command) {
try {
const inputPaths = resolvePaths(program.args);
validateInputFilePaths(inputPaths, validateFilePlugin);
const opts = program.opts();
- const format = getFormatOpt(opts);
+ const format = getFormatOpt(opts) as ImportFormat;
if (!opts.outFile && !opts.outDir) {
generate(inputPaths, (sql) => importer.import(sql, format), OutputConsolePlugin);
@@ -31,8 +43,10 @@ export default async function importHandler (program) {
console.log(` ${chalk.green(figures.main.tick)} Generated DBML file from SQL file (${config[format].name}): ${path.basename(opts.outFile)}`);
}
} catch (error) {
- if ('diags' in error) {
- logger.error(`\n ${error.diags.map((diag) => new SyntaxError(diag.filepath, diag)).map(({ message }) => message).join('\n ')}`);
+ if (error instanceof CompilerError) {
+ logger.error(`\n ${error.diags.map((diag) => new SyntaxError(diag.filepath ?? '', diag)).map(({
+ message,
+ }) => message).join('\n ')}`);
return;
}
throw error;
diff --git a/packages/dbml-cli/src/cli/index.js b/packages/dbml-cli/src/cli/index.ts
similarity index 96%
rename from packages/dbml-cli/src/cli/index.js
rename to packages/dbml-cli/src/cli/index.ts
index a04b855f4..4fae12d19 100644
--- a/packages/dbml-cli/src/cli/index.js
+++ b/packages/dbml-cli/src/cli/index.ts
@@ -1,14 +1,14 @@
import program from 'commander';
-import importHandler from './import';
-import exportHandler from './export';
-import connectionHandler from './connector';
import projectInfo from '../../package.json';
+import connectionHandler from './connector';
+import exportHandler from './export';
+import importHandler from './import';
-function showHelp (args) {
+function showHelp (args: string[]) {
if (args.length < 3) program.help();
}
-function dbml2sql (args) {
+function dbml2sql (args: string[]) {
program.version(projectInfo.version);
program
@@ -26,7 +26,7 @@ function dbml2sql (args) {
exportHandler(program);
}
-function sql2dbml (args) {
+function sql2dbml (args: string[]) {
program.version(projectInfo.version);
program
@@ -48,7 +48,7 @@ function sql2dbml (args) {
importHandler(program);
}
-function db2dbml (args) {
+function db2dbml (args: string[]) {
program.version(projectInfo.version);
const description = `Generate DBML directly from a database
diff --git a/packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.js b/packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.ts
similarity index 73%
rename from packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.js
rename to packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.ts
index cb1d82db0..73cbc7171 100644
--- a/packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.js
+++ b/packages/dbml-cli/src/cli/outputPlugins/outputConsolePlugin.ts
@@ -1,5 +1,5 @@
class OutputConsolePlugin {
- static write (content) {
+ static write (content: string) {
console.log(content);
}
}
diff --git a/packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.js b/packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.ts
similarity index 59%
rename from packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.js
rename to packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.ts
index d8c4db2c8..ce54c0f4b 100644
--- a/packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.js
+++ b/packages/dbml-cli/src/cli/outputPlugins/outputFilePlugin.ts
@@ -1,7 +1,12 @@
import fs from 'fs';
class OutputFilePlugin {
- constructor (filePath, header) {
+ filePath: string;
+ header: string | undefined;
+ isWrite: boolean;
+ stream!: fs.WriteStream;
+
+ constructor (filePath: string, header?: string) {
this.filePath = filePath;
this.header = header;
this.isWrite = false;
@@ -9,12 +14,14 @@ class OutputFilePlugin {
start () {
fs.writeFileSync(this.filePath, '');
- this.stream = fs.createWriteStream(this.filePath, { flags: 'a' });
+ this.stream = fs.createWriteStream(this.filePath, {
+ flags: 'a',
+ });
if (this.header) this.stream.write(this.header);
this.isWrite = true;
}
- write (content) {
+ write (content: string) {
if (!this.isWrite) this.start();
this.stream.write(content);
}
diff --git a/packages/dbml-cli/src/cli/utils.js b/packages/dbml-cli/src/cli/utils.ts
similarity index 66%
rename from packages/dbml-cli/src/cli/utils.js
rename to packages/dbml-cli/src/cli/utils.ts
index c5d8a7e62..b63eb544a 100644
--- a/packages/dbml-cli/src/cli/utils.js
+++ b/packages/dbml-cli/src/cli/utils.ts
@@ -1,41 +1,57 @@
-import path from 'path';
import fs from 'fs';
-import { CompilerError } from '@dbml/core';
-import { reduce } from 'lodash-es';
+import path from 'path';
+import {
+ CompilerError,
+ type ExportFormat,
+} from '@dbml/core';
+import {
+ reduce,
+} from 'lodash-es';
+
+interface OutputPlugin {
+ write(content: string): void;
+}
+
+interface ConnectionOpt {
+ connection: string;
+ databaseType: string;
+}
-function resolvePaths (paths) {
+function resolvePaths (paths: string): string;
+function resolvePaths (paths: string[]): string[];
+function resolvePaths (paths: string | string[]): string | string[] {
if (!Array.isArray(paths)) {
return path.resolve(process.cwd(), paths);
}
return paths.map((_path) => path.resolve(process.cwd(), _path));
}
-function validateInputFilePaths (paths, validatePlugin) {
+function validateInputFilePaths (paths: string[], validatePlugin: (_path: string) => void) {
return paths.every((_path) => validatePlugin(_path));
}
-function getFormatOpt (opts) {
+function getFormatOpt (opts: Record): ExportFormat {
const formatOpts = Object.keys(opts).filter((opt) => {
return ['postgres', 'mysql', 'mssql', 'postgresLegacy', 'mysqlLegacy', 'mssqlLegacy', 'oracle', 'snowflake'].includes(opt);
});
- let format = 'postgres';
+ let format: ExportFormat = 'postgres';
let cnt = 0;
formatOpts.forEach((opt) => {
if (opts[opt]) {
cnt += 1;
if (cnt > 1) throw new Error('Too many format options');
- format = opt;
+ format = opt as ExportFormat;
}
});
- return format;
+ return format as ExportFormat;
}
-function getConnectionOpt (args) {
+function getConnectionOpt (args: string[]): ConnectionOpt {
const supportedDatabases = ['postgres', 'mysql', 'mssql', 'snowflake', 'bigquery', 'oracle'];
- const defaultConnectionOpt = {
+ const defaultConnectionOpt: ConnectionOpt = {
connection: args[0],
databaseType: 'unknown',
};
@@ -61,7 +77,11 @@ function getConnectionOpt (args) {
}, defaultConnectionOpt);
}
-function generate (inputPaths, transform, outputPlugin) {
+function generate (
+ inputPaths: string[],
+ transform: (source: string) => string,
+ outputPlugin: OutputPlugin,
+): void {
inputPaths.forEach((_path) => {
const source = fs.readFileSync(_path, 'utf-8');
try {
@@ -70,7 +90,10 @@ function generate (inputPaths, transform, outputPlugin) {
} catch (e) {
if (e instanceof CompilerError) {
throw e.map((diag) => ({
- ...diag, message: diag.message, filepath: path.basename(_path), stack: diag.stack,
+ ...diag,
+ message: diag.message,
+ filepath: path.basename(_path),
+ stack: diag.stack,
}));
}
throw e;
diff --git a/packages/dbml-cli/src/cli/validatePlugins/validatePlugins.js b/packages/dbml-cli/src/cli/validatePlugins/validatePlugins.ts
similarity index 66%
rename from packages/dbml-cli/src/cli/validatePlugins/validatePlugins.js
rename to packages/dbml-cli/src/cli/validatePlugins/validatePlugins.ts
index 0d92adf53..79412bf25 100644
--- a/packages/dbml-cli/src/cli/validatePlugins/validatePlugins.js
+++ b/packages/dbml-cli/src/cli/validatePlugins/validatePlugins.ts
@@ -1,8 +1,8 @@
import fs from 'fs';
-function validateFilePlugin (_path) {
+function validateFilePlugin (_path: string) {
const stat = fs.statSync(_path);
- if (stat.isDirectory(_path)) {
+ if (stat.isDirectory()) {
throw new Error('Expect input to be files');
}
}
diff --git a/packages/dbml-cli/src/errors/domainError.js b/packages/dbml-cli/src/errors/domainError.ts
similarity index 72%
rename from packages/dbml-cli/src/errors/domainError.js
rename to packages/dbml-cli/src/errors/domainError.ts
index a656e0917..f0dfbed60 100644
--- a/packages/dbml-cli/src/errors/domainError.js
+++ b/packages/dbml-cli/src/errors/domainError.ts
@@ -1,5 +1,7 @@
class DomainError extends Error {
- constructor (message, rootError = {}) {
+ rootError: unknown;
+
+ constructor (message: string, rootError: unknown = {}) {
super(message);
this.name = this.constructor.name;
this.rootError = rootError;
diff --git a/packages/dbml-cli/src/errors/index.js b/packages/dbml-cli/src/errors/index.ts
similarity index 100%
rename from packages/dbml-cli/src/errors/index.js
rename to packages/dbml-cli/src/errors/index.ts
diff --git a/packages/dbml-cli/src/errors/syntaxError.js b/packages/dbml-cli/src/errors/syntaxError.ts
similarity index 64%
rename from packages/dbml-cli/src/errors/syntaxError.js
rename to packages/dbml-cli/src/errors/syntaxError.ts
index 0868ae135..e3e2725a2 100644
--- a/packages/dbml-cli/src/errors/syntaxError.js
+++ b/packages/dbml-cli/src/errors/syntaxError.ts
@@ -1,8 +1,21 @@
-import { isEmpty } from 'lodash-es';
+import {
+ isEmpty,
+} from 'lodash-es';
import DomainError from './domainError';
+interface RootError {
+ location?: {
+ start: {
+ line: number;
+ column: number;
+ };
+ };
+ message?: string;
+ stack?: unknown;
+}
+
class SyntaxError extends DomainError {
- constructor (fileName, rootError = {}) {
+ constructor (fileName: string, rootError: RootError = {}) {
let message = `You have a syntax error at "${fileName}"`;
if (rootError.location) {
diff --git a/packages/dbml-cli/src/helpers/logger.js b/packages/dbml-cli/src/helpers/logger.ts
similarity index 61%
rename from packages/dbml-cli/src/helpers/logger.js
rename to packages/dbml-cli/src/helpers/logger.ts
index fc76cda12..c5dc91c40 100644
--- a/packages/dbml-cli/src/helpers/logger.js
+++ b/packages/dbml-cli/src/helpers/logger.ts
@@ -1,19 +1,30 @@
-import { createLogger, format, transports } from 'winston';
-import chalk from 'chalk';
import path from 'path';
+import chalk from 'chalk';
+import {
+ createLogger, format, transports,
+} from 'winston';
-const { combine, timestamp, printf } = format;
+const {
+ combine, timestamp, printf,
+} = format;
const consoleFormat = printf((info) => {
- const { level, message } = info;
+ const {
+ level, message,
+ } = info;
return ` ${chalk.red(level.toUpperCase())}: ${message}\n
A complete log can be found in:
${path.resolve(process.cwd(), 'dbml-error.log')}`;
});
const fileFormat = printf((info) => {
- const { timestamp, stack, rootError } = info;
- let logContent = `${timestamp}\n${stack}\n`;
+ const {
+ timestamp: ts, stack, rootError,
+ } = info as unknown as { timestamp: string;
+ stack?: string;
+ rootError?: { stack?: string;
+ location?: unknown; }; };
+ let logContent = `${ts}\n${stack}\n`;
if (rootError) {
logContent += '\nROOT_ERROR:';
logContent += `\n${rootError.stack}`;
@@ -30,7 +41,9 @@ const consoleLogger = createLogger({
consoleFormat,
),
transports: [
- new transports.Console({ level: 'error' }),
+ new transports.Console({
+ level: 'error',
+ }),
],
});
@@ -40,28 +53,28 @@ const fileLogger = createLogger({
fileFormat,
),
transports: [
- new transports.File({ filename: 'dbml-error.log', level: 'error' }),
+ new transports.File({
+ filename: 'dbml-error.log',
+ level: 'error',
+ }),
],
});
const logger = {
- debug (msg) {
+ debug (msg: string) {
consoleLogger.debug(msg);
},
- info (msg) {
+ info (msg: string) {
consoleLogger.info(msg);
},
- warn (msg) {
+ warn (msg: string) {
consoleLogger.warn(msg);
},
- error (msg) {
+
+ error (msg: any) {
consoleLogger.error(msg);
fileLogger.error(msg);
},
- log (level, msg) {
- const lvl = exports[level];
- lvl(msg);
- },
};
export default logger;
diff --git a/packages/dbml-cli/src/index.js b/packages/dbml-cli/src/index.js
deleted file mode 100644
index 39d0046a8..000000000
--- a/packages/dbml-cli/src/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { dbml2sql, sql2dbml, db2dbml } from './cli';
-
-export {
- dbml2sql,
- sql2dbml,
- db2dbml,
-};
diff --git a/packages/dbml-cli/src/index.ts b/packages/dbml-cli/src/index.ts
new file mode 100644
index 000000000..6e36e02a6
--- /dev/null
+++ b/packages/dbml-cli/src/index.ts
@@ -0,0 +1,9 @@
+import {
+ db2dbml, dbml2sql, sql2dbml,
+} from './cli';
+
+export {
+ dbml2sql,
+ sql2dbml,
+ db2dbml,
+};
diff --git a/packages/dbml-cli/vite.config.ts b/packages/dbml-cli/vite.config.ts
index bdab8227a..b41e81b20 100644
--- a/packages/dbml-cli/vite.config.ts
+++ b/packages/dbml-cli/vite.config.ts
@@ -14,7 +14,7 @@ export default defineConfig({
outDir: 'lib',
minify: false,
lib: {
- entry: path.resolve(__dirname, 'src/index.js'),
+ entry: path.resolve(__dirname, 'src/index.ts'),
fileName: 'index',
formats: ['cjs'],
},
diff --git a/packages/dbml-connector/eslint.config.ts b/packages/dbml-connector/eslint.config.ts
index a081eaa27..a12bfbc23 100644
--- a/packages/dbml-connector/eslint.config.ts
+++ b/packages/dbml-connector/eslint.config.ts
@@ -54,18 +54,10 @@ export default defineConfig(
],
'consistent-return': 'off',
'@typescript-eslint/consistent-return': ['error'],
- '@stylistic/quotes': ['error', 'single', { 'avoidEscape': true }],
+ '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/max-statements-per-line': 'off',
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '=': 'after' } }],
},
- settings: {
- 'import/resolver': {
- typescript: {
- alwaysTryTypes: true,
- project: 'packages/*/{ts,js}config.json',
- },
- },
- },
},
],
);
diff --git a/packages/dbml-connector/src/connectors/connector.ts b/packages/dbml-connector/src/connectors/connector.ts
index cafccf60e..8639f23e4 100644
--- a/packages/dbml-connector/src/connectors/connector.ts
+++ b/packages/dbml-connector/src/connectors/connector.ts
@@ -1,10 +1,10 @@
-import { DatabaseSchema } from './types';
-import { fetchSchemaJson as fetchPostgresSchemaJson } from './postgres';
+import { fetchSchemaJson as fetchBigQuerySchemaJson } from './bigquery';
import { fetchSchemaJson as fetchMssqlSchemaJson } from './mssql';
import { fetchSchemaJson as fetchMysqlSchemaJson } from './mysql';
-import { fetchSchemaJson as fetchSnowflakeSchemaJson } from './snowflake';
-import { fetchSchemaJson as fetchBigQuerySchemaJson } from './bigquery';
import { fetchSchemaJson as fetchOracleSchemaJson } from './oracle';
+import { fetchSchemaJson as fetchPostgresSchemaJson } from './postgres';
+import { fetchSchemaJson as fetchSnowflakeSchemaJson } from './snowflake';
+import { DatabaseSchema } from './types';
const fetchSchemaJson = async (connection: string, databaseType: string): Promise => {
switch (databaseType) {
diff --git a/packages/dbml-connector/src/connectors/mysql/index.ts b/packages/dbml-connector/src/connectors/mysql/index.ts
index 459097fa8..00178a6a5 100644
--- a/packages/dbml-connector/src/connectors/mysql/index.ts
+++ b/packages/dbml-connector/src/connectors/mysql/index.ts
@@ -1,7 +1,7 @@
-import { createConnection, Connection } from 'mysql2/promise';
import { flatten } from 'lodash-es';
+import { Connection, createConnection } from 'mysql2/promise';
import {
- CheckConstraintDictionary, DefaultInfo, DatabaseSchema, DefaultType, Field, IndexColumn, FieldsDictionary, Table, Enum, IndexesDictionary, TableConstraintsDictionary, Index, Ref,
+ CheckConstraintDictionary, DatabaseSchema, DefaultInfo, DefaultType, Enum, Field, FieldsDictionary, Index, IndexColumn, IndexesDictionary, Ref, Table, TableConstraintsDictionary,
} from '../types';
const NUMBER_REGEX = '^-?[0-9]+(.[0-9]+)?$';
diff --git a/packages/dbml-connector/src/connectors/oracle/index.ts b/packages/dbml-connector/src/connectors/oracle/index.ts
index 18b4b240d..c942c0cf0 100644
--- a/packages/dbml-connector/src/connectors/oracle/index.ts
+++ b/packages/dbml-connector/src/connectors/oracle/index.ts
@@ -1,9 +1,9 @@
-import { getConnection, Connection } from 'oracledb';
+import { Connection, getConnection } from 'oracledb';
import { DatabaseSchema } from '../types';
-import { generateTablesAndFields } from './tables';
-import { generateRawRefs } from './refs';
import { generateConstraints } from './constraints';
import { generateIndexes } from './indexes';
+import { generateRawRefs } from './refs';
+import { generateTablesAndFields } from './tables';
import { processEasyConnectString } from './utils';
const getValidatedClient = async (username: string, password: string, dbidentifier: string): Promise => {
diff --git a/packages/dbml-connector/src/connectors/snowflake/index.ts b/packages/dbml-connector/src/connectors/snowflake/index.ts
index a77e5359f..8278a3dba 100644
--- a/packages/dbml-connector/src/connectors/snowflake/index.ts
+++ b/packages/dbml-connector/src/connectors/snowflake/index.ts
@@ -7,7 +7,7 @@
//
import snowflake from 'snowflake-sdk';
-import type { Connection, LogLevel, ConnectionOptions } from 'snowflake-sdk';
+import type { Connection, ConnectionOptions, LogLevel } from 'snowflake-sdk';
import { parseSchema } from '../../utils/parseSchema';
import {
DatabaseSchema,
diff --git a/packages/dbml-core/__benchmarks__/mysql.bench.js b/packages/dbml-core/__benchmarks__/mysql.bench.js
index 94510515b..0ef17b3bb 100644
--- a/packages/dbml-core/__benchmarks__/mysql.bench.js
+++ b/packages/dbml-core/__benchmarks__/mysql.bench.js
@@ -1,7 +1,6 @@
-import { bench, describe } from 'vitest';
import fs from 'fs';
import path from 'path';
-
+import { bench, describe } from 'vitest';
import importer from '../src/import';
const mySQL = fs.readFileSync(path.join(process.cwd(), './__benchmarks__/mysql.sql'), 'utf-8');
diff --git a/packages/dbml-core/__benchmarks__/postgres.bench.js b/packages/dbml-core/__benchmarks__/postgres.bench.js
index 0d006b76e..242d40523 100644
--- a/packages/dbml-core/__benchmarks__/postgres.bench.js
+++ b/packages/dbml-core/__benchmarks__/postgres.bench.js
@@ -1,7 +1,6 @@
-import { bench, describe } from 'vitest';
import fs from 'fs';
import path from 'path';
-
+import { bench, describe } from 'vitest';
// import importer from '../lib/import';
import importer from '../src/import';
diff --git a/packages/dbml-core/eslint.config.ts b/packages/dbml-core/eslint.config.ts
index ea37bec63..a703fa221 100644
--- a/packages/dbml-core/eslint.config.ts
+++ b/packages/dbml-core/eslint.config.ts
@@ -79,6 +79,13 @@ export default defineConfig(
},
},
rules: {
+ '@stylistic/object-curly-newline': ['error', {
+ ObjectExpression: { multiline: true, minProperties: 1 },
+ ObjectPattern: { multiline: true, minProperties: 1 },
+ ImportDeclaration: { multiline: true, minProperties: 1 },
+ ExportDeclaration: { multiline: true, minProperties: 1 },
+ }],
+ '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: false }],
'no-unused-vars': [
'warn',
{
diff --git a/packages/dbml-core/src/export/MysqlExporter.js b/packages/dbml-core/src/export/MysqlExporter.js
index 19d562217..2c8ab5135 100644
--- a/packages/dbml-core/src/export/MysqlExporter.js
+++ b/packages/dbml-core/src/export/MysqlExporter.js
@@ -1,17 +1,17 @@
+import {
+ isBinaryType,
+ isBooleanType,
+ isDateTimeType,
+ isNumericType,
+ isStringType,
+} from '@dbml/parse';
import { concat, flatten, isEmpty } from 'lodash-es';
import {
- shouldPrintSchema,
buildJunctionFields1,
buildJunctionFields2,
buildNewTableName,
+ shouldPrintSchema,
} from './utils';
-import {
- isNumericType,
- isStringType,
- isBooleanType,
- isDateTimeType,
- isBinaryType,
-} from '@dbml/parse';
class MySQLExporter {
static exportRecords (model) {
diff --git a/packages/dbml-core/src/export/OracleExporter.js b/packages/dbml-core/src/export/OracleExporter.js
index b607b15fc..b50e108e0 100644
--- a/packages/dbml-core/src/export/OracleExporter.js
+++ b/packages/dbml-core/src/export/OracleExporter.js
@@ -1,20 +1,20 @@
+import {
+ isBinaryType,
+ isBooleanType,
+ isDateTimeType,
+ isNumericType,
+ isStringType,
+} from '@dbml/parse';
import { concat, flatten, forEach, isEmpty } from 'lodash-es';
import {
buildJunctionFields1,
buildJunctionFields2,
buildUniqueTableName,
escapeObjectName,
- shouldPrintSchema,
- parseIsoDatetime,
formatDatetimeForOracle,
+ parseIsoDatetime,
+ shouldPrintSchema,
} from './utils';
-import {
- isNumericType,
- isStringType,
- isBooleanType,
- isDateTimeType,
- isBinaryType,
-} from '@dbml/parse';
class OracleExporter {
static exportRecords (model) {
diff --git a/packages/dbml-core/src/export/PostgresExporter.js b/packages/dbml-core/src/export/PostgresExporter.js
index 9ecb9d0ca..b65f1a850 100644
--- a/packages/dbml-core/src/export/PostgresExporter.js
+++ b/packages/dbml-core/src/export/PostgresExporter.js
@@ -1,20 +1,20 @@
+import {
+ isBinaryType,
+ isBooleanType,
+ isDateTimeType,
+ isNumericType,
+ isStringType,
+} from '@dbml/parse';
import { concat, flatten, isEmpty } from 'lodash-es';
+import { shouldPrintSchemaName } from '../model_structure/utils';
import {
- hasWhiteSpaceOrUpperCase,
- shouldPrintSchema,
buildJunctionFields1,
buildJunctionFields2,
buildNewTableName,
hasWhiteSpace,
+ hasWhiteSpaceOrUpperCase,
+ shouldPrintSchema,
} from './utils';
-import { shouldPrintSchemaName } from '../model_structure/utils';
-import {
- isNumericType,
- isStringType,
- isBooleanType,
- isDateTimeType,
- isBinaryType,
-} from '@dbml/parse';
// PostgreSQL built-in data types
// Generated from PostgreSQLParser.g4 and PostgreSQLLexer.g4
diff --git a/packages/dbml-core/src/export/SqlServerExporter.js b/packages/dbml-core/src/export/SqlServerExporter.js
index 9d1bbe91d..3e74e1fd0 100644
--- a/packages/dbml-core/src/export/SqlServerExporter.js
+++ b/packages/dbml-core/src/export/SqlServerExporter.js
@@ -1,17 +1,17 @@
+import {
+ isBinaryType,
+ isBooleanType,
+ isDateTimeType,
+ isNumericType,
+ isStringType,
+} from '@dbml/parse';
import { concat, flatten, isEmpty } from 'lodash-es';
import {
- shouldPrintSchema,
buildJunctionFields1,
buildJunctionFields2,
buildNewTableName,
+ shouldPrintSchema,
} from './utils';
-import {
- isNumericType,
- isStringType,
- isBooleanType,
- isDateTimeType,
- isBinaryType,
-} from '@dbml/parse';
class SqlServerExporter {
static exportRecords (model) {
diff --git a/packages/dbml-core/src/export/utils.js b/packages/dbml-core/src/export/utils.js
index 92f9e8038..5b484243f 100644
--- a/packages/dbml-core/src/export/utils.js
+++ b/packages/dbml-core/src/export/utils.js
@@ -1,6 +1,6 @@
import { tryExtractDateTime } from '@dbml/parse';
-import { DEFAULT_SCHEMA_NAME } from '../model_structure/config';
import { DateTime } from 'luxon';
+import { DEFAULT_SCHEMA_NAME } from '../model_structure/config';
export function hasWhiteSpace (s) {
return /\s/g.test(s);
diff --git a/packages/dbml-core/src/index.ts b/packages/dbml-core/src/index.ts
index 90aa56844..19cdacbde 100644
--- a/packages/dbml-core/src/index.ts
+++ b/packages/dbml-core/src/index.ts
@@ -46,9 +46,37 @@ export {
// Re-export types
export type {
- DiagramViewSyncOperation,
- DiagramViewBlock,
+ Database,
+ MasterDatabase,
+ Table,
+ Note,
+ Column,
+ ColumnType,
+ Index,
+ Check,
+ InlineRef,
+ Ref,
+ RefEndpointPair,
+ RefEndpoint,
+ RelationCardinality,
+ Enum,
+ EnumField,
+ TableGroup,
+ TableGroupField,
+ Alias,
+ AliasKind,
+ TablePartial,
+ TablePartialInjection,
+ RecordValue,
+ RecordValueType,
+ TableRecord,
+ Project,
+ SchemaElement,
+ TokenPosition,
+ ElementRef,
DiagramView,
FilterConfig,
+ DiagramViewSyncOperation,
+ DiagramViewBlock,
TextEdit,
} from '@dbml/parse';
diff --git a/packages/dbml-core/src/model_structure/database.js b/packages/dbml-core/src/model_structure/database.js
index 6370a6d3c..60ac4f291 100644
--- a/packages/dbml-core/src/model_structure/database.js
+++ b/packages/dbml-core/src/model_structure/database.js
@@ -1,15 +1,15 @@
import { capitalize, get } from 'lodash-es';
-import Schema from './schema';
-import Ref from './ref';
-import Enum from './enum';
-import TableGroup from './tableGroup';
-import Table from './table';
-import StickyNote from './stickyNote';
-import Element from './element';
import {
- DEFAULT_SCHEMA_NAME, TABLE, TABLE_GROUP, ENUM, REF, NOTE,
+ DEFAULT_SCHEMA_NAME, ENUM, NOTE, REF, TABLE, TABLE_GROUP,
} from './config';
import DbState from './dbState';
+import Element from './element';
+import Enum from './enum';
+import Ref from './ref';
+import Schema from './schema';
+import StickyNote from './stickyNote';
+import Table from './table';
+import TableGroup from './tableGroup';
import TablePartial from './tablePartial';
class Database extends Element {
diff --git a/packages/dbml-core/src/model_structure/endpoint.js b/packages/dbml-core/src/model_structure/endpoint.js
index df0a23aa1..7c73b2f3a 100644
--- a/packages/dbml-core/src/model_structure/endpoint.js
+++ b/packages/dbml-core/src/model_structure/endpoint.js
@@ -1,5 +1,5 @@
-import Element from './element';
import { DEFAULT_SCHEMA_NAME } from './config';
+import Element from './element';
import { shouldPrintSchema, shouldPrintSchemaName } from './utils';
class Endpoint extends Element {
diff --git a/packages/dbml-core/src/model_structure/field.js b/packages/dbml-core/src/model_structure/field.js
index 151433ee1..f6ae16555 100644
--- a/packages/dbml-core/src/model_structure/field.js
+++ b/packages/dbml-core/src/model_structure/field.js
@@ -1,7 +1,7 @@
import { get } from 'lodash-es';
-import Element from './element';
-import { DEFAULT_SCHEMA_NAME } from './config';
import Check from './check';
+import { DEFAULT_SCHEMA_NAME } from './config';
+import Element from './element';
class Field extends Element {
/**
diff --git a/packages/dbml-core/src/model_structure/ref.js b/packages/dbml-core/src/model_structure/ref.js
index 2a627d700..b35ed1dcb 100644
--- a/packages/dbml-core/src/model_structure/ref.js
+++ b/packages/dbml-core/src/model_structure/ref.js
@@ -1,6 +1,6 @@
+import { DEFAULT_SCHEMA_NAME } from './config';
import Element from './element';
import Endpoint from './endpoint';
-import { DEFAULT_SCHEMA_NAME } from './config';
/**
* Compare two pairs of objects
diff --git a/packages/dbml-core/src/model_structure/schema.js b/packages/dbml-core/src/model_structure/schema.js
index be42ee55d..c5eda0b23 100644
--- a/packages/dbml-core/src/model_structure/schema.js
+++ b/packages/dbml-core/src/model_structure/schema.js
@@ -1,10 +1,10 @@
import { get } from 'lodash-es';
-import Table from './table';
import Element from './element';
import Enum from './enum';
-import { shouldPrintSchema } from './utils';
-import TableGroup from './tableGroup';
import Ref from './ref';
+import Table from './table';
+import TableGroup from './tableGroup';
+import { shouldPrintSchema } from './utils';
class Schema extends Element {
/**
diff --git a/packages/dbml-core/src/model_structure/table.js b/packages/dbml-core/src/model_structure/table.js
index a0441d311..2f06c63fd 100644
--- a/packages/dbml-core/src/model_structure/table.js
+++ b/packages/dbml-core/src/model_structure/table.js
@@ -1,10 +1,10 @@
import { get, isNil } from 'lodash-es';
+import Check from './check';
+import { DEFAULT_SCHEMA_NAME } from './config';
import Element from './element';
import Field from './field';
import Index from './indexes';
-import { DEFAULT_SCHEMA_NAME } from './config';
import { shouldPrintSchema } from './utils';
-import Check from './check';
class Table extends Element {
/**
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/index.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/index.js
index ea1c6f95e..e32e561b7 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/index.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/index.js
@@ -1,25 +1,20 @@
import antlr4 from 'antlr4';
-import PostgreSQLLexer from '../parsers/postgresql/PostgreSQLLexer';
-import PostgreSQLParser from '../parsers/postgresql/PostgreSQLParser';
-import PostgresASTGen from './postgres/PostgresASTGen';
-
+import TSqlLexer from '../parsers/mssql/TSqlLexer';
+import TSqlParser from '../parsers/mssql/TSqlParser';
import MySQLLexer from '../parsers/mysql/MySqlLexer';
import MySQLParser from '../parsers/mysql/MySqlParser';
-import MySQLASTGen from './mysql/MySQLASTGen';
-
+import OracleSQLLexer from '../parsers/oraclesql/OracleSqlLexer';
+import OracleSQLParser from '../parsers/oraclesql/OracleSqlParser';
+import PostgreSQLLexer from '../parsers/postgresql/PostgreSQLLexer';
+import PostgreSQLParser from '../parsers/postgresql/PostgreSQLParser';
import SnowflakeLexer from '../parsers/snowflake/SnowflakeLexer';
import SnowflakeParser from '../parsers/snowflake/SnowflakeParser';
-import SnowflakeASTGen from './snowflake/SnowflakeASTGen';
-
import ParserErrorListener from './ParserErrorListener';
-
-import TSqlLexer from '../parsers/mssql/TSqlLexer';
-import TSqlParser from '../parsers/mssql/TSqlParser';
import MssqlASTGen from './mssql/MssqlASTGen';
-
-import OracleSQLLexer from '../parsers/oraclesql/OracleSqlLexer';
-import OracleSQLParser from '../parsers/oraclesql/OracleSqlParser';
+import MySQLASTGen from './mysql/MySQLASTGen';
import OracleSqlASTGen from './oraclesql/OracleSQLASTGen';
+import PostgresASTGen from './postgres/PostgresASTGen';
+import SnowflakeASTGen from './snowflake/SnowflakeASTGen';
function parse (input, format) {
const chars = new antlr4.InputStream(input);
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js
index 8da71e3dd..0e53ac97c 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js
@@ -2,11 +2,11 @@ import {
first, flatten, flattenDepth, last, nth,
} from 'lodash-es';
import TSqlParserVisitor from '../../parsers/mssql/TSqlParserVisitor';
-import { COLUMN_CONSTRAINT_KIND, DATA_TYPE, TABLE_CONSTRAINT_KIND } from '../constants';
-import { getOriginalText } from '../helpers';
import {
Field, Index, Table, TableRecord,
} from '../AST';
+import { COLUMN_CONSTRAINT_KIND, DATA_TYPE, TABLE_CONSTRAINT_KIND } from '../constants';
+import { getOriginalText } from '../helpers';
const ADD_DESCRIPTION_FUNCTION_NAME = 'sp_addextendedproperty';
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mysql/MySQLASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mysql/MySQLASTGen.js
index 1952da0bb..d42a9a667 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mysql/MySQLASTGen.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mysql/MySQLASTGen.js
@@ -1,11 +1,11 @@
-import { last, flatten, flattenDepth } from 'lodash-es';
+import { flatten, flattenDepth, last } from 'lodash-es';
import MySQLParserVisitor from '../../parsers/mysql/MySqlParserVisitor';
import {
- Endpoint, Enum, Field, Index, Table, Ref,
+ Endpoint, Enum, Field, Index, Ref, Table,
TableRecord,
} from '../AST';
import {
- TABLE_CONSTRAINT_KIND, COLUMN_CONSTRAINT_KIND, DATA_TYPE, CONSTRAINT_TYPE,
+ COLUMN_CONSTRAINT_KIND, CONSTRAINT_TYPE, DATA_TYPE, TABLE_CONSTRAINT_KIND,
} from '../constants';
import { getOriginalText } from '../helpers';
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/oraclesql/OracleSQLASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/oraclesql/OracleSQLASTGen.js
index 3df912ecd..41eac7a83 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/oraclesql/OracleSQLASTGen.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/oraclesql/OracleSQLASTGen.js
@@ -1,19 +1,19 @@
-import { last, flatten } from 'lodash-es';
+import { flatten, last } from 'lodash-es';
+import { CompilerError } from '../../../error';
+import OracleSqlParserVisitor from '../../parsers/oraclesql/OracleSqlParserVisitor';
import {
+ Endpoint,
Field,
Index,
+ Ref,
Table,
TableRecord,
- Ref,
- Endpoint,
} from '../AST';
import {
- DATA_TYPE,
CONSTRAINT_TYPE,
+ DATA_TYPE,
} from '../constants';
import { getOriginalText } from '../helpers';
-import { CompilerError } from '../../../error';
-import OracleSqlParserVisitor from '../../parsers/oraclesql/OracleSqlParserVisitor';
// We cannot use TABLE_CONSTRAINT_KIND and COLUMN_CONSTRAINT_KIND from '../constants' as their values are indistinguishable from each other
// For example: TABLE_CONSTRAINT_KIND.UNIQUE === COLUMN_CONSTRAINT_KIND.UNIQUE
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/postgres/PostgresASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/postgres/PostgresASTGen.js
index 0809184e4..21d464437 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/postgres/PostgresASTGen.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/postgres/PostgresASTGen.js
@@ -1,10 +1,10 @@
-import { last, flatten, flattenDepth } from 'lodash-es';
+import { flatten, flattenDepth, last } from 'lodash-es';
import PostgreSQLParserVisitor from '../../parsers/postgresql/PostgreSQLParserVisitor';
import {
- Enum, Field, Index, TableRecord, Table,
+ Enum, Field, Index, Table, TableRecord,
} from '../AST';
import {
- TABLE_CONSTRAINT_KIND, CONSTRAINT_TYPE, COLUMN_CONSTRAINT_KIND, DATA_TYPE,
+ COLUMN_CONSTRAINT_KIND, CONSTRAINT_TYPE, DATA_TYPE, TABLE_CONSTRAINT_KIND,
} from '../constants';
import { getOriginalText } from '../helpers';
diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/snowflake/SnowflakeASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/snowflake/SnowflakeASTGen.js
index d3623a866..0825b1cff 100644
--- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/snowflake/SnowflakeASTGen.js
+++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/snowflake/SnowflakeASTGen.js
@@ -1,7 +1,7 @@
-import { isEmpty, flatten, flattenDepth } from 'lodash-es';
+import { flatten, flattenDepth, isEmpty } from 'lodash-es';
import SnowflakeParserVisitor from '../../parsers/snowflake/SnowflakeParserVisitor';
import { Enum, Field, Index, Table, TableRecord } from '../AST';
-import { TABLE_CONSTRAINT_KIND, COLUMN_CONSTRAINT_KIND, DATA_TYPE, CONSTRAINT_TYPE } from '../constants';
+import { COLUMN_CONSTRAINT_KIND, CONSTRAINT_TYPE, DATA_TYPE, TABLE_CONSTRAINT_KIND } from '../constants';
import { getOriginalText } from '../helpers';
const DEFAULT_SCHEMA = 'public';
diff --git a/packages/dbml-core/src/parse/Parser.js b/packages/dbml-core/src/parse/Parser.js
index 47dd3cfd7..372e4ec09 100644
--- a/packages/dbml-core/src/parse/Parser.js
+++ b/packages/dbml-core/src/parse/Parser.js
@@ -1,12 +1,12 @@
import { Compiler } from '@dbml/parse';
import Database from '../model_structure/database';
import { parse } from './ANTLR/ASTGeneration';
-import { CompilerError } from './error';
+import dbmlParser from './deprecated/dbmlParser.cjs';
+import mssqlParser from './deprecated/mssqlParser.cjs';
import mysqlParser from './deprecated/mysqlParser.cjs';
import postgresParser from './deprecated/postgresParser.cjs';
-import dbmlParser from './deprecated/dbmlParser.cjs';
import schemarbParser from './deprecated/schemarbParser.cjs';
-import mssqlParser from './deprecated/mssqlParser.cjs';
+import { CompilerError } from './error';
class Parser {
constructor (dbmlCompiler) {
diff --git a/packages/dbml-core/src/parse/databaseGenerator.js b/packages/dbml-core/src/parse/databaseGenerator.js
index 7162ec680..5b93a73db 100644
--- a/packages/dbml-core/src/parse/databaseGenerator.js
+++ b/packages/dbml-core/src/parse/databaseGenerator.js
@@ -1,11 +1,11 @@
import Database from '../model_structure/database';
import {
- Table,
+ Endpoint,
+ Enum,
Field,
Index,
Ref,
- Endpoint,
- Enum,
+ Table,
} from './ANTLR/ASTGeneration/AST';
const parseJSONToDatabase = (rawDatabase) => {
diff --git a/packages/dbml-core/src/parse/error.js b/packages/dbml-core/src/parse/error.js
deleted file mode 100644
index 65116f47a..000000000
--- a/packages/dbml-core/src/parse/error.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export class CompilerError {
- /**
- * @param {import("../../types").CompilerDiagnostic[]} diags
- */
- constructor (diags) {
- this.diags = diags;
- }
-
- static create (nestedDiags) {
- return new CompilerError(flattenDiag(nestedDiags));
- }
-
- map (callback) {
- return CompilerError.create(this.diags.map(callback));
- }
-}
-
-function flattenDiag (diag) {
- if (Array.isArray(diag)) return diag.flatMap(flattenDiag);
- if (diag instanceof CompilerError) return diag.diags;
- return [diag];
-}
diff --git a/packages/dbml-core/src/parse/error.ts b/packages/dbml-core/src/parse/error.ts
new file mode 100644
index 000000000..1398b929c
--- /dev/null
+++ b/packages/dbml-core/src/parse/error.ts
@@ -0,0 +1,44 @@
+export type WarningLevel = 'error' | 'warning' | 'info';
+
+export type ErrorCode = number;
+
+export interface EditorPosition {
+ line: number;
+ column: number;
+}
+
+export interface CompilerDiagnostic {
+ readonly message: string;
+ readonly filepath?: string;
+ readonly stack?: unknown;
+ readonly location: {
+ start: EditorPosition;
+ end?: EditorPosition;
+ };
+ readonly type?: WarningLevel;
+ readonly code?: ErrorCode;
+}
+
+type DiagInput = CompilerDiagnostic | CompilerError | DiagInput[];
+
+export class CompilerError {
+ diags: CompilerDiagnostic[];
+
+ constructor (diags: CompilerDiagnostic[]) {
+ this.diags = diags;
+ }
+
+ static create (nestedDiags: DiagInput): CompilerError {
+ return new CompilerError(flattenDiag(nestedDiags));
+ }
+
+ map (callback: (diag: CompilerDiagnostic) => CompilerDiagnostic): CompilerError {
+ return CompilerError.create(this.diags.map(callback));
+ }
+}
+
+function flattenDiag (diag: DiagInput): CompilerDiagnostic[] {
+ if (Array.isArray(diag)) return diag.flatMap(flattenDiag);
+ if (diag instanceof CompilerError) return diag.diags;
+ return [diag];
+}
diff --git a/packages/dbml-core/src/transform/index.js b/packages/dbml-core/src/transform/index.js
deleted file mode 100644
index 31265ce82..000000000
--- a/packages/dbml-core/src/transform/index.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Compiler } from '@dbml/parse';
-
-/**
- * Renames a table in DBML code using symbol table and token-based replacement.
- *
- * @param {string | { schema?: string; table: string }} oldName - The current table name
- * @param {string | { schema?: string; table: string }} newName - The new table name
- * @param {string} dbmlCode - The DBML code containing the table
- * @returns {string} The updated DBML code with the renamed table
- *
- * @example
- * // String format
- * renameTable('users', 'customers', dbmlCode);
- * renameTable('public.users', 'auth.customers', dbmlCode);
- *
- * @example
- * // Object format
- * renameTable({ table: 'users' }, { table: 'customers' }, dbmlCode);
- * renameTable({ schema: 'auth', table: 'users' }, { schema: 'auth', table: 'customers' }, dbmlCode);
- */
-export function renameTable (oldName, newName, dbmlCode) {
- const compiler = new Compiler();
- compiler.setSource(dbmlCode);
- return compiler.renameTable(oldName, newName);
-}
-
-/**
- * Synchronizes DiagramView blocks in DBML source code.
- *
- * @param {string} dbmlCode - The DBML source code
- * @param {import('@dbml/parse').DiagramViewSyncOperation[]} operations - Operations to apply
- * @param {import('@dbml/parse').DiagramViewBlock[]} [blocks] - Optional pre-parsed blocks
- * @returns {{ newDbml: string, edits: import('@dbml/parse').TextEdit[] }}
- */
-export function syncDiagramView (dbmlCode, operations, blocks) {
- const compiler = new Compiler();
- compiler.setSource(dbmlCode);
- return compiler.syncDiagramView(operations, blocks);
-}
-
-/**
- * Finds all DiagramView blocks in DBML source code.
- *
- * @param {string} dbmlCode - The DBML source code
- * @returns {import('@dbml/parse').DiagramViewBlock[]}
- */
-export function findDiagramViewBlocks (dbmlCode) {
- const compiler = new Compiler();
- compiler.setSource(dbmlCode);
- return compiler.findDiagramViewBlocks();
-}
diff --git a/packages/dbml-core/src/transform/index.ts b/packages/dbml-core/src/transform/index.ts
new file mode 100644
index 000000000..c5b42f138
--- /dev/null
+++ b/packages/dbml-core/src/transform/index.ts
@@ -0,0 +1,26 @@
+import { Compiler } from '@dbml/parse';
+import type { DiagramViewSyncOperation, DiagramViewBlock, TextEdit } from '@dbml/parse';
+
+type TableName = string | { schema?: string; table: string };
+
+export function renameTable (oldName: TableName, newName: TableName, dbmlCode: string): string {
+ const compiler = new Compiler();
+ compiler.setSource(dbmlCode);
+ return compiler.renameTable(oldName, newName);
+}
+
+export function syncDiagramView (
+ dbmlCode: string,
+ operations: DiagramViewSyncOperation[],
+ blocks?: DiagramViewBlock[],
+): { newDbml: string; edits: TextEdit[] } {
+ const compiler = new Compiler();
+ compiler.setSource(dbmlCode);
+ return compiler.syncDiagramView(operations, blocks);
+}
+
+export function findDiagramViewBlocks (dbmlCode: string): DiagramViewBlock[] {
+ const compiler = new Compiler();
+ compiler.setSource(dbmlCode);
+ return compiler.findDiagramViewBlocks();
+}
diff --git a/packages/dbml-core/types/index.d.ts b/packages/dbml-core/types/index.d.ts
index 83f0d131e..2642bbd39 100644
--- a/packages/dbml-core/types/index.d.ts
+++ b/packages/dbml-core/types/index.d.ts
@@ -20,9 +20,8 @@ export type { ExportFormat, ExportOptions } from './export/index';
export type { DbmlExporterOptions } from './export/DbmlExporter';
export type { JsonExporterOptions } from './export/JsonExporter';
export type { ImportFormat, ImportOptions } from './import/index';
-export { CompilerDiagnostic, CompilerError as CompilerDiagnostics, EditorPosition, ErrorCode, WarningLevel } from './parse/error';
+export { CompilerDiagnostic, CompilerError, CompilerError as CompilerDiagnostics, EditorPosition, ErrorCode, WarningLevel } from './parse/error';
-export * from './model_structure';
export {
isIntegerType,
isFloatType,
@@ -41,4 +40,38 @@ export {
addDoubleQuoteIfNeeded,
formatRecordValue,
} from '@dbml/parse';
-export type { DiagramView, DiagramViewSyncOperation, DiagramViewBlock, FilterConfig, TextEdit } from '@dbml/parse';
+export type {
+ Database,
+ MasterDatabase,
+ Table,
+ Note,
+ Column,
+ ColumnType,
+ Index,
+ Check,
+ InlineRef,
+ Ref,
+ RefEndpointPair,
+ RefEndpoint,
+ RelationCardinality,
+ Enum,
+ EnumField,
+ TableGroup,
+ TableGroupField,
+ Alias,
+ AliasKind,
+ TablePartial,
+ TablePartialInjection,
+ RecordValue,
+ RecordValueType,
+ TableRecord,
+ Project,
+ SchemaElement,
+ TokenPosition,
+ ElementRef,
+ DiagramView,
+ FilterConfig,
+ DiagramViewSyncOperation,
+ DiagramViewBlock,
+ TextEdit,
+} from '@dbml/parse';
diff --git a/packages/dbml-core/types/model_structure/database.d.ts b/packages/dbml-core/types/model_structure/database.d.ts
index 2c35808f7..3df19eea1 100644
--- a/packages/dbml-core/types/model_structure/database.d.ts
+++ b/packages/dbml-core/types/model_structure/database.d.ts
@@ -59,6 +59,7 @@ export interface RawDatabase {
records: RawTableRecord[];
tablePartials: TablePartial[];
}
+
declare class Database extends Element {
dbState: DbState;
hasDefaultSchema: boolean;
diff --git a/packages/dbml-core/types/parse/error.d.ts b/packages/dbml-core/types/parse/error.d.ts
index 149370380..71bda5d46 100644
--- a/packages/dbml-core/types/parse/error.d.ts
+++ b/packages/dbml-core/types/parse/error.d.ts
@@ -10,20 +10,21 @@ export declare interface EditorPosition {
column: number;
}
-export interface CompilerDiagnostic {
+export declare interface CompilerDiagnostic {
readonly message: Readonly;
readonly filepath?: Readonly;
readonly stack?: Readonly;
readonly location: {
start: Readonly,
- // in monaco, if an end position is not specified
- // it consumes the word containing the start position
end?: Readonly,
};
readonly type?: Readonly;
- readonly code?: Readonly; // the error code
+ readonly code?: Readonly;
}
-export interface CompilerError {
- readonly diags: CompilerDiagnostic[];
+export declare class CompilerError {
+ diags: CompilerDiagnostic[];
+ constructor(diags: CompilerDiagnostic[]);
+ static create(nestedDiags: unknown): CompilerError;
+ map(callback: (diag: CompilerDiagnostic) => CompilerDiagnostic): CompilerError;
}
diff --git a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts
index 9fb7fde87..e1eaa1886 100644
--- a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts
+++ b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect } from 'vitest';
-import { SyntaxNodeKind, ElementDeclarationNode, BlockExpressionNode } from '@/core/parser/nodes';
-import { TableSymbol, EnumSymbol, TableGroupSymbol, TablePartialSymbol, ColumnSymbol, EnumFieldSymbol, SchemaSymbol } from '@/core/analyzer/symbol/symbols';
-import { analyze } from '@tests/utils';
+import {
+ describe, expect,
+} from 'vitest';
+import {
+ SyntaxNodeKind, ElementDeclarationNode, BlockExpressionNode,
+} from '@/core/types/nodes';
+import {
+ TableSymbol, EnumSymbol, TableGroupSymbol, TablePartialSymbol, ColumnSymbol, EnumFieldSymbol, SchemaSymbol,
+} from '@/core/types/symbol/symbols';
+import {
+ analyze,
+} from '@tests/utils';
describe('[example] binder', () => {
describe('Table', () => {
@@ -93,7 +101,7 @@ describe('[example] binder', () => {
const errors = analyze(source).getErrors();
expect(errors).toHaveLength(1);
- expect(errors[0].diagnostic).toBe("Table name 'users' already exists in schema 'public'");
+ expect(errors[0].diagnostic).toBe("Table 'users' already exists in schema 'public'");
});
test('should allow same table name in different schemas', () => {
@@ -836,7 +844,12 @@ describe('[example] binder', () => {
expect(countryColumn.references.length).toBe(1);
// Verify all references have correct referee
- [idColumn, countryCodeColumn, merchantIdColumn, countryColumn].forEach((col) => {
+ [
+ idColumn,
+ countryCodeColumn,
+ merchantIdColumn,
+ countryColumn,
+ ].forEach((col) => {
expect(col.references[0].referee).toBe(col);
});
});
diff --git a/packages/dbml-parse/__tests__/examples/binder/records.test.ts b/packages/dbml-parse/__tests__/examples/binder/records.test.ts
index 3e109a538..58ed29851 100644
--- a/packages/dbml-parse/__tests__/examples/binder/records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/binder/records.test.ts
@@ -1,6 +1,12 @@
-import { describe, expect, test } from 'vitest';
-import { TableSymbol, EnumSymbol, ColumnSymbol, EnumFieldSymbol, SchemaSymbol } from '@/core/analyzer/symbol/symbols';
-import { analyze } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ TableSymbol, EnumSymbol, ColumnSymbol, EnumFieldSymbol, SchemaSymbol,
+} from '@/core/types/symbol/symbols';
+import {
+ analyze,
+} from '@tests/utils';
describe('[example] records binder', () => {
test('should bind records to table and columns', () => {
diff --git a/packages/dbml-parse/__tests__/examples/compiler/applyTextEdits.test.ts b/packages/dbml-parse/__tests__/examples/compiler/applyTextEdits.test.ts
index 33df75ca0..8b237ab33 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/applyTextEdits.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/applyTextEdits.test.ts
@@ -1,13 +1,21 @@
-import { describe, expect, test } from 'vitest';
+import {
+ describe, expect, test,
+} from 'vitest';
import Compiler from '@/compiler/index';
-import { applyTextEdits } from '@/compiler/queries/transform/applyTextEdits';
+import {
+ applyTextEdits,
+} from '@/compiler/queries/transform/applyTextEdits';
describe('[example] applyTextEdits', () => {
describe('standalone function', () => {
test('should apply a single edit', () => {
const source = 'Hello world';
const result = applyTextEdits(source, [
- { start: 0, end: 5, newText: 'Hi' },
+ {
+ start: 0,
+ end: 5,
+ newText: 'Hi',
+ },
]);
expect(result).toBe('Hi world');
});
@@ -15,8 +23,16 @@ describe('[example] applyTextEdits', () => {
test('should apply multiple non-overlapping edits', () => {
const source = 'Hello world';
const result = applyTextEdits(source, [
- { start: 0, end: 5, newText: 'Hi' },
- { start: 6, end: 11, newText: 'there' },
+ {
+ start: 0,
+ end: 5,
+ newText: 'Hi',
+ },
+ {
+ start: 6,
+ end: 11,
+ newText: 'there',
+ },
]);
expect(result).toBe('Hi there');
});
@@ -25,8 +41,16 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello world';
// Edits provided in reverse order
const result = applyTextEdits(source, [
- { start: 6, end: 11, newText: 'there' },
- { start: 0, end: 5, newText: 'Hi' },
+ {
+ start: 6,
+ end: 11,
+ newText: 'there',
+ },
+ {
+ start: 0,
+ end: 5,
+ newText: 'Hi',
+ },
]);
expect(result).toBe('Hi there');
});
@@ -34,7 +58,11 @@ describe('[example] applyTextEdits', () => {
test('should handle insertion (start === end)', () => {
const source = 'Hello world';
const result = applyTextEdits(source, [
- { start: 5, end: 5, newText: ' beautiful' },
+ {
+ start: 5,
+ end: 5,
+ newText: ' beautiful',
+ },
]);
expect(result).toBe('Hello beautiful world');
});
@@ -42,7 +70,11 @@ describe('[example] applyTextEdits', () => {
test('should handle deletion (empty newText)', () => {
const source = 'Hello beautiful world';
const result = applyTextEdits(source, [
- { start: 5, end: 15, newText: '' },
+ {
+ start: 5,
+ end: 15,
+ newText: '',
+ },
]);
expect(result).toBe('Hello world');
});
@@ -56,7 +88,11 @@ describe('[example] applyTextEdits', () => {
test('should handle empty source', () => {
const source = '';
const result = applyTextEdits(source, [
- { start: 0, end: 0, newText: 'Hello' },
+ {
+ start: 0,
+ end: 0,
+ newText: 'Hello',
+ },
]);
expect(result).toBe('Hello');
});
@@ -64,7 +100,11 @@ describe('[example] applyTextEdits', () => {
test('should handle multiline text', () => {
const source = 'Line 1\nLine 2\nLine 3';
const result = applyTextEdits(source, [
- { start: 7, end: 13, newText: 'Modified' },
+ {
+ start: 7,
+ end: 13,
+ newText: 'Modified',
+ },
]);
expect(result).toBe('Line 1\nModified\nLine 3');
});
@@ -72,9 +112,21 @@ describe('[example] applyTextEdits', () => {
test('should handle adjacent edits', () => {
const source = 'AABBCC';
const result = applyTextEdits(source, [
- { start: 0, end: 2, newText: 'X' },
- { start: 2, end: 4, newText: 'Y' },
- { start: 4, end: 6, newText: 'Z' },
+ {
+ start: 0,
+ end: 2,
+ newText: 'X',
+ },
+ {
+ start: 2,
+ end: 4,
+ newText: 'Y',
+ },
+ {
+ start: 4,
+ end: 6,
+ newText: 'Z',
+ },
]);
expect(result).toBe('XYZ');
});
@@ -82,7 +134,11 @@ describe('[example] applyTextEdits', () => {
test('should handle edit at end of string', () => {
const source = 'Hello';
const result = applyTextEdits(source, [
- { start: 5, end: 5, newText: ' world' },
+ {
+ start: 5,
+ end: 5,
+ newText: ' world',
+ },
]);
expect(result).toBe('Hello world');
});
@@ -90,7 +146,11 @@ describe('[example] applyTextEdits', () => {
test('should handle replacing entire string', () => {
const source = 'Hello world';
const result = applyTextEdits(source, [
- { start: 0, end: 11, newText: 'Goodbye' },
+ {
+ start: 0,
+ end: 11,
+ newText: 'Goodbye',
+ },
]);
expect(result).toBe('Goodbye');
});
@@ -101,8 +161,16 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello world';
// These edits overlap: [0,5) and [3,8)
const result = applyTextEdits(source, [
- { start: 0, end: 5, newText: 'Hi' },
- { start: 3, end: 8, newText: 'XXX' },
+ {
+ start: 0,
+ end: 5,
+ newText: 'Hi',
+ },
+ {
+ start: 3,
+ end: 8,
+ newText: 'XXX',
+ },
]);
// Note: Due to reverse-order application, results may be unexpected
// Sorted: [3,8] first, [0,5] second
@@ -116,7 +184,11 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello';
// end is beyond the string length
const result = applyTextEdits(source, [
- { start: 3, end: 100, newText: 'p' },
+ {
+ start: 3,
+ end: 100,
+ newText: 'p',
+ },
]);
// substring(100) returns '' when beyond bounds, so this works
expect(result).toBe('Help');
@@ -125,7 +197,11 @@ describe('[example] applyTextEdits', () => {
test('should handle edit with start equal to string length (append)', () => {
const source = 'Hello';
const result = applyTextEdits(source, [
- { start: 5, end: 5, newText: '!' },
+ {
+ start: 5,
+ end: 5,
+ newText: '!',
+ },
]);
expect(result).toBe('Hello!');
});
@@ -134,7 +210,11 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello world';
// This is technically invalid but documents current behavior
const result = applyTextEdits(source, [
- { start: 8, end: 3, newText: 'X' },
+ {
+ start: 8,
+ end: 3,
+ newText: 'X',
+ },
]);
// substring(0, 8) + 'X' + substring(3) = 'Hello woX' + 'lo world' = 'Hello woXlo world'
expect(result).toBe('Hello woXlo world');
@@ -144,7 +224,11 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello';
// substring handles negative indices by treating them as 0
const result = applyTextEdits(source, [
- { start: -5, end: 2, newText: 'X' },
+ {
+ start: -5,
+ end: 2,
+ newText: 'X',
+ },
]);
// substring(0, -5) returns '', substring(2) returns 'llo'
expect(result).toBe('Xllo');
@@ -154,7 +238,11 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello 世界!';
// JavaScript strings use UTF-16, so characters like 世 are single code units
const result = applyTextEdits(source, [
- { start: 6, end: 8, newText: 'World' },
+ {
+ start: 6,
+ end: 8,
+ newText: 'World',
+ },
]);
expect(result).toBe('Hello World!');
});
@@ -164,7 +252,11 @@ describe('[example] applyTextEdits', () => {
// 👋 is 2 code units (surrogate pair)
// Indices: H(0) i(1) (2) 👋(3-4) (5) t(6)...
const result = applyTextEdits(source, [
- { start: 3, end: 5, newText: '!' },
+ {
+ start: 3,
+ end: 5,
+ newText: '!',
+ },
]);
expect(result).toBe('Hi ! there');
});
@@ -173,8 +265,16 @@ describe('[example] applyTextEdits', () => {
const source = 'AABBCCDD';
// Second edit is completely contained within first edit's range
const result = applyTextEdits(source, [
- { start: 0, end: 8, newText: 'XXXX' },
- { start: 2, end: 4, newText: 'Y' },
+ {
+ start: 0,
+ end: 8,
+ newText: 'XXXX',
+ },
+ {
+ start: 2,
+ end: 4,
+ newText: 'Y',
+ },
]);
// Due to reverse order: [2,4] -> 'AAY' + 'CCDD' = 'AAYCCDD'
// Then [0,8] -> 'XXXX' (replaces everything)
@@ -185,7 +285,11 @@ describe('[example] applyTextEdits', () => {
const source = 'Hello';
// NaN in substring is treated as 0
const result = applyTextEdits(source, [
- { start: NaN, end: 2, newText: 'X' },
+ {
+ start: NaN,
+ end: 2,
+ newText: 'X',
+ },
]);
expect(result).toBe('Xllo');
});
@@ -193,7 +297,11 @@ describe('[example] applyTextEdits', () => {
test('should handle very large indices', () => {
const source = 'Hello';
const result = applyTextEdits(source, [
- { start: Number.MAX_SAFE_INTEGER, end: Number.MAX_SAFE_INTEGER, newText: 'X' },
+ {
+ start: Number.MAX_SAFE_INTEGER,
+ end: Number.MAX_SAFE_INTEGER,
+ newText: 'X',
+ },
]);
// Inserting at position way beyond string length
expect(result).toBe('HelloX');
@@ -202,7 +310,11 @@ describe('[example] applyTextEdits', () => {
test('should preserve line endings (LF)', () => {
const source = 'Line 1\nLine 2\nLine 3';
const result = applyTextEdits(source, [
- { start: 7, end: 13, newText: 'Modified' },
+ {
+ start: 7,
+ end: 13,
+ newText: 'Modified',
+ },
]);
expect(result).toBe('Line 1\nModified\nLine 3');
expect(result.split('\n').length).toBe(3);
@@ -211,7 +323,11 @@ describe('[example] applyTextEdits', () => {
test('should preserve line endings (CRLF)', () => {
const source = 'Line 1\r\nLine 2\r\nLine 3';
const result = applyTextEdits(source, [
- { start: 8, end: 14, newText: 'Modified' },
+ {
+ start: 8,
+ end: 14,
+ newText: 'Modified',
+ },
]);
expect(result).toBe('Line 1\r\nModified\r\nLine 3');
});
@@ -223,7 +339,11 @@ describe('[example] applyTextEdits', () => {
compiler.setSource('Table users { id int }');
const result = compiler.applyTextEdits([
- { start: 6, end: 11, newText: 'customers' },
+ {
+ start: 6,
+ end: 11,
+ newText: 'customers',
+ },
]);
expect(result).toBe('Table customers { id int }');
@@ -237,8 +357,16 @@ describe('[example] applyTextEdits', () => {
}`);
const result = compiler.applyTextEdits([
- { start: 6, end: 11, newText: 'customers' },
- { start: 30, end: 35, newText: 'name' },
+ {
+ start: 6,
+ end: 11,
+ newText: 'customers',
+ },
+ {
+ start: 30,
+ end: 35,
+ newText: 'name',
+ },
]);
expect(result).toContain('Table customers');
@@ -251,7 +379,11 @@ describe('[example] applyTextEdits', () => {
compiler.setSource(originalSource);
compiler.applyTextEdits([
- { start: 6, end: 11, newText: 'customers' },
+ {
+ start: 6,
+ end: 11,
+ newText: 'customers',
+ },
]);
// Original source should be unchanged
diff --git a/packages/dbml-parse/__tests__/examples/compiler/identifierUtils.test.ts b/packages/dbml-parse/__tests__/examples/compiler/identifierUtils.test.ts
index 685c8db11..2a5872e96 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/identifierUtils.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/identifierUtils.test.ts
@@ -1,4 +1,6 @@
-import { isValidIdentifier, addDoubleQuoteIfNeeded } from '@/compiler/index';
+import {
+ isValidIdentifier, addDoubleQuoteIfNeeded,
+} from '@/compiler/index';
describe('isValidIdentifier', () => {
test('should return true for simple alphanumeric identifier', () => {
diff --git a/packages/dbml-parse/__tests__/examples/compiler/renameTable.test.ts b/packages/dbml-parse/__tests__/examples/compiler/renameTable.test.ts
index 64bde60ae..02e34b176 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/renameTable.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/renameTable.test.ts
@@ -1,6 +1,10 @@
-import { describe, expect, test } from 'vitest';
+import {
+ describe, expect, test,
+} from 'vitest';
import Compiler from '@/compiler/index';
-import { TableNameInput } from '@/compiler/queries/transform';
+import {
+ TableNameInput,
+} from '@/compiler/queries/transform';
function renameTable (
oldName: TableNameInput,
@@ -106,7 +110,9 @@ Table users {
id int [pk]
}
`;
- const result = renameTable('users', { table: 'customers' }, input);
+ const result = renameTable('users', {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).not.toContain('Table users');
});
@@ -117,7 +123,9 @@ Table users {
id int [pk]
}
`;
- const result = renameTable({ table: 'users' }, 'customers', input);
+ const result = renameTable({
+ table: 'users',
+ }, 'customers', input);
expect(result).toContain('Table customers');
expect(result).not.toContain('Table users');
});
@@ -240,7 +248,11 @@ Table users {
name varchar
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).not.toContain('Table users');
});
@@ -259,7 +271,11 @@ Table posts {
Ref: posts.user_id > users.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).not.toContain('Table users');
expect(result).toContain('customers.id');
@@ -282,7 +298,11 @@ TableGroup group1 {
posts
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).not.toContain('Table users');
expect(result).toContain('customers');
@@ -301,7 +321,11 @@ Table posts {
user_id int [ref: > users.id]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('"users"');
expect(result).toContain('customers.id');
@@ -314,7 +338,11 @@ Table users {
name varchar
}
`;
- const result = renameTable({ table: 'non_existent_table' }, { table: 'new_name' }, input);
+ const result = renameTable({
+ table: 'non_existent_table',
+ }, {
+ table: 'new_name',
+ }, input);
expect(result.trim()).toBe(input.trim());
});
@@ -334,7 +362,11 @@ Table posts {
Ref: posts.author_id > users.id
Ref: posts.reviewer_id > users.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
const customerReferences = result.match(/customers\.id/g);
@@ -349,7 +381,11 @@ Table users {
Note: 'User table'
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).toContain("Note: 'User table'");
expect(result).toContain('[pk]');
@@ -368,7 +404,11 @@ Table users {
}
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
expect(result).toContain('email');
@@ -383,7 +423,11 @@ Table users {
Note: 'This is a users table'
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('"users"');
expect(result).toContain("Note: 'This is a users table'");
@@ -406,8 +450,14 @@ Table public.posts {
Ref: public.posts.user_id > public.users.id
`;
const result = renameTable(
- { schema: 'public', table: 'users' },
- { schema: 'public', table: 'customers' },
+ {
+ schema: 'public',
+ table: 'users',
+ },
+ {
+ schema: 'public',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('customers');
@@ -436,8 +486,14 @@ Ref: public.posts.author_id > auth.users.id
Ref: public.comments.author_id > auth.users.id
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -468,8 +524,14 @@ Ref: public.posts.auth_user_id > auth.users.id
Ref: public.posts.public_user_id > public.users.id
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -494,8 +556,14 @@ Table public.posts {
Ref: public.posts.user_id > public.users.id
`;
const result = renameTable(
- { schema: 'public', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'public',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -516,8 +584,13 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'ecommerce', table: 'users' },
- { table: 'users' },
+ {
+ schema: 'ecommerce',
+ table: 'users',
+ },
+ {
+ table: 'users',
+ },
input,
);
expect(result).toContain('Table users');
@@ -537,7 +610,11 @@ Table posts {
Ref: posts.id > users.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('"users"');
});
@@ -558,7 +635,11 @@ Table posts {
Ref: posts.user_id > U.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
expect(result).toContain('as U');
@@ -579,7 +660,11 @@ Table posts {
Ref: posts.user_id > U.id
`;
- const result = renameTable({ table: 'U' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'U',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('"users"');
expect(result).toContain('as U');
@@ -601,8 +686,14 @@ Table public.posts {
Ref: public.posts.author_id > AuthUser.id
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -628,7 +719,11 @@ TableGroup group1 {
P
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('"users"');
expect(result).toContain('posts');
@@ -650,7 +745,11 @@ Table posts {
Ref: posts.user_id > users.id
Ref: posts.reviewer_id > U.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
expect(result).toContain('as U');
@@ -679,8 +778,13 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'ecommerce', table: 'users' },
- { table: 'users' },
+ {
+ schema: 'ecommerce',
+ table: 'users',
+ },
+ {
+ table: 'users',
+ },
input,
);
expect(result.trim()).toBe(input.trim());
@@ -700,8 +804,13 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'ecommerce', table: 'users' },
- { table: 'users' },
+ {
+ schema: 'ecommerce',
+ table: 'users',
+ },
+ {
+ table: 'users',
+ },
input,
);
expect(result).toContain('Table users');
@@ -722,8 +831,14 @@ Table auth.customers {
}
`;
const result = renameTable(
- { schema: 'auth', table: 'customers' },
- { schema: 'public', table: 'users' },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
+ {
+ schema: 'public',
+ table: 'users',
+ },
input,
);
expect(result.trim()).toBe(input.trim());
@@ -741,8 +856,14 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'public', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'public',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('Table customers');
@@ -760,7 +881,11 @@ Table "customers" {
id int [pk]
}
`;
- const result = renameTable({ table: 'app users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'app users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('"app users"');
expect(result).toContain('"customers"');
expect(result.trim()).toBe(input.trim());
@@ -778,8 +903,14 @@ Table auth.customers {
}
`;
const result = renameTable(
- { schema: 'ecommerce', table: 'users' },
- { schema: 'auth', table: 'users' },
+ {
+ schema: 'ecommerce',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
input,
);
expect(result).toContain('auth.users');
@@ -797,8 +928,13 @@ Table auth.admins {
}
`;
const result = renameTable(
- { schema: 'auth', table: 'admins' },
- { table: 'users' },
+ {
+ schema: 'auth',
+ table: 'admins',
+ },
+ {
+ table: 'users',
+ },
input,
);
expect(result).toContain('Table users');
@@ -812,7 +948,11 @@ Table users {
id int [pk]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'users' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'users',
+ }, input);
expect(result).toContain('Table users');
});
});
@@ -820,7 +960,11 @@ Table users {
describe('quoting', () => {
test('should preserve quotes when original table uses quotes', () => {
const input = 'Table "users" { id int }';
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('"customers"');
expect(result).not.toContain('users');
});
@@ -830,7 +974,11 @@ Table users {
Table users { id int }
Table posts { user_id int [ref: > users.id] }
`;
- const result = renameTable({ table: 'users' }, { table: 'app users' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'app users',
+ }, input);
expect(result).toContain('"app users"');
expect(result).toContain('"app users".id');
expect(result).not.toContain('Table users');
@@ -838,28 +986,46 @@ Table posts { user_id int [ref: > users.id] }
test('should add quotes when new name contains hyphens', () => {
const input = 'Table users { id int }';
- const result = renameTable({ table: 'users' }, { table: 'app-users' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'app-users',
+ }, input);
expect(result).toContain('"app-users"');
});
test('should not add quotes for valid identifiers', () => {
const input = 'Table users { id int }';
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).not.toContain('"customers"');
});
test('should handle special characters in quoted names', () => {
const input = 'Table users { id int }';
- const result = renameTable({ table: 'users' }, { table: 'user@domain' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'user@domain',
+ }, input);
expect(result).toContain('"user@domain"');
});
test('should preserve quotes when schema-qualified table uses quotes', () => {
const input = 'Table "schema1"."users" { id int }';
const result = renameTable(
- { schema: 'schema1', table: 'users' },
- { schema: 'schema1', table: 'customers' },
+ {
+ schema: 'schema1',
+ table: 'users',
+ },
+ {
+ schema: 'schema1',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('"customers"');
@@ -868,7 +1034,11 @@ Table posts { user_id int [ref: > users.id] }
test('should add quotes only to table name when needed, not schema', () => {
const input = 'Table users { id int }';
- const result = renameTable({ table: 'users' }, { table: 'my users' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'my users',
+ }, input);
expect(result).toContain('Table "my users"');
expect(result).not.toContain('public');
});
@@ -888,7 +1058,11 @@ TableGroup group1 {
posts
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table "customers"');
expect(result).toContain('"customers".id');
expect(result).toContain('"customers"');
@@ -914,7 +1088,11 @@ Table posts {
title varchar
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
const customerRefs = result.match(/customers\.id/g);
@@ -938,8 +1116,14 @@ Table public.posts {
}
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -963,7 +1147,11 @@ Table posts {
user_id int [ref: > users.id]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('created_at');
expect(result).toContain('updated_at');
expect(result).toContain('customers');
@@ -990,7 +1178,11 @@ Table orders {
product_id int [ref: > products.id]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).not.toContain('users');
expect(result).toContain('products');
@@ -1015,7 +1207,11 @@ Table users_extended {
user_id int [ref: > users.id]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
expect(result).toContain('Table customers');
expect(result).toContain('Table users_extended');
@@ -1057,8 +1253,14 @@ TableGroup social {
}
`;
const result = renameTable(
- { schema: 'auth', table: 'users' },
- { schema: 'auth', table: 'customers' },
+ {
+ schema: 'auth',
+ table: 'users',
+ },
+ {
+ schema: 'auth',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('auth.customers');
@@ -1071,13 +1273,21 @@ TableGroup social {
describe('malformed and edge cases', () => {
test('should handle empty input', () => {
- const result = renameTable({ table: 'users' }, { table: 'customers' }, '');
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, '');
expect(result).toBe('');
});
test('should handle whitespace-only input', () => {
const input = ' \n\t\n ';
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toBe(input);
});
@@ -1086,7 +1296,11 @@ TableGroup social {
// This is a comment
// Another comment
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toBe(input);
});
@@ -1095,7 +1309,11 @@ TableGroup social {
Table users {
id int [pk]
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
});
@@ -1106,7 +1324,11 @@ Table users {
name varchar
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
});
@@ -1120,7 +1342,11 @@ Table posts {
user_id int [ref: > ]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
});
@@ -1129,7 +1355,11 @@ Table posts {
Table users {
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
});
@@ -1143,7 +1373,11 @@ Table posts {
user_id int [ref: > "用户".id]
}
`;
- const result = renameTable({ table: '用户' }, { table: '客户' }, input);
+ const result = renameTable({
+ table: '用户',
+ }, {
+ table: '客户',
+ }, input);
expect(result).toContain('"客户"');
expect(result).not.toContain('"用户"');
});
@@ -1154,7 +1388,11 @@ Table users2024 {
id int [pk]
}
`;
- const result = renameTable({ table: 'users2024' }, { table: 'customers2024' }, input);
+ const result = renameTable({
+ table: 'users2024',
+ }, {
+ table: 'customers2024',
+ }, input);
expect(result).toContain('customers2024');
expect(result).not.toContain('users2024');
});
@@ -1166,7 +1404,11 @@ Table employees {
manager_id int [ref: > employees.id]
}
`;
- const result = renameTable({ table: 'employees' }, { table: 'staff' }, input);
+ const result = renameTable({
+ table: 'employees',
+ }, {
+ table: 'staff',
+ }, input);
expect(result).toContain('Table staff');
expect(result).toContain('staff.id');
expect(result).not.toContain('employees');
@@ -1180,7 +1422,11 @@ Table categories {
root_id int [ref: > categories.id]
}
`;
- const result = renameTable({ table: 'categories' }, { table: 'groups' }, input);
+ const result = renameTable({
+ table: 'categories',
+ }, {
+ table: 'groups',
+ }, input);
expect(result).toContain('Table groups');
const groupRefs = result.match(/groups\.id/g);
expect(groupRefs).toHaveLength(2);
@@ -1190,7 +1436,11 @@ Table categories {
const longName = 'a'.repeat(100);
const newLongName = 'b'.repeat(100);
const input = `Table ${longName} { id int [pk] }`;
- const result = renameTable({ table: longName }, { table: newLongName }, input);
+ const result = renameTable({
+ table: longName,
+ }, {
+ table: newLongName,
+ }, input);
expect(result).toContain(newLongName);
expect(result).not.toContain(longName);
});
@@ -1211,7 +1461,11 @@ Table user_profiles {
user_id int [ref: > user.id]
}
`;
- const result = renameTable({ table: 'user' }, { table: 'account' }, input);
+ const result = renameTable({
+ table: 'user',
+ }, {
+ table: 'account',
+ }, input);
expect(result).toContain('Table account');
expect(result).toContain('Table users');
expect(result).toContain('Table user_profiles');
@@ -1224,7 +1478,11 @@ Table users {
id int [pk]
}
`;
- const result = renameTable({ table: '' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: '',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toBe(input);
});
@@ -1235,7 +1493,11 @@ Table users {
}
`;
// Empty new name is not a valid identifier, so it gets quoted
- const result = renameTable({ table: 'users' }, { table: '' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: '',
+ }, input);
expect(result).toContain('Table ""');
});
@@ -1257,7 +1519,11 @@ Table posts {
status status
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).toContain('customers.id');
expect(result).toContain('Enum status');
@@ -1279,7 +1545,11 @@ Table user_permission {
user_id int [ref: > user.id]
}
`;
- const result = renameTable({ table: 'user' }, { table: 'account' }, input);
+ const result = renameTable({
+ table: 'user',
+ }, {
+ table: 'account',
+ }, input);
expect(result).toContain('Table account {');
expect(result).toContain('Table user_role {');
expect(result).toContain('Table user_permission {');
@@ -1297,7 +1567,11 @@ Table users {
id int [pk]
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Project my_project');
expect(result).toContain('Table customers');
});
@@ -1319,7 +1593,11 @@ Table posts {
}
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
});
@@ -1334,7 +1612,11 @@ Table users {
'''
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).toContain('about the users table');
});
@@ -1344,7 +1626,11 @@ Table users {
id int [pk]
name varchar(255)
}`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain(' id int [pk]');
expect(result).toContain(' name varchar(255)');
});
@@ -1359,7 +1645,11 @@ Table posts {
user_id int [ref: > "my.users".id]
}
`;
- const result = renameTable({ table: 'my.users' }, { table: 'my.customers' }, input);
+ const result = renameTable({
+ table: 'my.users',
+ }, {
+ table: 'my.customers',
+ }, input);
expect(result).toContain('"my.customers"');
expect(result).not.toContain('"my.users"');
});
@@ -1375,8 +1665,14 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'my.schema', table: 'my.users' },
- { schema: 'my.schema', table: 'my.customers' },
+ {
+ schema: 'my.schema',
+ table: 'my.users',
+ },
+ {
+ schema: 'my.schema',
+ table: 'my.customers',
+ },
input,
);
expect(result).toContain('"my.customers"');
@@ -1390,8 +1686,12 @@ Table "my.special.users" {
}
`;
const result = renameTable(
- { table: 'my.special.users' },
- { table: 'my.special.customers' },
+ {
+ table: 'my.special.users',
+ },
+ {
+ table: 'my.special.customers',
+ },
input,
);
expect(result).toContain('"my.special.customers"');
@@ -1404,7 +1704,11 @@ Table users {
id int [pk]
}
`;
- const result = renameTable({ table: 'users' }, { table: '2024_users' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: '2024_users',
+ }, input);
expect(result).toContain('"2024_users"');
});
@@ -1418,7 +1722,11 @@ Table posts {
user_id int [ref: > "2024_users".id]
}
`;
- const result = renameTable({ table: '2024_users' }, { table: 'users' }, input);
+ const result = renameTable({
+ table: '2024_users',
+ }, {
+ table: 'users',
+ }, input);
expect(result).toContain('Table "users"');
expect(result).toContain('"users".id');
});
@@ -1430,8 +1738,14 @@ Table "my-schema".users {
}
`;
const result = renameTable(
- { schema: 'my-schema', table: 'users' },
- { schema: 'my-schema', table: 'customers' },
+ {
+ schema: 'my-schema',
+ table: 'users',
+ },
+ {
+ schema: 'my-schema',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('customers');
@@ -1449,8 +1763,14 @@ Table posts {
}
`;
const result = renameTable(
- { schema: 'public', table: 'users' },
- { schema: 'public', table: 'customers' },
+ {
+ schema: 'public',
+ table: 'users',
+ },
+ {
+ schema: 'public',
+ table: 'customers',
+ },
input,
);
expect(result).toContain('customers');
@@ -1467,13 +1787,21 @@ Table posts {
}
`;
// First rename
- let result = renameTable({ table: 'users' }, { table: 'accounts' }, input);
+ let result = renameTable({
+ table: 'users',
+ }, {
+ table: 'accounts',
+ }, input);
expect(result).toContain('Table accounts');
expect(result).toContain('accounts.id');
// Simulate second rename by using the result
input = result;
- result = renameTable({ table: 'accounts' }, { table: 'customers' }, input);
+ result = renameTable({
+ table: 'accounts',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).toContain('customers.id');
});
@@ -1492,7 +1820,11 @@ Table posts {
Ref: posts.user_id > users.id
Ref many_to_many: posts.id <> users.id
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('customers');
const customerRefs = result.match(/customers\.id/g);
expect(customerRefs?.length).toBeGreaterThanOrEqual(2);
@@ -1505,7 +1837,11 @@ Table users {
Note: 'This users table stores user data'
}
`;
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table customers');
expect(result).toContain('This users table stores');
});
@@ -1520,7 +1856,11 @@ Table posts {
select_id int [ref: > "select".id]
}
`;
- const result = renameTable({ table: 'select' }, { table: 'chosen' }, input);
+ const result = renameTable({
+ table: 'select',
+ }, {
+ table: 'chosen',
+ }, input);
expect(result).toContain('"chosen"');
expect(result).not.toContain('"select"');
});
@@ -1535,7 +1875,11 @@ Table posts {
user_id int [ref: > Users.id]
}
`;
- const result = renameTable({ table: 'Users' }, { table: 'Customers' }, input);
+ const result = renameTable({
+ table: 'Users',
+ }, {
+ table: 'Customers',
+ }, input);
expect(result).toContain('Table Customers');
expect(result).toContain('Customers.id');
expect(result).not.toContain('Table Users');
@@ -1548,7 +1892,11 @@ Table Users {
}
`;
// Trying to rename 'users' (lowercase) when table is 'Users' (capitalized)
- const result = renameTable({ table: 'users' }, { table: 'customers' }, input);
+ const result = renameTable({
+ table: 'users',
+ }, {
+ table: 'customers',
+ }, input);
expect(result).toContain('Table Users');
expect(result).not.toContain('customers');
});
diff --git a/packages/dbml-parse/__tests__/examples/compiler/splitQualifiedIdentifier.test.ts b/packages/dbml-parse/__tests__/examples/compiler/splitQualifiedIdentifier.test.ts
index 0e09e990d..5461631fb 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/splitQualifiedIdentifier.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/splitQualifiedIdentifier.test.ts
@@ -1,4 +1,6 @@
-import { splitQualifiedIdentifier } from '@/compiler/queries/utils';
+import {
+ splitQualifiedIdentifier,
+} from '@/compiler/queries/utils';
describe('splitQualifiedIdentifier', () => {
it('should split simple unquoted identifiers', () => {
diff --git a/packages/dbml-parse/__tests__/examples/compiler/stringUtils.test.ts b/packages/dbml-parse/__tests__/examples/compiler/stringUtils.test.ts
index 5192f61ef..dab1082b8 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/stringUtils.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/stringUtils.test.ts
@@ -1,4 +1,6 @@
-import { unescapeString, escapeString } from '@/compiler/queries/utils';
+import {
+ unescapeString, escapeString,
+} from '@/compiler/queries/utils';
describe('unescapeString', () => {
it('should handle escaped quotes', () => {
diff --git a/packages/dbml-parse/__tests__/examples/compiler/syncDiagramView.test.ts b/packages/dbml-parse/__tests__/examples/compiler/syncDiagramView.test.ts
index fd9899831..09acaecb3 100644
--- a/packages/dbml-parse/__tests__/examples/compiler/syncDiagramView.test.ts
+++ b/packages/dbml-parse/__tests__/examples/compiler/syncDiagramView.test.ts
@@ -1,9 +1,18 @@
-import { describe, expect, it } from 'vitest';
-import { syncDiagramView } from '@/compiler/queries/transform/syncDiagramView';
+import {
+ describe, expect, it,
+} from 'vitest';
+import {
+ syncDiagramView,
+} from '@/compiler/queries/transform/syncDiagramView';
import Compiler from '@/compiler/index';
import Lexer from '@/core/lexer/lexer';
+import {
+ DEFAULT_ENTRY,
+} from '@/constants';
import Parser from '@/core/parser/parser';
-import { SyntaxNodeIdGenerator } from '@/core/parser/nodes';
+import {
+ SyntaxNodeIdGenerator,
+} from '@/core/types/nodes';
// update operation
@@ -13,8 +22,14 @@ describe('syncDiagramView - update', () => {
DiagramView my_view {
Tables { users }
}`;
- const { newDbml } = syncDiagramView(dbml, [
- { operation: 'update', name: 'my_view', newName: 'renamed_view' },
+ const {
+ newDbml,
+ } = syncDiagramView(dbml, [
+ {
+ operation: 'update',
+ name: 'my_view',
+ newName: 'renamed_view',
+ },
]);
expect(newDbml).toContain('DiagramView renamed_view');
expect(newDbml).not.toContain('DiagramView my_view');
@@ -34,8 +49,13 @@ DiagramView my_view {
Tables { users }
}
`;
- const { newDbml } = syncDiagramView(dbml, [
- { operation: 'delete', name: 'my_view' },
+ const {
+ newDbml,
+ } = syncDiagramView(dbml, [
+ {
+ operation: 'delete',
+ name: 'my_view',
+ },
]);
expect(newDbml).not.toContain('DiagramView my_view');
expect(newDbml).toContain('Table users');
@@ -47,8 +67,8 @@ DiagramView my_view {
describe('Parser - * wildcard in DiagramView', () => {
it('parses DiagramView with { * } without errors', () => {
const source = 'DiagramView v { * }';
- const tokens = new Lexer(source).lex().getValue();
- const result = new Parser(source, tokens, new SyntaxNodeIdGenerator()).parse();
+ const tokens = new Lexer(source, DEFAULT_ENTRY).lex().getValue();
+ const result = new Parser(source, tokens, new SyntaxNodeIdGenerator(), DEFAULT_ENTRY).parse();
expect(result.getErrors()).toHaveLength(0);
});
@@ -60,8 +80,8 @@ DiagramView v {
}
}
`;
- const tokens = new Lexer(source).lex().getValue();
- const result = new Parser(source, tokens, new SyntaxNodeIdGenerator()).parse();
+ const tokens = new Lexer(source, DEFAULT_ENTRY).lex().getValue();
+ const result = new Parser(source, tokens, new SyntaxNodeIdGenerator(), DEFAULT_ENTRY).parse();
expect(result.getErrors()).toHaveLength(0);
});
@@ -84,22 +104,36 @@ DiagramView "New View" {
describe('syncDiagramView - name quoting', () => {
it('quotes names containing spaces', () => {
- const { newDbml } = syncDiagramView('', [
+ const {
+ newDbml,
+ } = syncDiagramView('', [
{
operation: 'create',
name: 'My View',
- visibleEntities: { tables: null, stickyNotes: null, tableGroups: null, schemas: null },
+ visibleEntities: {
+ tables: null,
+ stickyNotes: null,
+ tableGroups: null,
+ schemas: null,
+ },
},
]);
expect(newDbml).toContain('DiagramView "My View"');
});
it('does not quote simple identifier names', () => {
- const { newDbml } = syncDiagramView('', [
+ const {
+ newDbml,
+ } = syncDiagramView('', [
{
operation: 'create',
name: 'my_view',
- visibleEntities: { tables: null, stickyNotes: null, tableGroups: null, schemas: null },
+ visibleEntities: {
+ tables: null,
+ stickyNotes: null,
+ tableGroups: null,
+ schemas: null,
+ },
},
]);
expect(newDbml).toContain('DiagramView my_view');
@@ -107,11 +141,18 @@ describe('syncDiagramView - name quoting', () => {
});
it('escapes internal double quotes in names', () => {
- const { newDbml } = syncDiagramView('', [
+ const {
+ newDbml,
+ } = syncDiagramView('', [
{
operation: 'create',
name: 'My "Special" View',
- visibleEntities: { tables: null, stickyNotes: null, tableGroups: null, schemas: null },
+ visibleEntities: {
+ tables: null,
+ stickyNotes: null,
+ tableGroups: null,
+ schemas: null,
+ },
},
]);
expect(newDbml).toContain('DiagramView "My \\"Special\\" View"');
@@ -123,8 +164,14 @@ DiagramView "My View" {
Tables { users }
}
`;
- const { newDbml } = syncDiagramView(dbml, [
- { operation: 'update', name: 'My View', newName: 'New Name' },
+ const {
+ newDbml,
+ } = syncDiagramView(dbml, [
+ {
+ operation: 'update',
+ name: 'My View',
+ newName: 'New Name',
+ },
]);
expect(newDbml).toContain('DiagramView "New Name"');
expect(newDbml).not.toContain('"My View"');
@@ -140,12 +187,19 @@ DiagramView my_view {
Tables { users }
}
`;
- const { newDbml } = syncDiagramView(dbml, [
+ const {
+ newDbml,
+ } = syncDiagramView(dbml, [
{
operation: 'create',
name: 'my_view',
visibleEntities: {
- tables: [{ name: 'posts', schemaName: 'public' }],
+ tables: [
+ {
+ name: 'posts',
+ schemaName: 'public',
+ },
+ ],
stickyNotes: null,
tableGroups: null,
schemas: null,
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts
index 5aac3639a..dd6bf017e 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts
@@ -1,6 +1,9 @@
-import { describe, expect, test } from 'vitest';
-import { CompileErrorCode } from '@/index';
-import { interpret, analyze } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret, analyze,
+} from '@tests/utils';
describe('[example] interpreter', () => {
describe('table interpretation', () => {
@@ -444,17 +447,75 @@ describe('[example] interpreter', () => {
describe('detailed field type verification', () => {
test('should interpret simple types with exact matching', () => {
const testCases = [
- { type: 'int', expected: { type_name: 'int', args: null, schemaName: null } },
- { type: 'integer', expected: { type_name: 'integer', args: null, schemaName: null } },
- { type: 'bigint', expected: { type_name: 'bigint', args: null, schemaName: null } },
- { type: 'varchar', expected: { type_name: 'varchar', args: null, schemaName: null } },
- { type: 'text', expected: { type_name: 'text', args: null, schemaName: null } },
- { type: 'boolean', expected: { type_name: 'boolean', args: null, schemaName: null } },
- { type: 'timestamp', expected: { type_name: 'timestamp', args: null, schemaName: null } },
- { type: 'uuid', expected: { type_name: 'uuid', args: null, schemaName: null } },
+ {
+ type: 'int',
+ expected: {
+ type_name: 'int',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'integer',
+ expected: {
+ type_name: 'integer',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'bigint',
+ expected: {
+ type_name: 'bigint',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'varchar',
+ expected: {
+ type_name: 'varchar',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'text',
+ expected: {
+ type_name: 'text',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'boolean',
+ expected: {
+ type_name: 'boolean',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'timestamp',
+ expected: {
+ type_name: 'timestamp',
+ args: null,
+ schemaName: null,
+ },
+ },
+ {
+ type: 'uuid',
+ expected: {
+ type_name: 'uuid',
+ args: null,
+ schemaName: null,
+ },
+ },
];
- testCases.forEach(({ type, expected }) => {
+ testCases.forEach(({
+ type, expected,
+ }) => {
const db = interpret(`Table t { col ${type} }`).getValue()!;
const field = db.tables[0].fields[0];
expect(field.type.type_name).toBe(expected.type_name);
@@ -466,13 +527,43 @@ describe('[example] interpreter', () => {
test('should interpret parameterized types with exact matching', () => {
// For parameterized types, type_name includes the full type string with args
const testCases = [
- { type: 'varchar(255)', expected: { type_name: 'varchar(255)', args: '255', schemaName: null } },
- { type: 'char(10)', expected: { type_name: 'char(10)', args: '10', schemaName: null } },
- { type: 'decimal(10,2)', expected: { type_name: 'decimal(10,2)', args: '10,2', schemaName: null } },
- { type: 'numeric(5)', expected: { type_name: 'numeric(5)', args: '5', schemaName: null } },
+ {
+ type: 'varchar(255)',
+ expected: {
+ type_name: 'varchar(255)',
+ args: '255',
+ schemaName: null,
+ },
+ },
+ {
+ type: 'char(10)',
+ expected: {
+ type_name: 'char(10)',
+ args: '10',
+ schemaName: null,
+ },
+ },
+ {
+ type: 'decimal(10,2)',
+ expected: {
+ type_name: 'decimal(10,2)',
+ args: '10,2',
+ schemaName: null,
+ },
+ },
+ {
+ type: 'numeric(5)',
+ expected: {
+ type_name: 'numeric(5)',
+ args: '5',
+ schemaName: null,
+ },
+ },
];
- testCases.forEach(({ type, expected }) => {
+ testCases.forEach(({
+ type, expected,
+ }) => {
const db = interpret(`Table t { col ${type} }`).getValue()!;
const field = db.tables[0].fields[0];
expect(field.type.type_name).toBe(expected.type_name);
@@ -629,14 +720,31 @@ describe('[example] interpreter', () => {
test('should interpret ref delete actions', () => {
const testCases = [
- { action: 'cascade', expected: 'cascade' },
- { action: 'no action', expected: 'no action' },
- { action: 'set null', expected: 'set null' },
- { action: 'set default', expected: 'set default' },
- { action: 'restrict', expected: 'restrict' },
+ {
+ action: 'cascade',
+ expected: 'cascade',
+ },
+ {
+ action: 'no action',
+ expected: 'no action',
+ },
+ {
+ action: 'set null',
+ expected: 'set null',
+ },
+ {
+ action: 'set default',
+ expected: 'set default',
+ },
+ {
+ action: 'restrict',
+ expected: 'restrict',
+ },
];
- testCases.forEach(({ action, expected }) => {
+ testCases.forEach(({
+ action, expected,
+ }) => {
const source = `
Table a { id int }
Table b { a_id int }
@@ -649,14 +757,31 @@ describe('[example] interpreter', () => {
test('should interpret ref update actions', () => {
const testCases = [
- { action: 'cascade', expected: 'cascade' },
- { action: 'no action', expected: 'no action' },
- { action: 'set null', expected: 'set null' },
- { action: 'set default', expected: 'set default' },
- { action: 'restrict', expected: 'restrict' },
+ {
+ action: 'cascade',
+ expected: 'cascade',
+ },
+ {
+ action: 'no action',
+ expected: 'no action',
+ },
+ {
+ action: 'set null',
+ expected: 'set null',
+ },
+ {
+ action: 'set default',
+ expected: 'set default',
+ },
+ {
+ action: 'restrict',
+ expected: 'restrict',
+ },
];
- testCases.forEach(({ action, expected }) => {
+ testCases.forEach(({
+ action, expected,
+ }) => {
const source = `
Table a { id int }
Table b { a_id int }
@@ -997,7 +1122,12 @@ describe('[example] interpreter', () => {
expect(db.diagramViews).toHaveLength(1);
const ve = db.diagramViews[0].visibleEntities;
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
expect(ve.tableGroups).toEqual([]);
expect(ve.schemas).toEqual([]);
expect(ve.stickyNotes).toBeNull();
@@ -1029,7 +1159,12 @@ describe('[example] interpreter', () => {
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
expect(ve.tableGroups).toEqual([]);
expect(ve.schemas).toEqual([]);
expect(ve.stickyNotes).toEqual([]);
@@ -1108,8 +1243,12 @@ describe('[example] interpreter', () => {
const ve = db.diagramViews[0].visibleEntities;
expect(ve.tableGroups).toEqual([
- { name: 'auth_tables' },
- { name: 'content_tables' },
+ {
+ name: 'auth_tables',
+ },
+ {
+ name: 'content_tables',
+ },
]);
// Trinity rule still applies for tables/schemas (promoted to [])
expect(ve.tables).toEqual([]);
@@ -1146,7 +1285,12 @@ describe('[example] interpreter', () => {
const ve = db.diagramViews[0].visibleEntities;
// tableGroups [] comes from Trinity promotion, not explicit wildcard — should stay []
expect(ve.tableGroups).toEqual([]);
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
expect(ve.schemas).toEqual([]);
});
@@ -1164,7 +1308,12 @@ describe('[example] interpreter', () => {
const ve = db.diagramViews[0].visibleEntities;
// Tables is explicitly set, so TableGroups wildcard stays as []
expect(ve.tableGroups).toEqual([]);
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
expect(ve.schemas).toEqual([]);
});
@@ -1192,7 +1341,12 @@ describe('[example] interpreter', () => {
`;
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
});
test('should resolve schema-qualified table alias', () => {
@@ -1204,7 +1358,12 @@ describe('[example] interpreter', () => {
`;
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
- expect(ve.tables).toEqual([{ name: 'articles', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'articles',
+ schemaName: 'public',
+ },
+ ]);
});
test('should keep real name when no alias is used', () => {
@@ -1216,7 +1375,12 @@ describe('[example] interpreter', () => {
`;
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
- expect(ve.tables).toEqual([{ name: 'users', schemaName: 'public' }]);
+ expect(ve.tables).toEqual([
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ ]);
});
test('should resolve multiple aliases in same block', () => {
@@ -1233,8 +1397,14 @@ describe('[example] interpreter', () => {
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
expect(ve.tables).toEqual([
- { name: 'users', schemaName: 'public' },
- { name: 'posts', schemaName: 'public' },
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ {
+ name: 'posts',
+ schemaName: 'public',
+ },
]);
});
@@ -1252,8 +1422,14 @@ describe('[example] interpreter', () => {
const db = interpret(source).getValue()!;
const ve = db.diagramViews[0].visibleEntities;
expect(ve.tables).toEqual([
- { name: 'users', schemaName: 'public' },
- { name: 'posts', schemaName: 'public' },
+ {
+ name: 'users',
+ schemaName: 'public',
+ },
+ {
+ name: 'posts',
+ schemaName: 'public',
+ },
]);
});
});
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/data.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/data.test.ts
index f52d058b2..19d8f042c 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/data.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/data.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect, test } from 'vitest';
-import { CompileErrorCode } from '@/index';
-import { interpret } from '@tests/utils';
-import { DateTime } from 'luxon';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ CompileErrorCode,
+} from '@/index';
+import {
+ interpret,
+} from '@tests/utils';
+import {
+ DateTime,
+} from 'luxon';
describe('[example - record] data type interpretation', () => {
test('should interpret integer values correctly', () => {
@@ -23,11 +31,26 @@ describe('[example - record] data type interpretation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'integer', value: 42 });
- expect(db.records[0].values[0][2]).toEqual({ type: 'integer', value: -100 });
- expect(db.records[0].values[0][3]).toEqual({ type: 'integer', value: 9999999999 });
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 0 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'integer',
+ value: 42,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'integer',
+ value: -100,
+ });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'integer',
+ value: 9999999999,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 0,
+ });
});
test('should interpret float and decimal values correctly', () => {
@@ -49,12 +72,30 @@ describe('[example - record] data type interpretation', () => {
const db = result.getValue()!;
// Note: float/numeric/decimal types are normalized to 'real'
- expect(db.records[0].values[0][0]).toEqual({ type: 'real', value: 99.99 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'real', value: 3.14159 });
- expect(db.records[0].values[0][2]).toEqual({ type: 'real', value: 0.001 });
- expect(db.records[0].values[1][0]).toEqual({ type: 'real', value: 50.5 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'real', value: 0.5 });
- expect(db.records[0].values[1][2]).toEqual({ type: 'real', value: 100 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'real',
+ value: 99.99,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'real',
+ value: 3.14159,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'real',
+ value: 0.001,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'real',
+ value: 50.5,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'real',
+ value: 0.5,
+ });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'real',
+ value: 100,
+ });
});
test('should interpret boolean values correctly', () => {
@@ -75,10 +116,22 @@ describe('[example - record] data type interpretation', () => {
const db = result.getValue()!;
// Note: boolean types are normalized to 'bool'
- expect(db.records[0].values[0][0]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[0][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[1][0]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[1][1]).toEqual({ type: 'bool', value: true });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
});
test('should interpret string values correctly', () => {
@@ -99,10 +152,22 @@ describe('[example - record] data type interpretation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][0]).toEqual({ type: 'string', value: 'Alice' });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'A short description' });
- expect(db.records[0].values[0][2]).toEqual({ type: 'string', value: 'ABC123' });
- expect(db.records[0].values[1][0]).toEqual({ type: 'string', value: 'Bob' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'A short description',
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'string',
+ value: 'ABC123',
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
});
test('should interpret datetime values correctly', () => {
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts
index f5479d05d..b135b755e 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts
@@ -1,6 +1,12 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
-import { CompileErrorCode } from '@/index';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
+import {
+ CompileErrorCode,
+} from '@/index';
describe('[example - record] composite foreign key constraints', () => {
test('should accept valid composite FK references', () => {
@@ -44,17 +50,35 @@ describe('[example - record] composite foreign key constraints', () => {
// columns = ['id', 'country_code']
expect(db.records[0].tableName).toBe('merchants');
expect(db.records[0].values.length).toBe(3);
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'US' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'US',
+ });
// Orders table
// columns = ['id', 'merchant_id', 'country', 'amount']
expect(db.records[1].tableName).toBe('orders');
expect(db.records[1].values.length).toBe(3);
- expect(db.records[1].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[1].values[0][1]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[1].values[0][2]).toEqual({ type: 'string', value: 'US' });
- expect(db.records[1].values[0][3]).toEqual({ type: 'real', value: 100.00 });
+ expect(db.records[1].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[1].values[0][1]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[1].values[0][2]).toEqual({
+ type: 'string',
+ value: 'US',
+ });
+ expect(db.records[1].values[0][3]).toEqual({
+ type: 'real',
+ value: 100.00,
+ });
});
test('should reject composite FK when partial key match fails', () => {
@@ -129,13 +153,25 @@ describe('[example - record] composite foreign key constraints', () => {
// Row 2: null FK column
// columns = ['id', 'merchant_id', 'country', 'status']
expect(db.records[1].values[1][1].value).toBe(null); // merchant_id
- expect(db.records[1].values[1][2]).toEqual({ type: 'string', value: 'UK' }); // country
- expect(db.records[1].values[1][3]).toEqual({ type: 'string', value: 'pending' }); // status
+ expect(db.records[1].values[1][2]).toEqual({
+ type: 'string',
+ value: 'UK',
+ }); // country
+ expect(db.records[1].values[1][3]).toEqual({
+ type: 'string',
+ value: 'pending',
+ }); // status
// Row 3: null FK column
- expect(db.records[1].values[2][0]).toEqual({ type: 'integer', value: 3 }); // id
+ expect(db.records[1].values[2][0]).toEqual({
+ type: 'integer',
+ value: 3,
+ }); // id
expect(db.records[1].values[2][2].value).toBe(null); // country
- expect(db.records[1].values[2][3]).toEqual({ type: 'string', value: 'processing' }); // status
+ expect(db.records[1].values[2][3]).toEqual({
+ type: 'string',
+ value: 'processing',
+ }); // status
});
test('should validate many-to-many composite FK both directions', () => {
@@ -292,29 +328,59 @@ describe('[example - record] simple foreign key constraints', () => {
// Verify users table
expect(db.records[0].tableName).toBe('users');
expect(db.records[0].values.length).toBe(2);
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'Alice' });
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 2 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: 'Bob' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
// Verify posts table (find it by name since order might vary)
const postsRecord = db.records.find((r) => r.tableName === 'posts');
expect(postsRecord).toBeDefined();
expect(postsRecord!.values.length).toBe(3);
- expect(postsRecord!.values[0][1]).toEqual({ type: 'integer', value: 1 }); // user_id
+ expect(postsRecord!.values[0][1]).toEqual({
+ type: 'integer',
+ value: 1,
+ }); // user_id
// Verify cities table with string FK
const citiesRecord = db.records.find((r) => r.tableName === 'cities');
expect(citiesRecord).toBeDefined();
- expect(citiesRecord!.values[0][1]).toEqual({ type: 'string', value: 'US' }); // country_code
- expect(citiesRecord!.values[1][1]).toEqual({ type: 'string', value: 'UK' });
+ expect(citiesRecord!.values[0][1]).toEqual({
+ type: 'string',
+ value: 'US',
+ }); // country_code
+ expect(citiesRecord!.values[1][1]).toEqual({
+ type: 'string',
+ value: 'UK',
+ });
// Verify transactions table with zero values
const transactionsRecord = db.records.find((r) => r.tableName === 'transactions');
expect(transactionsRecord).toBeDefined();
- expect(transactionsRecord!.values[0][1]).toEqual({ type: 'integer', value: 0 }); // account_id=0
- expect(transactionsRecord!.values[1][1]).toEqual({ type: 'integer', value: 1 }); // account_id=1
- expect(transactionsRecord!.values[2][1]).toEqual({ type: 'integer', value: 2 }); // account_id=2
+ expect(transactionsRecord!.values[0][1]).toEqual({
+ type: 'integer',
+ value: 0,
+ }); // account_id=0
+ expect(transactionsRecord!.values[1][1]).toEqual({
+ type: 'integer',
+ value: 1,
+ }); // account_id=1
+ expect(transactionsRecord!.values[2][1]).toEqual({
+ type: 'integer',
+ value: 2,
+ }); // account_id=2
});
test('should reject FK values that dont exist in referenced table', () => {
@@ -393,14 +459,29 @@ describe('[example - record] simple foreign key constraints', () => {
expect(db.records[1].values.length).toBe(2);
// Row 1: id=1, category_id=1, name="Laptop"
- expect(db.records[1].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[1].values[0][1]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[1].values[0][2]).toEqual({ type: 'string', value: 'Laptop' });
+ expect(db.records[1].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[1].values[0][1]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[1].values[0][2]).toEqual({
+ type: 'string',
+ value: 'Laptop',
+ });
// Row 2: id=2, category_id=null, name="Uncategorized Item"
- expect(db.records[1].values[1][0]).toEqual({ type: 'integer', value: 2 });
+ expect(db.records[1].values[1][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
expect(db.records[1].values[1][1].value).toBe(null);
- expect(db.records[1].values[1][2]).toEqual({ type: 'string', value: 'Uncategorized Item' });
+ expect(db.records[1].values[1][2]).toEqual({
+ type: 'string',
+ value: 'Uncategorized Item',
+ });
});
test('should validate one-to-one and one-to-many FK relationships', () => {
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/increment.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/increment.test.ts
index 0f8d542e9..ba9884845 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/increment.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/increment.test.ts
@@ -1,5 +1,9 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
describe('[example - record] auto-increment and serial type constraints', () => {
test('should allow NULL in pk column with increment flag', () => {
@@ -33,18 +37,36 @@ describe('[example - record] auto-increment and serial type constraints', () =>
// Verify ALL rows and ALL columns in each row
// Row 1: (null, "Alice") - id is auto-generated
expect(record.values[0].length).toBe(2);
- expect(record.values[0][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[0][1]).toEqual({ type: 'string', value: 'Alice' });
+ expect(record.values[0][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
// Row 2: (null, "Bob") - id is auto-generated
expect(record.values[1].length).toBe(2);
- expect(record.values[1][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[1][1]).toEqual({ type: 'string', value: 'Bob' });
+ expect(record.values[1][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[1][1]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
// Row 3: (1, "Charlie")
expect(record.values[2].length).toBe(2);
- expect(record.values[2][0]).toEqual({ type: 'integer', value: 1 });
- expect(record.values[2][1]).toEqual({ type: 'string', value: 'Charlie' });
+ expect(record.values[2][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(record.values[2][1]).toEqual({
+ type: 'string',
+ value: 'Charlie',
+ });
});
test('should allow NULL in pk column with serial type', () => {
@@ -77,13 +99,25 @@ describe('[example - record] auto-increment and serial type constraints', () =>
// Verify ALL rows and ALL columns in each row
// Row 1: (null, "Alice") - id is auto-generated
expect(record.values[0].length).toBe(2);
- expect(record.values[0][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[0][1]).toEqual({ type: 'string', value: 'Alice' });
+ expect(record.values[0][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
// Row 2: (null, "Bob") - id is auto-generated
expect(record.values[1].length).toBe(2);
- expect(record.values[1][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[1][1]).toEqual({ type: 'string', value: 'Bob' });
+ expect(record.values[1][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[1][1]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
});
test('should allow NULL in pk column with bigserial type', () => {
@@ -115,13 +149,25 @@ describe('[example - record] auto-increment and serial type constraints', () =>
// Verify ALL rows and ALL columns in each row
// Row 1: (null, "Alice") - id is auto-generated
expect(record.values[0].length).toBe(2);
- expect(record.values[0][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[0][1]).toEqual({ type: 'string', value: 'Alice' });
+ expect(record.values[0][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
// Row 2: (null, "Bob") - id is auto-generated
expect(record.values[1].length).toBe(2);
- expect(record.values[1][0]).toEqual({ type: 'integer', value: null });
- expect(record.values[1][1]).toEqual({ type: 'string', value: 'Bob' });
+ expect(record.values[1][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(record.values[1][1]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
});
test('should detect duplicate pk for non-null values with increment', () => {
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/multi_records.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/multi_records.test.ts
index b8ef932a0..af41a5725 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/multi_records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/multi_records.test.ts
@@ -1,5 +1,9 @@
-import { CompileErrorCode } from '@/index';
-import { interpret } from '@tests/utils';
+import {
+ CompileErrorCode,
+} from '@/index';
+import {
+ interpret,
+} from '@tests/utils';
describe('[example - record] multiple records blocks', () => {
// NOTE: Multiple records blocks for the same table are currently disallowed.
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/pk.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/pk.test.ts
index c98663e90..948258d2c 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/pk.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/pk.test.ts
@@ -1,6 +1,12 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
-import { CompileErrorCode } from '@/index';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
+import {
+ CompileErrorCode,
+} from '@/index';
describe('[example - record] composite primary key constraints', () => {
test('should accept valid unique composite primary key values', () => {
@@ -32,19 +38,46 @@ describe('[example - record] composite primary key constraints', () => {
expect(db.records[0].values.length).toBe(3);
// Row 1: order_id=1, product_id=100, quantity=2
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'integer', value: 100 });
- expect(db.records[0].values[0][2]).toEqual({ type: 'integer', value: 2 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'integer',
+ value: 100,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
// Row 2: order_id=1, product_id=101, quantity=1
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'integer', value: 101 });
- expect(db.records[0].values[1][2]).toEqual({ type: 'integer', value: 1 });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'integer',
+ value: 101,
+ });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
// Row 3: order_id=2, product_id=100, quantity=3
- expect(db.records[0].values[2][0]).toEqual({ type: 'integer', value: 2 });
- expect(db.records[0].values[2][1]).toEqual({ type: 'integer', value: 100 });
- expect(db.records[0].values[2][2]).toEqual({ type: 'integer', value: 3 });
+ expect(db.records[0].values[2][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'integer',
+ value: 100,
+ });
+ expect(db.records[0].values[2][2]).toEqual({
+ type: 'integer',
+ value: 3,
+ });
});
test('should reject duplicate composite primary key values', () => {
@@ -146,25 +179,52 @@ describe('[example - record] simple primary key constraints', () => {
const usersRecord = db.records.find((r) => r.tableName === 'users');
expect(usersRecord).toBeDefined();
expect(usersRecord!.values.length).toBe(2);
- expect(usersRecord!.values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(usersRecord!.values[0][1]).toEqual({ type: 'string', value: 'Alice' });
- expect(usersRecord!.values[1][0]).toEqual({ type: 'integer', value: 2 });
- expect(usersRecord!.values[1][1]).toEqual({ type: 'string', value: 'Bob' });
+ expect(usersRecord!.values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(usersRecord!.values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
+ expect(usersRecord!.values[1][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(usersRecord!.values[1][1]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
// Verify countries table with string PK
const countriesRecord = db.records.find((r) => r.tableName === 'countries');
expect(countriesRecord).toBeDefined();
expect(countriesRecord!.values.length).toBe(2);
- expect(countriesRecord!.values[0][0]).toEqual({ type: 'string', value: 'US' });
- expect(countriesRecord!.values[1][0]).toEqual({ type: 'string', value: 'UK' });
+ expect(countriesRecord!.values[0][0]).toEqual({
+ type: 'string',
+ value: 'US',
+ });
+ expect(countriesRecord!.values[1][0]).toEqual({
+ type: 'string',
+ value: 'UK',
+ });
// Verify accounts table with zero PK
const accountsRecord = db.records.find((r) => r.tableName === 'accounts');
expect(accountsRecord).toBeDefined();
expect(accountsRecord!.values.length).toBe(3);
- expect(accountsRecord!.values[0][0]).toEqual({ type: 'integer', value: 0 });
- expect(accountsRecord!.values[1][0]).toEqual({ type: 'integer', value: 1 });
- expect(accountsRecord!.values[2][0]).toEqual({ type: 'integer', value: 2 });
+ expect(accountsRecord!.values[0][0]).toEqual({
+ type: 'integer',
+ value: 0,
+ });
+ expect(accountsRecord!.values[1][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(accountsRecord!.values[2][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
});
test('should reject duplicate PK values', () => {
@@ -239,8 +299,14 @@ describe('[example - record] simple primary key constraints', () => {
const db = result.getValue()!;
expect(db.records[0].values.length).toBe(2);
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 2 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
});
test('should validate auto-increment PK values', () => {
@@ -262,9 +328,18 @@ describe('[example - record] simple primary key constraints', () => {
const db = result.getValue()!;
expect(db.records[0].values.length).toBe(3);
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 2 });
- expect(db.records[0].values[2][0]).toEqual({ type: 'integer', value: 3 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(db.records[0].values[2][0]).toEqual({
+ type: 'integer',
+ value: 3,
+ });
});
test('should report error for duplicate records blocks', () => {
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/table_partial.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/table_partial.test.ts
index f2fca16f1..1bb20932e 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/table_partial.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/table_partial.test.ts
@@ -1,5 +1,9 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
describe('[example - record] table partial with records', () => {
test('should handle records with explicit columns from merged table partial', () => {
@@ -30,15 +34,30 @@ describe('[example - record] table partial with records', () => {
expect(db.records.length).toBe(1);
// Explicit columns should match merged field order: created_at, updated_at, id, name, email
- expect(db.records[0].columns).toEqual(['created_at', 'updated_at', 'id', 'name', 'email']);
+ expect(db.records[0].columns).toEqual([
+ 'created_at',
+ 'updated_at',
+ 'id',
+ 'name',
+ 'email',
+ ]);
expect(db.records[0].values).toHaveLength(2);
// Check first row values
expect(db.records[0].values[0][0].value).toBe('2024-01-01T00:00:00');
expect(db.records[0].values[0][1].value).toBe('2024-01-01T00:00:00');
- expect(db.records[0].values[0][2]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][3]).toEqual({ type: 'string', value: 'Alice' });
- expect(db.records[0].values[0][4]).toEqual({ type: 'string', value: 'alice@example.com' });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
+ expect(db.records[0].values[0][4]).toEqual({
+ type: 'string',
+ value: 'alice@example.com',
+ });
expect(db.records[0].values[1][0].value).toBe('2024-01-02T00:00:00+08:00');
expect(db.records[0].values[1][1].value).toBe('2024-01-02T00:00:00.100+07:00');
});
@@ -135,7 +154,12 @@ describe('[example - record] table partial with records', () => {
const db = result.getValue()!;
expect(db.records.length).toBe(1);
- expect(db.records[0].columns).toEqual(['id', 'title', 'content', 'author_id']);
+ expect(db.records[0].columns).toEqual([
+ 'id',
+ 'title',
+ 'content',
+ 'author_id',
+ ]);
expect(db.records[0].values).toHaveLength(2);
});
@@ -179,7 +203,18 @@ describe('[example - record] table partial with records', () => {
expect(db.records.length).toBe(1);
// Columns should match merged order: s1, s2, a, b, m1, m2, c, d, e1, e2
- expect(db.records[0].columns).toEqual(['s1', 's2', 'a', 'b', 'm1', 'm2', 'c', 'd', 'e1', 'e2']);
+ expect(db.records[0].columns).toEqual([
+ 's1',
+ 's2',
+ 'a',
+ 'b',
+ 'm1',
+ 'm2',
+ 'c',
+ 'd',
+ 'e1',
+ 'e2',
+ ]);
expect(db.records[0].values).toHaveLength(1);
expect(db.records[0].values[0]).toHaveLength(10);
});
@@ -211,7 +246,12 @@ describe('[example - record] table partial with records', () => {
expect(db.records.length).toBe(1);
// Columns should respect merged field order: created_at, updated_at, id, name
- expect(db.records[0].columns).toEqual(['created_at', 'updated_at', 'id', 'name']);
+ expect(db.records[0].columns).toEqual([
+ 'created_at',
+ 'updated_at',
+ 'id',
+ 'name',
+ ]);
expect(db.records[0].values).toHaveLength(2);
});
@@ -248,11 +288,21 @@ describe('[example - record] table partial with records', () => {
expect(db.records.length).toBe(1);
// 'shared' should be at P2's position: a, b, x, shared, c, y
- expect(db.records[0].columns).toEqual(['a', 'b', 'x', 'shared', 'c', 'y']);
+ expect(db.records[0].columns).toEqual([
+ 'a',
+ 'b',
+ 'x',
+ 'shared',
+ 'c',
+ 'y',
+ ]);
expect(db.records[0].values).toHaveLength(1);
// 'shared' value should be at index 3
- expect(db.records[0].values[0][3]).toEqual({ type: 'string', value: 'shared_value' });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'string',
+ value: 'shared_value',
+ });
});
test('should handle empty partial with explicit columns', () => {
@@ -314,8 +364,17 @@ describe('[example - record] table partial with records', () => {
expect(db.records[0].values).toHaveLength(1);
// Values should match the types from direct definitions
- expect(db.records[0].values[0][0]).toEqual({ type: 'string', value: 'value_a' });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'value_b' });
- expect(db.records[0].values[0][2]).toEqual({ type: 'integer', value: 3 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'string',
+ value: 'value_a',
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'value_b',
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'integer',
+ value: 3,
+ });
});
});
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/type_compatibility.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/type_compatibility.test.ts
index c816c6dee..7861d54ea 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/type_compatibility.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/type_compatibility.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
-import { CompileErrorCode } from '@/index';
-import { DateTime } from 'luxon';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
+import {
+ CompileErrorCode,
+} from '@/index';
+import {
+ DateTime,
+} from 'luxon';
describe('[example - record] type compatibility validation', () => {
describe('boolean type validation', () => {
@@ -36,20 +44,62 @@ describe('[example - record] type compatibility validation', () => {
const db = result.getValue()!;
expect(db.records.length).toBe(1);
expect(db.records[0].values.length).toBe(14);
- expect(db.records[0].values[0][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[1][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[2][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[3][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[4][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[5][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[6][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[7][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[8][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[9][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[10][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[11][1]).toEqual({ type: 'bool', value: false });
- expect(db.records[0].values[12][1]).toEqual({ type: 'bool', value: true });
- expect(db.records[0].values[13][1]).toEqual({ type: 'bool', value: false });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[3][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[4][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[5][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[6][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[7][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[8][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[9][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[10][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[11][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
+ expect(db.records[0].values[12][1]).toEqual({
+ type: 'bool',
+ value: true,
+ });
+ expect(db.records[0].values[13][1]).toEqual({
+ type: 'bool',
+ value: false,
+ });
});
test('- should reject invalid boolean values', () => {
@@ -103,8 +153,14 @@ describe('[example - record] type compatibility validation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][3]).toEqual({ type: 'real', value: 99.99 });
- expect(db.records[0].values[0][4]).toEqual({ type: 'real', value: 3.14159 });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'real',
+ value: 99.99,
+ });
+ expect(db.records[0].values[0][4]).toEqual({
+ type: 'real',
+ value: 3.14159,
+ });
});
test('- should accept scientific notation for numeric columns', () => {
@@ -127,9 +183,18 @@ describe('[example - record] type compatibility validation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][1]).toEqual({ type: 'real', value: 1e10 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'real', value: 3.14e-5 });
- expect(db.records[0].values[2][1]).toEqual({ type: 'real', value: 2e8 });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'real',
+ value: 1e10,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'real',
+ value: 3.14e-5,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'real',
+ value: 2e8,
+ });
});
test('- should reject invalid numeric values', () => {
@@ -212,10 +277,22 @@ describe('[example - record] type compatibility validation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'Alice' });
- expect(db.records[0].values[0][2]).toEqual({ type: 'string', value: 'Bob' });
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: '' });
- expect(db.records[0].values[1][2]).toEqual({ type: 'string', value: '' });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'Alice',
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'string',
+ value: 'Bob',
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: '',
+ });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'string',
+ value: '',
+ });
});
test('- should validate string length for varchar, char, nvarchar, nchar', () => {
@@ -336,23 +413,62 @@ describe('[example - record] type compatibility validation', () => {
const db = result.getValue()!;
// Row 1: explicit null keyword
- expect(db.records[0].values[0][1]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[0][2]).toEqual({ type: 'bool', value: null });
- expect(db.records[0].values[0][3]).toEqual({ type: 'datetime', value: null });
- expect(db.records[0].values[0][4]).toEqual({ type: 'real', value: null });
- expect(db.records[0].values[0][5]).toEqual({ type: 'string', value: null });
- expect(db.records[0].values[0][6]).toEqual({ type: 'string', value: null });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'bool',
+ value: null,
+ });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'datetime',
+ value: null,
+ });
+ expect(db.records[0].values[0][4]).toEqual({
+ type: 'real',
+ value: null,
+ });
+ expect(db.records[0].values[0][5]).toEqual({
+ type: 'string',
+ value: null,
+ });
+ expect(db.records[0].values[0][6]).toEqual({
+ type: 'string',
+ value: null,
+ });
// Row 2: empty fields (treated as null)
- expect(db.records[0].values[1][1]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[1][2]).toEqual({ type: 'bool', value: null });
- expect(db.records[0].values[1][3]).toEqual({ type: 'datetime', value: null });
- expect(db.records[0].values[1][4]).toEqual({ type: 'real', value: null });
- expect(db.records[0].values[1][5]).toEqual({ type: 'string', value: null });
- expect(db.records[0].values[1][6]).toEqual({ type: 'string', value: null });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'bool',
+ value: null,
+ });
+ expect(db.records[0].values[1][3]).toEqual({
+ type: 'datetime',
+ value: null,
+ });
+ expect(db.records[0].values[1][4]).toEqual({
+ type: 'real',
+ value: null,
+ });
+ expect(db.records[0].values[1][5]).toEqual({
+ type: 'string',
+ value: null,
+ });
+ expect(db.records[0].values[1][6]).toEqual({
+ type: 'string',
+ value: null,
+ });
// Row 3: valid values
- expect(db.records[0].values[2][1]).toEqual({ type: 'integer', value: 10 });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'integer',
+ value: 10,
+ });
});
test('- should treat empty string as null for non-string types', () => {
@@ -373,10 +489,22 @@ describe('[example - record] type compatibility validation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[0][1]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[0][2]).toEqual({ type: 'bool', value: null });
- expect(db.records[0].values[0][3]).toEqual({ type: 'string', value: '' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'bool',
+ value: null,
+ });
+ expect(db.records[0].values[0][3]).toEqual({
+ type: 'string',
+ value: '',
+ });
});
});
@@ -398,8 +526,14 @@ describe('[example - record] type compatibility validation', () => {
expect(errors.length).toBe(0);
const db = result.getValue()!;
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: null });
- expect(db.records[0].values[0][2]).toEqual({ type: 'string', value: null });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: null,
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'string',
+ value: null,
+ });
});
test('- should reject NULL for NOT NULL column without default and increment', () => {
@@ -441,10 +575,22 @@ describe('[example - record] type compatibility validation', () => {
const db = result.getValue()!;
expect(db.records[0].values.length).toBe(2);
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: null });
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: null });
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: 'inactive' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: null,
+ });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: null,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: 'inactive',
+ });
});
});
diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/unique.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/unique.test.ts
index 15dd0ff28..b7f9a24ee 100644
--- a/packages/dbml-parse/__tests__/examples/interpreter/record/unique.test.ts
+++ b/packages/dbml-parse/__tests__/examples/interpreter/record/unique.test.ts
@@ -1,6 +1,12 @@
-import { describe, expect, test } from 'vitest';
-import { interpret } from '@tests/utils';
-import { CompileErrorCode } from '@/index';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ interpret,
+} from '@tests/utils';
+import {
+ CompileErrorCode,
+} from '@/index';
describe('[example - record] composite unique constraints', () => {
test('should accept valid unique composite values', () => {
@@ -32,19 +38,46 @@ describe('[example - record] composite unique constraints', () => {
expect(db.records[0].values.length).toBe(3);
// Row 1: user_id=1, profile_type="work", data="Software Engineer"
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'work' });
- expect(db.records[0].values[0][2]).toEqual({ type: 'string', value: 'Software Engineer' });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'work',
+ });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'string',
+ value: 'Software Engineer',
+ });
// Row 2: user_id=1, profile_type="personal", data="Loves hiking"
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: 'personal' });
- expect(db.records[0].values[1][2]).toEqual({ type: 'string', value: 'Loves hiking' });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: 'personal',
+ });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'string',
+ value: 'Loves hiking',
+ });
// Row 3: user_id=2, profile_type="work", data="Designer"
- expect(db.records[0].values[2][0]).toEqual({ type: 'integer', value: 2 });
- expect(db.records[0].values[2][1]).toEqual({ type: 'string', value: 'work' });
- expect(db.records[0].values[2][2]).toEqual({ type: 'string', value: 'Designer' });
+ expect(db.records[0].values[2][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'string',
+ value: 'work',
+ });
+ expect(db.records[0].values[2][2]).toEqual({
+ type: 'string',
+ value: 'Designer',
+ });
});
test('should reject duplicate composite unique values', () => {
@@ -97,19 +130,40 @@ describe('[example - record] composite unique constraints', () => {
expect(db.records[0].values.length).toBe(3);
// Row 1: user_id=1, category=null, value="default"
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
expect(db.records[0].values[0][1].value).toBe(null);
- expect(db.records[0].values[0][2]).toEqual({ type: 'string', value: 'default' });
+ expect(db.records[0].values[0][2]).toEqual({
+ type: 'string',
+ value: 'default',
+ });
// Row 2: user_id=1, category=null, value="another default"
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 1 });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
expect(db.records[0].values[1][1].value).toBe(null);
- expect(db.records[0].values[1][2]).toEqual({ type: 'string', value: 'another default' });
+ expect(db.records[0].values[1][2]).toEqual({
+ type: 'string',
+ value: 'another default',
+ });
// Row 3: user_id=1, category="theme", value="dark"
- expect(db.records[0].values[2][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[2][1]).toEqual({ type: 'string', value: 'theme' });
- expect(db.records[0].values[2][2]).toEqual({ type: 'string', value: 'dark' });
+ expect(db.records[0].values[2][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'string',
+ value: 'theme',
+ });
+ expect(db.records[0].values[2][2]).toEqual({
+ type: 'string',
+ value: 'dark',
+ });
});
test('should allow same value in one unique column when other differs', () => {
@@ -138,20 +192,38 @@ describe('[example - record] composite unique constraints', () => {
expect(db.records[0].values.length).toBe(3);
// Row 1: event_id=1, attendee_id=100, registration_date="2024-01-01"
- expect(db.records[0].values[0][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[0][1]).toEqual({ type: 'integer', value: 100 });
+ expect(db.records[0].values[0][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'integer',
+ value: 100,
+ });
expect(db.records[0].values[0][2].type).toBe('datetime');
expect(db.records[0].values[0][2].value).toBe('2024-01-01');
// Row 2: event_id=1, attendee_id=101, registration_date="2024-01-02"
- expect(db.records[0].values[1][0]).toEqual({ type: 'integer', value: 1 });
- expect(db.records[0].values[1][1]).toEqual({ type: 'integer', value: 101 });
+ expect(db.records[0].values[1][0]).toEqual({
+ type: 'integer',
+ value: 1,
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'integer',
+ value: 101,
+ });
expect(db.records[0].values[1][2].type).toBe('datetime');
expect(db.records[0].values[1][2].value).toBe('2024-01-02');
// Row 3: event_id=2, attendee_id=100, registration_date="2024-01-03"
- expect(db.records[0].values[2][0]).toEqual({ type: 'integer', value: 2 });
- expect(db.records[0].values[2][1]).toEqual({ type: 'integer', value: 100 });
+ expect(db.records[0].values[2][0]).toEqual({
+ type: 'integer',
+ value: 2,
+ });
+ expect(db.records[0].values[2][1]).toEqual({
+ type: 'integer',
+ value: 100,
+ });
expect(db.records[0].values[2][2].type).toBe('datetime');
expect(db.records[0].values[2][2].value).toBe('2024-01-03');
});
@@ -198,20 +270,38 @@ describe('[example - record] simple unique constraints', () => {
// Verify users table
expect(db.records[0].tableName).toBe('users');
expect(db.records[0].values.length).toBe(2);
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'alice@example.com' });
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: 'bob@example.com' });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'alice@example.com',
+ });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: 'bob@example.com',
+ });
// Verify products table
expect(db.records[1].tableName).toBe('products');
expect(db.records[1].values.length).toBe(2);
- expect(db.records[1].values[0][1]).toEqual({ type: 'string', value: 'PROD-001' });
+ expect(db.records[1].values[0][1]).toEqual({
+ type: 'string',
+ value: 'PROD-001',
+ });
// Verify accounts table with numeric unique values including zero and negative
expect(db.records[2].tableName).toBe('accounts');
expect(db.records[2].values.length).toBe(3);
- expect(db.records[2].values[0][1]).toEqual({ type: 'integer', value: 0 });
- expect(db.records[2].values[1][1]).toEqual({ type: 'integer', value: -1 });
- expect(db.records[2].values[2][1]).toEqual({ type: 'integer', value: 1000 });
+ expect(db.records[2].values[0][1]).toEqual({
+ type: 'integer',
+ value: 0,
+ });
+ expect(db.records[2].values[1][1]).toEqual({
+ type: 'integer',
+ value: -1,
+ });
+ expect(db.records[2].values[2][1]).toEqual({
+ type: 'integer',
+ value: 1000,
+ });
});
test('should reject duplicate unique values', () => {
@@ -273,20 +363,32 @@ describe('[example - record] simple unique constraints', () => {
expect(db.records[0].values.length).toBe(4);
// Row 1: email="alice@example.com", phone=null
- expect(db.records[0].values[0][1]).toEqual({ type: 'string', value: 'alice@example.com' });
+ expect(db.records[0].values[0][1]).toEqual({
+ type: 'string',
+ value: 'alice@example.com',
+ });
expect(db.records[0].values[0][2].value).toBe(null);
// Row 2: email="bob@example.com", phone=null
- expect(db.records[0].values[1][1]).toEqual({ type: 'string', value: 'bob@example.com' });
+ expect(db.records[0].values[1][1]).toEqual({
+ type: 'string',
+ value: 'bob@example.com',
+ });
expect(db.records[0].values[1][2].value).toBe(null);
// Row 3: email=null, phone="123-456"
expect(db.records[0].values[2][1].value).toBe(null);
- expect(db.records[0].values[2][2]).toEqual({ type: 'string', value: '123-456' });
+ expect(db.records[0].values[2][2]).toEqual({
+ type: 'string',
+ value: '123-456',
+ });
// Row 4: email=null, phone="789-012"
expect(db.records[0].values[3][1].value).toBe(null);
- expect(db.records[0].values[3][2]).toEqual({ type: 'string', value: '789-012' });
+ expect(db.records[0].values[3][2]).toEqual({
+ type: 'string',
+ value: '789-012',
+ });
});
test('should validate unique with PK constraint', () => {
diff --git a/packages/dbml-parse/__tests__/examples/lexer/lexer.test.ts b/packages/dbml-parse/__tests__/examples/lexer/lexer.test.ts
index c5b26ed35..64796affd 100644
--- a/packages/dbml-parse/__tests__/examples/lexer/lexer.test.ts
+++ b/packages/dbml-parse/__tests__/examples/lexer/lexer.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect, test } from 'vitest';
-import { SyntaxTokenKind, SyntaxToken, isTriviaToken } from '@/core/lexer/tokens';
-import { CompileErrorCode } from '@/core/errors';
-import { lex } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ SyntaxTokenKind, SyntaxToken, isTriviaToken,
+} from '@/core/types/tokens';
+import {
+ CompileErrorCode,
+} from '@/core/types/errors';
+import {
+ lex,
+} from '@tests/utils';
// Helper to get non-trivia, non-EOF tokens
function getTokens (source: string) {
@@ -119,15 +127,60 @@ describe('[example] lexer', () => {
expect(tokens).toHaveLength(9);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.LBRACE, value: '{', start: 0, end: 1 });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.RBRACE, value: '}', start: 1, end: 2 });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.LBRACKET, value: '[', start: 2, end: 3 });
- expect(tokens[3]).toMatchObject({ kind: SyntaxTokenKind.RBRACKET, value: ']', start: 3, end: 4 });
- expect(tokens[4]).toMatchObject({ kind: SyntaxTokenKind.LPAREN, value: '(', start: 4, end: 5 });
- expect(tokens[5]).toMatchObject({ kind: SyntaxTokenKind.RPAREN, value: ')', start: 5, end: 6 });
- expect(tokens[6]).toMatchObject({ kind: SyntaxTokenKind.COLON, value: ':', start: 6, end: 7 });
- expect(tokens[7]).toMatchObject({ kind: SyntaxTokenKind.COMMA, value: ',', start: 7, end: 8 });
- expect(tokens[8]).toMatchObject({ kind: SyntaxTokenKind.SEMICOLON, value: ';', start: 8, end: 9 });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.LBRACE,
+ value: '{',
+ start: 0,
+ end: 1,
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.RBRACE,
+ value: '}',
+ start: 1,
+ end: 2,
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.LBRACKET,
+ value: '[',
+ start: 2,
+ end: 3,
+ });
+ expect(tokens[3]).toMatchObject({
+ kind: SyntaxTokenKind.RBRACKET,
+ value: ']',
+ start: 3,
+ end: 4,
+ });
+ expect(tokens[4]).toMatchObject({
+ kind: SyntaxTokenKind.LPAREN,
+ value: '(',
+ start: 4,
+ end: 5,
+ });
+ expect(tokens[5]).toMatchObject({
+ kind: SyntaxTokenKind.RPAREN,
+ value: ')',
+ start: 5,
+ end: 6,
+ });
+ expect(tokens[6]).toMatchObject({
+ kind: SyntaxTokenKind.COLON,
+ value: ':',
+ start: 6,
+ end: 7,
+ });
+ expect(tokens[7]).toMatchObject({
+ kind: SyntaxTokenKind.COMMA,
+ value: ',',
+ start: 7,
+ end: 8,
+ });
+ expect(tokens[8]).toMatchObject({
+ kind: SyntaxTokenKind.SEMICOLON,
+ value: ';',
+ start: 8,
+ end: 9,
+ });
});
test('should tokenize single-char operators', () => {
@@ -136,15 +189,42 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(8);
- expect(tokens.map((t) => ({ kind: t.kind, value: t.value }))).toEqual([
- { kind: SyntaxTokenKind.OP, value: '+' },
- { kind: SyntaxTokenKind.OP, value: '-' },
- { kind: SyntaxTokenKind.WILDCARD, value: '*' },
- { kind: SyntaxTokenKind.OP, value: '/' },
- { kind: SyntaxTokenKind.OP, value: '<' },
- { kind: SyntaxTokenKind.OP, value: '>' },
- { kind: SyntaxTokenKind.OP, value: '=' },
- { kind: SyntaxTokenKind.OP, value: '.' },
+ expect(tokens.map((t) => ({
+ kind: t.kind,
+ value: t.value,
+ }))).toEqual([
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '+',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '-',
+ },
+ {
+ kind: SyntaxTokenKind.WILDCARD,
+ value: '*',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '/',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '<',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '>',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '=',
+ },
+ {
+ kind: SyntaxTokenKind.OP,
+ value: '.',
+ },
]);
});
@@ -188,24 +268,42 @@ describe('[example] lexer', () => {
value: 'a',
start: 0,
end: 1,
- startPos: { line: 0, column: 0 },
- endPos: { line: 0, column: 1 },
+ startPos: {
+ line: 0,
+ column: 0,
+ },
+ endPos: {
+ line: 0,
+ column: 1,
+ },
});
expect(tokens[1]).toMatchObject({
value: 'b',
start: 2,
end: 3,
- startPos: { line: 1, column: 0 },
- endPos: { line: 1, column: 1 },
+ startPos: {
+ line: 1,
+ column: 0,
+ },
+ endPos: {
+ line: 1,
+ column: 1,
+ },
});
expect(tokens[2]).toMatchObject({
value: 'c',
start: 4,
end: 5,
- startPos: { line: 2, column: 0 },
- endPos: { line: 2, column: 1 },
+ startPos: {
+ line: 2,
+ column: 0,
+ },
+ endPos: {
+ line: 2,
+ column: 1,
+ },
});
});
@@ -306,7 +404,15 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens.map((t) => t.value)).toEqual([
- 'Table', 'users', '{', 'id', 'int', '[', 'pk', ']', '}',
+ 'Table',
+ 'users',
+ '{',
+ 'id',
+ 'int',
+ '[',
+ 'pk',
+ ']',
+ '}',
]);
expect(tokens.map((t) => t.kind)).toEqual([
SyntaxTokenKind.IDENTIFIER,
@@ -326,16 +432,41 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'public', start: 0, end: 6 });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '.', start: 6, end: 7 });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'users', start: 7, end: 12 });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'public',
+ start: 0,
+ end: 6,
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '.',
+ start: 6,
+ end: 7,
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'users',
+ start: 7,
+ end: 12,
+ });
});
test('should tokenize ref with relationship operators', () => {
const source = 'Ref: a.id > b.id';
const tokens = getTokens(source);
- expect(tokens.map((t) => t.value)).toEqual(['Ref', ':', 'a', '.', 'id', '>', 'b', '.', 'id']);
+ expect(tokens.map((t) => t.value)).toEqual([
+ 'Ref',
+ ':',
+ 'a',
+ '.',
+ 'id',
+ '>',
+ 'b',
+ '.',
+ 'id',
+ ]);
});
test('should tokenize column with settings', () => {
@@ -343,7 +474,15 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens.map((t) => t.value)).toEqual([
- 'id', 'int', '[', 'pk', ',', 'default', ':', 'active', ']',
+ 'id',
+ 'int',
+ '[',
+ 'pk',
+ ',',
+ 'default',
+ ':',
+ 'active',
+ ']',
]);
});
});
@@ -370,10 +509,26 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(4);
- expect(tokens[0]).toMatchObject({ value: 'id', start: 0, end: 2 });
- expect(tokens[1]).toMatchObject({ value: '[', start: 2, end: 3 });
- expect(tokens[2]).toMatchObject({ value: 'pk', start: 3, end: 5 });
- expect(tokens[3]).toMatchObject({ value: ']', start: 5, end: 6 });
+ expect(tokens[0]).toMatchObject({
+ value: 'id',
+ start: 0,
+ end: 2,
+ });
+ expect(tokens[1]).toMatchObject({
+ value: '[',
+ start: 2,
+ end: 3,
+ });
+ expect(tokens[2]).toMatchObject({
+ value: 'pk',
+ start: 3,
+ end: 5,
+ });
+ expect(tokens[3]).toMatchObject({
+ value: ']',
+ start: 5,
+ end: 6,
+ });
});
test('should tokenize negative number as operator + literal', () => {
@@ -381,8 +536,18 @@ describe('[example] lexer', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(2);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '-', start: 0, end: 1 });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '42', start: 1, end: 3 });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '-',
+ start: 0,
+ end: 1,
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '42',
+ start: 1,
+ end: 3,
+ });
});
test('should handle unicode in quoted strings with correct byte positions', () => {
@@ -471,7 +636,9 @@ describe('[example] lexer', () => {
});
test('should handle source with many lines', () => {
- const lines = Array.from({ length: 1000 }, (_, i) => `id${i}`).join('\n');
+ const lines = Array.from({
+ length: 1000,
+ }, (_, i) => `id${i}`).join('\n');
const result = lex(lines);
const tokens = result.getValue().filter((t) => !isTriviaToken(t) && t.kind !== SyntaxTokenKind.EOF);
diff --git a/packages/dbml-parse/__tests__/examples/lexer/scientific-notation.test.ts b/packages/dbml-parse/__tests__/examples/lexer/scientific-notation.test.ts
index 680ba8f18..b7b3f85a9 100644
--- a/packages/dbml-parse/__tests__/examples/lexer/scientific-notation.test.ts
+++ b/packages/dbml-parse/__tests__/examples/lexer/scientific-notation.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect, test } from 'vitest';
-import { SyntaxTokenKind, isTriviaToken } from '@/core/lexer/tokens';
-import { CompileErrorCode } from '@/core/errors';
-import { lex } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ SyntaxTokenKind, isTriviaToken,
+} from '@/core/types/tokens';
+import {
+ CompileErrorCode,
+} from '@/core/types/errors';
+import {
+ lex,
+} from '@tests/utils';
// Helper to get non-trivia, non-EOF tokens
function getTokens (source: string) {
@@ -16,10 +24,22 @@ describe('[example] lexer - scientific notation', () => {
expect(tokens).toHaveLength(4);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e2' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1E2' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e+2' });
- expect(tokens[3]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e-2' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e2',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1E2',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e+2',
+ });
+ expect(tokens[3]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e-2',
+ });
});
test('should tokenize decimal with exponent', () => {
@@ -28,9 +48,18 @@ describe('[example] lexer - scientific notation', () => {
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '3.14e10' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '2.5E-3' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1.0e+5' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '3.14e10',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '2.5E-3',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1.0e+5',
+ });
});
test('should tokenize scientific notation at end of input', () => {
@@ -38,7 +67,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e2' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e2',
+ });
});
test('should tokenize scientific notation followed by delimiter', () => {
@@ -46,9 +78,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e2' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.COMMA, value: ',' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '3e4' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e2',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.COMMA,
+ value: ',',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '3e4',
+ });
});
test('should tokenize large exponents', () => {
@@ -56,8 +97,14 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(2);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e100' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '2.5e-50' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e100',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '2.5e-50',
+ });
});
test('should tokenize scientific notation in DBML context', () => {
@@ -65,9 +112,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'default' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.COLON, value: ':' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e-5' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'default',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.COLON,
+ value: ':',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e-5',
+ });
});
test('should tokenize zero exponent', () => {
@@ -75,8 +131,14 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(2);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e0' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '5.5e0' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e0',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '5.5e0',
+ });
});
});
@@ -86,9 +148,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '3.14' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '0.5' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '123.456' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '3.14',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '0.5',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '123.456',
+ });
});
test('should tokenize floating point at end of input', () => {
@@ -96,7 +167,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '3.14' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '3.14',
+ });
});
test('should tokenize floating point followed by delimiter', () => {
@@ -104,9 +178,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '3.14' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.COMMA, value: ',' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '2.71' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '3.14',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.COMMA,
+ value: ',',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '2.71',
+ });
});
});
@@ -116,9 +199,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1abc' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '2test' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '3rd' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1abc',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '2test',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '3rd',
+ });
});
test('should tokenize digit-letter-digit as identifier', () => {
@@ -126,7 +218,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1a2b3c' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1a2b3c',
+ });
});
test('should tokenize 1e as identifier (incomplete exponent)', () => {
@@ -134,7 +229,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e',
+ });
});
test('should tokenize 1ea as identifier', () => {
@@ -142,7 +240,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1ea' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1ea',
+ });
});
test('should tokenize 1e2abc as identifier (valid exponent followed by letters)', () => {
@@ -150,7 +251,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e2abc' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e2abc',
+ });
});
test('should tokenize 5e10abcbd as identifier', () => {
@@ -158,7 +262,10 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(1);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '5e10abcbd' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '5e10abcbd',
+ });
});
});
@@ -169,8 +276,14 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(2);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '+' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '+',
+ });
});
test('should tokenize 1e- as identifier and operator', () => {
@@ -178,8 +291,14 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(2);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '-' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '-',
+ });
});
test('should tokenize 1e+a as identifier, operator, identifier', () => {
@@ -187,9 +306,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '+' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'a' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '+',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'a',
+ });
});
test('should tokenize 1e-b as identifier, operator, identifier', () => {
@@ -197,9 +325,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: '1e' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '-' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'b' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: '1e',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '-',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'b',
+ });
});
});
@@ -228,9 +365,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1.5e2' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '.' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '5' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1.5e2',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '.',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '5',
+ });
});
test('should report error for decimal with letters', () => {
@@ -259,9 +405,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '1e2' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '.' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '5' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '1e2',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '.',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '5',
+ });
});
test('should tokenize 5e10.method as number, dot, identifier', () => {
@@ -269,9 +424,18 @@ describe('[example] lexer - scientific notation', () => {
const tokens = getTokens(source);
expect(tokens).toHaveLength(3);
- expect(tokens[0]).toMatchObject({ kind: SyntaxTokenKind.NUMERIC_LITERAL, value: '5e10' });
- expect(tokens[1]).toMatchObject({ kind: SyntaxTokenKind.OP, value: '.' });
- expect(tokens[2]).toMatchObject({ kind: SyntaxTokenKind.IDENTIFIER, value: 'method' });
+ expect(tokens[0]).toMatchObject({
+ kind: SyntaxTokenKind.NUMERIC_LITERAL,
+ value: '5e10',
+ });
+ expect(tokens[1]).toMatchObject({
+ kind: SyntaxTokenKind.OP,
+ value: '.',
+ });
+ expect(tokens[2]).toMatchObject({
+ kind: SyntaxTokenKind.IDENTIFIER,
+ value: 'method',
+ });
});
});
});
diff --git a/packages/dbml-parse/__tests__/examples/parser/parser.test.ts b/packages/dbml-parse/__tests__/examples/parser/parser.test.ts
index 557ab5e0e..48b6faf79 100644
--- a/packages/dbml-parse/__tests__/examples/parser/parser.test.ts
+++ b/packages/dbml-parse/__tests__/examples/parser/parser.test.ts
@@ -1,4 +1,6 @@
-import { describe, expect, test } from 'vitest';
+import {
+ describe, expect, test,
+} from 'vitest';
import {
SyntaxNodeKind,
ElementDeclarationNode,
@@ -14,9 +16,13 @@ import {
VariableNode,
CommaExpressionNode,
LiteralNode,
-} from '@/core/parser/nodes';
-import { SyntaxTokenKind } from '@/core/lexer/tokens';
-import { parse } from '@tests/utils';
+} from '@/core/types/nodes';
+import {
+ SyntaxTokenKind,
+} from '@/core/types/tokens';
+import {
+ parse,
+} from '@tests/utils';
// Helper to extract a value from a PrimaryExpressionNode
function getPrimaryValue (node: PrimaryExpressionNode | undefined): string | undefined {
diff --git a/packages/dbml-parse/__tests__/examples/services/definition/general.test.ts b/packages/dbml-parse/__tests__/examples/services/definition/general.test.ts
index 901e0f34c..d83cb7c23 100644
--- a/packages/dbml-parse/__tests__/examples/services/definition/general.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/definition/general.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLDefinitionProvider from '@/services/definition/provider';
-import { createMockTextModel, createPosition, extractTextFromRange } from '../../../utils';
+import {
+ createMockTextModel, createPosition, extractTextFromRange,
+} from '../../../utils';
describe('[example] DefinitionProvider', () => {
describe('should find definition for tables', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/definition/records.test.ts b/packages/dbml-parse/__tests__/examples/services/definition/records.test.ts
index 4163d53aa..08243ca3c 100644
--- a/packages/dbml-parse/__tests__/examples/services/definition/records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/definition/records.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLDefinitionProvider from '@/services/definition/provider';
-import { createMockTextModel, createPosition, extractTextFromRange } from '../../../utils';
+import {
+ createMockTextModel, createPosition, extractTextFromRange,
+} from '../../../utils';
describe('[example - records] DefinitionProvider - Records', () => {
describe('should find table definition from records', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/references/general.test.ts b/packages/dbml-parse/__tests__/examples/services/references/general.test.ts
index 0390c2967..67063b49b 100644
--- a/packages/dbml-parse/__tests__/examples/services/references/general.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/references/general.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLReferencesProvider from '@/services/references/provider';
-import { createPosition, createMockTextModel, extractTextFromRange } from '../../../utils';
+import {
+ createPosition, createMockTextModel, extractTextFromRange,
+} from '../../../utils';
describe('[example] ReferencesProvider', () => {
it('should return empty array when no references found', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/references/records.test.ts b/packages/dbml-parse/__tests__/examples/services/references/records.test.ts
index 2013a7c2e..21ae31a1e 100644
--- a/packages/dbml-parse/__tests__/examples/services/references/records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/references/records.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLReferencesProvider from '@/services/references/provider';
-import { createPosition, createMockTextModel, extractTextFromRange } from '../../../utils';
+import {
+ createPosition, createMockTextModel, extractTextFromRange,
+} from '../../../utils';
describe('[example] ReferencesProvider - Records', () => {
describe('should find all table references from records', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/service_utils.test.ts b/packages/dbml-parse/__tests__/examples/services/service_utils.test.ts
index cb742412f..f9d38c4e8 100644
--- a/packages/dbml-parse/__tests__/examples/services/service_utils.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/service_utils.test.ts
@@ -1,6 +1,12 @@
-import { describe, expect, it } from 'vitest';
-import { getOffsetFromMonacoPosition } from '@/services/utils';
-import { createPosition, MockTextModel } from '../../utils';
+import {
+ describe, expect, it,
+} from 'vitest';
+import {
+ getOffsetFromMonacoPosition,
+} from '@/services/utils';
+import {
+ createPosition, MockTextModel,
+} from '../../utils';
describe('[example] Services Utils', () => {
// Useful for detecting breaking changes of monaco-editor-core
diff --git a/packages/dbml-parse/__tests__/examples/services/suggestions/general.test.ts b/packages/dbml-parse/__tests__/examples/services/suggestions/general.test.ts
index ab077cd38..eeab8efa1 100644
--- a/packages/dbml-parse/__tests__/examples/services/suggestions/general.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/suggestions/general.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLCompletionItemProvider from '@/services/suggestions/provider';
-import { createMockTextModel, createPosition } from '@tests/utils';
+import {
+ createMockTextModel, createPosition,
+} from '@tests/utils';
describe('[example] CompletionItemProvider', () => {
describe('should suggest element types when at top level', () => {
@@ -160,19 +164,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'headercolor',
- 'note',
-
- ]);
+ expect(labels).toEqual(['headercolor', 'note']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'headercolor: ',
- 'note: ',
-
- ]);
+ expect(insertTexts).toEqual(['headercolor: ', 'note: ']);
});
it('- work when there is a comma preceding', () => {
@@ -186,19 +182,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'headercolor',
- 'note',
-
- ]);
+ expect(labels).toEqual(['headercolor', 'note']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'headercolor: ',
- 'note: ',
-
- ]);
+ expect(insertTexts).toEqual(['headercolor: ', 'note: ']);
});
});
@@ -796,17 +784,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'status',
-
- ]);
+ expect(labels).toEqual(['status']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'status',
-
- ]);
+ expect(insertTexts).toEqual(['status']);
});
it('- should suggest schemas in default context', () => {
@@ -820,17 +802,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'myschema',
-
- ]);
+ expect(labels).toEqual(['myschema']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'myschema',
-
- ]);
+ expect(insertTexts).toEqual(['myschema']);
});
});
@@ -846,21 +822,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'order_id',
- 'orders',
- 'users',
-
- ]);
+ expect(labels).toEqual(['order_id', 'orders', 'users']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'order_id',
- 'orders',
- 'users',
-
- ]);
+ expect(insertTexts).toEqual(['order_id', 'orders', 'users']);
});
it('- should suggest columns after table name in ref', () => {
@@ -874,17 +840,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'id',
-
- ]);
+ expect(labels).toEqual(['id']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'id',
-
- ]);
+ expect(insertTexts).toEqual(['id']);
});
it('- should suggest schemas in ref context', () => {
@@ -898,21 +858,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'order_id',
- 'myschema',
- 'users',
-
- ]);
+ expect(labels).toEqual(['order_id', 'myschema', 'users']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'order_id',
- 'myschema',
- 'users',
-
- ]);
+ expect(insertTexts).toEqual(['order_id', 'myschema', 'users']);
});
});
@@ -928,19 +878,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'orders',
- 'users',
-
- ]);
+ expect(labels).toEqual(['orders', 'users']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'orders',
- 'users',
-
- ]);
+ expect(insertTexts).toEqual(['orders', 'users']);
});
it('- should suggest columns in Ref after dot', () => {
@@ -954,17 +896,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'id',
-
- ]);
+ expect(labels).toEqual(['id']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'id',
-
- ]);
+ expect(insertTexts).toEqual(['id']);
});
it('- should suggest after relationship operators', () => {
@@ -978,19 +914,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'orders',
- 'users',
-
- ]);
+ expect(labels).toEqual(['orders', 'users']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'orders',
- 'users',
-
- ]);
+ expect(insertTexts).toEqual(['orders', 'users']);
});
it('- should suggest ref settings in brackets', () => {
@@ -1004,21 +932,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'update',
- 'delete',
- 'color',
-
- ]);
+ expect(labels).toEqual(['update', 'delete', 'color']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'update: ',
- 'delete: ',
- 'color: ',
-
- ]);
+ expect(insertTexts).toEqual(['update: ', 'delete: ', 'color: ']);
});
it('- should suggest action values for update/delete settings', () => {
@@ -1064,19 +982,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'id',
- 'email',
-
- ]);
+ expect(labels).toEqual(['id', 'email']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'id',
- 'email',
-
- ]);
+ expect(insertTexts).toEqual(['id', 'email']);
});
it('- should suggest in composite index', () => {
@@ -1090,19 +1000,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'id',
- 'email',
-
- ]);
+ expect(labels).toEqual(['id', 'email']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'id',
- 'email',
-
- ]);
+ expect(insertTexts).toEqual(['id', 'email']);
});
});
@@ -1150,19 +1052,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'btree',
- 'hash',
-
- ]);
+ expect(labels).toEqual(['btree', 'hash']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'btree',
- 'hash',
-
- ]);
+ expect(insertTexts).toEqual(['btree', 'hash']);
});
});
@@ -1178,17 +1072,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'name',
-
- ]);
+ expect(labels).toEqual(['name']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'name: ',
-
- ]);
+ expect(insertTexts).toEqual(['name: ']);
});
});
@@ -1204,21 +1092,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'users',
- 'orders',
- 'Note',
-
- ]);
+ expect(labels).toEqual(['users', 'orders', 'Note']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'users',
- 'orders',
- 'Note',
-
- ]);
+ expect(insertTexts).toEqual(['users', 'orders', 'Note']);
});
it('- should suggest schema-qualified tables', () => {
@@ -1232,19 +1110,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'myschema',
- 'Note',
-
- ]);
+ expect(labels).toEqual(['myschema', 'Note']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'myschema',
- 'Note',
-
- ]);
+ expect(insertTexts).toEqual(['myschema', 'Note']);
});
});
@@ -1282,15 +1152,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'mypartial',
- ]);
+ expect(labels).toEqual(['mypartial']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'mypartial',
- ]);
+ expect(insertTexts).toEqual(['mypartial']);
});
});
@@ -1324,15 +1190,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'id',
- ]);
+ expect(labels).toEqual(['id']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'id',
- ]);
+ expect(insertTexts).toEqual(['id']);
});
});
@@ -1415,15 +1277,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'user-table',
- ]);
+ expect(labels).toEqual(['user-table']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- '"user-table"',
- ]);
+ expect(insertTexts).toEqual(['"user-table"']);
});
it('- should handle enum field settings', () => {
@@ -1437,17 +1295,11 @@ describe('[example] CompletionItemProvider', () => {
// Test labels
const labels = result.suggestions.map((s) => s.label);
- expect(labels).toEqual([
- 'note',
-
- ]);
+ expect(labels).toEqual(['note']);
// Test insertTexts
const insertTexts = result.suggestions.map((s) => s.insertText);
- expect(insertTexts).toEqual([
- 'note: ',
-
- ]);
+ expect(insertTexts).toEqual(['note: ']);
});
it('- should handle project scope', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_diagramview.test.ts b/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_diagramview.test.ts
index a79d5c079..e7b8cc679 100644
--- a/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_diagramview.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_diagramview.test.ts
@@ -1,7 +1,11 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLCompletionItemProvider from '@/services/suggestions/provider';
-import { createMockTextModel, createPosition } from '@tests/utils';
+import {
+ createMockTextModel, createPosition,
+} from '@tests/utils';
describe('[DiagramView] CompletionItemProvider', () => {
describe('top-level suggestions should include DiagramView', () => {
diff --git a/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_records.test.ts b/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_records.test.ts
index b392390ac..7aac9d628 100644
--- a/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/services/suggestions/suggestions_records.test.ts
@@ -1,9 +1,17 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import Compiler from '@/compiler';
import DBMLCompletionItemProvider from '@/services/suggestions/provider';
-import { createMockTextModel, createPosition } from '@tests/utils';
-import { getColumnsFromTableSymbol } from '@/services/suggestions/utils';
-import { TableSymbol } from '@/core/analyzer/symbol/symbols';
+import {
+ createMockTextModel, createPosition,
+} from '@tests/utils';
+import {
+ getColumnsFromTableSymbol,
+} from '@/services/suggestions/utils';
+import {
+ TableSymbol,
+} from '@/core/types/symbol/symbols';
describe('[example] CompletionItemProvider - Records', () => {
describe('should NOT suggest record entry snippets in Records body (handled by inline completions)', () => {
diff --git a/packages/dbml-parse/__tests__/examples/validator/records.test.ts b/packages/dbml-parse/__tests__/examples/validator/records.test.ts
index 5013d937a..40ded2793 100644
--- a/packages/dbml-parse/__tests__/examples/validator/records.test.ts
+++ b/packages/dbml-parse/__tests__/examples/validator/records.test.ts
@@ -1,5 +1,9 @@
-import { describe, expect, test } from 'vitest';
-import { analyze } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ analyze,
+} from '@tests/utils';
describe('[example] records validator', () => {
test('should accept valid records', () => {
diff --git a/packages/dbml-parse/__tests__/examples/validator/validator.test.ts b/packages/dbml-parse/__tests__/examples/validator/validator.test.ts
index c48ba37fa..71a0da49d 100644
--- a/packages/dbml-parse/__tests__/examples/validator/validator.test.ts
+++ b/packages/dbml-parse/__tests__/examples/validator/validator.test.ts
@@ -1,7 +1,15 @@
-import { describe, expect } from 'vitest';
-import { CompileErrorCode } from '@/core/errors';
-import { SyntaxToken } from '@/core/lexer/tokens';
-import { analyze } from '@tests/utils';
+import {
+ describe, expect,
+} from 'vitest';
+import {
+ CompileErrorCode,
+} from '@/core/types/errors';
+import {
+ SyntaxToken,
+} from '@/core/types/tokens';
+import {
+ analyze,
+} from '@tests/utils';
describe('[example] validator', () => {
describe('table validation', () => {
@@ -21,7 +29,7 @@ describe('[example] validator', () => {
expect(errors).toHaveLength(1);
expect(errors[0].code).toBe(CompileErrorCode.DUPLICATE_NAME);
- expect(errors[0].diagnostic).toBe("Table name 'users' already exists in schema 'public'");
+ expect(errors[0].diagnostic).toBe("Table 'users' already exists in schema 'public'");
// Error should point to the SECOND (duplicate) table, not the first
expect((errors[0].nodeOrToken as SyntaxToken).startPos.line).toBe(2);
@@ -214,7 +222,7 @@ describe('[example] validator', () => {
expect(errors).toHaveLength(1);
expect(errors[0].code).toBe(CompileErrorCode.DUPLICATE_NAME);
- expect(errors[0].diagnostic).toBe("Enum name status already exists in schema 'public'");
+ expect(errors[0].diagnostic).toBe("Enum 'status' already exists in schema 'public'");
// Error should be on second enum
expect((errors[0].nodeOrToken as SyntaxToken).startPos.line).toBe(2);
@@ -957,7 +965,9 @@ Table users { name varchar }`;
});
test('should handle table with many columns', () => {
- const columns = Array.from({ length: 100 }, (_, i) => `col${i} int`).join('\n');
+ const columns = Array.from({
+ length: 100,
+ }, (_, i) => `col${i} int`).join('\n');
const source = `Table big_table { ${columns} }`;
const errors = analyze(source).getErrors();
@@ -965,7 +975,9 @@ Table users { name varchar }`;
});
test('should handle many tables', () => {
- const tables = Array.from({ length: 50 }, (_, i) => `Table t${i} { id int }`).join('\n');
+ const tables = Array.from({
+ length: 50,
+ }, (_, i) => `Table t${i} { id int }`).join('\n');
const errors = analyze(tables).getErrors();
expect(errors).toHaveLength(0);
@@ -1071,7 +1083,7 @@ Table users { name varchar }`;
const errors = analyze(source).getErrors();
expect(errors).toHaveLength(1);
- expect(errors[0].diagnostic).toBe("Table name 'users' already exists in schema 'public'");
+ expect(errors[0].diagnostic).toBe("Table 'users' already exists in schema 'public'");
});
test('should include schema context in error messages when relevant', () => {
@@ -1082,7 +1094,7 @@ Table users { name varchar }`;
const errors = analyze(source).getErrors();
expect(errors).toHaveLength(1);
- expect(errors[0].diagnostic).toBe("Table name 'users' already exists in schema 'auth'");
+ expect(errors[0].diagnostic).toBe("Table 'users' already exists in schema 'auth'");
});
test('should provide actionable error messages for unknown references', () => {
diff --git a/packages/dbml-parse/__tests__/examples/wildcard/wildcard.test.ts b/packages/dbml-parse/__tests__/examples/wildcard/wildcard.test.ts
index ff04b4fa9..57aebc13a 100644
--- a/packages/dbml-parse/__tests__/examples/wildcard/wildcard.test.ts
+++ b/packages/dbml-parse/__tests__/examples/wildcard/wildcard.test.ts
@@ -1,10 +1,24 @@
-import { describe, expect, test } from 'vitest';
-import { SyntaxTokenKind, isTriviaToken } from '@/core/lexer/tokens';
-import { CompileErrorCode } from '@/core/errors';
-import { SyntaxNodeKind } from '@/core/parser/nodes';
-import { WildcardNode } from '@/core/parser/nodes';
-import { isWildcardExpression } from '@/core/parser/utils';
-import { lex, parse, analyze } from '@tests/utils';
+import {
+ describe, expect, test,
+} from 'vitest';
+import {
+ SyntaxTokenKind, isTriviaToken,
+} from '@/core/types/tokens';
+import {
+ CompileErrorCode,
+} from '@/core/types/errors';
+import {
+ SyntaxNodeKind,
+} from '@/core/types/nodes';
+import {
+ WildcardNode,
+} from '@/core/types/nodes';
+import {
+ isWildcardExpression,
+} from '@/core/parser/utils';
+import {
+ lex, parse, analyze,
+} from '@tests/utils';
// Helper to get non-trivia, non-EOF tokens
function getTokens (source: string) {
@@ -45,7 +59,9 @@ describe('[example] wildcard', () => {
describe('parser', () => {
test('should parse * as WildcardNode', () => {
const source = 'Table t { * }';
- const { ast } = parse(source).getValue();
+ const {
+ ast,
+ } = parse(source).getValue();
const table = ast.body[0];
const body = table.body as any;
const field = body.body[0];
@@ -56,7 +72,9 @@ describe('[example] wildcard', () => {
test('isWildcardExpression should return true for WildcardNode', () => {
const source = 'Table t { * }';
- const { ast } = parse(source).getValue();
+ const {
+ ast,
+ } = parse(source).getValue();
const table = ast.body[0];
const body = table.body as any;
const field = body.body[0];
@@ -66,7 +84,9 @@ describe('[example] wildcard', () => {
test('isWildcardExpression should return false for non-wildcard', () => {
const source = 'Table t { id int }';
- const { ast } = parse(source).getValue();
+ const {
+ ast,
+ } = parse(source).getValue();
const table = ast.body[0];
const body = table.body as any;
const field = body.body[0];
diff --git a/packages/dbml-parse/__tests__/fuzz/interpreter.test.ts b/packages/dbml-parse/__tests__/fuzz/interpreter.test.ts
index 80c4d9a3c..8f8421a91 100644
--- a/packages/dbml-parse/__tests__/fuzz/interpreter.test.ts
+++ b/packages/dbml-parse/__tests__/fuzz/interpreter.test.ts
@@ -1,4 +1,6 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import * as fc from 'fast-check';
import {
dbmlSchemaArbitrary,
@@ -12,11 +14,19 @@ import {
tableWithZeroSettingsArbitrary,
charSubstitutionArbitrary,
} from '../utils/arbitraries';
-import { interpret, analyze } from '../utils';
-
-const FUZZ_CONFIG = { numRuns: 50 };
-const ROBUSTNESS_CONFIG = { numRuns: 25 };
-const SEMANTIC_CONFIG = { numRuns: 50 };
+import {
+ interpret, analyze,
+} from '../utils';
+
+const FUZZ_CONFIG = {
+ numRuns: 50,
+};
+const ROBUSTNESS_CONFIG = {
+ numRuns: 25,
+};
+const SEMANTIC_CONFIG = {
+ numRuns: 50,
+};
describe('[fuzz] interpreter - valid input', () => {
it('should interpret valid DBML schemas without throwing', () => {
@@ -153,7 +163,10 @@ describe('[fuzz] interpreter - robustness (arbitrary input)', () => {
it('should handle very long inputs (5-20KB) with valid structure', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 5000, maxLength: 20000 }).filter((s) => !s.includes('\0')),
+ fc.string({
+ minLength: 5000,
+ maxLength: 20000,
+ }).filter((s) => !s.includes('\0')),
(source: string) => {
const result = interpret(source);
@@ -166,7 +179,9 @@ describe('[fuzz] interpreter - robustness (arbitrary input)', () => {
}
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -328,7 +343,9 @@ describe('[fuzz] interpreter - consistency', () => {
expect(result1.getErrors().length).toBe(result2.getErrors().length);
expect(result2.getErrors().length).toBe(result3.getErrors().length);
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -376,7 +393,10 @@ describe('[fuzz] interpreter - mutation resilience', () => {
fc.property(
tableArbitrary,
fc.nat(),
- fc.string({ minLength: 1, maxLength: 1 }),
+ fc.string({
+ minLength: 1,
+ maxLength: 1,
+ }),
(source: string, position: number, char: string) => {
fc.pre(char !== '\0');
@@ -392,7 +412,9 @@ describe('[fuzz] interpreter - mutation resilience', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -423,14 +445,22 @@ describe('[fuzz] interpreter - edge cases', () => {
}
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
it('should handle comments-only input', () => {
fc.assert(
fc.property(
- fc.array(fc.string({ minLength: 0, maxLength: 100 }), { minLength: 1, maxLength: 10 }),
+ fc.array(fc.string({
+ minLength: 0,
+ maxLength: 100,
+ }), {
+ minLength: 1,
+ maxLength: 10,
+ }),
(comments: string[]) => {
const source = comments.map((c) => `// ${c.replace(/\n/g, ' ')}`).join('\n');
@@ -447,7 +477,9 @@ describe('[fuzz] interpreter - edge cases', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
diff --git a/packages/dbml-parse/__tests__/fuzz/parser.test.ts b/packages/dbml-parse/__tests__/fuzz/parser.test.ts
index a00efc49a..615bf9902 100644
--- a/packages/dbml-parse/__tests__/fuzz/parser.test.ts
+++ b/packages/dbml-parse/__tests__/fuzz/parser.test.ts
@@ -1,4 +1,6 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import * as fc from 'fast-check';
import {
tableArbitrary,
@@ -30,11 +32,19 @@ import {
// Line ending utilities
crlfSchemaArbitrary,
} from '../utils/arbitraries';
-import { parse, lex } from '../utils';
-import { SyntaxNodeKind } from '@/core/parser/nodes';
+import {
+ parse, lex,
+} from '../utils';
+import {
+ SyntaxNodeKind,
+} from '@/core/types/nodes';
-const FUZZ_CONFIG = { numRuns: 50 };
-const ROBUSTNESS_CONFIG = { numRuns: 25 };
+const FUZZ_CONFIG = {
+ numRuns: 50,
+};
+const ROBUSTNESS_CONFIG = {
+ numRuns: 25,
+};
describe('[fuzz] parser - valid input', () => {
it('should parse valid tables without errors and produce valid AST', () => {
@@ -201,7 +211,10 @@ describe('[fuzz] parser - robustness (arbitrary input)', () => {
it('should handle very long inputs (10-50KB) without stack overflow', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 10000, maxLength: 50000 }).filter((s) => !s.includes('\0')),
+ fc.string({
+ minLength: 10000,
+ maxLength: 50000,
+ }).filter((s) => !s.includes('\0')),
(source: string) => {
const result = parse(source);
const ast = result.getValue().ast;
@@ -211,14 +224,19 @@ describe('[fuzz] parser - robustness (arbitrary input)', () => {
expect(ast.end).toBeLessThanOrEqual(source.length);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
it('should handle deeply nested brackets (1-200 levels)', () => {
fc.assert(
fc.property(
- fc.integer({ min: 1, max: 200 }),
+ fc.integer({
+ min: 1,
+ max: 200,
+ }),
(depth: number) => {
const source = '['.repeat(depth) + ']'.repeat(depth);
const result = parse(source);
@@ -226,14 +244,19 @@ describe('[fuzz] parser - robustness (arbitrary input)', () => {
expect(result.getValue().ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
it('should handle deeply nested braces (1-100 levels)', () => {
fc.assert(
fc.property(
- fc.integer({ min: 1, max: 100 }),
+ fc.integer({
+ min: 1,
+ max: 100,
+ }),
(depth: number) => {
const source = 'Table t '.repeat(depth) + '{'.repeat(depth) + '}'.repeat(depth);
const result = parse(source);
@@ -241,7 +264,9 @@ describe('[fuzz] parser - robustness (arbitrary input)', () => {
expect(result.getValue().ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -261,7 +286,9 @@ describe('[fuzz] parser - robustness (arbitrary input)', () => {
expect(ast.body.length).toBeGreaterThanOrEqual(0);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -284,7 +311,9 @@ describe('[fuzz] parser - error recovery', () => {
it('should report errors with valid locations', () => {
fc.assert(
- fc.property(fc.string({ minLength: 1 }), (source: string) => {
+ fc.property(fc.string({
+ minLength: 1,
+ }), (source: string) => {
const result = parse(source);
const errors = result.getErrors();
@@ -344,7 +373,10 @@ describe('[fuzz] parser - mutation resilience', () => {
fc.property(
tableArbitrary,
fc.nat(),
- fc.string({ minLength: 1, maxLength: 1 }).filter((c) => c !== '\0'),
+ fc.string({
+ minLength: 1,
+ maxLength: 1,
+ }).filter((c) => c !== '\0'),
(source: string, position: number, char: string) => {
const pos = position % (source.length + 1);
const mutated = source.slice(0, pos) + char + source.slice(pos);
@@ -356,7 +388,9 @@ describe('[fuzz] parser - mutation resilience', () => {
expect(ast.end).toBeLessThanOrEqual(mutated.length);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -377,7 +411,9 @@ describe('[fuzz] parser - mutation resilience', () => {
expect(ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -392,7 +428,9 @@ describe('[fuzz] parser - mutation resilience', () => {
expect(ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -407,7 +445,9 @@ describe('[fuzz] parser - mutation resilience', () => {
expect(ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -422,7 +462,9 @@ describe('[fuzz] parser - mutation resilience', () => {
expect(ast.kind).toBe(SyntaxNodeKind.PROGRAM);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -432,7 +474,10 @@ describe('[fuzz] parser - true binary fuzzing (unconstrained)', () => {
it('should handle arbitrary byte sequences without crashing', () => {
fc.assert(
fc.property(
- fc.uint8Array({ minLength: 0, maxLength: 1000 }),
+ fc.uint8Array({
+ minLength: 0,
+ maxLength: 1000,
+ }),
(bytes: Uint8Array) => {
// Convert bytes to string - may contain null bytes, control chars, etc.
const source = String.fromCharCode(...bytes);
@@ -452,7 +497,9 @@ describe('[fuzz] parser - true binary fuzzing (unconstrained)', () => {
}
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -476,14 +523,22 @@ describe('[fuzz] parser - true binary fuzzing (unconstrained)', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
it('should handle high unicode codepoints', () => {
fc.assert(
fc.property(
- fc.array(fc.integer({ min: 0, max: 0x10FFFF }), { minLength: 1, maxLength: 100 }),
+ fc.array(fc.integer({
+ min: 0,
+ max: 0x10FFFF,
+ }), {
+ minLength: 1,
+ maxLength: 100,
+ }),
(codePoints: number[]) => {
// Convert codepoints to string, filtering out invalid surrogate pairs
const validCodePoints = codePoints.filter((cp) =>
@@ -501,22 +556,24 @@ describe('[fuzz] parser - true binary fuzzing (unconstrained)', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
it('should handle control characters in various positions', () => {
const controlChars = fc.constantFrom(
- '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
- '\x08', '\x0B', '\x0C', '\x0E', '\x0F', '\x10', '\x11', '\x12',
- '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A',
- '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', '\x7F',
+ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x0B', '\x0C', '\x0E', '\x0F', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', '\x7F',
);
fc.assert(
fc.property(
tableArbitrary,
- fc.array(fc.tuple(fc.nat(), controlChars), { minLength: 1, maxLength: 5 }),
+ fc.array(fc.tuple(fc.nat(), controlChars), {
+ minLength: 1,
+ maxLength: 5,
+ }),
(source: string, insertions: Array<[number, string]>) => {
// Insert control characters at various positions
let modified = source;
@@ -535,7 +592,9 @@ describe('[fuzz] parser - true binary fuzzing (unconstrained)', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -551,7 +610,9 @@ describe('[fuzz] parser - malformed input (true fuzzing)', () => {
expect(ast.kind).toBe(SyntaxNodeKind.PROGRAM);
expect(ast.body).toBeInstanceOf(Array);
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -623,7 +684,9 @@ describe('[fuzz] parser - malformed input (true fuzzing)', () => {
expect(result.getValue().ast.kind).toBe(SyntaxNodeKind.PROGRAM);
}),
- { numRuns: 25 },
+ {
+ numRuns: 25,
+ },
);
});
@@ -708,7 +771,10 @@ describe('[fuzz] parser - semantic correctness', () => {
it('should count elements correctly', () => {
fc.assert(
fc.property(
- fc.array(tableArbitrary, { minLength: 1, maxLength: 5 }),
+ fc.array(tableArbitrary, {
+ minLength: 1,
+ maxLength: 5,
+ }),
(tables: string[]) => {
const source = tables.join('\n\n');
const result = parse(source);
diff --git a/packages/dbml-parse/__tests__/fuzz/services.test.ts b/packages/dbml-parse/__tests__/fuzz/services.test.ts
index 542d28d4a..3be701ffb 100644
--- a/packages/dbml-parse/__tests__/fuzz/services.test.ts
+++ b/packages/dbml-parse/__tests__/fuzz/services.test.ts
@@ -1,23 +1,37 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import * as fc from 'fast-check';
import Compiler from '@/compiler';
import DBMLDefinitionProvider from '@/services/definition/provider';
import DBMLReferencesProvider from '@/services/references/provider';
import DBMLCompletionItemProvider from '@/services/suggestions/provider';
-import { dbmlSchemaArbitrary, tableArbitrary } from '../utils/arbitraries';
-import { MockTextModel, createPosition } from '../utils';
-
-const FUZZ_CONFIG = { numRuns: 50 };
-const ROBUSTNESS_CONFIG = { numRuns: 25 };
+import {
+ dbmlSchemaArbitrary, tableArbitrary,
+} from '../utils/arbitraries';
+import {
+ MockTextModel, createPosition,
+} from '../utils';
+
+const FUZZ_CONFIG = {
+ numRuns: 50,
+};
+const ROBUSTNESS_CONFIG = {
+ numRuns: 25,
+};
// Helper to create valid position within source bounds
-function clampPosition (line: number, col: number, source: string): { line: number; col: number } {
+function clampPosition (line: number, col: number, source: string): { line: number;
+ col: number; } {
const lines = source.split('\n');
const maxLine = Math.max(0, lines.length - 1);
const clampedLine = Math.min(Math.max(0, line), maxLine);
const maxCol = lines[clampedLine]?.length || 0;
const clampedCol = Math.min(Math.max(0, col), maxCol);
- return { line: clampedLine, col: clampedCol };
+ return {
+ line: clampedLine,
+ col: clampedCol,
+ };
}
describe('[fuzz] DefinitionProvider - robustness', () => {
@@ -85,7 +99,9 @@ describe('[fuzz] DefinitionProvider - robustness', () => {
const model = new MockTextModel(source) as any;
// Use clamped position to increase chance of valid results
- const { line: clampedLine, col: clampedCol } = clampPosition(line, col, source);
+ const {
+ line: clampedLine, col: clampedCol,
+ } = clampPosition(line, col, source);
const position = createPosition(clampedLine + 1, clampedCol + 1);
const result = definitionProvider.provideDefinition(model, position);
@@ -172,7 +188,9 @@ describe('[fuzz] ReferencesProvider - robustness', () => {
const referencesProvider = new DBMLReferencesProvider(compiler);
const model = new MockTextModel(source) as any;
- const { line: clampedLine, col: clampedCol } = clampPosition(line, col, source);
+ const {
+ line: clampedLine, col: clampedCol,
+ } = clampPosition(line, col, source);
const position = createPosition(clampedLine + 1, clampedCol + 1);
const result = referencesProvider.provideReferences(model, position);
@@ -256,7 +274,9 @@ describe('[fuzz] CompletionItemProvider - robustness', () => {
const completionProvider = new DBMLCompletionItemProvider(compiler);
const model = new MockTextModel(source) as any;
- const { line: clampedLine, col: clampedCol } = clampPosition(line, col, source);
+ const {
+ line: clampedLine, col: clampedCol,
+ } = clampPosition(line, col, source);
const position = createPosition(clampedLine + 1, clampedCol + 1);
const result = completionProvider.provideCompletionItems(model, position);
@@ -331,7 +351,9 @@ describe('[fuzz] services - consistency', () => {
expect(didThrow).toBe(false);
},
),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -378,7 +400,9 @@ describe('[fuzz] services - edge cases', () => {
}
expect(didThrow).toBe(false);
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -403,7 +427,9 @@ describe('[fuzz] services - edge cases', () => {
}
expect(didThrow).toBe(false);
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
@@ -418,7 +444,12 @@ describe('[fuzz] services - edge cases', () => {
const completionProvider = new DBMLCompletionItemProvider(compiler);
// Test at various positions
- [1, 100, 500, 1000].forEach((col) => {
+ [
+ 1,
+ 100,
+ 500,
+ 1000,
+ ].forEach((col) => {
expect(() => {
definitionProvider.provideDefinition(model, createPosition(1, col));
completionProvider.provideCompletionItems(model, createPosition(1, col));
@@ -427,7 +458,9 @@ describe('[fuzz] services - edge cases', () => {
});
it('should handle source with many lines', () => {
- const manyLines = Array.from({ length: 500 }, (_, i) => `Table t${i} { id int }`).join('\n');
+ const manyLines = Array.from({
+ length: 500,
+ }, (_, i) => `Table t${i} { id int }`).join('\n');
const compiler = new Compiler();
compiler.setSource(manyLines);
@@ -435,7 +468,12 @@ describe('[fuzz] services - edge cases', () => {
const definitionProvider = new DBMLDefinitionProvider(compiler);
// Test at various line positions
- [1, 100, 250, 500].forEach((line) => {
+ [
+ 1,
+ 100,
+ 250,
+ 500,
+ ].forEach((line) => {
expect(() => {
definitionProvider.provideDefinition(model, createPosition(line, 1));
}).not.toThrow();
@@ -446,7 +484,10 @@ describe('[fuzz] services - edge cases', () => {
describe('[fuzz] services - unicode handling', () => {
it('should handle unicode identifiers', () => {
fc.assert(
- fc.property(fc.string({ minLength: 1, maxLength: 20 }), (name: string) => {
+ fc.property(fc.string({
+ minLength: 1,
+ maxLength: 20,
+ }), (name: string) => {
// Escape special characters for valid DBML
const safeName = name.replace(/["\n\r\\\0]/g, '');
if (safeName.length === 0) return;
@@ -466,7 +507,9 @@ describe('[fuzz] services - unicode handling', () => {
}
expect(didThrow).toBe(false);
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
diff --git a/packages/dbml-parse/__tests__/properties/lexer.test.ts b/packages/dbml-parse/__tests__/properties/lexer.test.ts
index d7decfe1a..f93c03916 100644
--- a/packages/dbml-parse/__tests__/properties/lexer.test.ts
+++ b/packages/dbml-parse/__tests__/properties/lexer.test.ts
@@ -1,11 +1,23 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import * as fc from 'fast-check';
-import { SyntaxTokenKind } from '../../src';
-import { tokenStreamArbitrary, identifierArbitrary } from '../utils/arbitraries';
-import { lex } from '../utils';
-
-const PROPERTY_TEST_CONFIG = { numRuns: 50 };
-const EXTENDED_CONFIG = { numRuns: 25 };
+import {
+ SyntaxTokenKind,
+} from '../../src';
+import {
+ tokenStreamArbitrary, identifierArbitrary,
+} from '../utils/arbitraries';
+import {
+ lex,
+} from '../utils';
+
+const PROPERTY_TEST_CONFIG = {
+ numRuns: 50,
+};
+const EXTENDED_CONFIG = {
+ numRuns: 25,
+};
describe('[property] lexer', () => {
it('should roundtrip', () => {
@@ -46,7 +58,9 @@ describe('[property] lexer', () => {
fc.property(tokenStreamArbitrary, (source: string) => {
const tokens = lex(source).getValue().flatMap((token) => [...token.leadingTrivia, token, ...token.trailingTrivia]);
tokens.forEach((token) => {
- const { startPos, endPos } = token;
+ const {
+ startPos, endPos,
+ } = token;
expect(startPos.offset).toEqual(convertLineAndColumnToOffset(source, startPos.line, startPos.column));
expect(endPos.offset).toEqual(convertLineAndColumnToOffset(source, endPos.line, endPos.column));
});
@@ -76,10 +90,7 @@ describe('[property] lexer', () => {
const tokens = lex(source).getValue().flatMap((token) => [...token.leadingTrivia, token, ...token.trailingTrivia]);
tokens.reduce(([isNewlinePrevious, previousLine], token) => {
if (isNewlinePrevious) expect(token.startPos.line).toBe(previousLine + 1);
- return [
- token.kind === SyntaxTokenKind.NEWLINE,
- token.startPos.line,
- ] as [boolean, number];
+ return [token.kind === SyntaxTokenKind.NEWLINE, token.startPos.line] as [boolean, number];
}, [true, -1] as [boolean, number]);
}),
PROPERTY_TEST_CONFIG,
@@ -306,7 +317,10 @@ describe('[property] lexer - negative tests', () => {
it('should report error for unclosed single-quote strings', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 0, maxLength: 100 }).filter((s) => !s.includes("'")),
+ fc.string({
+ minLength: 0,
+ maxLength: 100,
+ }).filter((s) => !s.includes("'")),
(content: string) => {
// String with opening quote but no closing
const source = `'${content}`;
@@ -323,7 +337,10 @@ describe('[property] lexer - negative tests', () => {
it('should report error for unclosed double-quote strings', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 0, maxLength: 100 }).filter((s) => !s.includes('"')),
+ fc.string({
+ minLength: 0,
+ maxLength: 100,
+ }).filter((s) => !s.includes('"')),
(content: string) => {
const source = `"${content}`;
const result = lex(source);
@@ -339,7 +356,10 @@ describe('[property] lexer - negative tests', () => {
it('should report error for unclosed backtick expressions', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 0, maxLength: 100 }).filter((s) => !s.includes('`')),
+ fc.string({
+ minLength: 0,
+ maxLength: 100,
+ }).filter((s) => !s.includes('`')),
(content: string) => {
const source = `\`${content}`;
const result = lex(source);
@@ -356,9 +376,18 @@ describe('[property] lexer - negative tests', () => {
// Multiple decimal points should error
fc.assert(
fc.property(
- fc.integer({ min: 0, max: 999 }),
- fc.integer({ min: 0, max: 999 }),
- fc.integer({ min: 0, max: 999 }),
+ fc.integer({
+ min: 0,
+ max: 999,
+ }),
+ fc.integer({
+ min: 0,
+ max: 999,
+ }),
+ fc.integer({
+ min: 0,
+ max: 999,
+ }),
(a: number, b: number, c: number) => {
const source = `${a}.${b}.${c}`;
const result = lex(source);
@@ -374,7 +403,10 @@ describe('[property] lexer - negative tests', () => {
it('should handle at-sign as unknown symbol', () => {
fc.assert(
fc.property(
- fc.string({ minLength: 0, maxLength: 20 }),
+ fc.string({
+ minLength: 0,
+ maxLength: 20,
+ }),
(suffix: string) => {
const source = `@${suffix.replace(/@/g, '')}`;
const result = lex(source);
diff --git a/packages/dbml-parse/__tests__/properties/parser.test.ts b/packages/dbml-parse/__tests__/properties/parser.test.ts
index d56a00aa2..a107513e9 100644
--- a/packages/dbml-parse/__tests__/properties/parser.test.ts
+++ b/packages/dbml-parse/__tests__/properties/parser.test.ts
@@ -1,15 +1,31 @@
-import { describe, expect, it } from 'vitest';
+import {
+ describe, expect, it,
+} from 'vitest';
import * as fc from 'fast-check';
-import { dbmlSchemaArbitrary, tableArbitrary, enumArbitrary, tablePartialArbitrary, partialInjectionArbitrary } from '../utils/arbitraries';
-import { isEqual } from 'lodash-es';
-import { parse, print, lex } from '../utils';
-import { SyntaxNodeKind, BlockExpressionNode } from '@/core/parser/nodes';
-
-const PROPERTY_TEST_CONFIG = { numRuns: 50 };
-const EXTENDED_CONFIG = { numRuns: 25 };
+import {
+ dbmlSchemaArbitrary, tableArbitrary, enumArbitrary, tablePartialArbitrary, partialInjectionArbitrary,
+} from '../utils/arbitraries';
+import {
+ isEqual,
+} from 'lodash-es';
+import {
+ parse, print, lex,
+} from '../utils';
+import {
+ SyntaxNodeKind, BlockExpressionNode,
+} from '@/core/types/nodes';
+
+const PROPERTY_TEST_CONFIG = {
+ numRuns: 50,
+};
+const EXTENDED_CONFIG = {
+ numRuns: 25,
+};
describe('[property] parser', () => {
- it('should produce consistent ASTs', { timeout: 30000 }, () => {
+ it('should produce consistent ASTs', {
+ timeout: 30000,
+ }, () => {
// Property: Parsing the same source twice should produce the same ASTs
fc.assert(
fc.property(dbmlSchemaArbitrary, (source: string) => {
@@ -51,7 +67,9 @@ describe('[property] parser', () => {
// Property: Source 1 -parse-> ast -print-> Source 2
// Then: Source 1 === Source 2
fc.assert(
- fc.property(dbmlSchemaArbitrary, fc.nat({ max: 10 }), fc.string(), (source: string, injectedPos: number, injectSource: string) => {
+ fc.property(dbmlSchemaArbitrary, fc.nat({
+ max: 10,
+ }), fc.string(), (source: string, injectedPos: number, injectSource: string) => {
const injectedSource = `${source.slice(0, injectedPos)}${injectSource}${source.slice(injectedPos)}`;
const ast = parse(injectedSource).getValue().ast;
const newSource = print(injectedSource, ast);
@@ -351,7 +369,9 @@ describe('[property] parser - AST structure semantics', () => {
ast.body.forEach((elem) => checkNested(elem, 0, source.length));
}),
- { numRuns: 50 },
+ {
+ numRuns: 50,
+ },
);
});
});
@@ -361,7 +381,10 @@ describe('[property] parser - negative tests', () => {
it('should report errors for unclosed braces', () => {
fc.assert(
fc.property(
- fc.integer({ min: 1, max: 10 }),
+ fc.integer({
+ min: 1,
+ max: 10,
+ }),
(count: number) => {
const source = 'Table t '.repeat(count) + '{'.repeat(count);
const result = parse(source);
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/binder.test.ts b/packages/dbml-parse/__tests__/snapshots/binder/binder.test.ts
index 95d653c8a..0d1628c73 100644
--- a/packages/dbml-parse/__tests__/snapshots/binder/binder.test.ts
+++ b/packages/dbml-parse/__tests__/snapshots/binder/binder.test.ts
@@ -1,29 +1,93 @@
-import { readFileSync } from 'fs';
-import path from 'path';
-import { describe, expect, it } from 'vitest';
+import {
+ DEFAULT_ENTRY,
+} from '@/constants';
+import {
+ readFileSync,
+} from 'node:fs';
+import path from 'node:path';
+import {
+ describe, expect, it,
+} from 'vitest';
import Lexer from '@/core/lexer/lexer';
import Parser from '@/core/parser/parser';
-import { NodeSymbolIdGenerator } from '@/core/analyzer/symbol/symbols';
-import { SyntaxNodeIdGenerator } from '@/core/parser/nodes';
+import type {
+ ProgramNode,
+} from '@/core/types/nodes';
import Analyzer from '@/core/analyzer/analyzer';
-import { serialize, scanTestNames } from '@tests/utils';
+import {
+ scanTestNames, toSnapshot, collectNodesWithReferee,
+} from '@tests/utils';
+import type Report from '@/core/types/report';
+import Compiler from '@/compiler';
+import {
+ SymbolKind,
+} from '@/core/types/symbol';
+import type {
+ NodeSymbol,
+} from '@/core/types/symbol';
+
+function serializeBinderResult (compiler: Compiler, report: Report): string {
+ const value = report.getValue();
+ const errors = report.getErrors();
+ const warnings = report.getWarnings();
+ const nodeReferees = collectNodesWithReferee(value);
+
+ // FIXME: this snapshot manually splits the program's symbol table into
+ // (named schemas at the top, public-schema members below) so the output
+ // matches what we want long-term: named schemas owned by the program node and
+ // the public schema only carrying non-schema members. The current analyzer
+ // can't express that directly. The query-based-compiler rework will surface
+ // this shape natively, at which point this loop can be replaced by a single
+ // `compiler.symbolMembers(programSymbol)` walk.
+ const programSymbol = value.symbol;
+ const schemas: NodeSymbol[] = [];
+ const publicSchemaMembers: NodeSymbol[] = [];
+ if (programSymbol?.symbolTable) {
+ for (const [, sym] of programSymbol.symbolTable.entries()) {
+ if (sym.isKind(SymbolKind.Schema)) {
+ schemas.push(sym);
+ } else {
+ publicSchemaMembers.push(sym);
+ }
+ }
+ }
+
+ return JSON.stringify(toSnapshot(compiler, {
+ program: {
+ schemas,
+ publicSchema: publicSchemaMembers,
+ } as any,
+ nodeReferees,
+ errors,
+ warnings,
+ }), null, 2);
+}
describe('[snapshot] binder', () => {
const testNames = scanTestNames(path.resolve(__dirname, './input/'));
testNames.forEach((testName) => {
const program = readFileSync(path.resolve(__dirname, `./input/${testName}.in.dbml`), 'utf-8');
- const symbolIdGenerator = new NodeSymbolIdGenerator();
- const nodeIdGenerator = new SyntaxNodeIdGenerator();
- const report = new Lexer(program)
+
+ const compiler = new Compiler();
+ compiler.setSource(program);
+
+ const {
+ // @ts-expect-error "Current workaround to use compiler but only trigger analyzer"
+ nodeIdGenerator, symbolIdGenerator,
+ } = compiler;
+
+ const report = new Lexer(program, DEFAULT_ENTRY)
.lex()
.chain((tokens) => {
- return new Parser(program, tokens, nodeIdGenerator).parse();
+ return new Parser(program, tokens, nodeIdGenerator, DEFAULT_ENTRY).parse();
})
- .chain(({ ast }) => {
+ .chain(({
+ ast,
+ }) => {
return new Analyzer(ast, symbolIdGenerator).analyze();
});
- const output = serialize(report, true);
+ const output = serializeBinderResult(compiler, report);
it(testName, () => expect(output).toMatchFileSnapshot(path.resolve(__dirname, `./output/${testName}.out.json`)));
});
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/alias_of_duplicated_names.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/alias_of_duplicated_names.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/alias_of_duplicated_names.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/alias_of_duplicated_names.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_alias_name.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_alias_name.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_alias_name.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_alias_name.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_columns.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_columns.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_columns.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_columns.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_enum_field.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_enum_field.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_enum_field.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_enum_field.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_names.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_names.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_names.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_names.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_table_partial_injections.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_table_partial_injections.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/duplicate_table_partial_injections.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/duplicate_table_partial_injections.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/multiple_notes_in_table_group.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/multiple_notes_in_table_group.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/multiple_notes_in_table_group.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/multiple_notes_in_table_group.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/ref_validator.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/ref.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/ref_validator.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/sticky_notes.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/sticky_notes_validator.in.dbml
similarity index 100%
rename from packages/dbml-parse/__tests__/snapshots/validator/input/sticky_notes.in.dbml
rename to packages/dbml-parse/__tests__/snapshots/binder/input/sticky_notes_validator.in.dbml
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/alias_of_duplicated_names.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/alias_of_duplicated_names.out.json
new file mode 100644
index 000000000..5878026c9
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/alias_of_duplicated_names.out.json
@@ -0,0 +1,72 @@
+{
+ "errors": [
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "Table 'Users' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L4:C6, L4:C11]",
+ "snippet": "Users"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "Table 'U1' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Products@[L8:C6, L8:C14]",
+ "snippet": "Products"
+ }
+ }
+ }
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Table@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User... U2 {\r\n\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User... U2 {\r\n\r\n}"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@Table@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User... U2 {\r\n\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User... U2 {\r\n\r\n}"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@Table@Products@[L8:C0, L10:C1]",
+ "snippet": "Table Prod...{\r\n \r\n}"
+ },
+ "declaration": {
+ "id": "node@@Products@[L8:C0, L10:C1]",
+ "snippet": "Table Prod...{\r\n \r\n}"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@Table@Products@[L8:C0, L10:C1]",
+ "snippet": "Table Prod...{\r\n \r\n}"
+ },
+ "declaration": {
+ "id": "node@@Products@[L8:C0, L10:C1]",
+ "snippet": "Table Prod...{\r\n \r\n}"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_alias_name.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_alias_name.out.json
new file mode 100644
index 000000000..a59f6a61d
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_alias_name.out.json
@@ -0,0 +1,78 @@
+{
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Table@A@[L0:C0, L2:C1]",
+ "snippet": "Table A as...ary key]\n}"
+ },
+ "declaration": {
+ "id": "node@@A@[L0:C0, L2:C1]",
+ "snippet": "Table A as...ary key]\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L1:C4, L1:C24]",
+ "snippet": "id int [primary key]"
+ }
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@Table@B@[L4:C0, L6:C1]",
+ "snippet": "Table \"B\" ...ary key]\n}"
+ },
+ "declaration": {
+ "id": "node@@B@[L4:C0, L6:C1]",
+ "snippet": "Table \"B\" ...ary key]\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L5:C4, L5:C24]",
+ "snippet": "id int [primary key]"
+ }
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@Table@C@[L8:C0, L10:C1]",
+ "snippet": "Table C as...ary key]\n}"
+ },
+ "declaration": {
+ "id": "node@@C@[L8:C0, L10:C1]",
+ "snippet": "Table C as...ary key]\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L9:C4, L9:C24]",
+ "snippet": "id int [primary key]"
+ }
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@Table@D@[L12:C0, L14:C1]",
+ "snippet": "Table \"D\" ...ary key]\n}"
+ },
+ "declaration": {
+ "id": "node@@D@[L12:C0, L14:C1]",
+ "snippet": "Table \"D\" ...ary key]\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L13:C4, L13:C24]",
+ "snippet": "id int [primary key]"
+ }
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_columns.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_columns.out.json
new file mode 100644
index 000000000..b8f2d16ca
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_columns.out.json
@@ -0,0 +1,140 @@
+{
+ "errors": [
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L1:C4, L1:C14]",
+ "snippet": "id integer"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L1:C4, L1:C14]",
+ "snippet": "id integer"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L2:C4, L2:C14]",
+ "snippet": "id integer"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L3:C4, L3:C14]",
+ "snippet": "id integer"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L7:C4, L7:C10]",
+ "snippet": "id int"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L7:C4, L7:C10]",
+ "snippet": "id int"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L8:C4, L8:C10]",
+ "snippet": "id int"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate column id",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@id@[L9:C4, L9:C10]",
+ "snippet": "id int"
+ }
+ }
+ }
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Table@Users@[L0:C0, L4:C1]",
+ "snippet": "Table User...integer\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L0:C0, L4:C1]",
+ "snippet": "Table User...integer\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L1:C4, L1:C14]",
+ "snippet": "id integer"
+ }
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@TablePartial@userPartial@[L6:C0, L10:C1]",
+ "snippet": "TableParti... id int\r\n}"
+ },
+ "declaration": {
+ "id": "node@@userPartial@[L6:C0, L10:C1]",
+ "snippet": "TableParti... id int\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@id@[L7:C4, L7:C10]",
+ "snippet": "id int"
+ }
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_enum_field.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_enum_field.out.json
new file mode 100644
index 000000000..407256cea
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_enum_field.out.json
@@ -0,0 +1,74 @@
+{
+ "errors": [
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate enum field churn",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@churn@[L1:C4, L1:C9]",
+ "snippet": "churn"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate enum field churn",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@churn@[L1:C4, L1:C9]",
+ "snippet": "churn"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate enum field churn",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@churn@[L2:C4, L2:C9]",
+ "snippet": "churn"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_COLUMN_NAME",
+ "diagnostic": "Duplicate enum field churn",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@churn@[L3:C4, L3:C9]",
+ "snippet": "churn"
+ }
+ }
+ }
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Enum@status@[L0:C0, L4:C1]",
+ "snippet": "Enum statu... churn\r\n}"
+ },
+ "declaration": {
+ "id": "node@@status@[L0:C0, L4:C1]",
+ "snippet": "Enum statu... churn\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Enum field@churn@[L1:C4, L1:C9]",
+ "snippet": "churn"
+ }
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_name.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_name.out.json
index cb8fdf72a..0062527d5 100644
--- a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_name.out.json
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_name.out.json
@@ -1,765 +1,30 @@
{
- "value": {
- "id": 8,
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
- },
- "fullStart": 0,
- "endPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "fullEnd": 38,
- "start": 0,
- "end": 38,
- "body": [
- {
- "id": 3,
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
- },
- "fullStart": 0,
- "endPos": {
- "offset": 15,
- "line": 2,
- "column": 1
- },
- "fullEnd": 17,
- "start": 0,
- "end": 15,
- "type": {
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
- },
- "endPos": {
- "offset": 5,
- "line": 0,
- "column": 5
- },
- "value": "Table",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 5,
- "line": 0,
- "column": 5
- },
- "endPos": {
- "offset": 6,
- "line": 0,
- "column": 6
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 5,
- "end": 6
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 0,
- "end": 5
- },
- "name": {
- "id": 1,
- "kind": "",
- "startPos": {
- "offset": 6,
- "line": 0,
- "column": 6
- },
- "fullStart": 6,
- "endPos": {
- "offset": 8,
- "line": 0,
- "column": 8
- },
- "fullEnd": 9,
- "start": 6,
- "end": 8,
- "expression": {
- "id": 0,
- "kind": "",
- "startPos": {
- "offset": 6,
- "line": 0,
- "column": 6
- },
- "fullStart": 6,
- "endPos": {
- "offset": 8,
- "line": 0,
- "column": 8
- },
- "fullEnd": 9,
- "start": 6,
- "end": 8,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 6,
- "line": 0,
- "column": 6
- },
- "endPos": {
- "offset": 8,
- "line": 0,
- "column": 8
- },
- "value": "AB",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 8,
- "line": 0,
- "column": 8
- },
- "endPos": {
- "offset": 9,
- "line": 0,
- "column": 9
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 8,
- "end": 9
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 6,
- "end": 8
- }
- }
- },
- "body": {
- "id": 2,
- "kind": "",
- "startPos": {
- "offset": 9,
- "line": 0,
- "column": 9
- },
- "fullStart": 9,
- "endPos": {
- "offset": 15,
- "line": 2,
- "column": 1
- },
- "fullEnd": 17,
- "start": 9,
- "end": 15,
- "blockOpenBrace": {
- "kind": "",
- "startPos": {
- "offset": 9,
- "line": 0,
- "column": 9
- },
- "endPos": {
- "offset": 10,
- "line": 0,
- "column": 10
- },
- "value": "{",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 11,
- "line": 0,
- "column": 11
- },
- "endPos": {
- "offset": 12,
- "line": 1,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 11,
- "end": 12
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 9,
- "end": 10
- },
- "body": [],
- "blockCloseBrace": {
- "kind": "",
- "startPos": {
- "offset": 14,
- "line": 2,
- "column": 0
- },
- "endPos": {
- "offset": 15,
- "line": 2,
- "column": 1
- },
- "value": "}",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 13,
- "line": 1,
- "column": 1
- },
- "endPos": {
- "offset": 14,
- "line": 2,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 13,
- "end": 14
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 16,
- "line": 2,
- "column": 2
- },
- "endPos": {
- "offset": 17,
- "line": 3,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 16,
- "end": 17
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 14,
- "end": 15
- }
- },
- "parent": 8,
- "symbol": 1
- },
- {
- "id": 7,
- "kind": "",
- "startPos": {
- "offset": 19,
- "line": 4,
- "column": 0
- },
- "fullStart": 18,
- "endPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "fullEnd": 38,
- "start": 19,
- "end": 38,
- "type": {
- "kind": "",
- "startPos": {
- "offset": 19,
- "line": 4,
- "column": 0
- },
- "endPos": {
- "offset": 24,
- "line": 4,
- "column": 5
- },
- "value": "Table",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 18,
- "line": 3,
- "column": 1
- },
- "endPos": {
- "offset": 19,
- "line": 4,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 18,
- "end": 19
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 24,
- "line": 4,
- "column": 5
- },
- "endPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 24,
- "end": 25
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 19,
- "end": 24
- },
- "name": {
- "id": 5,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "fullStart": 25,
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "fullEnd": 28,
- "start": 25,
- "end": 27,
- "expression": {
- "id": 4,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "fullStart": 25,
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "fullEnd": 28,
- "start": 25,
- "end": 27,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "value": "AB",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "endPos": {
- "offset": 28,
- "line": 4,
- "column": 9
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 27,
- "end": 28
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 25,
- "end": 27
- }
- }
- },
- "body": {
- "id": 6,
- "kind": "",
- "startPos": {
- "offset": 28,
- "line": 4,
- "column": 9
- },
- "fullStart": 28,
- "endPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "fullEnd": 38,
- "start": 28,
- "end": 38,
- "blockOpenBrace": {
- "kind": "",
- "startPos": {
- "offset": 28,
- "line": 4,
- "column": 9
- },
- "endPos": {
- "offset": 29,
- "line": 4,
- "column": 10
- },
- "value": "{",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 30,
- "line": 4,
- "column": 11
- },
- "endPos": {
- "offset": 31,
- "line": 5,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 30,
- "end": 31
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 28,
- "end": 29
- },
- "body": [],
- "blockCloseBrace": {
- "kind": "",
- "startPos": {
- "offset": 37,
- "line": 6,
- "column": 0
- },
- "endPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "value": "}",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 31,
- "line": 5,
- "column": 0
- },
- "endPos": {
- "offset": 32,
- "line": 5,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 31,
- "end": 32
- },
- {
- "kind": "",
- "startPos": {
- "offset": 32,
- "line": 5,
- "column": 1
- },
- "endPos": {
- "offset": 33,
- "line": 5,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 32,
- "end": 33
- },
- {
- "kind": "",
- "startPos": {
- "offset": 33,
- "line": 5,
- "column": 2
- },
- "endPos": {
- "offset": 34,
- "line": 5,
- "column": 3
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 33,
- "end": 34
- },
- {
- "kind": "",
- "startPos": {
- "offset": 34,
- "line": 5,
- "column": 3
- },
- "endPos": {
- "offset": 35,
- "line": 5,
- "column": 4
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 34,
- "end": 35
- },
- {
- "kind": "",
- "startPos": {
- "offset": 36,
- "line": 5,
- "column": 5
- },
- "endPos": {
- "offset": 37,
- "line": 6,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 36,
- "end": 37
- }
- ],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 37,
- "end": 38
- }
- },
- "parent": 8,
- "symbol": 2
- }
- ],
- "eof": {
- "kind": "",
- "startPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "endPos": {
- "offset": 38,
- "line": 6,
- "column": 1
- },
- "value": "",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 38,
- "end": 38
- },
- "symbol": {
- "symbolTable": {
- "Table:AB": {
- "references": [],
- "id": 2,
- "symbolTable": {},
- "declaration": 7
- }
- },
- "id": 0,
- "references": []
- }
- },
"errors": [
{
- "code": 3003,
- "diagnostic": "Table name 'AB' already exists in schema 'public'",
- "nodeOrToken": {
- "id": 5,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "fullStart": 25,
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "fullEnd": 28,
- "start": 25,
- "end": 27,
- "expression": {
- "id": 4,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "fullStart": 25,
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "fullEnd": 28,
- "start": 25,
- "end": 27,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 4,
- "column": 6
- },
- "endPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "value": "AB",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 27,
- "line": 4,
- "column": 8
- },
- "endPos": {
- "offset": 28,
- "line": 4,
- "column": 9
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 27,
- "end": 28
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 25,
- "end": 27
- }
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "Table 'AB' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@AB@[L4:C6, L4:C8]",
+ "snippet": "AB"
}
- },
- "start": 25,
- "end": 27,
- "name": "CompileError"
+ }
}
- ]
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Table@AB@[L4:C0, L6:C1]",
+ "snippet": "Table AB {\r\n \r\n}"
+ },
+ "declaration": {
+ "id": "node@@AB@[L4:C0, L6:C1]",
+ "snippet": "Table AB {\r\n \r\n}"
+ }
+ }
+ ]
+ }
}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_names.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_names.out.json
new file mode 100644
index 000000000..eab796654
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_names.out.json
@@ -0,0 +1,128 @@
+{
+ "errors": [
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "Table 'Users' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L4:C6, L4:C11]",
+ "snippet": "Users"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "TableGroup 'Users' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L12:C11, L12:C16]",
+ "snippet": "Users"
+ }
+ }
+ },
+ {
+ "code": "EMPTY_ENUM",
+ "diagnostic": "An Enum must have at least one element",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L16:C0, L18:C1]",
+ "snippet": "Enum Users {\r\n\r\n}"
+ }
+ }
+ },
+ {
+ "code": "EMPTY_ENUM",
+ "diagnostic": "An Enum must have at least one element",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L20:C0, L22:C1]",
+ "snippet": "Enum Users {\r\n\r\n}"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "Enum 'Users' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L20:C5, L20:C10]",
+ "snippet": "Users"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_NAME",
+ "diagnostic": "TablePartial 'Users' already exists in schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@Users@[L28:C13, L28:C18]",
+ "snippet": "Users"
+ }
+ }
+ }
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@Table@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User...ar(255)\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L4:C0, L6:C1]",
+ "snippet": "Table User...ar(255)\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@name@[L5:C2, L5:C16]",
+ "snippet": "name char(255)"
+ }
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@TableGroup@Users@[L8:C0, L10:C1]",
+ "snippet": "TableGroup...ers {\r\n\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L8:C0, L10:C1]",
+ "snippet": "TableGroup...ers {\r\n\r\n}"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@Enum@Users@[L20:C0, L22:C1]",
+ "snippet": "Enum Users {\r\n\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L20:C0, L22:C1]",
+ "snippet": "Enum Users {\r\n\r\n}"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@TablePartial@Users@[L24:C0, L26:C1]",
+ "snippet": "TableParti...ers {\r\n\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L24:C0, L26:C1]",
+ "snippet": "TableParti...ers {\r\n\r\n}"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_table_partial_injections.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_table_partial_injections.out.json
new file mode 100644
index 000000000..a05d198aa
--- /dev/null
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/duplicate_table_partial_injections.out.json
@@ -0,0 +1,225 @@
+{
+ "errors": [
+ {
+ "code": "DUPLICATE_TABLE_PARTIAL_INJECTION_NAME",
+ "diagnostic": "Duplicate table partial injection 'common'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@@[L6:C2, L6:C9]",
+ "snippet": "~common"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_TABLE_PARTIAL_INJECTION_NAME",
+ "diagnostic": "Duplicate table partial injection 'common'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@@[L6:C2, L6:C9]",
+ "snippet": "~common"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_TABLE_PARTIAL_INJECTION_NAME",
+ "diagnostic": "Duplicate table partial injection 'common'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@@[L7:C2, L7:C9]",
+ "snippet": "~common"
+ }
+ }
+ },
+ {
+ "code": "DUPLICATE_TABLE_PARTIAL_INJECTION_NAME",
+ "diagnostic": "Duplicate table partial injection 'common'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@@[L8:C2, L8:C9]",
+ "snippet": "~common"
+ }
+ }
+ }
+ ],
+ "nodeReferees": [
+ {
+ "context": {
+ "id": "node@@common@[L6:C3, L6:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@common@[L6:C3, L6:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@common@[L6:C3, L6:C9]",
+ "snippet": "common"
+ },
+ "leadingTrivia": "",
+ "trailingTrivia": "\n",
+ "value": "common"
+ }
+ },
+ "fullEnd": 77,
+ "fullStart": 69
+ }
+ },
+ "fullEnd": 77,
+ "fullStart": 69,
+ "referee": {
+ "context": {
+ "id": "symbol@TablePartial@common@[L0:C0, L2:C1]",
+ "snippet": "TableParti...me text\r\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@common@[L7:C3, L7:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@common@[L7:C3, L7:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@common@[L7:C3, L7:C9]",
+ "snippet": "common"
+ },
+ "leadingTrivia": "",
+ "trailingTrivia": "\n",
+ "value": "common"
+ }
+ },
+ "fullEnd": 88,
+ "fullStart": 80
+ }
+ },
+ "fullEnd": 88,
+ "fullStart": 80,
+ "referee": {
+ "context": {
+ "id": "symbol@TablePartial@common@[L0:C0, L2:C1]",
+ "snippet": "TableParti...me text\r\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@common@[L8:C3, L8:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@common@[L8:C3, L8:C9]",
+ "snippet": "common"
+ },
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@common@[L8:C3, L8:C9]",
+ "snippet": "common"
+ },
+ "leadingTrivia": "",
+ "trailingTrivia": "\n",
+ "value": "common"
+ }
+ },
+ "fullEnd": 99,
+ "fullStart": 91
+ }
+ },
+ "fullEnd": 99,
+ "fullStart": 91,
+ "referee": {
+ "context": {
+ "id": "symbol@TablePartial@common@[L0:C0, L2:C1]",
+ "snippet": "TableParti...me text\r\n}"
+ }
+ }
+ }
+ ],
+ "program": {
+ "publicSchema": [
+ {
+ "context": {
+ "id": "symbol@TablePartial@common@[L0:C0, L2:C1]",
+ "snippet": "TableParti...me text\r\n}"
+ },
+ "declaration": {
+ "id": "node@@common@[L0:C0, L2:C1]",
+ "snippet": "TableParti...me text\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@name@[L1:C2, L1:C11]",
+ "snippet": "name text"
+ }
+ }
+ ],
+ "references": [
+ {
+ "id": "node@@common@[L6:C3, L6:C9]",
+ "snippet": "common"
+ },
+ {
+ "id": "node@@common@[L7:C3, L7:C9]",
+ "snippet": "common"
+ },
+ {
+ "id": "node@@common@[L8:C3, L8:C9]",
+ "snippet": "common"
+ }
+ ]
+ },
+ {
+ "context": {
+ "id": "symbol@Table@Users@[L4:C0, L9:C1]",
+ "snippet": "Table User...~common\r\n}"
+ },
+ "declaration": {
+ "id": "node@@Users@[L4:C0, L9:C1]",
+ "snippet": "Table User...~common\r\n}"
+ },
+ "members": [
+ {
+ "context": {
+ "id": "symbol@Column@@[L8:C2, L8:C9]",
+ "snippet": "~common"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@Column@id@[L5:C2, L5:C8]",
+ "snippet": "id int"
+ }
+ },
+ {
+ "context": {
+ "id": "symbol@PartialInjection@@[L6:C2, L6:C9]",
+ "snippet": "~common"
+ }
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/enum_as_default_column_value.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/enum_as_default_column_value.out.json
index 6460ff58d..8ccb20d13 100644
--- a/packages/dbml-parse/__tests__/snapshots/binder/output/enum_as_default_column_value.out.json
+++ b/packages/dbml-parse/__tests__/snapshots/binder/output/enum_as_default_column_value.out.json
@@ -1,8546 +1,648 @@
{
- "value": {
- "id": 155,
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
+ "errors": [
+ {
+ "code": "INVALID_COLUMN_SETTING_VALUE",
+ "diagnostic": "'default' must be an enum value, a string literal, number literal, function expression, true, false or null",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@invalid_value@[L26:C46, L26:C59]",
+ "snippet": "invalid_value"
+ }
+ }
},
- "fullStart": 0,
- "endPos": {
- "offset": 616,
- "line": 30,
- "column": 0
+ {
+ "code": "INVALID_COLUMN_SETTING_VALUE",
+ "diagnostic": "'default' must be an enum value, a string literal, number literal, function expression, true, false or null",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@@[L27:C47, L27:C74]",
+ "snippet": "field1.fie...ld3.field4"
+ }
+ }
},
- "fullEnd": 616,
- "start": 0,
- "end": 616,
- "body": [
- {
- "id": 12,
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
- },
- "fullStart": 0,
- "endPos": {
- "offset": 45,
- "line": 4,
- "column": 1
- },
- "fullEnd": 46,
- "start": 0,
- "end": 45,
- "type": {
- "kind": "",
- "startPos": {
- "offset": 0,
- "line": 0,
- "column": 0
- },
- "endPos": {
- "offset": 4,
- "line": 0,
- "column": 4
+ {
+ "code": "BINDING_ERROR",
+ "diagnostic": "Schema 'field1' does not exist in Schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@field1@[L27:C47, L27:C53]",
+ "snippet": "field1"
+ }
+ }
+ },
+ {
+ "code": "BINDING_ERROR",
+ "diagnostic": "Enum 'invalid_enum' does not exist in Schema 'public'",
+ "filepath": "/main.dbml",
+ "level": "error",
+ "node": {
+ "context": {
+ "id": "node@@invalid_enum@[L28:C42, L28:C54]",
+ "snippet": "invalid_enum"
+ }
+ }
+ }
+ ],
+ "nodeReferees": [
+ {
+ "context": {
+ "id": "node@@status@[L22:C9, L22:C15]",
+ "snippet": "status"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@status@[L22:C9, L22:C15]",
+ "snippet": "status"
},
- "value": "Enum",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 4,
- "line": 0,
- "column": 4
- },
- "endPos": {
- "offset": 5,
- "line": 0,
- "column": 5
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@status@[L22:C9, L22:C15]",
+ "snippet": "status"
},
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 4,
- "end": 5
+ "leadingTrivia": "",
+ "trailingTrivia": " ",
+ "value": "status"
}
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 0,
- "end": 4
- },
- "name": {
- "id": 1,
- "kind": "",
- "startPos": {
- "offset": 5,
- "line": 0,
- "column": 5
},
- "fullStart": 5,
- "endPos": {
- "offset": 11,
- "line": 0,
- "column": 11
+ "fullEnd": 232,
+ "fullStart": 225
+ }
+ },
+ "fullEnd": 232,
+ "fullStart": 225,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum@status@[L0:C0, L4:C1]",
+ "snippet": "Enum statu...inactive\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@status@[L22:C26, L22:C32]",
+ "snippet": "status"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@status@[L22:C26, L22:C32]",
+ "snippet": "status"
},
- "fullEnd": 12,
- "start": 5,
- "end": 11,
- "expression": {
- "id": 0,
- "kind": "",
- "startPos": {
- "offset": 5,
- "line": 0,
- "column": 5
- },
- "fullStart": 5,
- "endPos": {
- "offset": 11,
- "line": 0,
- "column": 11
- },
- "fullEnd": 12,
- "start": 5,
- "end": 11,
+ "children": {
"variable": {
- "kind": "",
- "startPos": {
- "offset": 5,
- "line": 0,
- "column": 5
- },
- "endPos": {
- "offset": 11,
- "line": 0,
- "column": 11
+ "context": {
+ "id": "token@@status@[L22:C26, L22:C32]",
+ "snippet": "status"
},
- "value": "status",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 11,
- "line": 0,
- "column": 11
- },
- "endPos": {
- "offset": 12,
- "line": 0,
- "column": 12
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 11,
- "end": 12
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 5,
- "end": 11
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "status"
}
- }
- },
- "body": {
- "id": 11,
- "kind": "",
- "startPos": {
- "offset": 12,
- "line": 0,
- "column": 12
- },
- "fullStart": 12,
- "endPos": {
- "offset": 45,
- "line": 4,
- "column": 1
},
- "fullEnd": 46,
- "start": 12,
- "end": 45,
- "blockOpenBrace": {
- "kind": "",
- "startPos": {
- "offset": 12,
- "line": 0,
- "column": 12
- },
- "endPos": {
- "offset": 13,
- "line": 0,
- "column": 13
- },
- "value": "{",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 13,
- "line": 0,
- "column": 13
- },
- "endPos": {
- "offset": 14,
- "line": 1,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 13,
- "end": 14
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 12,
- "end": 13
- },
- "body": [
- {
- "id": 4,
- "kind": "",
- "startPos": {
- "offset": 16,
- "line": 1,
- "column": 2
- },
- "fullStart": 14,
- "endPos": {
- "offset": 22,
- "line": 1,
- "column": 8
- },
- "fullEnd": 23,
- "start": 16,
- "end": 22,
- "callee": {
- "id": 3,
- "kind": "",
- "startPos": {
- "offset": 16,
- "line": 1,
- "column": 2
- },
- "fullStart": 14,
- "endPos": {
- "offset": 22,
- "line": 1,
- "column": 8
- },
- "fullEnd": 23,
- "start": 16,
- "end": 22,
- "expression": {
- "id": 2,
- "kind": "",
- "startPos": {
- "offset": 16,
- "line": 1,
- "column": 2
- },
- "fullStart": 14,
- "endPos": {
- "offset": 22,
- "line": 1,
- "column": 8
- },
- "fullEnd": 23,
- "start": 16,
- "end": 22,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 16,
- "line": 1,
- "column": 2
- },
- "endPos": {
- "offset": 22,
- "line": 1,
- "column": 8
- },
- "value": "active",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 14,
- "line": 1,
- "column": 0
- },
- "endPos": {
- "offset": 15,
- "line": 1,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 14,
- "end": 15
- },
- {
- "kind": "",
- "startPos": {
- "offset": 15,
- "line": 1,
- "column": 1
- },
- "endPos": {
- "offset": 16,
- "line": 1,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 15,
- "end": 16
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 22,
- "line": 1,
- "column": 8
- },
- "endPos": {
- "offset": 23,
- "line": 2,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 22,
- "end": 23
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 16,
- "end": 22
- }
- }
- },
- "args": [],
- "symbol": 2
- },
- {
- "id": 7,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 2,
- "column": 2
- },
- "fullStart": 23,
- "endPos": {
- "offset": 32,
- "line": 2,
- "column": 9
- },
- "fullEnd": 33,
- "start": 25,
- "end": 32,
- "callee": {
- "id": 6,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 2,
- "column": 2
- },
- "fullStart": 23,
- "endPos": {
- "offset": 32,
- "line": 2,
- "column": 9
- },
- "fullEnd": 33,
- "start": 25,
- "end": 32,
- "expression": {
- "id": 5,
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 2,
- "column": 2
- },
- "fullStart": 23,
- "endPos": {
- "offset": 32,
- "line": 2,
- "column": 9
- },
- "fullEnd": 33,
- "start": 25,
- "end": 32,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 25,
- "line": 2,
- "column": 2
- },
- "endPos": {
- "offset": 32,
- "line": 2,
- "column": 9
- },
- "value": "churned",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 23,
- "line": 2,
- "column": 0
- },
- "endPos": {
- "offset": 24,
- "line": 2,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 23,
- "end": 24
- },
- {
- "kind": "",
- "startPos": {
- "offset": 24,
- "line": 2,
- "column": 1
- },
- "endPos": {
- "offset": 25,
- "line": 2,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 24,
- "end": 25
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 32,
- "line": 2,
- "column": 9
- },
- "endPos": {
- "offset": 33,
- "line": 3,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 32,
- "end": 33
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 25,
- "end": 32
- }
- }
- },
- "args": [],
- "symbol": 3
- },
- {
- "id": 10,
- "kind": "",
- "startPos": {
- "offset": 35,
- "line": 3,
- "column": 2
- },
- "fullStart": 33,
- "endPos": {
- "offset": 43,
- "line": 3,
- "column": 10
- },
- "fullEnd": 44,
- "start": 35,
- "end": 43,
- "callee": {
- "id": 9,
- "kind": "",
- "startPos": {
- "offset": 35,
- "line": 3,
- "column": 2
- },
- "fullStart": 33,
- "endPos": {
- "offset": 43,
- "line": 3,
- "column": 10
- },
- "fullEnd": 44,
- "start": 35,
- "end": 43,
- "expression": {
- "id": 8,
- "kind": "",
- "startPos": {
- "offset": 35,
- "line": 3,
- "column": 2
- },
- "fullStart": 33,
- "endPos": {
- "offset": 43,
- "line": 3,
- "column": 10
- },
- "fullEnd": 44,
- "start": 35,
- "end": 43,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 35,
- "line": 3,
- "column": 2
- },
- "endPos": {
- "offset": 43,
- "line": 3,
- "column": 10
- },
- "value": "inactive",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 33,
- "line": 3,
- "column": 0
- },
- "endPos": {
- "offset": 34,
- "line": 3,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 33,
- "end": 34
- },
- {
- "kind": "",
- "startPos": {
- "offset": 34,
- "line": 3,
- "column": 1
- },
- "endPos": {
- "offset": 35,
- "line": 3,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 34,
- "end": 35
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 43,
- "line": 3,
- "column": 10
- },
- "endPos": {
- "offset": 44,
- "line": 4,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 43,
- "end": 44
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 35,
- "end": 43
- }
- }
- },
- "args": [],
- "symbol": 4
- }
- ],
- "blockCloseBrace": {
- "kind": "",
- "startPos": {
- "offset": 44,
- "line": 4,
- "column": 0
- },
- "endPos": {
- "offset": 45,
- "line": 4,
- "column": 1
- },
- "value": "}",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 45,
- "line": 4,
- "column": 1
- },
- "endPos": {
- "offset": 46,
- "line": 5,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 45,
- "end": 46
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 44,
- "end": 45
- }
- },
- "parent": 155,
- "symbol": 1
+ "fullEnd": 248,
+ "fullStart": 242
+ }
},
- {
- "id": 25,
- "kind": "",
- "startPos": {
- "offset": 47,
- "line": 6,
- "column": 0
- },
- "fullStart": 46,
- "endPos": {
- "offset": 90,
- "line": 9,
- "column": 1
- },
- "fullEnd": 91,
- "start": 47,
- "end": 90,
- "type": {
- "kind": "",
- "startPos": {
- "offset": 47,
- "line": 6,
- "column": 0
- },
- "endPos": {
- "offset": 51,
- "line": 6,
- "column": 4
+ "fullEnd": 248,
+ "fullStart": 242,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum@status@[L0:C0, L4:C1]",
+ "snippet": "Enum statu...inactive\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@active@[L22:C33, L22:C39]",
+ "snippet": "active"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@active@[L22:C33, L22:C39]",
+ "snippet": "active"
},
- "value": "Enum",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 46,
- "line": 5,
- "column": 0
- },
- "endPos": {
- "offset": 47,
- "line": 6,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 46,
- "end": 47
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 51,
- "line": 6,
- "column": 4
- },
- "endPos": {
- "offset": 52,
- "line": 6,
- "column": 5
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@active@[L22:C33, L22:C39]",
+ "snippet": "active"
},
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 51,
- "end": 52
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "active"
}
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 47,
- "end": 51
- },
- "name": {
- "id": 17,
- "kind": "",
- "startPos": {
- "offset": 52,
- "line": 6,
- "column": 5
},
- "fullStart": 52,
- "endPos": {
- "offset": 70,
- "line": 6,
- "column": 23
- },
- "fullEnd": 71,
- "start": 52,
- "end": 70,
- "op": {
- "kind": "",
- "startPos": {
- "offset": 63,
- "line": 6,
- "column": 16
- },
- "endPos": {
- "offset": 64,
- "line": 6,
- "column": 17
- },
- "value": ".",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 63,
- "end": 64
+ "fullEnd": 255,
+ "fullStart": 249
+ }
+ },
+ "fullEnd": 255,
+ "fullStart": 249,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum field@active@[L1:C2, L1:C8]",
+ "snippet": "active"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@demographic@[L23:C9, L23:C20]",
+ "snippet": "demographic"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@demographic@[L23:C9, L23:C20]",
+ "snippet": "demographic"
},
- "leftExpression": {
- "id": 14,
- "kind": "",
- "startPos": {
- "offset": 52,
- "line": 6,
- "column": 5
- },
- "fullStart": 52,
- "endPos": {
- "offset": 63,
- "line": 6,
- "column": 16
- },
- "fullEnd": 63,
- "start": 52,
- "end": 63,
- "expression": {
- "id": 13,
- "kind": "",
- "startPos": {
- "offset": 52,
- "line": 6,
- "column": 5
- },
- "fullStart": 52,
- "endPos": {
- "offset": 63,
- "line": 6,
- "column": 16
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@demographic@[L23:C9, L23:C20]",
+ "snippet": "demographic"
},
- "fullEnd": 63,
- "start": 52,
- "end": 63,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 52,
- "line": 6,
- "column": 5
- },
- "endPos": {
- "offset": 63,
- "line": 6,
- "column": 16
- },
- "value": "demographic",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 52,
- "end": 63
- }
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "demographic"
}
},
- "rightExpression": {
- "id": 16,
- "kind": "",
- "startPos": {
- "offset": 64,
- "line": 6,
- "column": 17
- },
- "fullStart": 64,
- "endPos": {
- "offset": 70,
- "line": 6,
- "column": 23
- },
- "fullEnd": 71,
- "start": 64,
- "end": 70,
- "expression": {
- "id": 15,
- "kind": "",
- "startPos": {
- "offset": 64,
- "line": 6,
- "column": 17
- },
- "fullStart": 64,
- "endPos": {
- "offset": 70,
- "line": 6,
- "column": 23
- },
- "fullEnd": 71,
- "start": 64,
- "end": 70,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 64,
- "line": 6,
- "column": 17
- },
- "endPos": {
- "offset": 70,
- "line": 6,
- "column": 23
- },
- "value": "gender",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 70,
- "line": 6,
- "column": 23
- },
- "endPos": {
- "offset": 71,
- "line": 6,
- "column": 24
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 70,
- "end": 71
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 64,
- "end": 70
- }
- }
- }
- },
- "body": {
- "id": 24,
- "kind": "",
- "startPos": {
- "offset": 71,
- "line": 6,
- "column": 24
- },
- "fullStart": 71,
- "endPos": {
- "offset": 90,
- "line": 9,
- "column": 1
- },
- "fullEnd": 91,
- "start": 71,
- "end": 90,
- "blockOpenBrace": {
- "kind": "",
- "startPos": {
- "offset": 71,
- "line": 6,
- "column": 24
- },
- "endPos": {
- "offset": 72,
- "line": 6,
- "column": 25
- },
- "value": "{",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 72,
- "line": 6,
- "column": 25
- },
- "endPos": {
- "offset": 73,
- "line": 7,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 72,
- "end": 73
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 71,
- "end": 72
+ "fullEnd": 277,
+ "fullStart": 266
+ }
+ },
+ "fullEnd": 277,
+ "fullStart": 266,
+ "referee": {
+ "context": {
+ "id": "symbol@Schema@demographic@[L?:C?, L?:C?]"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@gender@[L23:C21, L23:C27]",
+ "snippet": "gender"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@gender@[L23:C21, L23:C27]",
+ "snippet": "gender"
},
- "body": [
- {
- "id": 20,
- "kind": "",
- "startPos": {
- "offset": 75,
- "line": 7,
- "column": 2
- },
- "fullStart": 73,
- "endPos": {
- "offset": 79,
- "line": 7,
- "column": 6
- },
- "fullEnd": 80,
- "start": 75,
- "end": 79,
- "callee": {
- "id": 19,
- "kind": "",
- "startPos": {
- "offset": 75,
- "line": 7,
- "column": 2
- },
- "fullStart": 73,
- "endPos": {
- "offset": 79,
- "line": 7,
- "column": 6
- },
- "fullEnd": 80,
- "start": 75,
- "end": 79,
- "expression": {
- "id": 18,
- "kind": "",
- "startPos": {
- "offset": 75,
- "line": 7,
- "column": 2
- },
- "fullStart": 73,
- "endPos": {
- "offset": 79,
- "line": 7,
- "column": 6
- },
- "fullEnd": 80,
- "start": 75,
- "end": 79,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 75,
- "line": 7,
- "column": 2
- },
- "endPos": {
- "offset": 79,
- "line": 7,
- "column": 6
- },
- "value": "male",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 73,
- "line": 7,
- "column": 0
- },
- "endPos": {
- "offset": 74,
- "line": 7,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 73,
- "end": 74
- },
- {
- "kind": "",
- "startPos": {
- "offset": 74,
- "line": 7,
- "column": 1
- },
- "endPos": {
- "offset": 75,
- "line": 7,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 74,
- "end": 75
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 79,
- "line": 7,
- "column": 6
- },
- "endPos": {
- "offset": 80,
- "line": 8,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 79,
- "end": 80
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 75,
- "end": 79
- }
- }
- },
- "args": [],
- "symbol": 7
- },
- {
- "id": 23,
- "kind": "",
- "startPos": {
- "offset": 82,
- "line": 8,
- "column": 2
- },
- "fullStart": 80,
- "endPos": {
- "offset": 88,
- "line": 8,
- "column": 8
- },
- "fullEnd": 89,
- "start": 82,
- "end": 88,
- "callee": {
- "id": 22,
- "kind": "",
- "startPos": {
- "offset": 82,
- "line": 8,
- "column": 2
- },
- "fullStart": 80,
- "endPos": {
- "offset": 88,
- "line": 8,
- "column": 8
- },
- "fullEnd": 89,
- "start": 82,
- "end": 88,
- "expression": {
- "id": 21,
- "kind": "",
- "startPos": {
- "offset": 82,
- "line": 8,
- "column": 2
- },
- "fullStart": 80,
- "endPos": {
- "offset": 88,
- "line": 8,
- "column": 8
- },
- "fullEnd": 89,
- "start": 82,
- "end": 88,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 82,
- "line": 8,
- "column": 2
- },
- "endPos": {
- "offset": 88,
- "line": 8,
- "column": 8
- },
- "value": "female",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 80,
- "line": 8,
- "column": 0
- },
- "endPos": {
- "offset": 81,
- "line": 8,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 80,
- "end": 81
- },
- {
- "kind": "",
- "startPos": {
- "offset": 81,
- "line": 8,
- "column": 1
- },
- "endPos": {
- "offset": 82,
- "line": 8,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 81,
- "end": 82
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 88,
- "line": 8,
- "column": 8
- },
- "endPos": {
- "offset": 89,
- "line": 9,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 88,
- "end": 89
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 82,
- "end": 88
- }
- }
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@gender@[L23:C21, L23:C27]",
+ "snippet": "gender"
},
- "args": [],
- "symbol": 8
+ "leadingTrivia": "",
+ "trailingTrivia": " ",
+ "value": "gender"
}
- ],
- "blockCloseBrace": {
- "kind": "",
- "startPos": {
- "offset": 89,
- "line": 9,
- "column": 0
- },
- "endPos": {
- "offset": 90,
- "line": 9,
- "column": 1
- },
- "value": "}",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 90,
- "line": 9,
- "column": 1
- },
- "endPos": {
- "offset": 91,
- "line": 10,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 90,
- "end": 91
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 89,
- "end": 90
- }
- },
- "parent": 155,
- "symbol": 5
- },
- {
- "id": 47,
- "kind": "",
- "startPos": {
- "offset": 92,
- "line": 11,
- "column": 0
- },
- "fullStart": 91,
- "endPos": {
- "offset": 180,
- "line": 17,
- "column": 1
- },
- "fullEnd": 181,
- "start": 92,
- "end": 180,
- "type": {
- "kind": "",
- "startPos": {
- "offset": 92,
- "line": 11,
- "column": 0
},
- "endPos": {
- "offset": 96,
- "line": 11,
- "column": 4
+ "fullEnd": 285,
+ "fullStart": 278
+ }
+ },
+ "fullEnd": 285,
+ "fullStart": 278,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum@demographic.gender@[L6:C0, L9:C1]",
+ "snippet": "Enum demog... female\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@demographic@[L23:C38, L23:C49]",
+ "snippet": "demographic"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@demographic@[L23:C38, L23:C49]",
+ "snippet": "demographic"
},
- "value": "Enum",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 91,
- "line": 10,
- "column": 0
- },
- "endPos": {
- "offset": 92,
- "line": 11,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 91,
- "end": 92
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 96,
- "line": 11,
- "column": 4
- },
- "endPos": {
- "offset": 97,
- "line": 11,
- "column": 5
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@demographic@[L23:C38, L23:C49]",
+ "snippet": "demographic"
},
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 96,
- "end": 97
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "demographic"
}
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 92,
- "end": 96
- },
- "name": {
- "id": 30,
- "kind": "",
- "startPos": {
- "offset": 97,
- "line": 11,
- "column": 5
- },
- "fullStart": 97,
- "endPos": {
- "offset": 122,
- "line": 11,
- "column": 30
},
- "fullEnd": 123,
- "start": 97,
- "end": 122,
- "op": {
- "kind": "",
- "startPos": {
- "offset": 108,
- "line": 11,
- "column": 16
- },
- "endPos": {
- "offset": 109,
- "line": 11,
- "column": 17
- },
- "value": ".",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 108,
- "end": 109
+ "fullEnd": 306,
+ "fullStart": 295
+ }
+ },
+ "fullEnd": 306,
+ "fullStart": 295,
+ "referee": {
+ "context": {
+ "id": "symbol@Schema@demographic@[L?:C?, L?:C?]"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@gender@[L23:C50, L23:C56]",
+ "snippet": "gender"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@gender@[L23:C50, L23:C56]",
+ "snippet": "gender"
},
- "leftExpression": {
- "id": 27,
- "kind": "",
- "startPos": {
- "offset": 97,
- "line": 11,
- "column": 5
- },
- "fullStart": 97,
- "endPos": {
- "offset": 108,
- "line": 11,
- "column": 16
- },
- "fullEnd": 108,
- "start": 97,
- "end": 108,
- "expression": {
- "id": 26,
- "kind": "",
- "startPos": {
- "offset": 97,
- "line": 11,
- "column": 5
- },
- "fullStart": 97,
- "endPos": {
- "offset": 108,
- "line": 11,
- "column": 16
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@gender@[L23:C50, L23:C56]",
+ "snippet": "gender"
},
- "fullEnd": 108,
- "start": 97,
- "end": 108,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 97,
- "line": 11,
- "column": 5
- },
- "endPos": {
- "offset": 108,
- "line": 11,
- "column": 16
- },
- "value": "demographic",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 97,
- "end": 108
- }
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "gender"
}
},
- "rightExpression": {
- "id": 29,
- "kind": "",
- "startPos": {
- "offset": 109,
- "line": 11,
- "column": 17
- },
- "fullStart": 109,
- "endPos": {
- "offset": 122,
- "line": 11,
- "column": 30
- },
- "fullEnd": 123,
- "start": 109,
- "end": 122,
- "expression": {
- "id": 28,
- "kind": "",
- "startPos": {
- "offset": 109,
- "line": 11,
- "column": 17
- },
- "fullStart": 109,
- "endPos": {
- "offset": 122,
- "line": 11,
- "column": 30
+ "fullEnd": 313,
+ "fullStart": 307
+ }
+ },
+ "fullEnd": 313,
+ "fullStart": 307,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum@demographic.gender@[L6:C0, L9:C1]",
+ "snippet": "Enum demog... female\n}"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@male@[L23:C57, L23:C61]",
+ "snippet": "male"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@male@[L23:C57, L23:C61]",
+ "snippet": "male"
+ },
+ "children": {
+ "variable": {
+ "context": {
+ "id": "token@@male@[L23:C57, L23:C61]",
+ "snippet": "male"
},
- "fullEnd": 123,
- "start": 109,
- "end": 122,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 109,
- "line": 11,
- "column": 17
- },
- "endPos": {
- "offset": 122,
- "line": 11,
- "column": 30
- },
- "value": "age segment",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 122,
- "line": 11,
- "column": 30
- },
- "endPos": {
- "offset": 123,
- "line": 11,
- "column": 31
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 122,
- "end": 123
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 109,
- "end": 122
- }
+ "leadingTrivia": "",
+ "trailingTrivia": "",
+ "value": "male"
}
- }
- },
- "body": {
- "id": 46,
- "kind": "",
- "startPos": {
- "offset": 123,
- "line": 11,
- "column": 31
- },
- "fullStart": 123,
- "endPos": {
- "offset": 180,
- "line": 17,
- "column": 1
},
- "fullEnd": 181,
- "start": 123,
- "end": 180,
- "blockOpenBrace": {
- "kind": "",
- "startPos": {
- "offset": 123,
- "line": 11,
- "column": 31
- },
- "endPos": {
- "offset": 124,
- "line": 11,
- "column": 32
- },
- "value": "{",
- "leadingTrivia": [],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 124,
- "line": 11,
- "column": 32
- },
- "endPos": {
- "offset": 125,
- "line": 12,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 124,
- "end": 125
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 123,
- "end": 124
+ "fullEnd": 318,
+ "fullStart": 314
+ }
+ },
+ "fullEnd": 318,
+ "fullStart": 314,
+ "referee": {
+ "context": {
+ "id": "symbol@Enum field@male@[L7:C2, L7:C6]",
+ "snippet": "male"
+ }
+ }
+ },
+ {
+ "context": {
+ "id": "node@@demographic@[L24:C11, L24:C22]",
+ "snippet": "demographic"
+ },
+ "children": {
+ "expression": {
+ "context": {
+ "id": "node@@demographic@[L24:C11, L24:C22]",
+ "snippet": "demographic"
},
- "body": [
- {
- "id": 33,
- "kind": "",
- "startPos": {
- "offset": 127,
- "line": 12,
- "column": 2
- },
- "fullStart": 125,
- "endPos": {
- "offset": 134,
- "line": 12,
- "column": 9
- },
- "fullEnd": 135,
- "start": 127,
- "end": 134,
- "callee": {
- "id": 32,
- "kind": "",
- "startPos": {
- "offset": 127,
- "line": 12,
- "column": 2
- },
- "fullStart": 125,
- "endPos": {
- "offset": 134,
- "line": 12,
- "column": 9
- },
- "fullEnd": 135,
- "start": 127,
- "end": 134,
- "expression": {
- "id": 31,
- "kind": "",
- "startPos": {
- "offset": 127,
- "line": 12,
- "column": 2
- },
- "fullStart": 125,
- "endPos": {
- "offset": 134,
- "line": 12,
- "column": 9
- },
- "fullEnd": 135,
- "start": 127,
- "end": 134,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 127,
- "line": 12,
- "column": 2
- },
- "endPos": {
- "offset": 134,
- "line": 12,
- "column": 9
- },
- "value": "toddler",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 125,
- "line": 12,
- "column": 0
- },
- "endPos": {
- "offset": 126,
- "line": 12,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 125,
- "end": 126
- },
- {
- "kind": "",
- "startPos": {
- "offset": 126,
- "line": 12,
- "column": 1
- },
- "endPos": {
- "offset": 127,
- "line": 12,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 126,
- "end": 127
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 134,
- "line": 12,
- "column": 9
- },
- "endPos": {
- "offset": 135,
- "line": 13,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 134,
- "end": 135
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 127,
- "end": 134
- }
- }
- },
- "args": [],
- "symbol": 10
- },
- {
- "id": 36,
- "kind": "",
- "startPos": {
- "offset": 137,
- "line": 13,
- "column": 2
- },
- "fullStart": 135,
- "endPos": {
- "offset": 145,
- "line": 13,
- "column": 10
- },
- "fullEnd": 146,
- "start": 137,
- "end": 145,
- "callee": {
- "id": 35,
- "kind": "",
- "startPos": {
- "offset": 137,
- "line": 13,
- "column": 2
- },
- "fullStart": 135,
- "endPos": {
- "offset": 145,
- "line": 13,
- "column": 10
- },
- "fullEnd": 146,
- "start": 137,
- "end": 145,
- "expression": {
- "id": 34,
- "kind": "",
- "startPos": {
- "offset": 137,
- "line": 13,
- "column": 2
- },
- "fullStart": 135,
- "endPos": {
- "offset": 145,
- "line": 13,
- "column": 10
- },
- "fullEnd": 146,
- "start": 137,
- "end": 145,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 137,
- "line": 13,
- "column": 2
- },
- "endPos": {
- "offset": 145,
- "line": 13,
- "column": 10
- },
- "value": "children",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 135,
- "line": 13,
- "column": 0
- },
- "endPos": {
- "offset": 136,
- "line": 13,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 135,
- "end": 136
- },
- {
- "kind": "",
- "startPos": {
- "offset": 136,
- "line": 13,
- "column": 1
- },
- "endPos": {
- "offset": 137,
- "line": 13,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 136,
- "end": 137
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 145,
- "line": 13,
- "column": 10
- },
- "endPos": {
- "offset": 146,
- "line": 14,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 145,
- "end": 146
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 137,
- "end": 145
- }
- }
- },
- "args": [],
- "symbol": 11
- },
- {
- "id": 39,
- "kind": "",
- "startPos": {
- "offset": 148,
- "line": 14,
- "column": 2
- },
- "fullStart": 146,
- "endPos": {
- "offset": 156,
- "line": 14,
- "column": 10
- },
- "fullEnd": 157,
- "start": 148,
- "end": 156,
- "callee": {
- "id": 38,
- "kind": "",
- "startPos": {
- "offset": 148,
- "line": 14,
- "column": 2
- },
- "fullStart": 146,
- "endPos": {
- "offset": 156,
- "line": 14,
- "column": 10
- },
- "fullEnd": 157,
- "start": 148,
- "end": 156,
- "expression": {
- "id": 37,
- "kind": "",
- "startPos": {
- "offset": 148,
- "line": 14,
- "column": 2
- },
- "fullStart": 146,
- "endPos": {
- "offset": 156,
- "line": 14,
- "column": 10
- },
- "fullEnd": 157,
- "start": 148,
- "end": 156,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 148,
- "line": 14,
- "column": 2
- },
- "endPos": {
- "offset": 156,
- "line": 14,
- "column": 10
- },
- "value": "teenager",
- "leadingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 146,
- "line": 14,
- "column": 0
- },
- "endPos": {
- "offset": 147,
- "line": 14,
- "column": 1
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 146,
- "end": 147
- },
- {
- "kind": "",
- "startPos": {
- "offset": 147,
- "line": 14,
- "column": 1
- },
- "endPos": {
- "offset": 148,
- "line": 14,
- "column": 2
- },
- "value": " ",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 147,
- "end": 148
- }
- ],
- "trailingTrivia": [
- {
- "kind": "",
- "startPos": {
- "offset": 156,
- "line": 14,
- "column": 10
- },
- "endPos": {
- "offset": 157,
- "line": 15,
- "column": 0
- },
- "value": "\n",
- "leadingTrivia": [],
- "trailingTrivia": [],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 156,
- "end": 157
- }
- ],
- "leadingInvalid": [],
- "trailingInvalid": [],
- "isInvalid": false,
- "start": 148,
- "end": 156
- }
- }
- },
- "args": [],
- "symbol": 12
- },
- {
- "id": 42,
- "kind": "",
- "startPos": {
- "offset": 159,
- "line": 15,
- "column": 2
- },
- "fullStart": 157,
- "endPos": {
- "offset": 170,
- "line": 15,
- "column": 13
- },
- "fullEnd": 171,
- "start": 159,
- "end": 170,
- "callee": {
- "id": 41,
- "kind": "",
- "startPos": {
- "offset": 159,
- "line": 15,
- "column": 2
- },
- "fullStart": 157,
- "endPos": {
- "offset": 170,
- "line": 15,
- "column": 13
- },
- "fullEnd": 171,
- "start": 159,
- "end": 170,
- "expression": {
- "id": 40,
- "kind": "",
- "startPos": {
- "offset": 159,
- "line": 15,
- "column": 2
- },
- "fullStart": 157,
- "endPos": {
- "offset": 170,
- "line": 15,
- "column": 13
- },
- "fullEnd": 171,
- "start": 159,
- "end": 170,
- "variable": {
- "kind": "",
- "startPos": {
- "offset": 159,
- "line": 15,
- "column": 2
- },
- "endPos": {
- "offset": 170,
- "line": 15,
- "column": 13
- },
- "value": "young_adult",
- "leadingTrivia": [
- {
- "kind": "