@likec4/language-server 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/browser.cjs +1 -1
  2. package/dist/browser.d.cts +3 -4
  3. package/dist/browser.d.mts +3 -4
  4. package/dist/browser.d.ts +3 -4
  5. package/dist/browser.mjs +1 -1
  6. package/dist/index.cjs +1 -1
  7. package/dist/index.d.cts +2 -2
  8. package/dist/index.d.mts +2 -2
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/model-graph/index.cjs +1 -1
  12. package/dist/model-graph/index.mjs +1 -1
  13. package/dist/node.cjs +1 -1
  14. package/dist/node.d.cts +3 -4
  15. package/dist/node.d.mts +3 -4
  16. package/dist/node.d.ts +3 -4
  17. package/dist/node.mjs +1 -1
  18. package/dist/shared/{language-server.DBJJUUgF.mjs → language-server.BFBeyvV8.mjs} +4 -3
  19. package/dist/shared/{language-server.CjFzaJwI.d.cts → language-server.BGy3FJPJ.d.cts} +2 -2
  20. package/dist/shared/{language-server.JWkqVjGv.cjs → language-server.Bfc-5M8A.cjs} +4 -3
  21. package/dist/shared/{language-server.DtBRb9os.mjs → language-server.CbqwHp7Q.mjs} +23 -9
  22. package/dist/shared/{language-server.CtKHXJDD.d.ts → language-server.CnVuAxDh.d.ts} +2 -2
  23. package/dist/shared/{language-server.D-84I33F.d.mts → language-server.DEK39RmI.d.mts} +2 -2
  24. package/dist/shared/{language-server.DwyCJvXm.cjs → language-server.DJhoJBWh.cjs} +17 -3
  25. package/package.json +12 -11
  26. package/src/formatting/LikeC4Formatter.ts +4 -2
  27. package/src/generated/ast.ts +99 -1
  28. package/src/generated/grammar.ts +1 -1
  29. package/src/generated/module.ts +1 -1
  30. package/src/lsp/CompletionProvider.ts +1 -1
  31. package/src/model-graph/compute-view/compute.ts +9 -4
  32. package/src/model-graph/utils/uniqueTags.test.ts +42 -0
  33. package/src/model-graph/utils/uniqueTags.ts +19 -0
package/dist/browser.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const lsp = require('langium/lsp');
4
4
  const browser = require('vscode-languageserver/browser');
5
- const module$1 = require('./shared/language-server.JWkqVjGv.cjs');
5
+ const module$1 = require('./shared/language-server.Bfc-5M8A.cjs');
6
6
 
7
7
  function startLanguageServer() {
8
8
  const messageReader = new browser.BrowserMessageReader(self);
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CjFzaJwI.cjs';
2
- import * as vscode_languageserver_browser from 'vscode-languageserver/browser';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.BGy3FJPJ.cjs';
2
+ import * as vscode_languageserver from 'vscode-languageserver';
3
3
  import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.cjs';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_browser.Connection;
14
+ connection: vscode_languageserver.Connection;
16
15
  messageReader: BrowserMessageReader;
17
16
  messageWriter: BrowserMessageWriter;
18
17
  shared: LikeC4SharedServices;
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.D-84I33F.mjs';
2
- import * as vscode_languageserver_browser from 'vscode-languageserver/browser';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.DEK39RmI.mjs';
2
+ import * as vscode_languageserver from 'vscode-languageserver';
3
3
  import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.mjs';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_browser.Connection;
14
+ connection: vscode_languageserver.Connection;
16
15
  messageReader: BrowserMessageReader;
17
16
  messageWriter: BrowserMessageWriter;
18
17
  shared: LikeC4SharedServices;
package/dist/browser.d.ts CHANGED
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CtKHXJDD.js';
2
- import * as vscode_languageserver_browser from 'vscode-languageserver/browser';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CnVuAxDh.js';
2
+ import * as vscode_languageserver from 'vscode-languageserver';
3
3
  import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.js';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_browser.Connection;
14
+ connection: vscode_languageserver.Connection;
16
15
  messageReader: BrowserMessageReader;
17
16
  messageWriter: BrowserMessageWriter;
18
17
  shared: LikeC4SharedServices;
package/dist/browser.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { startLanguageServer as startLanguageServer$1 } from 'langium/lsp';
2
2
  import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser';
3
- import { a as createLanguageServices } from './shared/language-server.DBJJUUgF.mjs';
3
+ import { a as createLanguageServices } from './shared/language-server.BFBeyvV8.mjs';
4
4
 
5
5
  function startLanguageServer() {
6
6
  const messageReader = new BrowserMessageReader(self);
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const module$1 = require('./shared/language-server.JWkqVjGv.cjs');
3
+ const module$1 = require('./shared/language-server.Bfc-5M8A.cjs');
4
4
 
5
5
 
6
6
 
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LogLevels } from '@likec4/log';
2
- import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.CjFzaJwI.cjs';
3
- export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.CjFzaJwI.cjs';
2
+ import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.BGy3FJPJ.cjs';
3
+ export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.BGy3FJPJ.cjs';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LogLevels } from '@likec4/log';
2
- import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.D-84I33F.mjs';
3
- export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.D-84I33F.mjs';
2
+ import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.DEK39RmI.mjs';
3
+ export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.DEK39RmI.mjs';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LogLevels } from '@likec4/log';
2
- import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.CtKHXJDD.js';
3
- export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.CtKHXJDD.js';
2
+ import { L as LikeC4LangiumDocument, a as LikeC4Services } from './shared/language-server.CnVuAxDh.js';
3
+ export { e as FqnIndex, F as FqnIndexEntry, I as IsValidFn, k as LanguageServicesContext, j as LikeC4AddedServices, f as LikeC4ModelBuilder, g as LikeC4ModelLocator, h as LikeC4ModelParser, d as LikeC4Module, i as LikeC4SharedServices, M as ModelParsedListener, c as createCustomLanguageServices, b as createLanguageServices, l as createSharedServices } from './shared/language-server.CnVuAxDh.js';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { L as LikeC4Module, c as createCustomLanguageServices, a as createLanguageServices, s as setLogLevel } from './shared/language-server.DBJJUUgF.mjs';
1
+ export { L as LikeC4Module, c as createCustomLanguageServices, a as createLanguageServices, s as setLogLevel } from './shared/language-server.BFBeyvV8.mjs';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const LikeC4ModelGraph = require('../shared/language-server.DwyCJvXm.cjs');
3
+ const LikeC4ModelGraph = require('../shared/language-server.DJhoJBWh.cjs');
4
4
 
5
5
 
6
6
 
@@ -1 +1 @@
1
- export { L as LikeC4ModelGraph, b as computeDynamicView, c as computeElementView, a as computeView } from '../shared/language-server.DtBRb9os.mjs';
1
+ export { L as LikeC4ModelGraph, b as computeDynamicView, c as computeElementView, a as computeView } from '../shared/language-server.CbqwHp7Q.mjs';
package/dist/node.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const lsp = require('langium/lsp');
4
4
  const node$1 = require('langium/node');
5
5
  const node = require('vscode-languageserver/node');
6
- const module$1 = require('./shared/language-server.JWkqVjGv.cjs');
6
+ const module$1 = require('./shared/language-server.Bfc-5M8A.cjs');
7
7
 
8
8
  function startLanguageServer() {
9
9
  const connection = node.createConnection(node.ProposedFeatures.all);
package/dist/node.d.cts CHANGED
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CjFzaJwI.cjs';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.BGy3FJPJ.cjs';
2
2
  import * as vscode_languageserver_lib_common_inlineCompletion_proposed from 'vscode-languageserver/lib/common/inlineCompletion.proposed';
3
- import * as vscode_languageserver_node from 'vscode-languageserver/node';
3
+ import * as vscode_languageserver from 'vscode-languageserver';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.cjs';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_node._Connection<vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver_node._>;
14
+ connection: vscode_languageserver._Connection<vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver._>;
16
15
  shared: LikeC4SharedServices;
17
16
  likec4: LikeC4Services;
18
17
  };
package/dist/node.d.mts CHANGED
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.D-84I33F.mjs';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.DEK39RmI.mjs';
2
2
  import * as vscode_languageserver_lib_common_inlineCompletion_proposed from 'vscode-languageserver/lib/common/inlineCompletion.proposed';
3
- import * as vscode_languageserver_node from 'vscode-languageserver/node';
3
+ import * as vscode_languageserver from 'vscode-languageserver';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.mjs';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_node._Connection<vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver_node._>;
14
+ connection: vscode_languageserver._Connection<vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver._>;
16
15
  shared: LikeC4SharedServices;
17
16
  likec4: LikeC4Services;
18
17
  };
package/dist/node.d.ts CHANGED
@@ -1,18 +1,17 @@
1
- import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CtKHXJDD.js';
1
+ import { i as LikeC4SharedServices, a as LikeC4Services } from './shared/language-server.CnVuAxDh.js';
2
2
  import * as vscode_languageserver_lib_common_inlineCompletion_proposed from 'vscode-languageserver/lib/common/inlineCompletion.proposed';
3
- import * as vscode_languageserver_node from 'vscode-languageserver/node';
3
+ import * as vscode_languageserver from 'vscode-languageserver';
4
4
  import '@likec4/core';
5
5
  import 'langium';
6
6
  import 'type-fest';
7
7
  import 'vscode-languageserver-types';
8
8
  import 'langium/lsp';
9
- import 'vscode-languageserver';
10
9
  import './protocol.js';
11
10
  import 'vscode-jsonrpc';
12
11
  import 'vscode-uri';
13
12
 
14
13
  declare function startLanguageServer(): {
15
- connection: vscode_languageserver_node._Connection<vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_node._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver_node._>;
14
+ connection: vscode_languageserver._Connection<vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver._, vscode_languageserver_lib_common_inlineCompletion_proposed.InlineCompletionFeatureShape, vscode_languageserver._>;
16
15
  shared: LikeC4SharedServices;
17
16
  likec4: LikeC4Services;
18
17
  };
package/dist/node.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { startLanguageServer as startLanguageServer$1 } from 'langium/lsp';
2
2
  import { NodeFileSystem } from 'langium/node';
3
3
  import { createConnection, ProposedFeatures } from 'vscode-languageserver/node';
4
- import { a as createLanguageServices } from './shared/language-server.DBJJUUgF.mjs';
4
+ import { a as createLanguageServices } from './shared/language-server.BFBeyvV8.mjs';
5
5
 
6
6
  function startLanguageServer() {
7
7
  const connection = createConnection(ProposedFeatures.all);
@@ -13,7 +13,7 @@ import { URI as URI$1 } from 'vscode-uri';
13
13
  import stripIndent from 'strip-indent';
14
14
  import hash from 'string-hash';
15
15
  import { deepEqual } from 'fast-equals';
16
- import { i as isAcyclic, f as findCycles, p as postorder, L as LikeC4ModelGraph, a as computeView, b as computeDynamicView } from './language-server.DtBRb9os.mjs';
16
+ import { i as isAcyclic, f as findCycles, p as postorder, L as LikeC4ModelGraph, a as computeView, b as computeDynamicView } from './language-server.CbqwHp7Q.mjs';
17
17
  import { hasProtocol, hasLeadingSlash, isRelative, withoutLeadingSlash, withoutBase, parsePath } from 'ufo';
18
18
  import { Graph } from '@dagrejs/graphlib';
19
19
  import { DocumentHighlight, DocumentHighlightKind } from 'vscode-languageserver';
@@ -5450,7 +5450,8 @@ class LikeC4Formatter extends AbstractFormatter {
5450
5450
  }
5451
5451
  formatRelation(node) {
5452
5452
  this.on(node, isRelation, (n, f) => {
5453
- f.property("source").append(FormattingOptions.oneSpace);
5453
+ const sourceNodes = n?.source?.$cstNode ? [n?.source?.$cstNode] : [];
5454
+ f.cst(sourceNodes).append(FormattingOptions.oneSpace);
5454
5455
  f.keywords("]->").prepend(FormattingOptions.noSpace);
5455
5456
  f.keywords("-[").append(FormattingOptions.noSpace);
5456
5457
  f.properties("target", "title", "technology", "tags").prepend(FormattingOptions.oneSpace);
@@ -5468,7 +5469,7 @@ class LikeC4Formatter extends AbstractFormatter {
5468
5469
  f.keywords("-[").append(FormattingOptions.noSpace);
5469
5470
  });
5470
5471
  this.on(node, isIncomingRelationExpression)?.keywords("->").append(FormattingOptions.oneSpace);
5471
- this.on(node, isInOutRelationExpression)?.property("inout").append(FormattingOptions.oneSpace);
5472
+ this.on(node, isInOutRelationExpression)?.keyword("->").prepend(FormattingOptions.oneSpace);
5472
5473
  }
5473
5474
  removeIndentFromTopLevelStatements(node) {
5474
5475
  if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node)) {
@@ -9,7 +9,7 @@ import { ChangeViewRequestParams } from '../protocol.cjs';
9
9
  import { URI } from 'vscode-uri';
10
10
 
11
11
  /******************************************************************************
12
- * This file was generated by langium-cli 3.1.1.
12
+ * This file was generated by langium-cli 3.2.0.
13
13
  * DO NOT EDIT MANUALLY!
14
14
  ******************************************************************************/
15
15
 
@@ -923,7 +923,7 @@ interface ParsedLikeC4LangiumDocument extends Omit<LangiumDocument<LikeC4Grammar
923
923
  }
924
924
  type Guard<N extends AstNode> = (n: AstNode) => n is N;
925
925
  type Guarded<G> = G extends Guard<infer N> ? N : never;
926
- declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isLikeC4View | typeof isRelation | typeof isExtendElement | typeof isElement | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
926
+ declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isRelation | typeof isExtendElement | typeof isElement | typeof isLikeC4View | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
927
927
  type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
928
928
  declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
929
929
  isValid: (n: ValidatableAstNode) => boolean;
@@ -15,7 +15,7 @@ const vscodeUri = require('vscode-uri');
15
15
  const stripIndent = require('strip-indent');
16
16
  const hash = require('string-hash');
17
17
  const fastEquals = require('fast-equals');
18
- const LikeC4ModelGraph = require('./language-server.DwyCJvXm.cjs');
18
+ const LikeC4ModelGraph = require('./language-server.DJhoJBWh.cjs');
19
19
  const ufo = require('ufo');
20
20
  const graphlib = require('@dagrejs/graphlib');
21
21
  const vscodeLanguageserver = require('vscode-languageserver');
@@ -5458,7 +5458,8 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5458
5458
  }
5459
5459
  formatRelation(node) {
5460
5460
  this.on(node, isRelation, (n, f) => {
5461
- f.property("source").append(FormattingOptions.oneSpace);
5461
+ const sourceNodes = n?.source?.$cstNode ? [n?.source?.$cstNode] : [];
5462
+ f.cst(sourceNodes).append(FormattingOptions.oneSpace);
5462
5463
  f.keywords("]->").prepend(FormattingOptions.noSpace);
5463
5464
  f.keywords("-[").append(FormattingOptions.noSpace);
5464
5465
  f.properties("target", "title", "technology", "tags").prepend(FormattingOptions.oneSpace);
@@ -5476,7 +5477,7 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5476
5477
  f.keywords("-[").append(FormattingOptions.noSpace);
5477
5478
  });
5478
5479
  this.on(node, isIncomingRelationExpression)?.keywords("->").append(FormattingOptions.oneSpace);
5479
- this.on(node, isInOutRelationExpression)?.property("inout").append(FormattingOptions.oneSpace);
5480
+ this.on(node, isInOutRelationExpression)?.keyword("->").prepend(FormattingOptions.oneSpace);
5480
5481
  }
5481
5482
  removeIndentFromTopLevelStatements(node) {
5482
5483
  if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node)) {
@@ -1,5 +1,5 @@
1
- import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, isViewRuleStyle, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, nonNullable, isAncestor, isViewRulePredicate, ancestorsFqn, isViewRuleAutoLayout, isScopedElementView, commonAncestor, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, isSameHierarchy } from '@likec4/core';
2
- import { pipe, map, pick, mapToObj, isTruthy, isNullish, omitBy, isEmpty, filter, anyPass, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, difference, allPass, omit, hasAtLeast, reduce, only, isNonNull, isString, isArray } from 'remeda';
1
+ import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, isViewRuleStyle, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, nonNullable, compareNatural, hasAtLeast, isAncestor, isViewRulePredicate, ancestorsFqn, isViewRuleAutoLayout, isScopedElementView, commonAncestor, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, isSameHierarchy } from '@likec4/core';
2
+ import { pipe, map, pick, mapToObj, isTruthy, isNullish, omitBy, isEmpty, filter, anyPass, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, difference, allPass, omit, hasAtLeast as hasAtLeast$1, only, reduce, isNonNull, isString, isArray } from 'remeda';
3
3
  import graphlib, { Graph } from '@dagrejs/graphlib';
4
4
  import objectHash from 'object-hash';
5
5
 
@@ -366,6 +366,16 @@ function sortNodes({
366
366
  return sorted;
367
367
  }
368
368
 
369
+ function uniqueTags(elements) {
370
+ const tags = pipe(
371
+ elements,
372
+ flatMap((e) => e.tags ?? []),
373
+ unique(),
374
+ sort(compareNatural)
375
+ );
376
+ return hasAtLeast(tags, 1) ? tags : null;
377
+ }
378
+
369
379
  const NoFilter = () => true;
370
380
  const Identity = (x) => x;
371
381
  const filterBy = (pred) => pred === NoFilter ? Identity : filter(pred);
@@ -865,7 +875,7 @@ class ComputeCtx {
865
875
  }
866
876
  computeEdges() {
867
877
  return this.ctxEdges.map((e) => {
868
- invariant(hasAtLeast(e.relations, 1), "Edge must have at least one relation");
878
+ invariant(hasAtLeast$1(e.relations, 1), "Edge must have at least one relation");
869
879
  const relations = sort(e.relations, compareRelations);
870
880
  const source = e.source.id;
871
881
  const target = e.target.id;
@@ -878,7 +888,11 @@ class ComputeCtx {
878
888
  relations: relations.map((r) => r.id)
879
889
  };
880
890
  let relation;
881
- relation = relations.length === 1 ? relations[0] : relations.find((r) => r.source === source && r.target === target);
891
+ relation = only(relations) ?? pipe(
892
+ relations,
893
+ filter((r) => r.source === source && r.target === target),
894
+ only()
895
+ );
882
896
  if (!relation) {
883
897
  const allprops = pipe(
884
898
  relations,
@@ -935,7 +949,7 @@ class ComputeCtx {
935
949
  navigateTo: only(allprops.navigateTo)
936
950
  };
937
951
  }
938
- const tags = unique(flatMap(relations, (r) => r.tags ?? []));
952
+ const tags = uniqueTags(relations);
939
953
  return Object.assign(
940
954
  edge,
941
955
  this.getEdgeLabel(relation),
@@ -947,7 +961,7 @@ class ComputeCtx {
947
961
  relation.head && { head: relation.head },
948
962
  relation.tail && { tail: relation.tail },
949
963
  relation.navigateTo && { navigateTo: relation.navigateTo },
950
- hasAtLeast(tags, 1) && { tags }
964
+ tags && { tags }
951
965
  );
952
966
  });
953
967
  }
@@ -969,7 +983,7 @@ class ComputeCtx {
969
983
  }
970
984
  addEdges(edges) {
971
985
  for (const e of edges) {
972
- if (!hasAtLeast(e.relations, 1)) {
986
+ if (!hasAtLeast$1(e.relations, 1)) {
973
987
  continue;
974
988
  }
975
989
  const existing = this.ctxEdges.find(
@@ -1357,8 +1371,8 @@ class DynamicViewComputeCtx {
1357
1371
  filter(isTruthy),
1358
1372
  unique()
1359
1373
  );
1360
- const tags = hasAtLeast(alltags, 1) ? alltags : null;
1361
- const relations = hasAtLeast(relationships, 1) ? map(relationships, (r) => r.id) : null;
1374
+ const tags = hasAtLeast$1(alltags, 1) ? alltags : null;
1375
+ const relations = hasAtLeast$1(relationships, 1) ? map(relationships, (r) => r.id) : null;
1362
1376
  const relation = only(relationships) || relationships.find((r) => r.source === source.id && r.target === target.id);
1363
1377
  const title = isTruthy(relation?.title) ? relation.title : pipe(
1364
1378
  relationships,
@@ -9,7 +9,7 @@ import { ChangeViewRequestParams } from '../protocol.js';
9
9
  import { URI } from 'vscode-uri';
10
10
 
11
11
  /******************************************************************************
12
- * This file was generated by langium-cli 3.1.1.
12
+ * This file was generated by langium-cli 3.2.0.
13
13
  * DO NOT EDIT MANUALLY!
14
14
  ******************************************************************************/
15
15
 
@@ -923,7 +923,7 @@ interface ParsedLikeC4LangiumDocument extends Omit<LangiumDocument<LikeC4Grammar
923
923
  }
924
924
  type Guard<N extends AstNode> = (n: AstNode) => n is N;
925
925
  type Guarded<G> = G extends Guard<infer N> ? N : never;
926
- declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isLikeC4View | typeof isRelation | typeof isExtendElement | typeof isElement | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
926
+ declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isRelation | typeof isExtendElement | typeof isElement | typeof isLikeC4View | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
927
927
  type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
928
928
  declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
929
929
  isValid: (n: ValidatableAstNode) => boolean;
@@ -9,7 +9,7 @@ import { ChangeViewRequestParams } from '../protocol.mjs';
9
9
  import { URI } from 'vscode-uri';
10
10
 
11
11
  /******************************************************************************
12
- * This file was generated by langium-cli 3.1.1.
12
+ * This file was generated by langium-cli 3.2.0.
13
13
  * DO NOT EDIT MANUALLY!
14
14
  ******************************************************************************/
15
15
 
@@ -923,7 +923,7 @@ interface ParsedLikeC4LangiumDocument extends Omit<LangiumDocument<LikeC4Grammar
923
923
  }
924
924
  type Guard<N extends AstNode> = (n: AstNode) => n is N;
925
925
  type Guarded<G> = G extends Guard<infer N> ? N : never;
926
- declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isLikeC4View | typeof isRelation | typeof isExtendElement | typeof isElement | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
926
+ declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isRelation | typeof isExtendElement | typeof isElement | typeof isLikeC4View | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
927
927
  type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
928
928
  declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
929
929
  isValid: (n: ValidatableAstNode) => boolean;
@@ -373,6 +373,16 @@ function sortNodes({
373
373
  return sorted;
374
374
  }
375
375
 
376
+ function uniqueTags(elements) {
377
+ const tags = remeda.pipe(
378
+ elements,
379
+ remeda.flatMap((e) => e.tags ?? []),
380
+ remeda.unique(),
381
+ remeda.sort(core.compareNatural)
382
+ );
383
+ return core.hasAtLeast(tags, 1) ? tags : null;
384
+ }
385
+
376
386
  const NoFilter = () => true;
377
387
  const Identity = (x) => x;
378
388
  const filterBy = (pred) => pred === NoFilter ? Identity : remeda.filter(pred);
@@ -885,7 +895,11 @@ class ComputeCtx {
885
895
  relations: relations.map((r) => r.id)
886
896
  };
887
897
  let relation;
888
- relation = relations.length === 1 ? relations[0] : relations.find((r) => r.source === source && r.target === target);
898
+ relation = remeda.only(relations) ?? remeda.pipe(
899
+ relations,
900
+ remeda.filter((r) => r.source === source && r.target === target),
901
+ remeda.only()
902
+ );
889
903
  if (!relation) {
890
904
  const allprops = remeda.pipe(
891
905
  relations,
@@ -942,7 +956,7 @@ class ComputeCtx {
942
956
  navigateTo: remeda.only(allprops.navigateTo)
943
957
  };
944
958
  }
945
- const tags = remeda.unique(remeda.flatMap(relations, (r) => r.tags ?? []));
959
+ const tags = uniqueTags(relations);
946
960
  return Object.assign(
947
961
  edge,
948
962
  this.getEdgeLabel(relation),
@@ -954,7 +968,7 @@ class ComputeCtx {
954
968
  relation.head && { head: relation.head },
955
969
  relation.tail && { tail: relation.tail },
956
970
  relation.navigateTo && { navigateTo: relation.navigateTo },
957
- remeda.hasAtLeast(tags, 1) && { tags }
971
+ tags && { tags }
958
972
  );
959
973
  });
960
974
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.10.0",
4
+ "version": "1.10.1",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -108,20 +108,20 @@
108
108
  },
109
109
  "dependencies": {
110
110
  "@dagrejs/graphlib": "^2.2.4",
111
- "@likec4/core": "1.10.0",
112
- "@likec4/log": "1.10.0",
111
+ "@likec4/core": "1.10.1",
112
+ "@likec4/log": "1.10.1",
113
113
  "@msgpack/msgpack": "^3.0.0-beta2",
114
114
  "@smithy/util-base64": "^3.0.0",
115
115
  "fast-equals": "^5.0.1",
116
116
  "indent-string": "^5.0.0",
117
117
  "json5": "^2.2.3",
118
- "langium": "3.1.3",
118
+ "langium": "3.2.0",
119
119
  "object-hash": "^3.0.0",
120
120
  "p-debounce": "^4.0.0",
121
121
  "remeda": "^2.11.0",
122
122
  "string-hash": "^1.1.3",
123
123
  "strip-indent": "^4.0.0",
124
- "type-fest": "^4.26.0",
124
+ "type-fest": "^4.26.1",
125
125
  "ufo": "^1.5.4",
126
126
  "vscode-jsonrpc": "8.2.0",
127
127
  "vscode-languageserver": "9.0.1",
@@ -129,19 +129,20 @@
129
129
  "vscode-uri": "3.0.8"
130
130
  },
131
131
  "devDependencies": {
132
- "@likec4/icons": "1.10.0",
133
- "@likec4/tsconfig": "1.10.0",
132
+ "@likec4/icons": "1.10.1",
133
+ "@likec4/tsconfig": "1.10.1",
134
134
  "@types/node": "^20.16.1",
135
135
  "@types/object-hash": "^3.0.6",
136
136
  "@types/string-hash": "^1.1.3",
137
+ "@vitest/coverage-v8": "^2.1.0",
137
138
  "execa": "^9.3.1",
138
- "langium-cli": "3.1.1",
139
+ "langium-cli": "3.2.0",
139
140
  "npm-run-all2": "^6.2.2",
140
141
  "tsx": "~4.9.3",
141
142
  "turbo": "^2.1.1",
142
- "typescript": "^5.5.4",
143
+ "typescript": "^5.6.2",
143
144
  "unbuild": "^3.0.0-rc.7",
144
- "vitest": "~2.0.5"
145
+ "vitest": "^2.1.0"
145
146
  },
146
- "packageManager": "yarn@4.3.1"
147
+ "packageManager": "yarn@4.4.1"
147
148
  }
@@ -46,7 +46,9 @@ export class LikeC4Formatter extends AbstractFormatter {
46
46
 
47
47
  protected formatRelation(node: AstNode) {
48
48
  this.on(node, ast.isRelation, (n, f) => {
49
- f.property('source').append(FormattingOptions.oneSpace)
49
+ const sourceNodes = n?.source?.$cstNode ? [n?.source?.$cstNode] : []
50
+
51
+ f.cst(sourceNodes).append(FormattingOptions.oneSpace)
50
52
  f.keywords(']->').prepend(FormattingOptions.noSpace)
51
53
  f.keywords('-[').append(FormattingOptions.noSpace)
52
54
 
@@ -73,7 +75,7 @@ export class LikeC4Formatter extends AbstractFormatter {
73
75
  ?.keywords('->').append(FormattingOptions.oneSpace)
74
76
 
75
77
  this.on(node, ast.isInOutRelationExpression)
76
- ?.property('inout').append(FormattingOptions.oneSpace)
78
+ ?.keyword('->').prepend(FormattingOptions.oneSpace)
77
79
  }
78
80
 
79
81
  protected removeIndentFromTopLevelStatements(node: AstNode) {
@@ -1,5 +1,5 @@
1
1
  /******************************************************************************
2
- * This file was generated by langium-cli 3.1.1.
2
+ * This file was generated by langium-cli 3.2.0.
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
 
@@ -28,6 +28,104 @@ export const LikeC4Terminals = {
28
28
  Hex: /[a-zA-Z0-9]+/,
29
29
  };
30
30
 
31
+ export type LikeC4TerminalNames = keyof typeof LikeC4Terminals;
32
+
33
+ export type LikeC4KeywordNames =
34
+ | "("
35
+ | ")"
36
+ | "*"
37
+ | ","
38
+ | "->"
39
+ | "-["
40
+ | ":"
41
+ | ";"
42
+ | "<-"
43
+ | "<->"
44
+ | "BottomTop"
45
+ | "LeftRight"
46
+ | "RightLeft"
47
+ | "TopBottom"
48
+ | "]->"
49
+ | "amber"
50
+ | "and"
51
+ | "autoLayout"
52
+ | "blue"
53
+ | "border"
54
+ | "browser"
55
+ | "color"
56
+ | "crow"
57
+ | "cylinder"
58
+ | "dashed"
59
+ | "description"
60
+ | "diamond"
61
+ | "dot"
62
+ | "dotted"
63
+ | "dynamic"
64
+ | "element"
65
+ | "element.kind"
66
+ | "element.tag"
67
+ | "exclude"
68
+ | "extend"
69
+ | "extends"
70
+ | "gray"
71
+ | "green"
72
+ | "head"
73
+ | "icon"
74
+ | "icons"
75
+ | "include"
76
+ | "indigo"
77
+ | "is"
78
+ | "kind"
79
+ | "likec4lib"
80
+ | "line"
81
+ | "link"
82
+ | "metadata"
83
+ | "mobile"
84
+ | "model"
85
+ | "muted"
86
+ | "navigateTo"
87
+ | "none"
88
+ | "normal"
89
+ | "not"
90
+ | "notation"
91
+ | "notes"
92
+ | "odiamond"
93
+ | "odot"
94
+ | "of"
95
+ | "onormal"
96
+ | "opacity"
97
+ | "open"
98
+ | "or"
99
+ | "par"
100
+ | "parallel"
101
+ | "person"
102
+ | "primary"
103
+ | "queue"
104
+ | "rectangle"
105
+ | "red"
106
+ | "relationship"
107
+ | "secondary"
108
+ | "shape"
109
+ | "sky"
110
+ | "slate"
111
+ | "solid"
112
+ | "specification"
113
+ | "storage"
114
+ | "style"
115
+ | "tag"
116
+ | "tail"
117
+ | "technology"
118
+ | "title"
119
+ | "vee"
120
+ | "view"
121
+ | "views"
122
+ | "where"
123
+ | "with"
124
+ | "{"
125
+ | "}";
126
+
127
+ export type LikeC4TokenNames = LikeC4TerminalNames | LikeC4KeywordNames;
128
+
31
129
  export type ArrowType = 'crow' | 'diamond' | 'dot' | 'none' | 'normal' | 'odiamond' | 'odot' | 'onormal' | 'open' | 'vee';
32
130
 
33
131
  export function isArrowType(item: unknown): item is ArrowType {
@@ -1,5 +1,5 @@
1
1
  /******************************************************************************
2
- * This file was generated by langium-cli 3.1.1.
2
+ * This file was generated by langium-cli 3.2.0.
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
 
@@ -1,5 +1,5 @@
1
1
  /******************************************************************************
2
- * This file was generated by langium-cli 3.1.1.
2
+ * This file was generated by langium-cli 3.2.0.
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { type CompletionProviderOptions, DefaultCompletionProvider } from 'langium/lsp'
2
2
 
3
3
  export class LikeC4CompletionProvider extends DefaultCompletionProvider {
4
- readonly completionOptions = {
4
+ override readonly completionOptions = {
5
5
  triggerCharacters: ['.']
6
6
  } satisfies CompletionProviderOptions
7
7
  }
@@ -30,7 +30,7 @@ import {
30
30
  parentFqn,
31
31
  whereOperatorAsPredicate
32
32
  } from '@likec4/core'
33
- import { filter, flatMap, hasAtLeast, isNonNull, isTruthy, map, omit, only, pipe, reduce, sort, unique } from 'remeda'
33
+ import { filter, hasAtLeast, isNonNull, isTruthy, map, omit, only, pipe, reduce, sort, unique } from 'remeda'
34
34
  import { calcViewLayoutHash } from '../../view-utils/view-hash'
35
35
  import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
36
36
  import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
@@ -39,6 +39,7 @@ import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'
39
39
  import { buildComputeNodes } from '../utils/buildComputeNodes'
40
40
  import { buildElementNotations } from '../utils/buildElementNotations'
41
41
  import { sortNodes } from '../utils/sortNodes'
42
+ import { uniqueTags } from '../utils/uniqueTags'
42
43
  import {
43
44
  type ElementPredicateFn,
44
45
  excludeElementKindOrTag,
@@ -218,7 +219,11 @@ export class ComputeCtx {
218
219
  tags?: NonEmptyArray<Tag>
219
220
  navigateTo?: ViewID | undefined
220
221
  } | undefined
221
- relation = relations.length === 1 ? relations[0] : relations.find(r => r.source === source && r.target === target)
222
+ relation = only(relations) ?? pipe(
223
+ relations,
224
+ filter(r => r.source === source && r.target === target),
225
+ only()
226
+ )
222
227
 
223
228
  // This edge represents mutliple relations
224
229
  // We use label if only it is the same for all relations
@@ -279,7 +284,7 @@ export class ComputeCtx {
279
284
  }
280
285
  }
281
286
 
282
- const tags = unique(flatMap(relations, r => r.tags ?? []))
287
+ const tags = uniqueTags(relations)
283
288
 
284
289
  return Object.assign(
285
290
  edge,
@@ -292,7 +297,7 @@ export class ComputeCtx {
292
297
  relation.head && { head: relation.head },
293
298
  relation.tail && { tail: relation.tail },
294
299
  relation.navigateTo && { navigateTo: relation.navigateTo },
295
- hasAtLeast(tags, 1) && { tags }
300
+ tags && { tags }
296
301
  )
297
302
  })
298
303
  }
@@ -0,0 +1,42 @@
1
+ import { compareNatural } from '@likec4/core'
2
+ import { describe, expect, it } from 'vitest'
3
+ import { uniqueTags } from './uniqueTags'
4
+
5
+ describe('uniqueTags function', () => {
6
+ it('returns unique tags from an array of elements', () => {
7
+ const input = [
8
+ { tags: ['tag1', 'tag2', 'tag3'] },
9
+ { tags: ['tag2', 'tag3', 'tag4'] },
10
+ { tags: ['tag3', 'tag4', 'tag5'] }
11
+ ] as const
12
+ const result = uniqueTags(input)
13
+ expect(result).toEqual(['tag1', 'tag2', 'tag3', 'tag4', 'tag5'])
14
+ })
15
+
16
+ it('should return unique tags naturally sorted', () => {
17
+ const input = [
18
+ { tags: ['tag1', 'tag20', 'tag30'] },
19
+ { tags: ['tag2', 'tag23', 'tag34'] },
20
+ { tags: ['tag3'] }
21
+ ] as const
22
+ const result = uniqueTags(input)
23
+ expect(result).toEqual([
24
+ 'tag1',
25
+ 'tag2',
26
+ 'tag3',
27
+ 'tag20',
28
+ 'tag23',
29
+ 'tag30',
30
+ 'tag34'
31
+ ].sort(compareNatural))
32
+ })
33
+
34
+ it('returns null if the tags array is null', () => {
35
+ const input = [
36
+ { tags: null },
37
+ {}
38
+ ]
39
+ const result = uniqueTags(input)
40
+ expect(result).toBeNull()
41
+ })
42
+ })
@@ -0,0 +1,19 @@
1
+ import { compareNatural, hasAtLeast, type NonEmptyReadonlyArray, type Tag } from '@likec4/core'
2
+ import { flatMap, pipe, sort, unique } from 'remeda'
3
+ import type { LiteralUnion } from 'type-fest'
4
+
5
+ /**
6
+ * Extracts unique tags from an array of elements.
7
+ * and sort in natural order; returns null if no tags are present.
8
+ */
9
+ export function uniqueTags<T extends { tags?: NonEmptyReadonlyArray<LiteralUnion<Tag, string>> | null }>(
10
+ elements: ReadonlyArray<T>
11
+ ) {
12
+ const tags = pipe(
13
+ elements,
14
+ flatMap(e => e.tags ?? []),
15
+ unique(),
16
+ sort(compareNatural)
17
+ )
18
+ return hasAtLeast(tags, 1) ? tags : null
19
+ }