@likec4/language-server 1.32.0 → 1.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast.d.ts +1 -0
- package/dist/ast.js +4 -1
- package/dist/bundled.mjs +2314 -2312
- package/dist/config/schema.d.ts +2 -2
- package/dist/config/schema.js +8 -11
- package/dist/formatting/LikeC4Formatter.js +2 -2
- package/dist/generated/ast.d.ts +30 -6
- package/dist/generated/ast.js +44 -9
- package/dist/generated/grammar.js +1 -1
- package/dist/mcp/LikeC4MCPServerFactory.d.ts +4 -0
- package/dist/mcp/LikeC4MCPServerFactory.js +6 -0
- package/dist/mcp/sseserver/MCPServer.d.ts +4 -2
- package/dist/mcp/sseserver/MCPServer.js +12 -6
- package/dist/mcp/sseserver/with-mcp-server.js +22 -6
- package/dist/model/model-builder.js +20 -19
- package/dist/model/model-parser.d.ts +9 -0
- package/dist/model/parser/Base.d.ts +1 -0
- package/dist/model/parser/Base.js +22 -0
- package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
- package/dist/model/parser/DeploymentModelParser.js +7 -5
- package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
- package/dist/model/parser/FqnRefParser.d.ts +1 -0
- package/dist/model/parser/GlobalsParser.d.ts +1 -0
- package/dist/model/parser/ImportsParser.d.ts +1 -0
- package/dist/model/parser/ModelParser.d.ts +1 -0
- package/dist/model/parser/ModelParser.js +7 -5
- package/dist/model/parser/PredicatesParser.d.ts +1 -0
- package/dist/model/parser/SpecificationParser.d.ts +1 -0
- package/dist/model/parser/SpecificationParser.js +4 -2
- package/dist/model/parser/ViewsParser.d.ts +1 -0
- package/dist/module.d.ts +2 -2
- package/dist/module.js +4 -4
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +19 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +10 -3
- package/dist/validation/property-checks.d.ts +1 -0
- package/dist/validation/property-checks.js +62 -0
- package/dist/views/configurable-layouter.d.ts +2 -2
- package/dist/views/configurable-layouter.js +4 -2
- package/dist/views/likec4-views.d.ts +2 -3
- package/dist/views/likec4-views.js +29 -50
- package/dist/workspace/ProjectsManager.d.ts +0 -1
- package/dist/workspace/ProjectsManager.js +15 -4
- package/package.json +16 -16
|
@@ -2,6 +2,8 @@ import type { ServerOptions } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
2
2
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
export interface LikeC4MCPServer {
|
|
5
|
+
readonly isStarted: boolean;
|
|
6
|
+
readonly port: number;
|
|
5
7
|
start(port: number): Promise<void>;
|
|
6
8
|
stop(): Promise<void>;
|
|
7
9
|
}
|
|
@@ -9,6 +11,8 @@ export interface LikeC4MCPServerFactory {
|
|
|
9
11
|
create(options?: ServerOptions): McpServer;
|
|
10
12
|
}
|
|
11
13
|
export declare class NoopLikeC4MCPServer implements LikeC4MCPServer {
|
|
14
|
+
get isStarted(): boolean;
|
|
15
|
+
get port(): number;
|
|
12
16
|
start(port: number): Promise<never>;
|
|
13
17
|
stop(): Promise<never>;
|
|
14
18
|
}
|
|
@@ -3,10 +3,12 @@ import type { LikeC4Services } from '../../module';
|
|
|
3
3
|
import type { LikeC4MCPServer } from '../LikeC4MCPServerFactory';
|
|
4
4
|
export declare class SSELikeC4MCPServer implements LikeC4MCPServer, AsyncDisposable {
|
|
5
5
|
private services;
|
|
6
|
-
private
|
|
6
|
+
private transports;
|
|
7
7
|
private server;
|
|
8
|
-
private
|
|
8
|
+
private _port;
|
|
9
9
|
constructor(services: LikeC4Services);
|
|
10
|
+
get isStarted(): boolean;
|
|
11
|
+
get port(): number;
|
|
10
12
|
dispose(): Promise<void>;
|
|
11
13
|
start(port?: number): Promise<void>;
|
|
12
14
|
stop(): Promise<void>;
|
|
@@ -9,7 +9,13 @@ export class SSELikeC4MCPServer {
|
|
|
9
9
|
// Store transports by session ID to send notifications
|
|
10
10
|
transports = {};
|
|
11
11
|
server = void 0;
|
|
12
|
-
|
|
12
|
+
_port = 33335;
|
|
13
|
+
get isStarted() {
|
|
14
|
+
return this.server?.listening === true;
|
|
15
|
+
}
|
|
16
|
+
get port() {
|
|
17
|
+
return this._port;
|
|
18
|
+
}
|
|
13
19
|
async dispose() {
|
|
14
20
|
await this.stop();
|
|
15
21
|
}
|
|
@@ -20,8 +26,8 @@ export class SSELikeC4MCPServer {
|
|
|
20
26
|
}
|
|
21
27
|
await this.stop();
|
|
22
28
|
}
|
|
23
|
-
logger.info("Starting server on port {port}", { port });
|
|
24
|
-
this.
|
|
29
|
+
logger.info("Starting MCP server on port {port}", { port });
|
|
30
|
+
this._port = port;
|
|
25
31
|
const mcp = this.services.mcp.ServerFactory.create();
|
|
26
32
|
const app = express();
|
|
27
33
|
app.get("/sse", async (_, res) => {
|
|
@@ -44,13 +50,12 @@ export class SSELikeC4MCPServer {
|
|
|
44
50
|
}
|
|
45
51
|
});
|
|
46
52
|
return new Promise((resolve, reject) => {
|
|
47
|
-
this.server = app.listen(
|
|
53
|
+
this.server = app.listen(this._port, (err) => {
|
|
48
54
|
if (err) {
|
|
49
|
-
logger.error("Failed to start server", { err });
|
|
50
55
|
reject(err);
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
|
-
logger.info("server listening on port {port}", { port });
|
|
58
|
+
logger.info("MCP server listening on port {port}", { port: this._port });
|
|
54
59
|
resolve();
|
|
55
60
|
});
|
|
56
61
|
});
|
|
@@ -62,6 +67,7 @@ export class SSELikeC4MCPServer {
|
|
|
62
67
|
}
|
|
63
68
|
logger.info("Stopping server");
|
|
64
69
|
this.server = void 0;
|
|
70
|
+
this.transports = {};
|
|
65
71
|
return new Promise((resolve) => {
|
|
66
72
|
server.close((err) => {
|
|
67
73
|
if (err) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { loggable } from "@likec4/log";
|
|
2
|
+
import { isError } from "remeda";
|
|
1
3
|
import { logger } from "../../logger.js";
|
|
2
4
|
import { SSELikeC4MCPServer } from "./MCPServer.js";
|
|
3
5
|
import { LikeC4MCPServerFactory } from "./MCPServerFactory.js";
|
|
@@ -14,19 +16,33 @@ export const WithMCPServer = {
|
|
|
14
16
|
return;
|
|
15
17
|
}
|
|
16
18
|
logger.debug("Configuration update: {update}", { update });
|
|
17
|
-
const {
|
|
18
|
-
enabled
|
|
19
|
-
port
|
|
20
|
-
};
|
|
19
|
+
const {
|
|
20
|
+
enabled = false,
|
|
21
|
+
port = 33335
|
|
22
|
+
} = update.configuration.mcp;
|
|
21
23
|
if (!enabled) {
|
|
22
24
|
server.stop();
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
Promise.resolve().then(() => server.start(port)).then(() => {
|
|
26
28
|
connection?.telemetry?.logEvent({
|
|
27
|
-
eventName: "mcp-server-started"
|
|
29
|
+
eventName: "mcp-server-started",
|
|
30
|
+
mcpPort: port
|
|
28
31
|
});
|
|
29
|
-
}).catch((err) =>
|
|
32
|
+
}).catch((err) => {
|
|
33
|
+
const message = isError(err) ? err.message : void 0;
|
|
34
|
+
connection?.telemetry?.logEvent({
|
|
35
|
+
eventName: "mcp-server-start-failed",
|
|
36
|
+
mcpPort: port,
|
|
37
|
+
...message && { message }
|
|
38
|
+
});
|
|
39
|
+
logger.error("Failed to start LikeC4 MCP Server", { err });
|
|
40
|
+
if (connection) {
|
|
41
|
+
connection.window.showErrorMessage(`LikeC4: Failed to start MCP Server
|
|
42
|
+
|
|
43
|
+
${loggable(err)}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
30
46
|
});
|
|
31
47
|
return server;
|
|
32
48
|
},
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
DocumentState,
|
|
12
12
|
interruptAndCheck
|
|
13
13
|
} from "langium";
|
|
14
|
-
import prettyMs from "pretty-ms";
|
|
15
14
|
import {
|
|
16
15
|
filter,
|
|
17
16
|
flatMap,
|
|
@@ -26,13 +25,13 @@ import {
|
|
|
26
25
|
import { CancellationToken } from "vscode-jsonrpc";
|
|
27
26
|
import { isLikeC4Builtin } from "../likec4lib.js";
|
|
28
27
|
import { logger as mainLogger, logWarnError } from "../logger.js";
|
|
29
|
-
import { ADisposable } from "../utils/index.js";
|
|
28
|
+
import { ADisposable, performanceMark } from "../utils/index.js";
|
|
30
29
|
import { assignNavigateTo } from "../view-utils/index.js";
|
|
31
30
|
import { buildModelData } from "./builder/buildModel.js";
|
|
32
31
|
const parsedWithoutImportsCacheKey = (projectId) => `parsed-without-imports-${projectId}`;
|
|
33
32
|
const parsedModelCacheKey = (projectId) => `parsed-model-${projectId}`;
|
|
34
33
|
const computedModelCacheKey = (projectId) => `computed-model-${projectId}`;
|
|
35
|
-
const
|
|
34
|
+
const builderLogger = mainLogger.getChild("model-builder");
|
|
36
35
|
export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
37
36
|
projects;
|
|
38
37
|
parser;
|
|
@@ -65,7 +64,7 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
65
64
|
}
|
|
66
65
|
)
|
|
67
66
|
);
|
|
68
|
-
|
|
67
|
+
builderLogger.debug`created`;
|
|
69
68
|
}
|
|
70
69
|
/**
|
|
71
70
|
* WARNING:
|
|
@@ -76,19 +75,19 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
76
75
|
*/
|
|
77
76
|
unsafeSyncParseModelData(projectId) {
|
|
78
77
|
const cache = this.cache;
|
|
79
|
-
const
|
|
78
|
+
const logger = builderLogger.getChild(projectId);
|
|
80
79
|
const key = parsedWithoutImportsCacheKey(projectId);
|
|
81
80
|
if (cache.has(key)) {
|
|
82
|
-
|
|
81
|
+
logger.debug`unsafeSyncBuildModelData from cache`;
|
|
83
82
|
}
|
|
84
83
|
return cache.get(key, () => {
|
|
85
84
|
try {
|
|
86
85
|
const docs = this.documents(projectId);
|
|
87
86
|
if (docs.length === 0) {
|
|
88
|
-
logger.debug`no documents to build model
|
|
87
|
+
logger.debug`no documents to build model`;
|
|
89
88
|
return null;
|
|
90
89
|
}
|
|
91
|
-
|
|
90
|
+
logger.debug`unsafeSyncBuildModelData`;
|
|
92
91
|
return buildModelData(projectId, docs);
|
|
93
92
|
} catch (e) {
|
|
94
93
|
logWarnError(e);
|
|
@@ -100,6 +99,7 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
100
99
|
* To avoid circular dependencies, first we parse all documents and then we join them.
|
|
101
100
|
*/
|
|
102
101
|
unsafeSyncJoinedModelData(projectId) {
|
|
102
|
+
const logger = builderLogger.getChild(projectId);
|
|
103
103
|
const cache = this.cache;
|
|
104
104
|
const key = parsedModelCacheKey(projectId);
|
|
105
105
|
return cache.get(key, () => {
|
|
@@ -133,18 +133,18 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
133
133
|
}
|
|
134
134
|
async parseModel(projectId, cancelToken = CancellationToken.None) {
|
|
135
135
|
const project = this.projects.ensureProjectId(projectId);
|
|
136
|
-
const
|
|
136
|
+
const logger = builderLogger.getChild(project);
|
|
137
137
|
const cache = this.cache;
|
|
138
138
|
const cached = cache.get(parsedModelCacheKey(project));
|
|
139
139
|
if (cached) {
|
|
140
|
-
|
|
140
|
+
logger.debug`parseModel from cache`;
|
|
141
141
|
return cached;
|
|
142
142
|
}
|
|
143
|
-
const t0 =
|
|
143
|
+
const t0 = performanceMark();
|
|
144
144
|
return await this.mutex.read(async () => {
|
|
145
145
|
await interruptAndCheck(cancelToken);
|
|
146
146
|
const result = this.unsafeSyncJoinedModelData(project);
|
|
147
|
-
|
|
147
|
+
logger.debug`parseModel in ${t0.pretty}`;
|
|
148
148
|
return result;
|
|
149
149
|
});
|
|
150
150
|
}
|
|
@@ -155,6 +155,7 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
155
155
|
* Otherwise, the model may be incomplete.
|
|
156
156
|
*/
|
|
157
157
|
unsafeSyncBuildModel(projectId) {
|
|
158
|
+
const logger = builderLogger.getChild(projectId);
|
|
158
159
|
const cache = this.cache;
|
|
159
160
|
const viewsCache = this.cache;
|
|
160
161
|
return cache.get(computedModelCacheKey(projectId), () => {
|
|
@@ -189,24 +190,24 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
189
190
|
}
|
|
190
191
|
async buildLikeC4Model(projectId, cancelToken = CancellationToken.None) {
|
|
191
192
|
const project = this.projects.ensureProjectId(projectId);
|
|
192
|
-
const
|
|
193
|
+
const logger = builderLogger.getChild(project);
|
|
193
194
|
const cache = this.cache;
|
|
194
195
|
const cached = cache.get(computedModelCacheKey(project));
|
|
195
196
|
if (cached) {
|
|
196
|
-
|
|
197
|
+
logger.debug("buildLikeC4Model from cache");
|
|
197
198
|
return cached;
|
|
198
199
|
}
|
|
199
|
-
const t0 =
|
|
200
|
+
const t0 = performanceMark();
|
|
200
201
|
return await this.mutex.read(async () => {
|
|
201
202
|
await interruptAndCheck(cancelToken);
|
|
202
203
|
const result = this.unsafeSyncBuildModel(project);
|
|
203
|
-
|
|
204
|
+
logger.debug(`buildLikeC4Model in ${t0.pretty}`);
|
|
204
205
|
return result;
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
async computeView(viewId, projectId, cancelToken = CancellationToken.None) {
|
|
208
209
|
const project = this.projects.ensureProjectId(projectId);
|
|
209
|
-
const
|
|
210
|
+
const logger = builderLogger.getChild(project);
|
|
210
211
|
const cache = this.cache;
|
|
211
212
|
const cacheKey = computedViewKey(project, viewId);
|
|
212
213
|
if (cache.has(cacheKey)) {
|
|
@@ -219,10 +220,10 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
|
|
|
219
220
|
return cache.get(cacheKey, () => {
|
|
220
221
|
const view = parsed.$data.views[viewId];
|
|
221
222
|
if (!view) {
|
|
222
|
-
|
|
223
|
+
logger.warn`computeView: cant find view ${viewId}`;
|
|
223
224
|
return null;
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
+
logger.debug`computeView: ${viewId}`;
|
|
226
227
|
const result = computeView(view, parsed);
|
|
227
228
|
if (!result.isSuccess) {
|
|
228
229
|
logWarnError(result.error);
|
|
@@ -72,6 +72,7 @@ declare const DocumentParserFromMixins: {
|
|
|
72
72
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
73
73
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
74
74
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
75
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
75
76
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
76
77
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
77
78
|
parseDeploymentView(astNode: import("../generated/ast").DeploymentView): import("../ast").ParsedAstDeploymentView;
|
|
@@ -148,6 +149,7 @@ declare const DocumentParserFromMixins: {
|
|
|
148
149
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
149
150
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
150
151
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
152
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
151
153
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
152
154
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
153
155
|
parseDeploymentView(astNode: import("../generated/ast").DeploymentView): import("../ast").ParsedAstDeploymentView;
|
|
@@ -188,6 +190,7 @@ declare const DocumentParserFromMixins: {
|
|
|
188
190
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
189
191
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
190
192
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
193
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
191
194
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
192
195
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
193
196
|
};
|
|
@@ -240,6 +243,7 @@ declare const DocumentParserFromMixins: {
|
|
|
240
243
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
241
244
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
242
245
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
246
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
243
247
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
244
248
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
245
249
|
};
|
|
@@ -285,6 +289,7 @@ declare const DocumentParserFromMixins: {
|
|
|
285
289
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
286
290
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
287
291
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
292
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
288
293
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
289
294
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
290
295
|
parseDeployment(): void;
|
|
@@ -338,6 +343,7 @@ declare const DocumentParserFromMixins: {
|
|
|
338
343
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
339
344
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
340
345
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
346
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
341
347
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
342
348
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
343
349
|
};
|
|
@@ -384,6 +390,7 @@ declare const DocumentParserFromMixins: {
|
|
|
384
390
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
385
391
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
386
392
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
393
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
387
394
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
388
395
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
389
396
|
};
|
|
@@ -412,6 +419,7 @@ declare const DocumentParserFromMixins: {
|
|
|
412
419
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
413
420
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
414
421
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
422
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
415
423
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
416
424
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
417
425
|
};
|
|
@@ -453,6 +461,7 @@ declare const DocumentParserFromMixins: {
|
|
|
453
461
|
convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
454
462
|
parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): ProjectId[] | undefined;
|
|
455
463
|
parseIconProperty(prop: import("../generated/ast").IconProperty | undefined): ProjectId | undefined;
|
|
464
|
+
parseColorLiteral(astNode: import("../generated/ast").ColorLiteral): ProjectId | undefined;
|
|
456
465
|
parseElementStyle(elementProps: Array<import("../generated/ast").ElementProperty> | import("../generated/ast").ElementStyleProperty | undefined): import("../ast").ParsedElementStyle;
|
|
457
466
|
parseStyleProps(styleProps: Array<import("../generated/ast").StyleProperty> | undefined): import("../ast").ParsedElementStyle;
|
|
458
467
|
};
|
|
@@ -32,6 +32,7 @@ export declare class BaseParser {
|
|
|
32
32
|
convertLinks(source?: ast.LinkProperty['$container']): c4.Link[] | undefined;
|
|
33
33
|
parseLinks(source?: ast.LinkProperty['$container']): c4.Link[] | undefined;
|
|
34
34
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
35
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
35
36
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): ParsedElementStyle;
|
|
36
37
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): ParsedElementStyle;
|
|
37
38
|
}
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
isBoolean,
|
|
8
8
|
isEmpty,
|
|
9
9
|
isNonNullish,
|
|
10
|
+
isNumber,
|
|
11
|
+
isString,
|
|
10
12
|
isTruthy,
|
|
11
13
|
map,
|
|
12
14
|
pipe,
|
|
@@ -17,6 +19,7 @@ import { hasLeadingSlash, hasProtocol, isRelative, joinRelativeURL, joinURL } fr
|
|
|
17
19
|
import {
|
|
18
20
|
ast,
|
|
19
21
|
parseAstOpacityProperty,
|
|
22
|
+
parseAstPercent,
|
|
20
23
|
parseAstSizeValue,
|
|
21
24
|
toColor
|
|
22
25
|
} from "../../ast.js";
|
|
@@ -154,6 +157,25 @@ export class BaseParser {
|
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
}
|
|
160
|
+
parseColorLiteral(astNode) {
|
|
161
|
+
if (!this.isValid(astNode)) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
if (ast.isHexColor(astNode)) {
|
|
165
|
+
return `#${astNode.hex}`;
|
|
166
|
+
}
|
|
167
|
+
if (ast.isRGBAColor(astNode)) {
|
|
168
|
+
let alpha = isNumber(astNode.alpha) ? astNode.alpha : void 0;
|
|
169
|
+
if (isString(astNode.alpha)) {
|
|
170
|
+
alpha = parseAstPercent(astNode.alpha) / 100;
|
|
171
|
+
}
|
|
172
|
+
if (alpha !== void 0) {
|
|
173
|
+
return `rgba(${astNode.red},${astNode.green},${astNode.blue},${alpha})`;
|
|
174
|
+
}
|
|
175
|
+
return `rgb(${astNode.red},${astNode.green},${astNode.blue})`;
|
|
176
|
+
}
|
|
177
|
+
nonexhaustive(astNode);
|
|
178
|
+
}
|
|
157
179
|
parseElementStyle(elementProps) {
|
|
158
180
|
if (!elementProps) {
|
|
159
181
|
return {};
|
|
@@ -47,6 +47,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
|
|
|
47
47
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
48
48
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
49
49
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
50
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
50
51
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
51
52
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
52
53
|
};
|
|
@@ -164,9 +164,11 @@ export function DeploymentModelParser(B) {
|
|
|
164
164
|
const links = this.convertLinks(astNode.body);
|
|
165
165
|
const kind = (astNode.kind ?? astNode.dotKind?.kind)?.ref?.name;
|
|
166
166
|
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
|
|
167
|
-
const bodyProps =
|
|
168
|
-
astNode.body?.props
|
|
169
|
-
(
|
|
167
|
+
const bodyProps = pipe(
|
|
168
|
+
astNode.body?.props ?? [],
|
|
169
|
+
filter(ast.isRelationStringProperty),
|
|
170
|
+
filter((p) => isTruthy(p.value)),
|
|
171
|
+
mapToObj((p) => [p.key, p.value || void 0])
|
|
170
172
|
);
|
|
171
173
|
const navigateTo = pipe(
|
|
172
174
|
astNode.body?.props ?? [],
|
|
@@ -175,8 +177,8 @@ export function DeploymentModelParser(B) {
|
|
|
175
177
|
filter(isTruthy),
|
|
176
178
|
first()
|
|
177
179
|
);
|
|
178
|
-
const title = removeIndent(astNode.title ?? bodyProps.title);
|
|
179
|
-
const description = removeIndent(bodyProps.description);
|
|
180
|
+
const title = removeIndent(astNode.title ?? bodyProps.title) ?? "";
|
|
181
|
+
const description = removeIndent(astNode.description ?? bodyProps.description);
|
|
180
182
|
const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology);
|
|
181
183
|
const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
|
|
182
184
|
const id = stringHash(
|
|
@@ -45,6 +45,7 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
|
|
|
45
45
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
46
46
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
47
47
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
48
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
48
49
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
49
50
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
50
51
|
parseDeployment(): void;
|
|
@@ -40,6 +40,7 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
|
|
|
40
40
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
41
41
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
42
42
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
43
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
43
44
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
44
45
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
45
46
|
};
|
|
@@ -69,6 +69,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
|
|
|
69
69
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
70
70
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
71
71
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
72
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
72
73
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
73
74
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
74
75
|
parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
|
|
@@ -26,6 +26,7 @@ export declare function ImportsParser<TBase extends Base>(B: TBase): {
|
|
|
26
26
|
convertLinks(source?: ast.LinkProperty["$container"]): ProjectId[] | undefined;
|
|
27
27
|
parseLinks(source?: ast.LinkProperty["$container"]): ProjectId[] | undefined;
|
|
28
28
|
parseIconProperty(prop: ast.IconProperty | undefined): ProjectId | undefined;
|
|
29
|
+
parseColorLiteral(astNode: ast.ColorLiteral): ProjectId | undefined;
|
|
29
30
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
30
31
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
31
32
|
};
|
|
@@ -46,6 +46,7 @@ export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
|
|
|
46
46
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
47
47
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
48
48
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
49
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
49
50
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
50
51
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
51
52
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { invariant, isNonEmptyArray, LinkedList, nonexhaustive, nonNullable } from "@likec4/core";
|
|
2
2
|
import { FqnRef } from "@likec4/core/types";
|
|
3
3
|
import { loggable } from "@likec4/log";
|
|
4
|
-
import { filter, first, isDefined, isEmpty,
|
|
4
|
+
import { filter, first, isDefined, isEmpty, isTruthy, map, mapToObj, pipe } from "remeda";
|
|
5
5
|
import {
|
|
6
6
|
ast,
|
|
7
7
|
toRelationshipStyleExcludeDefaults
|
|
@@ -140,9 +140,11 @@ ${error}`, {
|
|
|
140
140
|
const kind = (astNode.kind ?? astNode.dotKind?.kind)?.ref?.name;
|
|
141
141
|
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
|
|
142
142
|
const astPath = this.getAstNodePath(astNode);
|
|
143
|
-
const bodyProps =
|
|
144
|
-
astNode.body?.props
|
|
145
|
-
(
|
|
143
|
+
const bodyProps = pipe(
|
|
144
|
+
astNode.body?.props ?? [],
|
|
145
|
+
filter(ast.isRelationStringProperty),
|
|
146
|
+
filter((p) => isTruthy(p.value)),
|
|
147
|
+
mapToObj((p) => [p.key, p.value || void 0])
|
|
146
148
|
);
|
|
147
149
|
const navigateTo = pipe(
|
|
148
150
|
astNode.body?.props ?? [],
|
|
@@ -152,7 +154,7 @@ ${error}`, {
|
|
|
152
154
|
first()
|
|
153
155
|
);
|
|
154
156
|
const title = removeIndent(astNode.title ?? bodyProps.title) ?? "";
|
|
155
|
-
const description = removeIndent(bodyProps.description);
|
|
157
|
+
const description = removeIndent(astNode.description ?? bodyProps.description);
|
|
156
158
|
const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology);
|
|
157
159
|
const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
|
|
158
160
|
const id = stringHash(
|
|
@@ -51,6 +51,7 @@ export declare function PredicatesParser<TBase extends WithExpressionV2>(B: TBas
|
|
|
51
51
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
52
52
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
53
53
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
54
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
54
55
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
55
56
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
56
57
|
};
|
|
@@ -32,6 +32,7 @@ export declare function SpecificationParser<TBase extends Base>(B: TBase): {
|
|
|
32
32
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
33
33
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
34
34
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
35
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
35
36
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
36
37
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
37
38
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nonNullable } from "@likec4/core/utils";
|
|
1
2
|
import { filter, isNonNullish, isTruthy, mapToObj, pipe } from "remeda";
|
|
2
3
|
import { ast, toRelationshipStyleExcludeDefaults } from "../../ast.js";
|
|
3
4
|
import { logger, logWarnError } from "../../logger.js";
|
|
@@ -56,10 +57,11 @@ export function SpecificationParser(B) {
|
|
|
56
57
|
for (const tagSpec of tags_specs) {
|
|
57
58
|
const tag = tagSpec.tag.name;
|
|
58
59
|
const astPath = this.getAstNodePath(tagSpec.tag);
|
|
60
|
+
const color = tagSpec.color && this.parseColorLiteral(tagSpec.color);
|
|
59
61
|
if (isTruthy(tag)) {
|
|
60
62
|
c4Specification.tags[tag] = {
|
|
61
63
|
astPath,
|
|
62
|
-
...
|
|
64
|
+
...color ? { color } : {}
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -72,7 +74,7 @@ export function SpecificationParser(B) {
|
|
|
72
74
|
continue;
|
|
73
75
|
}
|
|
74
76
|
c4Specification.colors[colorName] = {
|
|
75
|
-
color
|
|
77
|
+
color: nonNullable(this.parseColorLiteral(color), `Color "${colorName}" is not valid: ${color}`)
|
|
76
78
|
};
|
|
77
79
|
} catch (e) {
|
|
78
80
|
logWarnError(e);
|
|
@@ -67,6 +67,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
|
|
|
67
67
|
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
68
68
|
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
69
69
|
parseIconProperty(prop: ast.IconProperty | undefined): c4.IconUrl | undefined;
|
|
70
|
+
parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
|
|
70
71
|
parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
|
|
71
72
|
parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
|
|
72
73
|
parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
|
package/dist/module.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { QueueGraphvizLayoter } from '@likec4/layouts';
|
|
2
2
|
import { type Module, WorkspaceCache } from 'langium';
|
|
3
3
|
import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
|
|
4
4
|
import { LikeC4DocumentationProvider } from './documentation';
|
|
@@ -43,7 +43,7 @@ export interface LikeC4AddedServices {
|
|
|
43
43
|
likec4: {
|
|
44
44
|
LanguageServices: LikeC4LanguageServices;
|
|
45
45
|
Views: LikeC4Views;
|
|
46
|
-
Layouter:
|
|
46
|
+
Layouter: QueueGraphvizLayoter;
|
|
47
47
|
DeploymentsIndex: DeploymentsIndex;
|
|
48
48
|
FqnIndex: FqnIndex;
|
|
49
49
|
ModelParser: LikeC4ModelParser;
|
package/dist/module.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphvizWasmAdapter, QueueGraphvizLayoter } from "@likec4/layouts";
|
|
2
2
|
import {
|
|
3
3
|
DocumentState,
|
|
4
4
|
EmptyFileSystem,
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
LikeC4GeneratedSharedModule
|
|
17
17
|
} from "./generated/module.js";
|
|
18
18
|
import { DefaultLikeC4LanguageServices } from "./LikeC4LanguageServices.js";
|
|
19
|
-
import { logger } from "./logger.js";
|
|
20
19
|
import {
|
|
21
20
|
LikeC4CodeLensProvider,
|
|
22
21
|
LikeC4CompletionProvider,
|
|
@@ -87,8 +86,9 @@ export const LikeC4Module = {
|
|
|
87
86
|
likec4: {
|
|
88
87
|
LanguageServices: bind(DefaultLikeC4LanguageServices),
|
|
89
88
|
Layouter: (_services) => {
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
return new QueueGraphvizLayoter({
|
|
90
|
+
graphviz: new GraphvizWasmAdapter()
|
|
91
|
+
});
|
|
92
92
|
},
|
|
93
93
|
Views: bind(DefaultLikeC4Views),
|
|
94
94
|
DeploymentsIndex: bind(DeploymentsIndex),
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -4,3 +4,8 @@ export * from './fqnRef';
|
|
|
4
4
|
export * from './projectId';
|
|
5
5
|
export * from './stringHash';
|
|
6
6
|
export declare function safeCall<T>(fn: () => T): T | undefined;
|
|
7
|
+
export declare function performanceNow(): number;
|
|
8
|
+
export declare function performanceMark(): {
|
|
9
|
+
readonly ms: number;
|
|
10
|
+
readonly pretty: string;
|
|
11
|
+
};
|