@likec4/language-services 1.48.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Denis Davydkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # @likec4/language-services
2
+
3
+ This package provides initialization of language services from given workspace directory, or in-memory from sources.
4
+
5
+ It is designed to be browser-compatible and bundled within the main [`likec4`](https://www.npmjs.com/package/likec4) package.
6
+
7
+ > [!WARNING]
8
+ > **This package is intended for internal use within other LikeC4 packages.**
9
+ >
10
+ > Please use the main [`likec4`](https://www.npmjs.com/package/likec4) package instead.
11
+
12
+ ## Features
13
+
14
+ - **Browser and Node.js Support** - Dual runtime compatibility with separate entry points
15
+ - **Source Parsing** - Parse LikeC4 DSL from strings or workspace directories
16
+ - **Model Building** - Build and validate architecture models from parsed sources
17
+ - **View Computation** - Compute views with predicates and element filtering
18
+ - **Layout Engine** - Graphviz-based automatic layout for diagrams
19
+ - **Multi-project Support** - Handle multiple LikeC4 projects in a workspace
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @likec4/language-services
25
+ # or
26
+ pnpm add @likec4/language-services
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Browser Environment
32
+
33
+ ```typescript
34
+ import { fromSource, fromSources } from '@likec4/language-services/browser'
35
+
36
+ // From a single source string
37
+ const likec4 = await fromSource(`
38
+ model {
39
+ customer = person 'Customer'
40
+ system = softwareSystem 'System'
41
+
42
+ customer -> system 'uses'
43
+ }
44
+ `)
45
+
46
+ // From multiple source files
47
+ const likec4 = await fromSources({
48
+ 'model.c4': 'model { ... }',
49
+ 'views.c4': 'views { ... }',
50
+ /**
51
+ * Optional, but allows to inject project config
52
+ * with theme customization
53
+ */
54
+ 'likec4.config.json': '{ "name": "project-name" }',
55
+ })
56
+
57
+ // Get computed model
58
+ const model = await likec4.computedModel()
59
+
60
+ // Get layouted diagrams
61
+ const diagrams = await likec4.diagrams()
62
+ ```
63
+
64
+ ### Node.js Environment
65
+
66
+ ```typescript
67
+ import { fromWorkdir, fromWorkspace } from '@likec4/language-services/node'
68
+
69
+ // From a workspace directory
70
+ const likec4 = await fromWorkspace('./architecture')
71
+
72
+ // From current working directory
73
+ const likec4 = await fromWorkdir()
74
+
75
+ // Get layouted model
76
+ const model = await likec4.layoutedModel()
77
+
78
+ // Get all projects
79
+ const projects = likec4.projects()
80
+ ```
81
+
82
+ > [!NOTE]
83
+ > There is a separate entry point for node.js environment without MCP support: `@likec4/language-services/node/without-mcp`
84
+ >
85
+ > Useful to reduce bundle size
86
+
87
+ ## API
88
+
89
+ ### Factory Functions
90
+
91
+ #### Browser
92
+
93
+ - `fromSource(source: string): Promise<LikeC4>` - Create from a single source string
94
+ - `fromSources(sources: Record<string, string>): Promise<LikeC4>` - Create from multiple source files
95
+
96
+ > [!NOTE]
97
+ > `fromSource` and `fromSources` are available in both browser and node.js environments.
98
+
99
+ #### Node.js
100
+
101
+ - `fromWorkspace(path: string, options?: FromWorkspaceOptions): Promise<LikeC4>` - Create from a workspace directory
102
+ - `fromWorkdir(options?: FromWorkspaceOptions): Promise<LikeC4>` - Create from current working directory
103
+ - `fromSources(sources: Record<string, string>, options?: InitOptions): Promise<LikeC4>` - Create from multiple source files
104
+ - `fromSource(source: string, options?: InitOptions): Promise<LikeC4>` - Create from a single source string
105
+
106
+ ### LikeC4 Class
107
+
108
+ - `computedModel(project?: ProjectId): Promise<LikeC4Model.Computed>` - Build model with computed views (no layout)
109
+ - `layoutedModel(project?: ProjectId): Promise<LikeC4Model.Layouted>` - Build model with layout applied
110
+ - `diagrams(project?: ProjectId): Promise<LayoutedView[]>` - Get all layouted diagrams
111
+ - `projects(): NonEmptyArray<ProjectId>` - Get all project IDs
112
+ - `validate(): Promise<ValidationResult>` - Validate all documents
113
+ - `export(format: ExportFormat): Promise<string>` - Export to various formats
114
+
115
+ ## Runtime Compatibility
116
+
117
+ The package provides conditional exports for browser and Node.js environments:
118
+
119
+ - **Browser**: Uses in-memory workspace, no filesystem access
120
+ - **Node.js**: Full filesystem support, workspace scanning, and file watching
121
+
122
+ The appropriate version is automatically selected based on your environment.
123
+
124
+ ## Getting help
125
+
126
+ We are always happy to help you get started:
127
+
128
+ - [Join Discord community](https://discord.gg/86ZSpjKAdA) – it is the easiest way to get help
129
+ - [GitHub Discussions](https://github.com/likec4/likec4/discussions) – ask anything about the project or give feedback
130
+
131
+ ## Contributors
132
+
133
+ <a href="https://github.com/likec4/likec4/graphs/contributors">
134
+ <img src="https://contrib.rocks/image?repo=likec4/likec4" />
135
+ </a>
136
+
137
+ [Become a contributor](../../CONTRIBUTING.md)
138
+
139
+ ## Support development
140
+
141
+ LikeC4 is a MIT-licensed open source project with its ongoing development made possible entirely by your support.\
142
+ If you like the project, please consider contributing financially to help grow and improve it.\
143
+ You can support us via [OpenCollective](https://opencollective.com/likec4) or [GitHub Sponsors](https://github.com/sponsors/likec4).
144
+
145
+ ## License
146
+
147
+ This project is released under the [MIT License](LICENSE)
@@ -0,0 +1,4 @@
1
+ {
2
+ "types": "../dist/browser/index.d.mts",
3
+ "module": "../dist/browser/index.mjs"
4
+ }
@@ -0,0 +1,172 @@
1
+ import { loggable, rootLogger } from "@likec4/log";
2
+ import defu from "defu";
3
+ import { extname } from "pathe";
4
+ import k from "tinyrainbow";
5
+ import { LikeC4ProjectConfigOps, isLikeC4JsonConfig } from "@likec4/config";
6
+ import { URI, UriUtils } from "langium";
7
+ import { entries, map, partition, pipe, prop } from "remeda";
8
+ import { DiagnosticSeverity } from "vscode-languageserver-types";
9
+ var LikeC4 = class {
10
+ langium;
11
+ logger;
12
+ constructor(langium, logger = rootLogger) {
13
+ this.langium = langium;
14
+ this.logger = logger;
15
+ }
16
+ get workspace() {
17
+ return this.langium.shared.workspace.WorkspaceManager.workspaceUri.fsPath;
18
+ }
19
+ get workspaceURI() {
20
+ return this.langium.shared.workspace.WorkspaceManager.workspaceUri;
21
+ }
22
+ get workspaceURL() {
23
+ return this.langium.shared.workspace.WorkspaceManager.workspaceURL;
24
+ }
25
+ get languageServices() {
26
+ return this.langium.likec4.likec4.LanguageServices;
27
+ }
28
+ get projectsManager() {
29
+ return this.langium.shared.workspace.ProjectsManager;
30
+ }
31
+ get viewsService() {
32
+ return this.langium.likec4.likec4.Views;
33
+ }
34
+ get modelBuilder() {
35
+ return this.langium.likec4.likec4.ModelBuilder;
36
+ }
37
+ get LangiumDocuments() {
38
+ return this.langium.shared.workspace.LangiumDocuments;
39
+ }
40
+ ensureSingleProject() {
41
+ const projects = this.languageServices.projects();
42
+ if (projects.length > 1) {
43
+ this.logger.error(`Multiple projects found:
44
+ ${projects.map((p) => ` - ${p.folder.fsPath}`).join("\n")}
45
+
46
+ Please specify a project folder`);
47
+ throw new Error(`Multiple projects found`);
48
+ }
49
+ }
50
+ async diagrams(project) {
51
+ const projectId = this.projectsManager.ensureProjectId(project);
52
+ return await this.viewsService.diagrams(projectId);
53
+ }
54
+ syncComputedModel(project) {
55
+ const projectId = this.projectsManager.ensureProjectId(project);
56
+ return this.modelBuilder.unsafeSyncComputeModel(projectId);
57
+ }
58
+ async computedModel(project) {
59
+ const projectId = this.projectsManager.ensureProjectId(project);
60
+ return await this.modelBuilder.computeModel(projectId);
61
+ }
62
+ projects() {
63
+ return map(this.languageServices.projects(), prop("id"));
64
+ }
65
+ async layoutedModel(project) {
66
+ const projectId = this.projectsManager.ensureProjectId(project);
67
+ return await this.languageServices.layoutedModel(projectId);
68
+ }
69
+ getErrors() {
70
+ return this.LangiumDocuments.all.toArray().flatMap((doc) => {
71
+ return (doc.diagnostics ?? []).filter((d) => d.severity === DiagnosticSeverity.Error).map(({ message, range }) => ({
72
+ message,
73
+ line: range.start.line,
74
+ range,
75
+ sourceFsPath: doc.uri.fsPath
76
+ }));
77
+ });
78
+ }
79
+ hasErrors() {
80
+ return this.LangiumDocuments.all.some((doc) => {
81
+ return doc.diagnostics?.some((d) => d.severity === DiagnosticSeverity.Error) ?? false;
82
+ });
83
+ }
84
+ printErrors() {
85
+ let hasErrors = false;
86
+ for (const doc of this.LangiumDocuments.all) {
87
+ const errors = doc.diagnostics?.filter((e) => e.severity === 1);
88
+ if (errors && errors.length > 0) {
89
+ hasErrors = true;
90
+ const messages = errors.flatMap((validationError) => {
91
+ const line = validationError.range.start.line;
92
+ const messages = validationError.message.split("\n");
93
+ if (messages.length > 10) {
94
+ messages.length = 10;
95
+ messages.push("...");
96
+ }
97
+ return messages.map((message, i) => {
98
+ if (i === 0) return " " + k.dim(`Line ${line}: `) + k.red(message);
99
+ return " ".repeat(10) + k.red(message);
100
+ });
101
+ }).join("\n");
102
+ this.logger.error(`Invalid ${doc.uri.fsPath}\n${messages}`);
103
+ }
104
+ }
105
+ return hasErrors;
106
+ }
107
+ onModelUpdate(listener) {
108
+ const sib = this.modelBuilder.onModelParsed(() => listener());
109
+ return () => {
110
+ sib.dispose();
111
+ };
112
+ }
113
+ async dispose() {
114
+ await this.languageServices.dispose();
115
+ }
116
+ async [Symbol.asyncDispose]() {
117
+ await this.dispose();
118
+ }
119
+ };
120
+ const validationErrorsToError = (likec4) => /* @__PURE__ */ new Error(`Invalid model:\n${likec4.getErrors().map((e) => ` ${e.sourceFsPath}:${e.line} ${e.message.slice(0, 200)}`).join("\n")}`);
121
+ async function handleInitOptions(langium, logger = rootLogger, options) {
122
+ const likec4 = new LikeC4(langium, logger);
123
+ const opts = defu(options, {
124
+ printErrors: true,
125
+ throwIfInvalid: false
126
+ });
127
+ if (opts.throwIfInvalid === true && likec4.hasErrors()) {
128
+ await likec4.dispose();
129
+ return Promise.reject(validationErrorsToError(likec4));
130
+ }
131
+ if (opts.printErrors !== false && likec4.hasErrors()) likec4.printErrors();
132
+ return likec4;
133
+ }
134
+ async function createFromSources(langium, logger, sources, initOptions) {
135
+ const uri = URI.from({
136
+ scheme: "virtual",
137
+ path: "/workspace"
138
+ });
139
+ const workspace = {
140
+ name: "virtual",
141
+ uri: uri.toString()
142
+ };
143
+ const WorkspaceManager = langium.shared.workspace.WorkspaceManager;
144
+ WorkspaceManager.initialize({
145
+ capabilities: {},
146
+ processId: null,
147
+ rootUri: workspace.uri,
148
+ workspaceFolders: [workspace]
149
+ });
150
+ await WorkspaceManager.initializeWorkspace([workspace]);
151
+ const fileExtensions = langium.shared.ServiceRegistry.all.flatMap((e) => e.LanguageMetaData.fileExtensions);
152
+ const [configs, docentries] = partition(entries(sources), ([path]) => isLikeC4JsonConfig(path));
153
+ for (const [path, content] of configs) {
154
+ const configUri = UriUtils.joinPath(uri, path);
155
+ try {
156
+ await langium.shared.workspace.ProjectsManager.registerProject({
157
+ configUri,
158
+ config: LikeC4ProjectConfigOps.parse(content)
159
+ });
160
+ } catch (error) {
161
+ logger.error(loggable(error));
162
+ }
163
+ }
164
+ const docs = pipe(docentries, map(([path, content]) => {
165
+ if (!fileExtensions.includes(extname(path))) path += ".c4";
166
+ const docuri = UriUtils.joinPath(uri, path);
167
+ return langium.shared.workspace.LangiumDocuments.createDocument(docuri, content);
168
+ }));
169
+ await langium.shared.workspace.DocumentBuilder.build(docs, { validation: true });
170
+ return handleInitOptions(langium, logger, initOptions);
171
+ }
172
+ export { handleInitOptions as n, LikeC4 as r, createFromSources as t };
@@ -0,0 +1,133 @@
1
+ import { Logger } from "@likec4/log";
2
+ import * as vscode_uri0 from "vscode-uri";
3
+ import { LikeC4Model } from "@likec4/core/model";
4
+ import { LayoutedView, NonEmptyArray, ProjectId } from "@likec4/core/types";
5
+ import { LikeC4LanguageServices, LikeC4ModelBuilder, LikeC4Services, LikeC4SharedServices, LikeC4Views, ProjectsManager } from "@likec4/language-server";
6
+
7
+ //#region src/common/LikeC4.d.ts
8
+ interface LikeC4Langium {
9
+ shared: LikeC4SharedServices;
10
+ likec4: LikeC4Services;
11
+ }
12
+ declare class LikeC4 {
13
+ protected readonly langium: LikeC4Langium;
14
+ protected readonly logger: Logger;
15
+ constructor(langium: LikeC4Langium, logger?: Logger);
16
+ /**
17
+ * File system path to the workspace root
18
+ */
19
+ get workspace(): string;
20
+ /**
21
+ * URI of the workspace root
22
+ */
23
+ get workspaceURI(): vscode_uri0.URI;
24
+ /**
25
+ * URL of the workspace root
26
+ */
27
+ get workspaceURL(): URL;
28
+ get languageServices(): LikeC4LanguageServices;
29
+ get projectsManager(): ProjectsManager;
30
+ get viewsService(): LikeC4Views;
31
+ get modelBuilder(): LikeC4ModelBuilder;
32
+ private get LangiumDocuments();
33
+ ensureSingleProject(): void;
34
+ /**
35
+ * Diagram is a computed view, layouted using Graphviz
36
+ * If diagram has manual layout, it will be used.
37
+ * Used in React components
38
+ */
39
+ diagrams(project?: string | undefined): Promise<LayoutedView[]>;
40
+ /**
41
+ * Builds LikeC4Model from all documents
42
+ * Only computes view predicates {@link ComputedView} - i.e. no layout
43
+ * Not ready for rendering, but enough to traverse
44
+ *
45
+ * Sync version does not read manual layouts
46
+ * Use {@link computedModel} for a version that includes manual layouts
47
+ */
48
+ syncComputedModel(project?: string | undefined): LikeC4Model.Computed;
49
+ /**
50
+ * Builds LikeC4Model from all documents
51
+ * Only computes view predicates {@link ComputedView} - i.e. no layout
52
+ * Not ready for rendering, but enough to traverse
53
+ */
54
+ computedModel(project?: string | undefined): Promise<LikeC4Model.Computed>;
55
+ projects(): NonEmptyArray<ProjectId>;
56
+ /**
57
+ * Same as {@link computedModel()}, but also applies layout
58
+ * Ready for rendering
59
+ */
60
+ layoutedModel(project?: string | undefined): Promise<LikeC4Model.Layouted>;
61
+ getErrors(): Array<{
62
+ message: string;
63
+ line: number;
64
+ range: {
65
+ start: {
66
+ line: number;
67
+ character: number;
68
+ };
69
+ end: {
70
+ line: number;
71
+ character: number;
72
+ };
73
+ };
74
+ sourceFsPath: string;
75
+ }>;
76
+ hasErrors(): boolean;
77
+ /**
78
+ * @returns true if there are errors
79
+ */
80
+ printErrors(): boolean;
81
+ /**
82
+ * @returns a function to dispose the listener
83
+ */
84
+ onModelUpdate(listener: () => void): () => void;
85
+ dispose(): Promise<void>;
86
+ [Symbol.asyncDispose](): Promise<void>;
87
+ }
88
+ //#endregion
89
+ //#region src/common/options.d.ts
90
+ type InitOptions = {
91
+ /**
92
+ * By default, if LikeC4 model is invalid, errors are printed to the console.
93
+ * Disable this behavior by setting this option to false.
94
+ *
95
+ * @default true
96
+ */
97
+ printErrors?: boolean;
98
+ /**
99
+ * If true, initialization will return rejected promise with the LikeC4 instance.
100
+ * Use `likec4.getErrors()` to get the errors.
101
+ * @default false
102
+ */
103
+ throwIfInvalid?: boolean;
104
+ /**
105
+ * Whether to use the `dot` binary for layouting or the WebAssembly version.
106
+ * @default 'wasm'
107
+ */
108
+ graphviz?: 'wasm' | 'binary';
109
+ /**
110
+ * Whether to start MCP server
111
+ *
112
+ * if port is specified, starts HTTP Streamable MCP server
113
+ *
114
+ * @default false
115
+ */
116
+ mcp?: false | 'stdio' | {
117
+ port: number;
118
+ };
119
+ };
120
+ type FromWorkspaceOptions = InitOptions & {
121
+ /**
122
+ * Whether to read and use manual layouts from the workspace.
123
+ * @default true
124
+ */
125
+ manualLayouts?: boolean;
126
+ /**
127
+ * Whether to watch for changes in the workspace.
128
+ * @default false
129
+ */
130
+ watch?: boolean;
131
+ };
132
+ //#endregion
133
+ export { LikeC4Langium as i, InitOptions as n, LikeC4 as r, FromWorkspaceOptions as t };
@@ -0,0 +1,41 @@
1
+ import { n as InitOptions, r as LikeC4, t as FromWorkspaceOptions } from "../_chunks/options.mjs";
2
+
3
+ //#region src/browser/index.d.ts
4
+ /**
5
+ * Create a LikeC4 instance from a workspace directory
6
+ * @param _workspace - The workspace directory path
7
+ * @param options - Optional configuration options
8
+ * @returns A Promise that resolves to a LikeC4 instance
9
+ */
10
+ declare function fromWorkspace(_workspace: string, _options?: FromWorkspaceOptions): Promise<LikeC4>;
11
+ /**
12
+ * Create a LikeC4 instance from the current working directory
13
+ * @param options - Optional configuration options
14
+ * @returns A Promise that resolves to a LikeC4 instance
15
+ */
16
+ declare function fromWorkdir(_options?: FromWorkspaceOptions): Promise<LikeC4>;
17
+ /**
18
+ * Create a LikeC4 instance from a record of source files
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const likec4 = await fromSources({
23
+ * 'likec4.config.json': '...', // optional, stringified LikeC4Config
24
+ * 'model.c4': 'model { ... }',
25
+ * 'path/views.c4': 'views { ... }',
26
+ * })
27
+ * ```
28
+ *
29
+ * @param sources - A record of file paths to source content
30
+ * @returns A Promise that resolves to a LikeC4 instance
31
+ */
32
+ declare function fromSources(sources: Record<string, string>): Promise<LikeC4>;
33
+ /**
34
+ * Create a LikeC4 instance from a single source string
35
+ * @param source - The LikeC4 source code
36
+
37
+ * @returns A Promise that resolves to a LikeC4 instance
38
+ */
39
+ declare function fromSource(source: string): Promise<LikeC4>;
40
+ //#endregion
41
+ export { type FromWorkspaceOptions, type InitOptions, type LikeC4, fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,27 @@
1
+ import { t as createFromSources } from "../_chunks/createFromSources.mjs";
2
+ import { configureLogger, getConsoleSink, getTextFormatter, rootLogger } from "@likec4/log";
3
+ import { createLanguageServices } from "@likec4/language-server/browser";
4
+ async function fromWorkspace(_workspace, _options) {
5
+ throw new Error(`fromWorkspace is not yet implemented in the browser environment. use fromSources`);
6
+ }
7
+ async function fromWorkdir(_options) {
8
+ throw new Error(`fromWorkdir is not yet implemented in the browser environment, use fromSources`);
9
+ }
10
+ async function fromSources(sources) {
11
+ configureLogger({
12
+ sinks: { console: getConsoleSink({ formatter: getTextFormatter({ format: ({ level, category, message }) => {
13
+ return `${level} ${category} ${message}`;
14
+ } }) }) },
15
+ loggers: [{
16
+ category: "likec4",
17
+ sinks: ["console"],
18
+ lowestLevel: "debug"
19
+ }]
20
+ });
21
+ const logger = rootLogger.getChild("lang");
22
+ return await createFromSources(createLanguageServices(), logger, sources, {});
23
+ }
24
+ async function fromSource(source) {
25
+ return fromSources({ "source.c4": source });
26
+ }
27
+ export { fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,44 @@
1
+ import { i as LikeC4Langium, n as InitOptions, r as LikeC4, t as FromWorkspaceOptions } from "../_chunks/options.mjs";
2
+
3
+ //#region src/node/index.d.ts
4
+ /**
5
+ * Create a LikeC4 instance from a workspace directory
6
+ * The instance is cached in globalThis to avoid creating multiple instances for the same workspace
7
+ *
8
+ * @param path - The workspace directory path
9
+ * @param options - Optional configuration options
10
+ * @returns A Promise that resolves to a LikeC4 instance
11
+ */
12
+ declare function fromWorkspace(path: string, options?: FromWorkspaceOptions): Promise<LikeC4>;
13
+ /**
14
+ * Create a LikeC4 instance from the current working directory
15
+ * @param options - Optional configuration options
16
+ * @returns A Promise that resolves to a LikeC4 instance
17
+ */
18
+ declare function fromWorkdir(options?: FromWorkspaceOptions): Promise<LikeC4>;
19
+ /**
20
+ * Create a LikeC4 instance from a record of source files
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const likec4 = await fromSources({
25
+ * 'likec4.config.json': '...', // optional, stringified LikeC4Config
26
+ * 'model.c4': 'model { ... }',
27
+ * 'path/views.c4': 'views { ... }',
28
+ * })
29
+ * ```
30
+ *
31
+ * @param sources - A record of file paths to source content
32
+ * @param options - Optional configuration options
33
+ * @returns A Promise that resolves to a LikeC4 instance
34
+ */
35
+ declare function fromSources(sources: Record<string, string>, options?: InitOptions): Promise<LikeC4>;
36
+ /**
37
+ * Create a LikeC4 instance from a single source string
38
+ * @param source - The LikeC4 source code
39
+ * @param options - Optional configuration options
40
+ * @returns A Promise that resolves to a LikeC4 instance
41
+ */
42
+ declare function fromSource(source: string, options?: InitOptions): Promise<LikeC4>;
43
+ //#endregion
44
+ export { type FromWorkspaceOptions, type InitOptions, LikeC4, type LikeC4Langium, fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,98 @@
1
+ import { n as handleInitOptions, r as LikeC4, t as createFromSources } from "../_chunks/createFromSources.mjs";
2
+ import { memoizeProp } from "@likec4/core";
3
+ import { configureLogger, getConsoleStderrSink, loggable, rootLogger } from "@likec4/log";
4
+ import defu from "defu";
5
+ import { basename, resolve } from "pathe";
6
+ import k from "tinyrainbow";
7
+ import { pathToFileURL } from "url";
8
+ import { WithFileSystem, WithLikeC4ManualLayouts } from "@likec4/language-server/filesystem";
9
+ import { WithMCPServer } from "@likec4/language-server/mcp";
10
+ import { NoFileSystem, NoLikeC4ManualLayouts, createLanguageServices } from "@likec4/language-server/module";
11
+ import { GraphvizWasmAdapter, QueueGraphvizLayoter } from "@likec4/layouts";
12
+ import { GraphvizBinaryAdapter } from "@likec4/layouts/graphviz/binary";
13
+ function createLanguageServices$1(opts) {
14
+ const logger = rootLogger.getChild("lang");
15
+ const options = defu(opts, {
16
+ useFileSystem: true,
17
+ manualLayouts: true,
18
+ watch: false,
19
+ graphviz: "wasm",
20
+ mcp: false
21
+ });
22
+ if (options.mcp === "stdio") configureLogger({
23
+ reset: true,
24
+ sinks: { console: getConsoleStderrSink() },
25
+ loggers: [{
26
+ category: "likec4",
27
+ sinks: ["console"],
28
+ lowestLevel: "warning"
29
+ }]
30
+ });
31
+ const useDotBin = options.graphviz === "binary";
32
+ logger.info(`${k.dim("layout")} ${useDotBin ? "binary" : "wasm"}`);
33
+ const langium = createLanguageServices({
34
+ ...options.useFileSystem ? {
35
+ ...WithFileSystem(options.watch),
36
+ ...options.manualLayouts ? WithLikeC4ManualLayouts : NoLikeC4ManualLayouts
37
+ } : {
38
+ ...NoFileSystem,
39
+ ...NoLikeC4ManualLayouts
40
+ },
41
+ ...options.mcp ? WithMCPServer(options.mcp === "stdio" ? "stdio" : options.mcp) : {}
42
+ }, { likec4: { Layouter: () => new QueueGraphvizLayoter({ graphviz: useDotBin ? new GraphvizBinaryAdapter() : new GraphvizWasmAdapter() }) } });
43
+ if (typeof options.mcp === "object" && options.mcp.port) langium.likec4.mcp.Server.start(options.mcp.port).catch((e) => {
44
+ logger.error(loggable(e));
45
+ });
46
+ if (options.mcp === "stdio") langium.likec4.mcp.Server.start().catch((e) => {
47
+ logger.error(loggable(e));
48
+ });
49
+ return langium;
50
+ }
51
+ async function fromWorkspace(path, options) {
52
+ const workspacePath = resolve(path);
53
+ return memoizeProp(globalThis, "likec4:" + workspacePath, async () => {
54
+ const logger = rootLogger.getChild("lang");
55
+ const langium = createLanguageServices$1(defu(options, {
56
+ useFileSystem: true,
57
+ manualLayouts: true,
58
+ watch: false,
59
+ mcp: false
60
+ }));
61
+ const workspace = {
62
+ name: basename(workspacePath),
63
+ uri: pathToFileURL(workspacePath).toString()
64
+ };
65
+ const WorkspaceManager = langium.shared.workspace.WorkspaceManager;
66
+ logger.info(`${k.dim("workspace:")} ${workspacePath}`);
67
+ WorkspaceManager.initialize({
68
+ capabilities: {},
69
+ processId: null,
70
+ rootUri: null,
71
+ workspaceFolders: [workspace]
72
+ });
73
+ await WorkspaceManager.initializeWorkspace([workspace]);
74
+ const userDocuments = langium.shared.workspace.LangiumDocuments.userDocuments.toArray();
75
+ if (userDocuments.length === 0) {
76
+ logger.error(`no LikeC4 sources found`);
77
+ if (options?.throwIfInvalid) throw new Error(`no LikeC4 sources found`);
78
+ }
79
+ logger.info(`${k.dim("workspace:")} found ${userDocuments.length} source files`);
80
+ return handleInitOptions(langium, rootLogger, options);
81
+ });
82
+ }
83
+ async function fromWorkdir(options) {
84
+ return fromWorkspace(".", options);
85
+ }
86
+ async function fromSources(sources, options) {
87
+ const logger = rootLogger.getChild("lang");
88
+ return await createFromSources(createLanguageServices$1(defu(options, {
89
+ useFileSystem: false,
90
+ watch: false,
91
+ manualLayouts: false,
92
+ mcp: false
93
+ })), logger, sources, options);
94
+ }
95
+ async function fromSource(source, options) {
96
+ return fromSources({ "source.c4": source }, options);
97
+ }
98
+ export { LikeC4, fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,44 @@
1
+ import { i as LikeC4Langium, n as InitOptions, r as LikeC4, t as FromWorkspaceOptions } from "../../_chunks/options.mjs";
2
+
3
+ //#region src/node/without-mcp/index.d.ts
4
+ /**
5
+ * Create a LikeC4 instance from a workspace directory
6
+ * The instance is cached in globalThis to avoid creating multiple instances for the same workspace
7
+ *
8
+ * @param path - The workspace directory path
9
+ * @param options - Optional configuration options
10
+ * @returns A Promise that resolves to a LikeC4 instance
11
+ */
12
+ declare function fromWorkspace(path: string, options?: FromWorkspaceOptions): Promise<LikeC4>;
13
+ /**
14
+ * Create a LikeC4 instance from the current working directory
15
+ * @param options - Optional configuration options
16
+ * @returns A Promise that resolves to a LikeC4 instance
17
+ */
18
+ declare function fromWorkdir(options?: FromWorkspaceOptions): Promise<LikeC4>;
19
+ /**
20
+ * Create a LikeC4 instance from a record of source files
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const likec4 = await fromSources({
25
+ * 'likec4.config.json': '...', // optional, stringified LikeC4Config
26
+ * 'model.c4': 'model { ... }',
27
+ * 'path/views.c4': 'views { ... }',
28
+ * })
29
+ * ```
30
+ *
31
+ * @param sources - A record of file paths to source content
32
+ * @param options - Optional configuration options
33
+ * @returns A Promise that resolves to a LikeC4 instance
34
+ */
35
+ declare function fromSources(sources: Record<string, string>, options?: InitOptions): Promise<LikeC4>;
36
+ /**
37
+ * Create a LikeC4 instance from a single source string
38
+ * @param source - The LikeC4 source code
39
+ * @param options - Optional configuration options
40
+ * @returns A Promise that resolves to a LikeC4 instance
41
+ */
42
+ declare function fromSource(source: string, options?: InitOptions): Promise<LikeC4>;
43
+ //#endregion
44
+ export { type FromWorkspaceOptions, type InitOptions, LikeC4, type LikeC4Langium, fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,77 @@
1
+ import { n as handleInitOptions, r as LikeC4, t as createFromSources } from "../../_chunks/createFromSources.mjs";
2
+ import { memoizeProp } from "@likec4/core";
3
+ import { rootLogger } from "@likec4/log";
4
+ import defu from "defu";
5
+ import { basename, resolve } from "pathe";
6
+ import k from "tinyrainbow";
7
+ import { pathToFileURL } from "url";
8
+ import { WithFileSystem, WithLikeC4ManualLayouts } from "@likec4/language-server/filesystem";
9
+ import { NoFileSystem, NoLikeC4ManualLayouts, createLanguageServices } from "@likec4/language-server/module";
10
+ import { GraphvizWasmAdapter, QueueGraphvizLayoter } from "@likec4/layouts";
11
+ import { GraphvizBinaryAdapter } from "@likec4/layouts/graphviz/binary";
12
+ function createLanguageServices$1(opts) {
13
+ const logger = rootLogger.getChild("lang");
14
+ const options = defu(opts, {
15
+ useFileSystem: true,
16
+ manualLayouts: true,
17
+ watch: false,
18
+ graphviz: "wasm"
19
+ });
20
+ const useDotBin = options.graphviz === "binary";
21
+ logger.info(`${k.dim("layout")} ${useDotBin ? "binary" : "wasm"}`);
22
+ return createLanguageServices({ ...options.useFileSystem ? {
23
+ ...WithFileSystem(options.watch),
24
+ ...options.manualLayouts ? WithLikeC4ManualLayouts : NoLikeC4ManualLayouts
25
+ } : {
26
+ ...NoFileSystem,
27
+ ...NoLikeC4ManualLayouts
28
+ } }, { likec4: { Layouter: () => new QueueGraphvizLayoter({ graphviz: useDotBin ? new GraphvizBinaryAdapter() : new GraphvizWasmAdapter() }) } });
29
+ }
30
+ async function fromWorkspace(path, options) {
31
+ const workspacePath = resolve(path);
32
+ return memoizeProp(globalThis, "likec4:" + workspacePath, async () => {
33
+ const logger = rootLogger.getChild("lang");
34
+ const mergedOptions = defu(options, {
35
+ useFileSystem: true,
36
+ manualLayouts: true,
37
+ watch: false
38
+ });
39
+ if (mergedOptions.mcp) throw new Error("MCP server is not supported in this build");
40
+ const langium = createLanguageServices$1(mergedOptions);
41
+ const workspace = {
42
+ name: basename(workspacePath),
43
+ uri: pathToFileURL(workspacePath).toString()
44
+ };
45
+ const WorkspaceManager = langium.shared.workspace.WorkspaceManager;
46
+ logger.info(`${k.dim("workspace:")} ${workspacePath}`);
47
+ WorkspaceManager.initialize({
48
+ capabilities: {},
49
+ processId: null,
50
+ rootUri: null,
51
+ workspaceFolders: [workspace]
52
+ });
53
+ await WorkspaceManager.initializeWorkspace([workspace]);
54
+ const userDocuments = langium.shared.workspace.LangiumDocuments.userDocuments.toArray();
55
+ if (userDocuments.length === 0) {
56
+ logger.error(`no LikeC4 sources found`);
57
+ throw new Error(`no LikeC4 sources found`);
58
+ }
59
+ logger.info(`${k.dim("workspace:")} found ${userDocuments.length} source files`);
60
+ return handleInitOptions(langium, rootLogger, options);
61
+ });
62
+ }
63
+ async function fromWorkdir(options) {
64
+ return fromWorkspace(".", options);
65
+ }
66
+ async function fromSources(sources, options) {
67
+ const logger = rootLogger.getChild("lang");
68
+ return await createFromSources(createLanguageServices$1(defu(options, {
69
+ useFileSystem: false,
70
+ watch: false,
71
+ manualLayouts: false
72
+ })), logger, sources, options);
73
+ }
74
+ async function fromSource(source, options) {
75
+ return fromSources({ "source.c4": source }, options);
76
+ }
77
+ export { LikeC4, fromSource, fromSources, fromWorkdir, fromWorkspace };
@@ -0,0 +1,4 @@
1
+ {
2
+ "types": "../dist/node/index.d.mts",
3
+ "module": "../dist/node/index.mjs"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "types": "../../dist/node/without-mcp/index.d.mts",
3
+ "module": "../../dist/node/without-mcp/index.mjs"
4
+ }
package/package.json ADDED
@@ -0,0 +1,113 @@
1
+ {
2
+ "name": "@likec4/language-services",
3
+ "description": "LikeC4 Language Services",
4
+ "version": "1.48.0",
5
+ "license": "MIT",
6
+ "bugs": "https://github.com/likec4/likec4/issues",
7
+ "homepage": "https://likec4.dev",
8
+ "author": "Denis Davydkov <denis@davydkov.com>",
9
+ "files": [
10
+ "dist",
11
+ "**/package.json",
12
+ "!**/__mocks__/",
13
+ "!**/__tests__/",
14
+ "!**/*.spec.*",
15
+ "!**/*.map"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/likec4/likec4.git",
20
+ "directory": "packages/language-services"
21
+ },
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "exports": {
25
+ ".": {
26
+ "browser": {
27
+ "sources": "./src/browser/index.ts",
28
+ "default": {
29
+ "types": "./dist/browser/index.d.mts",
30
+ "import": "./dist/browser/index.mjs",
31
+ "default": "./dist/browser/index.mjs"
32
+ }
33
+ },
34
+ "default": {
35
+ "sources": "./src/node/index.ts",
36
+ "types": "./dist/node/index.d.mts",
37
+ "import": "./dist/node/index.mjs",
38
+ "default": "./dist/node/index.mjs"
39
+ }
40
+ },
41
+ "./browser": {
42
+ "sources": "./src/browser/index.ts",
43
+ "default": {
44
+ "types": "./dist/browser/index.d.mts",
45
+ "import": "./dist/browser/index.mjs",
46
+ "default": "./dist/browser/index.mjs"
47
+ }
48
+ },
49
+ "./node/without-mcp": {
50
+ "sources": "./src/node/without-mcp/index.ts",
51
+ "default": {
52
+ "types": "./dist/node/without-mcp/index.d.mts",
53
+ "import": "./dist/node/without-mcp/index.mjs",
54
+ "default": "./dist/node/without-mcp/index.mjs"
55
+ }
56
+ },
57
+ "./node": {
58
+ "sources": "./src/node/index.ts",
59
+ "default": {
60
+ "types": "./dist/node/index.d.mts",
61
+ "import": "./dist/node/index.mjs",
62
+ "default": "./dist/node/index.mjs"
63
+ }
64
+ },
65
+ "./package.json": "./package.json"
66
+ },
67
+ "publishConfig": {
68
+ "registry": "https://registry.npmjs.org",
69
+ "access": "public"
70
+ },
71
+ "dependencies": {
72
+ "defu": "^6.1.4",
73
+ "remeda": "^2.33.5",
74
+ "langium": "3.5.0",
75
+ "tinyrainbow": "^2.0.0",
76
+ "pathe": "^2.0.3",
77
+ "std-env": "^3.10.0",
78
+ "type-fest": "^4.41.0",
79
+ "vscode-languageserver-types": "3.17.5",
80
+ "@likec4/config": "1.48.0",
81
+ "@likec4/core": "1.48.0",
82
+ "@likec4/generators": "1.48.0",
83
+ "@likec4/icons": "1.46.4",
84
+ "@likec4/language-server": "1.48.0",
85
+ "@likec4/layouts": "1.48.0",
86
+ "@likec4/log": "1.48.0"
87
+ },
88
+ "devDependencies": {
89
+ "@types/node": "~22.19.10",
90
+ "obuild": "^0.4.27",
91
+ "oxlint": "1.43.0",
92
+ "tsx": "4.21.0",
93
+ "turbo": "2.8.3",
94
+ "typescript": "5.9.3",
95
+ "vitest": "4.0.18",
96
+ "@likec4/tsconfig": "1.48.0",
97
+ "@likec4/devops": "1.42.0"
98
+ },
99
+ "scripts": {
100
+ "typecheck": "tsc -b --verbose",
101
+ "build": "obuild",
102
+ "pack": "pnpm pack",
103
+ "lint": "oxlint --type-aware",
104
+ "lint:fix": "oxlint --type-aware --fix",
105
+ "lint:package": "pnpx publint ./package.tgz",
106
+ "clean": "likec4ops clean"
107
+ },
108
+ "types": "dist/node/index.d.mts",
109
+ "module": "dist/node/index.mjs",
110
+ "browser": {
111
+ "dist/node/index.mjs": "dist/browser/index.mjs"
112
+ }
113
+ }