@domainlang/language 0.1.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +32 -0
  2. package/out/ast-augmentation.d.ts +6 -0
  3. package/out/ast-augmentation.js +2 -0
  4. package/out/ast-augmentation.js.map +1 -0
  5. package/out/domain-lang-module.d.ts +55 -0
  6. package/out/domain-lang-module.js +59 -0
  7. package/out/domain-lang-module.js.map +1 -0
  8. package/out/generated/ast.d.ts +770 -0
  9. package/out/generated/ast.js +565 -0
  10. package/out/generated/ast.js.map +1 -0
  11. package/out/generated/grammar.d.ts +6 -0
  12. package/out/generated/grammar.js +2502 -0
  13. package/out/generated/grammar.js.map +1 -0
  14. package/out/generated/module.d.ts +13 -0
  15. package/out/generated/module.js +21 -0
  16. package/out/generated/module.js.map +1 -0
  17. package/out/index.d.ts +13 -0
  18. package/out/index.js +17 -0
  19. package/out/index.js.map +1 -0
  20. package/out/lsp/domain-lang-completion.d.ts +37 -0
  21. package/out/lsp/domain-lang-completion.js +452 -0
  22. package/out/lsp/domain-lang-completion.js.map +1 -0
  23. package/out/lsp/domain-lang-formatter.d.ts +15 -0
  24. package/out/lsp/domain-lang-formatter.js +43 -0
  25. package/out/lsp/domain-lang-formatter.js.map +1 -0
  26. package/out/lsp/domain-lang-naming.d.ts +34 -0
  27. package/out/lsp/domain-lang-naming.js +49 -0
  28. package/out/lsp/domain-lang-naming.js.map +1 -0
  29. package/out/lsp/domain-lang-scope.d.ts +59 -0
  30. package/out/lsp/domain-lang-scope.js +102 -0
  31. package/out/lsp/domain-lang-scope.js.map +1 -0
  32. package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
  33. package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
  34. package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
  35. package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
  36. package/out/lsp/hover/domain-lang-hover.js +306 -0
  37. package/out/lsp/hover/domain-lang-hover.js.map +1 -0
  38. package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
  39. package/out/lsp/hover/domain-lang-keywords.js +47 -0
  40. package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
  41. package/out/main-browser.d.ts +1 -0
  42. package/out/main-browser.js +11 -0
  43. package/out/main-browser.js.map +1 -0
  44. package/out/main.d.ts +1 -0
  45. package/out/main.js +74 -0
  46. package/out/main.js.map +1 -0
  47. package/out/sdk/ast-augmentation.d.ts +136 -0
  48. package/out/sdk/ast-augmentation.js +62 -0
  49. package/out/sdk/ast-augmentation.js.map +1 -0
  50. package/out/sdk/index.d.ts +94 -0
  51. package/out/sdk/index.js +97 -0
  52. package/out/sdk/index.js.map +1 -0
  53. package/out/sdk/indexes.d.ts +16 -0
  54. package/out/sdk/indexes.js +97 -0
  55. package/out/sdk/indexes.js.map +1 -0
  56. package/out/sdk/loader-node.d.ts +47 -0
  57. package/out/sdk/loader-node.js +104 -0
  58. package/out/sdk/loader-node.js.map +1 -0
  59. package/out/sdk/loader.d.ts +49 -0
  60. package/out/sdk/loader.js +85 -0
  61. package/out/sdk/loader.js.map +1 -0
  62. package/out/sdk/patterns.d.ts +93 -0
  63. package/out/sdk/patterns.js +123 -0
  64. package/out/sdk/patterns.js.map +1 -0
  65. package/out/sdk/query.d.ts +90 -0
  66. package/out/sdk/query.js +679 -0
  67. package/out/sdk/query.js.map +1 -0
  68. package/out/sdk/resolution.d.ts +52 -0
  69. package/out/sdk/resolution.js +68 -0
  70. package/out/sdk/resolution.js.map +1 -0
  71. package/out/sdk/types.d.ts +301 -0
  72. package/out/sdk/types.js +8 -0
  73. package/out/sdk/types.js.map +1 -0
  74. package/out/services/dependency-analyzer.d.ts +94 -0
  75. package/out/services/dependency-analyzer.js +279 -0
  76. package/out/services/dependency-analyzer.js.map +1 -0
  77. package/out/services/dependency-resolver.d.ts +123 -0
  78. package/out/services/dependency-resolver.js +252 -0
  79. package/out/services/dependency-resolver.js.map +1 -0
  80. package/out/services/git-url-resolver.browser.d.ts +18 -0
  81. package/out/services/git-url-resolver.browser.js +15 -0
  82. package/out/services/git-url-resolver.browser.js.map +1 -0
  83. package/out/services/git-url-resolver.d.ts +192 -0
  84. package/out/services/git-url-resolver.js +382 -0
  85. package/out/services/git-url-resolver.js.map +1 -0
  86. package/out/services/governance-validator.d.ts +80 -0
  87. package/out/services/governance-validator.js +159 -0
  88. package/out/services/governance-validator.js.map +1 -0
  89. package/out/services/import-resolver.d.ts +18 -0
  90. package/out/services/import-resolver.js +22 -0
  91. package/out/services/import-resolver.js.map +1 -0
  92. package/out/services/performance-optimizer.d.ts +60 -0
  93. package/out/services/performance-optimizer.js +140 -0
  94. package/out/services/performance-optimizer.js.map +1 -0
  95. package/out/services/relationship-inference.d.ts +11 -0
  96. package/out/services/relationship-inference.js +98 -0
  97. package/out/services/relationship-inference.js.map +1 -0
  98. package/out/services/workspace-manager.d.ts +76 -0
  99. package/out/services/workspace-manager.js +323 -0
  100. package/out/services/workspace-manager.js.map +1 -0
  101. package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
  102. package/out/syntaxes/domain-lang.monarch.js +29 -0
  103. package/out/syntaxes/domain-lang.monarch.js.map +1 -0
  104. package/out/utils/import-utils.d.ts +57 -0
  105. package/out/utils/import-utils.js +228 -0
  106. package/out/utils/import-utils.js.map +1 -0
  107. package/out/validation/bounded-context.d.ts +11 -0
  108. package/out/validation/bounded-context.js +79 -0
  109. package/out/validation/bounded-context.js.map +1 -0
  110. package/out/validation/classification.d.ts +3 -0
  111. package/out/validation/classification.js +3 -0
  112. package/out/validation/classification.js.map +1 -0
  113. package/out/validation/constants.d.ts +77 -0
  114. package/out/validation/constants.js +96 -0
  115. package/out/validation/constants.js.map +1 -0
  116. package/out/validation/domain-lang-validator.d.ts +2 -0
  117. package/out/validation/domain-lang-validator.js +27 -0
  118. package/out/validation/domain-lang-validator.js.map +1 -0
  119. package/out/validation/domain.d.ts +11 -0
  120. package/out/validation/domain.js +18 -0
  121. package/out/validation/domain.js.map +1 -0
  122. package/out/validation/import.d.ts +44 -0
  123. package/out/validation/import.js +135 -0
  124. package/out/validation/import.js.map +1 -0
  125. package/out/validation/maps.d.ts +21 -0
  126. package/out/validation/maps.js +56 -0
  127. package/out/validation/maps.js.map +1 -0
  128. package/out/validation/metadata.d.ts +7 -0
  129. package/out/validation/metadata.js +12 -0
  130. package/out/validation/metadata.js.map +1 -0
  131. package/out/validation/model.d.ts +12 -0
  132. package/out/validation/model.js +29 -0
  133. package/out/validation/model.js.map +1 -0
  134. package/out/validation/relationships.d.ts +12 -0
  135. package/out/validation/relationships.js +94 -0
  136. package/out/validation/relationships.js.map +1 -0
  137. package/out/validation/shared.d.ts +6 -0
  138. package/out/validation/shared.js +12 -0
  139. package/out/validation/shared.js.map +1 -0
  140. package/package.json +97 -0
  141. package/src/ast-augmentation.ts +5 -0
  142. package/src/domain-lang-module.ts +100 -0
  143. package/src/domain-lang.langium +356 -0
  144. package/src/generated/ast.ts +999 -0
  145. package/src/generated/grammar.ts +2504 -0
  146. package/src/generated/module.ts +25 -0
  147. package/src/index.ts +17 -0
  148. package/src/lsp/domain-lang-completion.ts +514 -0
  149. package/src/lsp/domain-lang-formatter.ts +51 -0
  150. package/src/lsp/domain-lang-naming.ts +56 -0
  151. package/src/lsp/domain-lang-scope.ts +137 -0
  152. package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
  153. package/src/lsp/hover/domain-lang-hover.ts +340 -0
  154. package/src/lsp/hover/domain-lang-keywords.ts +50 -0
  155. package/src/main-browser.ts +15 -0
  156. package/src/main.ts +85 -0
  157. package/src/sdk/README.md +297 -0
  158. package/src/sdk/ast-augmentation.ts +157 -0
  159. package/src/sdk/index.ts +128 -0
  160. package/src/sdk/indexes.ts +155 -0
  161. package/src/sdk/loader-node.ts +126 -0
  162. package/src/sdk/loader.ts +99 -0
  163. package/src/sdk/patterns.ts +147 -0
  164. package/src/sdk/query.ts +802 -0
  165. package/src/sdk/resolution.ts +78 -0
  166. package/src/sdk/types.ts +346 -0
  167. package/src/services/dependency-analyzer.ts +381 -0
  168. package/src/services/dependency-resolver.ts +334 -0
  169. package/src/services/git-url-resolver.browser.ts +31 -0
  170. package/src/services/git-url-resolver.ts +524 -0
  171. package/src/services/governance-validator.ts +219 -0
  172. package/src/services/import-resolver.ts +30 -0
  173. package/src/services/performance-optimizer.ts +170 -0
  174. package/src/services/relationship-inference.ts +121 -0
  175. package/src/services/workspace-manager.ts +416 -0
  176. package/src/syntaxes/domain-lang.monarch.ts +29 -0
  177. package/src/utils/import-utils.ts +274 -0
  178. package/src/validation/bounded-context.ts +99 -0
  179. package/src/validation/classification.ts +5 -0
  180. package/src/validation/constants.ts +124 -0
  181. package/src/validation/domain-lang-validator.ts +33 -0
  182. package/src/validation/domain.ts +24 -0
  183. package/src/validation/import.ts +171 -0
  184. package/src/validation/maps.ts +72 -0
  185. package/src/validation/metadata.ts +14 -0
  186. package/src/validation/model.ts +37 -0
  187. package/src/validation/relationships.ts +154 -0
  188. package/src/validation/shared.ts +14 -0
@@ -0,0 +1,340 @@
1
+ import type {
2
+ AstNode,
3
+ CommentProvider,
4
+ DocumentationProvider,
5
+ LangiumDocument,
6
+ MaybePromise,
7
+ Reference
8
+ } from 'langium';
9
+ import { CstUtils, isAstNodeWithComment, isJSDoc, isReference, parseJSDoc } from 'langium';
10
+ import type { LangiumServices } from 'langium/lsp';
11
+ import { AstNodeHoverProvider } from 'langium/lsp';
12
+ import type { Hover, HoverParams } from 'vscode-languageserver';
13
+ import * as ast from '../../generated/ast.js';
14
+ import type { DomainLangServices } from '../../domain-lang-module.js';
15
+ import { QualifiedNameProvider } from '../domain-lang-naming.js';
16
+ import { keywordExplanations } from './domain-lang-keywords.js';
17
+ import { effectiveClassification, effectiveTeam } from '../../sdk/resolution.js';
18
+
19
+ /**
20
+ * Provides hover information for DomainLang elements.
21
+ */
22
+ export class DomainLangHoverProvider extends AstNodeHoverProvider {
23
+ protected readonly documentationProvider: DocumentationProvider;
24
+ protected readonly commentProvider: CommentProvider;
25
+ protected readonly qualifiedNameProvider: QualifiedNameProvider;
26
+
27
+ constructor(services: LangiumServices) {
28
+ super(services);
29
+ this.documentationProvider = services.documentation.DocumentationProvider;
30
+ this.commentProvider = services.documentation.CommentProvider;
31
+ const domainServices = services as DomainLangServices;
32
+ this.qualifiedNameProvider = domainServices.references.QualifiedNameProvider;
33
+ }
34
+
35
+ override async getHoverContent(document: LangiumDocument, params: HoverParams): Promise<Hover | undefined> {
36
+ try {
37
+ const rootNode = document.parseResult?.value?.$cstNode;
38
+ if (!rootNode) {
39
+ return undefined;
40
+ }
41
+
42
+ const offset = document.textDocument.offsetAt(params.position);
43
+ const cstNode = CstUtils.findDeclarationNodeAtOffset(rootNode, offset, this.grammarConfig.nameRegexp);
44
+ if (!cstNode || cstNode.offset + cstNode.length <= offset) {
45
+ return undefined;
46
+ }
47
+
48
+ const targetNodes = this.references.findDeclarations(cstNode);
49
+ const targetNode = targetNodes?.[0];
50
+ if (targetNode) {
51
+ const content = await this.getAstNodeHoverContent(targetNode);
52
+ if (content) {
53
+ return { contents: { kind: 'markdown', value: content } };
54
+ }
55
+ }
56
+
57
+ if (cstNode.astNode && ast.isThisRef(cstNode.astNode)) {
58
+ const content = await this.getAstNodeHoverContent(cstNode.astNode);
59
+ if (content) {
60
+ return { contents: { kind: 'markdown', value: content } };
61
+ }
62
+ }
63
+
64
+ if (cstNode.grammarSource?.$type === 'Keyword') {
65
+ const keywordHover = this.getKeywordHoverContent(cstNode.grammarSource);
66
+ if (keywordHover) {
67
+ return keywordHover;
68
+ }
69
+
70
+ const explanation = keywordExplanations[cstNode.text.toLowerCase()];
71
+ if (explanation) {
72
+ return { contents: { kind: 'markdown', value: `💡 ${explanation}` } };
73
+ }
74
+ }
75
+ } catch (error) {
76
+ console.error('Error in getHoverContent:', error);
77
+ }
78
+
79
+ return undefined;
80
+ }
81
+
82
+ protected getAstNodeHoverContent(node: AstNode): MaybePromise<string | undefined> {
83
+ try {
84
+ const content = this.documentationProvider.getDocumentation(node);
85
+ const commentBlock = content ? `*${content}*\n\n` : '';
86
+
87
+ if (ast.isDomain(node)) {
88
+ const description = node.description ?? '';
89
+ const vision = node.vision ?? '';
90
+ const typeRef = node.type?.ref;
91
+ const type = this.getRefName(typeRef);
92
+
93
+ const signatureParts = ['Domain', node.name];
94
+ if (node.parent?.ref?.name) signatureParts.push('in', node.parent.ref.name);
95
+ const signature = `\`\`\`domain-lang\n${signatureParts.join(' ')}\n\`\`\``;
96
+
97
+ const fields: string[] = [signature];
98
+ if (description) fields.push(description);
99
+ if (vision || type || node.parent) fields.push('---');
100
+ if (vision) fields.push(`**Vision:** ${vision}`);
101
+ if (type) fields.push(`**Type:** ${this.refLink(typeRef, type)}`);
102
+ if (node.parent) fields.push(`**Parent:** ${this.refLink(node.parent)}`);
103
+
104
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
105
+ `📁 **\`(domain)\` ${node.name}**\n\n` +
106
+ fields.join('\n\n');
107
+ }
108
+
109
+ if (ast.isThisRef(node)) {
110
+ let parent = node.$container;
111
+ while (parent) {
112
+ if (
113
+ ast.isDomain(parent) ||
114
+ ast.isBoundedContext(parent) ||
115
+ ast.isNamespaceDeclaration(parent) ||
116
+ ast.isContextMap(parent) ||
117
+ ast.isDomainMap(parent) ||
118
+ ast.isModel(parent)
119
+ ) {
120
+ return this.getAstNodeHoverContent(parent);
121
+ }
122
+ parent = parent.$container;
123
+ }
124
+
125
+ return '*this* refers to the current context';
126
+ }
127
+
128
+ if (ast.isBoundedContext(node)) {
129
+ const description = node.description ?? '';
130
+ const classification = effectiveClassification(node);
131
+ const team = effectiveTeam(node);
132
+ const businessModel = node.businessModel?.ref;
133
+ const evolution = node.evolution?.ref;
134
+ const relationships = node.relationships ?? [];
135
+ const terminology = node.terminology ?? [];
136
+ const decisions = node.decisions ?? [];
137
+ const classificationName = classification?.name;
138
+ const teamName = team?.name;
139
+
140
+ const signatureParts = ['boundedcontext', node.name];
141
+ if (node.domain?.ref?.name) signatureParts.push('for', node.domain.ref.name);
142
+ if (classificationName) signatureParts.push('as', classificationName);
143
+ if (teamName) signatureParts.push('by', teamName);
144
+ const signature = `\`\`\`domain-lang\n${signatureParts.join(' ')}\n\`\`\``;
145
+
146
+ const fields: string[] = [signature];
147
+ if (description) fields.push(description);
148
+ if (classification || team || businessModel || evolution) fields.push('---');
149
+ if (classification) fields.push(`🔖 **Classification:** ${this.refLink(classification)}`);
150
+ if (team) fields.push(`👥 **Team:** ${this.refLink(team)}`);
151
+ if (businessModel) fields.push(`💼 **Business Model:** ${this.refLink(businessModel)}`);
152
+ if (evolution) fields.push(`🔄 **Evolution:** ${this.refLink(evolution)}`);
153
+ if (relationships.length) {
154
+ const relationshipLines = relationships.map(rel => `- ${this.refLink(rel.left?.link)} ${rel.arrow} ${this.refLink(rel.right?.link)}${rel.type ? ` \`${rel.type}\`` : ''}`);
155
+ fields.push(`**Relationships:**\n${relationshipLines.join('\n')}`);
156
+ }
157
+ if (terminology.length) {
158
+ const termLines = terminology.map(t => `- \`${t.name}\`: ${t.meaning ?? ''}`);
159
+ fields.push(`**Terminology:**\n${termLines.join('\n')}`);
160
+ }
161
+ if (decisions.length) {
162
+ const decisionLines = decisions.map(d => `- \`${d.name}\`: ${d.value ?? ''}`);
163
+ fields.push(`**Decisions:**\n${decisionLines.join('\n')}`);
164
+ }
165
+
166
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
167
+ `📕 **\`(boundedcontext)\` ${node.name}**\n\n` +
168
+ fields.join('\n\n');
169
+ }
170
+
171
+ if (ast.isNamespaceDeclaration && ast.isNamespaceDeclaration(node)) {
172
+ const fields: string[] = [`Contains ${node.children.length} elements.`];
173
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
174
+ `🧭 **\`(namespace)\` ${node.name}**\n\n` +
175
+ fields.join('\n\n');
176
+ }
177
+
178
+ if (ast.isContextMap && ast.isContextMap(node)) {
179
+ const fields: string[] = [];
180
+ if (node.boundedContexts.length) {
181
+ fields.push('---');
182
+ fields.push(`**📕 Bounded Contexts**\n${node.boundedContexts.flatMap(bc => bc.items.map(item => `- ${this.refLink(item.ref)}`)).join('\n')}`);
183
+ }
184
+ if (node.relationships.length) {
185
+ fields.push('---');
186
+ fields.push(`**🔗 Relationships**\n${node.relationships.map(r => `- ${this.refLink(r.left?.link)} ${r.arrow} ${this.refLink(r.right?.link)}${r.type ? ` \`${r.type}\`` : ''}`).join('\n')}`);
187
+ }
188
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
189
+ `🗺️ **\`(contextmap)\` ${node.name}**\n\n` +
190
+ fields.join('\n\n');
191
+ }
192
+
193
+ if (ast.isDomainMap && ast.isDomainMap(node)) {
194
+ const fields: string[] = [];
195
+ if (node.domains.length) {
196
+ fields.push('---');
197
+ fields.push(`**📁 Domains**\n${node.domains.flatMap(d => d.items.map(item => `- ${this.refLink(item.ref)}`)).join('\n')}`);
198
+ }
199
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
200
+ `🗺️ **\`(domainmap)\` ${node.name}**\n\n` +
201
+ fields.join('\n\n');
202
+ }
203
+
204
+ if (ast.isDecision && ast.isDecision(node)) {
205
+ const fields: string[] = [];
206
+ if (node.value) {
207
+ fields.push('---');
208
+ fields.push(`*Definition:* ${node.value}`);
209
+ }
210
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
211
+ `⚖️ **\`(decision)\` ${node.name}**\n\n` +
212
+ fields.join('\n\n');
213
+ }
214
+
215
+ if (ast.isPolicy && ast.isPolicy(node)) {
216
+ const fields: string[] = [];
217
+ if (node.value) {
218
+ fields.push('---');
219
+ fields.push(`*Definition:* ${node.value}`);
220
+ }
221
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
222
+ `📜 **\`(policy)\` ${node.name}**\n\n` +
223
+ fields.join('\n\n');
224
+ }
225
+
226
+ if (ast.isBusinessRule && ast.isBusinessRule(node)) {
227
+ const fields: string[] = [];
228
+ if (node.value) {
229
+ fields.push('---');
230
+ fields.push(`*Definition:* ${node.value}`);
231
+ }
232
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
233
+ `⚖️ **\`(rule)\` ${node.name}**\n\n` +
234
+ fields.join('\n\n');
235
+ }
236
+
237
+ if (ast.isDomainTerm && ast.isDomainTerm(node)) {
238
+ const fields: string[] = [];
239
+ if (node.meaning) {
240
+ fields.push('---');
241
+ fields.push(`*${node.meaning}*`);
242
+ }
243
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
244
+ `🗝️ **\`(term)\` ${node.name}**\n\n` +
245
+ fields.join('\n\n');
246
+ }
247
+
248
+ if (ast.isTeam && ast.isTeam(node)) {
249
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
250
+ `👥 **\`(team)\` ${node.name}**`;
251
+ }
252
+
253
+ if (ast.isClassification && ast.isClassification(node)) {
254
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
255
+ `🏷️ **\`(classification)\` ${node.name}**`;
256
+ }
257
+
258
+ if (ast.isMetadata && ast.isMetadata(node)) {
259
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
260
+ `🔖 **\`(metadata)\` ${node.name}**`;
261
+ }
262
+
263
+ if (ast.isRelationship && ast.isRelationship(node)) {
264
+ const leftPatterns = node.leftPatterns.join(', ');
265
+ const rightPatterns = node.rightPatterns.join(', ');
266
+ const fields: string[] = [];
267
+ fields.push(`${this.refLink(node.left.link)} ${node.arrow} ${this.refLink(node.right.link)}`);
268
+ if (node.type) fields.push(`**Type:** \`${node.type}\``);
269
+ if (leftPatterns) fields.push(`**Left patterns:** ${leftPatterns}`);
270
+ if (rightPatterns) fields.push(`**Right patterns:** ${rightPatterns}`);
271
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
272
+ `🔗 **\`(relationship)\`**\n\n` +
273
+ fields.join('\n\n');
274
+ }
275
+
276
+ if (ast.isImportStatement && ast.isImportStatement(node)) {
277
+ const fields: string[] = [];
278
+ fields.push(`**URI:** \`${node.uri}\``);
279
+ if (node.symbols.length) fields.push(`**Imports:** ${node.symbols.map(s => `\`${s}\``).join(', ')}`);
280
+ if (node.alias) fields.push(`**Alias:** \`${node.alias}\``);
281
+ if (node.integrity) fields.push(`**Integrity:** \`${node.integrity}\``);
282
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
283
+ `📦 **\`(import)\`**\n\n` +
284
+ fields.join('\n\n');
285
+ }
286
+
287
+ const title = ast.isType(node) ? node.name : node.$type.toLowerCase();
288
+ return (commentBlock ? `${commentBlock}\n\n---\n\n` : '') +
289
+ `ℹ️ **\`(${node.$type.toLowerCase()})\`${ast.isType(node) ? ` ${title}` : ''}**`;
290
+ } catch (error) {
291
+ console.error('Error in getAstNodeHoverContent:', error);
292
+ return 'Unable to display complete information.';
293
+ }
294
+ }
295
+
296
+ private getRefName(ref: ast.Type | Reference<ast.Type> | undefined): string {
297
+ const node = isReference(ref) ? ref.ref : ref;
298
+ if (node && ast.isType(node)) {
299
+ return node.name;
300
+ }
301
+ return '';
302
+ }
303
+
304
+ private refLink(ref: Reference<ast.Type> | ast.Type | undefined, label?: string): string {
305
+ if (label) {
306
+ return `[${label}](#${encodeURIComponent(label)})`;
307
+ }
308
+
309
+ const node = isReference(ref) ? ref.ref : ref;
310
+
311
+ if (node && ast.isType(node)) {
312
+ let linkLabel = node.name;
313
+ try {
314
+ linkLabel = this.qualifiedNameProvider.getQualifiedName(node.$container, node.name);
315
+ } catch {
316
+ // fallback to name
317
+ }
318
+ return `[${linkLabel}](#${encodeURIComponent(linkLabel)})`;
319
+ }
320
+ return '';
321
+ }
322
+
323
+ protected override getKeywordHoverContent(node: AstNode): MaybePromise<Hover | undefined> {
324
+ let comment = isAstNodeWithComment(node) ? node.$comment : undefined;
325
+
326
+ if (!comment) {
327
+ comment = CstUtils.findCommentNode(node.$cstNode, ['ML_COMMENT'])?.text;
328
+ }
329
+
330
+ if (comment && isJSDoc(comment)) {
331
+ const markdown = parseJSDoc(comment).toMarkdown();
332
+ if (markdown) {
333
+ return { contents: { kind: 'markdown', value: markdown } };
334
+ }
335
+ }
336
+
337
+ return undefined;
338
+ }
339
+ }
340
+
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Keyword explanations for DomainLang hover documentation.
3
+ *
4
+ * This dictionary provides fallback hover content for keywords that don't have
5
+ * JSDoc comments in the grammar file, or for providing richer DDD pattern explanations.
6
+ *
7
+ * Basic keyword documentation (domain, boundedcontext, etc.) is now in the grammar file
8
+ * as JSDoc comments. This dictionary focuses on DDD integration patterns and advanced concepts.
9
+ *
10
+ * @see src/language/domain-lang.langium for basic keyword JSDoc
11
+ * @see ddd-pattern-explanations.ts for role patterns and relationship types
12
+ */
13
+ export const keywordExplanations: Record<string, string> = {
14
+ // Advanced syntax keywords
15
+ implements: "**implements** - Declares that a Bounded Context or type implements a Domain or interface.",
16
+ as: "**as** - Used for aliasing or renaming imports or types.",
17
+ from: "**from** - Specifies the source module or file for an import statement.",
18
+ type: "**type** - Declares a new type or alias in the model.",
19
+ map: "**map** - Defines a mapping or transformation between elements.",
20
+ this: "**this** - Refers to the current context or object.",
21
+
22
+ // DDD Classifiers
23
+ entity: "**Entity** - Domain object with distinct identity that runs through time.",
24
+ valueobject: "**Value Object** - Immutable object that describes a characteristic.",
25
+ aggregate: "**Aggregate** - Cluster of domain objects with a root and boundary.",
26
+ service: "**Service** - Stateless domain operation.",
27
+ event: "**Event** - Significant domain occurrence or state change.",
28
+ businessrule: "**Business Rule** - Rule that constrains business behavior.",
29
+
30
+ // DDD Integration Patterns
31
+ acl: "**ACL (Anti-Corruption Layer)** - Translation layer protecting downstream context from upstream changes.",
32
+ ohs: "**OHS (Open Host Service)** - Well-defined protocol providing access to a subsystem.",
33
+ pl: "**PL (Published Language)** - Documented shared language for context communication.",
34
+ cf: "**CF (Conformist)** - Downstream adopts upstream model without translation.",
35
+ bbom: "**BBoM (Big Ball of Mud)** - System with tangled architecture and no clear boundaries.",
36
+ sk: "**SK (Shared Kernel)** - Shared domain model subset requiring coordination.",
37
+ p: "**P (Partnership)** - Teams collaborate closely with shared success/failure.",
38
+
39
+ // DDD Relationship Types
40
+ separateways: "**Separate Ways** - No connection between contexts, each solves problems independently.",
41
+ partnership: "**Partnership** - Teams share risks and rewards with close collaboration.",
42
+ sharedkernel: "**Shared Kernel** - Shared domain model subset requiring coordination.",
43
+ customersupplier: "**Customer-Supplier** - Upstream prioritizes downstream needs.",
44
+ upstreamdownstream: "**Upstream-Downstream** - Upstream changes affect downstream.",
45
+
46
+ // Relationship arrows
47
+ '<->': "**Bidirectional** - Two contexts connected in both directions.",
48
+ '->': "**Upstream → Downstream** - Left depends on right.",
49
+ '<-': "**Downstream ← Upstream** - Right depends on left.",
50
+ };
@@ -0,0 +1,15 @@
1
+ import { EmptyFileSystem } from 'langium';
2
+ import { startLanguageServer } from 'langium/lsp';
3
+ import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser.js';
4
+ import { createDomainLangServices } from './domain-lang-module.js';
5
+
6
+ // declare const self: DedicatedWorkerGlobalScope;
7
+
8
+ const messageReader = new BrowserMessageReader(self);
9
+ const messageWriter = new BrowserMessageWriter(self);
10
+
11
+ const connection = createConnection(messageReader, messageWriter);
12
+
13
+ const { shared } = createDomainLangServices({ connection, ...EmptyFileSystem });
14
+
15
+ startLanguageServer(shared);
package/src/main.ts ADDED
@@ -0,0 +1,85 @@
1
+ import { startLanguageServer } from 'langium/lsp';
2
+ import { NodeFileSystem } from 'langium/node';
3
+ import { createConnection, ProposedFeatures } from 'vscode-languageserver/node.js';
4
+ import { createDomainLangServices } from './domain-lang-module.js';
5
+ import { ensureImportGraphFromEntryFile } from './utils/import-utils.js';
6
+ import { URI } from 'langium';
7
+
8
+ // Create a connection to the client
9
+ const connection = createConnection(ProposedFeatures.all);
10
+
11
+ // Inject the shared services and language-specific services
12
+ const { shared, DomainLang } = createDomainLangServices({ connection, ...NodeFileSystem });
13
+
14
+ // Initialize workspace on connection
15
+ connection.onInitialize(async (params) => {
16
+ const workspaceRoot = params.rootUri ? URI.parse(params.rootUri).fsPath : undefined;
17
+
18
+ if (workspaceRoot) {
19
+ try {
20
+ // Initialize workspace manager
21
+ const workspaceManager = DomainLang.imports.WorkspaceManager;
22
+ await workspaceManager.initialize(workspaceRoot);
23
+ console.warn(`DomainLang workspace initialized: ${workspaceRoot}`);
24
+ } catch (error) {
25
+ const message = error instanceof Error ? error.message : String(error);
26
+ console.warn(`Failed to initialize workspace: ${message}`);
27
+ // Continue without workspace - local imports will still work
28
+ }
29
+ }
30
+
31
+ return {
32
+ capabilities: {
33
+ // Language server capabilities are configured by Langium
34
+ }
35
+ };
36
+ });
37
+
38
+ // Optionally start from a single entry file and follow imports.
39
+ // Configure via env DOMAINLANG_ENTRY (absolute or workspace-relative path)
40
+ const entryFile = process.env.DOMAINLANG_ENTRY;
41
+ if (entryFile) {
42
+ let currentGraph = new Set<string>();
43
+
44
+ /**
45
+ * Reloads the import graph from the entry file.
46
+ * Handles errors gracefully and notifies the LSP client.
47
+ */
48
+ const reloadFromEntry = async (): Promise<void> => {
49
+ try {
50
+ currentGraph = await ensureImportGraphFromEntryFile(
51
+ entryFile,
52
+ shared.workspace.LangiumDocuments
53
+ );
54
+ console.warn(`Successfully loaded import graph from ${entryFile}`);
55
+ } catch (error) {
56
+ const message = error instanceof Error ? error.message : String(error);
57
+ console.error(`Failed to preload import graph from ${entryFile}: ${message}`);
58
+ // Notify LSP client of the error
59
+ connection.console.error(
60
+ `DomainLang: Could not load entry file ${entryFile}. Error: ${message}`
61
+ );
62
+ }
63
+ };
64
+
65
+ // Initial load from entry file, then start the server
66
+ reloadFromEntry().finally(() => startLanguageServer(shared));
67
+
68
+ // Any change within the loaded graph should trigger a reload from the entry
69
+ shared.workspace.TextDocuments.onDidChangeContent(async (event) => {
70
+ const changed = event.document.uri;
71
+ if (currentGraph.has(changed)) {
72
+ await reloadFromEntry();
73
+ }
74
+ });
75
+
76
+ // If the entry file itself is opened/changed, also reload
77
+ shared.workspace.TextDocuments.onDidOpen(async (event) => {
78
+ if (URI.parse(event.document.uri).fsPath === URI.file(entryFile).fsPath) {
79
+ await reloadFromEntry();
80
+ }
81
+ });
82
+ } else {
83
+ // No entry file configured: start normally
84
+ startLanguageServer(shared);
85
+ }