@domainlang/language 0.10.0 → 0.11.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/README.md +44 -102
- package/out/domain-lang-module.d.ts +2 -2
- package/out/domain-lang-module.js +2 -2
- package/out/domain-lang-module.js.map +1 -1
- package/out/index.d.ts +3 -0
- package/out/index.js +3 -0
- package/out/index.js.map +1 -1
- package/out/lsp/domain-lang-completion.js +1 -1
- package/out/lsp/domain-lang-completion.js.map +1 -1
- package/out/lsp/domain-lang-index-manager.d.ts +149 -5
- package/out/lsp/domain-lang-index-manager.js +388 -52
- package/out/lsp/domain-lang-index-manager.js.map +1 -1
- package/out/lsp/domain-lang-refresh.d.ts +35 -0
- package/out/lsp/domain-lang-refresh.js +129 -0
- package/out/lsp/domain-lang-refresh.js.map +1 -0
- package/out/lsp/domain-lang-workspace-manager.d.ts +10 -0
- package/out/lsp/domain-lang-workspace-manager.js +35 -0
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
- package/out/main.js +30 -190
- package/out/main.js.map +1 -1
- package/out/sdk/loader-node.js +1 -1
- package/out/sdk/loader-node.js.map +1 -1
- package/out/sdk/validator.js +17 -14
- package/out/sdk/validator.js.map +1 -1
- package/out/services/import-resolver.d.ts +67 -17
- package/out/services/import-resolver.js +146 -65
- package/out/services/import-resolver.js.map +1 -1
- package/out/services/lsp-logger.d.ts +42 -0
- package/out/services/lsp-logger.js +50 -0
- package/out/services/lsp-logger.js.map +1 -0
- package/out/services/lsp-runtime-settings.d.ts +20 -0
- package/out/services/lsp-runtime-settings.js +20 -0
- package/out/services/lsp-runtime-settings.js.map +1 -0
- package/out/services/performance-optimizer.d.ts +9 -9
- package/out/services/performance-optimizer.js +17 -41
- package/out/services/performance-optimizer.js.map +1 -1
- package/out/services/workspace-manager.d.ts +22 -1
- package/out/services/workspace-manager.js +57 -9
- package/out/services/workspace-manager.js.map +1 -1
- package/out/utils/import-utils.js +6 -6
- package/out/utils/import-utils.js.map +1 -1
- package/out/validation/constants.d.ts +6 -0
- package/out/validation/constants.js +7 -0
- package/out/validation/constants.js.map +1 -1
- package/out/validation/import.d.ts +13 -3
- package/out/validation/import.js +54 -10
- package/out/validation/import.js.map +1 -1
- package/package.json +1 -1
- package/src/domain-lang-module.ts +3 -3
- package/src/index.ts +3 -0
- package/src/lsp/domain-lang-completion.ts +3 -3
- package/src/lsp/domain-lang-index-manager.ts +438 -56
- package/src/lsp/domain-lang-refresh.ts +205 -0
- package/src/lsp/domain-lang-workspace-manager.ts +45 -0
- package/src/main.ts +36 -244
- package/src/sdk/loader-node.ts +1 -1
- package/src/sdk/validator.ts +17 -13
- package/src/services/import-resolver.ts +196 -89
- package/src/services/lsp-logger.ts +89 -0
- package/src/services/lsp-runtime-settings.ts +34 -0
- package/src/services/performance-optimizer.ts +18 -57
- package/src/services/workspace-manager.ts +62 -10
- package/src/utils/import-utils.ts +6 -6
- package/src/validation/constants.ts +9 -0
- package/src/validation/import.ts +67 -12
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
3
4
|
import YAML from 'yaml';
|
|
4
5
|
import { getGlobalOptimizer } from './performance-optimizer.js';
|
|
5
6
|
import { fileExists as checkFileExists, findWorkspaceRoot as findWorkspaceRootUtil } from '../utils/manifest-utils.js';
|
|
@@ -24,7 +25,8 @@ const DEFAULT_LOCK_FILES = [
|
|
|
24
25
|
interface ManifestCache {
|
|
25
26
|
readonly manifest: ModelManifest;
|
|
26
27
|
readonly path: string;
|
|
27
|
-
|
|
28
|
+
/** SHA-256 content hash for reliable change detection (PRS-017 R5) */
|
|
29
|
+
readonly contentHash: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
interface LoadedLockFile {
|
|
@@ -65,7 +67,7 @@ interface WorkspaceContext {
|
|
|
65
67
|
* - Read manifest configuration (path aliases, dependencies)
|
|
66
68
|
* - Read lock file (to resolve cached package locations)
|
|
67
69
|
*/
|
|
68
|
-
export class
|
|
70
|
+
export class ManifestManager {
|
|
69
71
|
private readonly manifestFiles: readonly string[];
|
|
70
72
|
private readonly lockFiles: readonly string[];
|
|
71
73
|
|
|
@@ -80,6 +82,14 @@ export class WorkspaceManager {
|
|
|
80
82
|
* Avoids repeated directory tree walking for the same paths.
|
|
81
83
|
*/
|
|
82
84
|
private readonly pathToRootCache = new Map<string, string>();
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* PRS-017 R11: Cached set of directories known to contain a manifest file.
|
|
88
|
+
* Populated during `findWorkspaceRoot()` walks and updated incrementally
|
|
89
|
+
* when manifest creation/deletion events arrive via `onManifestEvent()`.
|
|
90
|
+
* Prevents redundant filesystem walks for paths already explored.
|
|
91
|
+
*/
|
|
92
|
+
private readonly knownManifestDirs = new Set<string>();
|
|
83
93
|
|
|
84
94
|
/**
|
|
85
95
|
* The currently active workspace root (set by last initialize() call).
|
|
@@ -158,7 +168,7 @@ export class WorkspaceManager {
|
|
|
158
168
|
*/
|
|
159
169
|
getWorkspaceRoot(): string {
|
|
160
170
|
if (!this.activeRoot) {
|
|
161
|
-
throw new Error('
|
|
171
|
+
throw new Error('ManifestManager not initialized. Call initialize() first.');
|
|
162
172
|
}
|
|
163
173
|
return this.activeRoot;
|
|
164
174
|
}
|
|
@@ -172,7 +182,7 @@ export class WorkspaceManager {
|
|
|
172
182
|
*/
|
|
173
183
|
getCacheDir(): string {
|
|
174
184
|
if (!this.activeRoot) {
|
|
175
|
-
throw new Error('
|
|
185
|
+
throw new Error('ManifestManager not initialized. Call initialize() first.');
|
|
176
186
|
}
|
|
177
187
|
|
|
178
188
|
// If workspace root is inside .dlang/packages, find the project root
|
|
@@ -320,6 +330,28 @@ export class WorkspaceManager {
|
|
|
320
330
|
}
|
|
321
331
|
}
|
|
322
332
|
|
|
333
|
+
/**
|
|
334
|
+
* PRS-017 R11: Incrementally updates the workspace layout cache
|
|
335
|
+
* when a manifest file is created or deleted.
|
|
336
|
+
*
|
|
337
|
+
* @param manifestDir - Directory where the manifest was created/deleted
|
|
338
|
+
* @param created - true if manifest was created, false if deleted
|
|
339
|
+
*/
|
|
340
|
+
onManifestEvent(manifestDir: string, created: boolean): void {
|
|
341
|
+
const normalized = path.resolve(manifestDir);
|
|
342
|
+
if (created) {
|
|
343
|
+
this.knownManifestDirs.add(normalized);
|
|
344
|
+
} else {
|
|
345
|
+
this.knownManifestDirs.delete(normalized);
|
|
346
|
+
// Invalidate path-to-root cache entries that pointed to this dir
|
|
347
|
+
for (const [startPath, root] of this.pathToRootCache) {
|
|
348
|
+
if (root === normalized) {
|
|
349
|
+
this.pathToRootCache.delete(startPath);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
323
355
|
/**
|
|
324
356
|
* Returns the path aliases from the manifest, if present.
|
|
325
357
|
*/
|
|
@@ -437,7 +469,7 @@ export class WorkspaceManager {
|
|
|
437
469
|
}
|
|
438
470
|
}
|
|
439
471
|
|
|
440
|
-
throw new Error('
|
|
472
|
+
throw new Error('ManifestManager not initialized. Call initialize() first.');
|
|
441
473
|
}
|
|
442
474
|
|
|
443
475
|
private async loadLockFileFromDisk(root?: string): Promise<LoadedLockFile | undefined> {
|
|
@@ -498,13 +530,16 @@ export class WorkspaceManager {
|
|
|
498
530
|
manifestPath: string,
|
|
499
531
|
context: WorkspaceContext | undefined
|
|
500
532
|
): Promise<ModelManifest> {
|
|
501
|
-
|
|
533
|
+
// PRS-017 R5: Use content hash instead of mtime for reliable change detection.
|
|
534
|
+
// Content hashing is immune to mtime skew after git operations or on NFS.
|
|
535
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
536
|
+
const contentHash = this.computeHash(content);
|
|
537
|
+
|
|
502
538
|
if (context?.manifestCache?.path === manifestPath &&
|
|
503
|
-
context.manifestCache.
|
|
539
|
+
context.manifestCache.contentHash === contentHash) {
|
|
504
540
|
return context.manifestCache.manifest;
|
|
505
541
|
}
|
|
506
542
|
|
|
507
|
-
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
508
543
|
const manifest = (YAML.parse(content) ?? {}) as ModelManifest;
|
|
509
544
|
|
|
510
545
|
// Validate manifest structure
|
|
@@ -514,7 +549,7 @@ export class WorkspaceManager {
|
|
|
514
549
|
context.manifestCache = {
|
|
515
550
|
manifest,
|
|
516
551
|
path: manifestPath,
|
|
517
|
-
|
|
552
|
+
contentHash,
|
|
518
553
|
};
|
|
519
554
|
}
|
|
520
555
|
return manifest;
|
|
@@ -690,11 +725,14 @@ export class WorkspaceManager {
|
|
|
690
725
|
/**
|
|
691
726
|
* Finds workspace root by walking up from startPath looking for model.yaml.
|
|
692
727
|
* Uses configurable manifest files if specified in constructor options.
|
|
728
|
+
* PRS-017 R11: Consults `knownManifestDirs` before hitting the filesystem.
|
|
693
729
|
*/
|
|
694
730
|
private async findWorkspaceRoot(startPath: string): Promise<string | undefined> {
|
|
695
731
|
// Use shared utility for default case (single manifest file)
|
|
696
732
|
if (this.manifestFiles.length === 1 && this.manifestFiles[0] === 'model.yaml') {
|
|
697
|
-
|
|
733
|
+
const result = await findWorkspaceRootUtil(startPath);
|
|
734
|
+
if (result) this.knownManifestDirs.add(result);
|
|
735
|
+
return result;
|
|
698
736
|
}
|
|
699
737
|
|
|
700
738
|
// Custom logic for multiple or non-default manifest files
|
|
@@ -702,7 +740,13 @@ export class WorkspaceManager {
|
|
|
702
740
|
const { root } = path.parse(current);
|
|
703
741
|
|
|
704
742
|
while (true) {
|
|
743
|
+
// R11: Check cached knowledge first
|
|
744
|
+
if (this.knownManifestDirs.has(current)) {
|
|
745
|
+
return current;
|
|
746
|
+
}
|
|
747
|
+
|
|
705
748
|
if (await this.containsManifest(current)) {
|
|
749
|
+
this.knownManifestDirs.add(current);
|
|
706
750
|
return current;
|
|
707
751
|
}
|
|
708
752
|
|
|
@@ -727,4 +771,12 @@ export class WorkspaceManager {
|
|
|
727
771
|
}
|
|
728
772
|
return false;
|
|
729
773
|
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Computes a SHA-256 hex digest of the given content.
|
|
777
|
+
* Used for content-hash based cache validation (PRS-017 R5).
|
|
778
|
+
*/
|
|
779
|
+
private computeHash(content: string): string {
|
|
780
|
+
return createHash('sha256').update(content).digest('hex');
|
|
781
|
+
}
|
|
730
782
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { URI, type LangiumDocument, type LangiumDocuments } from 'langium';
|
|
3
3
|
import type { Model } from '../generated/ast.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ManifestManager } from '../services/workspace-manager.js';
|
|
5
5
|
import { ImportResolver } from '../services/import-resolver.js';
|
|
6
6
|
import type { DomainLangServices } from '../domain-lang-module.js';
|
|
7
7
|
|
|
@@ -13,13 +13,13 @@ import type { DomainLangServices } from '../domain-lang-module.js';
|
|
|
13
13
|
* These singletons exist only for backwards compatibility with callers
|
|
14
14
|
* that haven't been updated to pass through DI services.
|
|
15
15
|
*/
|
|
16
|
-
let
|
|
16
|
+
let standaloneManifestManager: ManifestManager | undefined;
|
|
17
17
|
let standaloneImportResolver: ImportResolver | undefined;
|
|
18
18
|
let lastInitializedDir: string | undefined;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Gets or creates a standalone import resolver for non-LSP contexts.
|
|
22
|
-
* Creates its own
|
|
22
|
+
* Creates its own ManifestManager if not previously initialized for this directory.
|
|
23
23
|
*
|
|
24
24
|
* @deprecated Prefer using services.imports.ImportResolver directly.
|
|
25
25
|
* @param startDir - Directory to start workspace search from
|
|
@@ -28,14 +28,14 @@ let lastInitializedDir: string | undefined;
|
|
|
28
28
|
async function getStandaloneImportResolver(startDir: string): Promise<ImportResolver> {
|
|
29
29
|
// Re-initialize if directory changed (workspace boundary)
|
|
30
30
|
if (lastInitializedDir !== startDir || !standaloneImportResolver) {
|
|
31
|
-
|
|
31
|
+
standaloneManifestManager = new ManifestManager();
|
|
32
32
|
try {
|
|
33
|
-
await
|
|
33
|
+
await standaloneManifestManager.initialize(startDir);
|
|
34
34
|
} catch (error) {
|
|
35
35
|
console.warn(`Failed to initialize workspace: ${error instanceof Error ? error.message : String(error)}`);
|
|
36
36
|
}
|
|
37
37
|
const services = {
|
|
38
|
-
imports: {
|
|
38
|
+
imports: { ManifestManager: standaloneManifestManager }
|
|
39
39
|
} as DomainLangServices;
|
|
40
40
|
standaloneImportResolver = new ImportResolver(services);
|
|
41
41
|
lastInitializedDir = startDir;
|
|
@@ -35,6 +35,7 @@ export const IssueCodes = {
|
|
|
35
35
|
ImportAbsolutePath: 'import-absolute-path',
|
|
36
36
|
ImportEscapesWorkspace: 'import-escapes-workspace',
|
|
37
37
|
ImportUnresolved: 'import-unresolved',
|
|
38
|
+
ImportCycleDetected: 'import-cycle-detected',
|
|
38
39
|
|
|
39
40
|
// Domain Issues
|
|
40
41
|
DomainNoVision: 'domain-no-vision',
|
|
@@ -283,6 +284,14 @@ export const ValidationMessages = {
|
|
|
283
284
|
`Cannot resolve import '${uri}'.\n` +
|
|
284
285
|
`Hint: Check that the file exists and the path is correct.`,
|
|
285
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Error when an import creates a cycle in the dependency graph.
|
|
289
|
+
* @param cycleDisplay - Human-readable cycle path (e.g., "a.dlang → b.dlang → a.dlang")
|
|
290
|
+
*/
|
|
291
|
+
IMPORT_CYCLE_DETECTED: (cycleDisplay: string) =>
|
|
292
|
+
`Import cycle detected: ${cycleDisplay}.\n` +
|
|
293
|
+
`Hint: Break the cycle by extracting shared definitions into a separate file.`,
|
|
294
|
+
|
|
286
295
|
// ========================================================================
|
|
287
296
|
// Context Map & Domain Map Validation
|
|
288
297
|
// ========================================================================
|
package/src/validation/import.ts
CHANGED
|
@@ -4,8 +4,10 @@ import type { ValidationAcceptor, ValidationChecks, LangiumDocument } from 'lang
|
|
|
4
4
|
import { Cancellation } from 'langium';
|
|
5
5
|
import type { DomainLangAstType, ImportStatement } from '../generated/ast.js';
|
|
6
6
|
import type { DomainLangServices } from '../domain-lang-module.js';
|
|
7
|
-
import type {
|
|
7
|
+
import type { ManifestManager } from '../services/workspace-manager.js';
|
|
8
8
|
import type { ImportResolver } from '../services/import-resolver.js';
|
|
9
|
+
import { ImportResolutionError } from '../services/import-resolver.js';
|
|
10
|
+
import type { DomainLangIndexManager } from '../lsp/domain-lang-index-manager.js';
|
|
9
11
|
import type { ExtendedDependencySpec, ModelManifest, LockFile } from '../services/types.js';
|
|
10
12
|
import { ValidationMessages, buildCodeDescription, IssueCodes } from './constants.js';
|
|
11
13
|
|
|
@@ -13,28 +15,35 @@ import { ValidationMessages, buildCodeDescription, IssueCodes } from './constant
|
|
|
13
15
|
* Validates import statements in DomainLang.
|
|
14
16
|
*
|
|
15
17
|
* Uses async validators (Langium 4.x supports MaybePromise<void>) to leverage
|
|
16
|
-
* the shared
|
|
18
|
+
* the shared ManifestManager service with its cached manifest/lock file reading.
|
|
17
19
|
*
|
|
18
20
|
* Checks:
|
|
19
21
|
* - All import URIs resolve to existing files
|
|
20
22
|
* - External imports require manifest + alias
|
|
21
23
|
* - Local path dependencies stay inside workspace
|
|
22
24
|
* - Lock file exists for external dependencies
|
|
25
|
+
* - Import cycles are detected and reported (PRS-017 R3)
|
|
23
26
|
*/
|
|
24
27
|
export class ImportValidator {
|
|
25
|
-
private readonly workspaceManager:
|
|
28
|
+
private readonly workspaceManager: ManifestManager;
|
|
26
29
|
private readonly importResolver: ImportResolver;
|
|
30
|
+
private readonly indexManager: DomainLangIndexManager | undefined;
|
|
27
31
|
|
|
28
32
|
constructor(services: DomainLangServices) {
|
|
29
|
-
this.workspaceManager = services.imports.
|
|
33
|
+
this.workspaceManager = services.imports.ManifestManager;
|
|
30
34
|
this.importResolver = services.imports.ImportResolver;
|
|
35
|
+
// IndexManager is in shared services — cast to DomainLangIndexManager for cycle detection
|
|
36
|
+
const indexMgr = services.shared.workspace.IndexManager;
|
|
37
|
+
this.indexManager = 'getCycleForDocument' in indexMgr
|
|
38
|
+
? indexMgr as DomainLangIndexManager
|
|
39
|
+
: undefined;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
/**
|
|
34
43
|
* Validates an import statement asynchronously.
|
|
35
44
|
*
|
|
36
45
|
* Langium validators can return MaybePromise<void>, enabling async operations
|
|
37
|
-
* like reading manifests via the shared, cached
|
|
46
|
+
* like reading manifests via the shared, cached ManifestManager.
|
|
38
47
|
*/
|
|
39
48
|
async checkImportPath(
|
|
40
49
|
imp: ImportStatement,
|
|
@@ -52,6 +61,9 @@ export class ImportValidator {
|
|
|
52
61
|
return;
|
|
53
62
|
}
|
|
54
63
|
|
|
64
|
+
// PRS-017 R3: Check for import cycles detected during indexing
|
|
65
|
+
this.checkImportCycle(document, imp, accept);
|
|
66
|
+
|
|
55
67
|
// First, verify the import resolves to a valid file
|
|
56
68
|
// This catches renamed/moved/deleted files immediately
|
|
57
69
|
const resolveError = await this.validateImportResolves(imp, document, accept);
|
|
@@ -162,13 +174,21 @@ export class ImportValidator {
|
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
return false;
|
|
165
|
-
} catch {
|
|
166
|
-
//
|
|
167
|
-
|
|
177
|
+
} catch (error: unknown) {
|
|
178
|
+
// R8: Use structured error properties for precise diagnostics
|
|
179
|
+
const message = error instanceof ImportResolutionError && error.hint
|
|
180
|
+
? `${ValidationMessages.IMPORT_UNRESOLVED(imp.uri)}: ${error.hint}`
|
|
181
|
+
: ValidationMessages.IMPORT_UNRESOLVED(imp.uri);
|
|
182
|
+
|
|
183
|
+
accept('error', message, {
|
|
168
184
|
node: imp,
|
|
169
185
|
property: 'uri',
|
|
170
186
|
codeDescription: buildCodeDescription('language.md', 'imports'),
|
|
171
|
-
data: {
|
|
187
|
+
data: {
|
|
188
|
+
code: IssueCodes.ImportUnresolved,
|
|
189
|
+
uri: imp.uri,
|
|
190
|
+
...(error instanceof ImportResolutionError && { reason: error.reason }),
|
|
191
|
+
}
|
|
172
192
|
});
|
|
173
193
|
return true;
|
|
174
194
|
}
|
|
@@ -316,7 +336,7 @@ export class ImportValidator {
|
|
|
316
336
|
});
|
|
317
337
|
}
|
|
318
338
|
} catch (error) {
|
|
319
|
-
//
|
|
339
|
+
// ManifestManager not initialized - skip workspace boundary check
|
|
320
340
|
// This can happen for standalone files without model.yaml
|
|
321
341
|
console.warn(`Could not validate workspace boundary for path dependency: ${error}`);
|
|
322
342
|
}
|
|
@@ -360,7 +380,7 @@ export class ImportValidator {
|
|
|
360
380
|
});
|
|
361
381
|
}
|
|
362
382
|
} catch (error) {
|
|
363
|
-
//
|
|
383
|
+
// ManifestManager not initialized - log warning but continue
|
|
364
384
|
console.warn(`Could not validate cached package for ${alias}: ${error}`);
|
|
365
385
|
}
|
|
366
386
|
}
|
|
@@ -385,12 +405,47 @@ export class ImportValidator {
|
|
|
385
405
|
return false;
|
|
386
406
|
}
|
|
387
407
|
}
|
|
408
|
+
|
|
409
|
+
// --- PRS-017 R3: Import cycle detection ---
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Reports a warning if the current document is part of an import cycle.
|
|
413
|
+
*
|
|
414
|
+
* Cycle data is populated during indexing by DomainLangIndexManager.
|
|
415
|
+
* This method reads the pre-computed cycles and emits a diagnostic
|
|
416
|
+
* on the import statement contributing to the cycle.
|
|
417
|
+
*/
|
|
418
|
+
private checkImportCycle(
|
|
419
|
+
document: LangiumDocument,
|
|
420
|
+
imp: ImportStatement,
|
|
421
|
+
accept: ValidationAcceptor
|
|
422
|
+
): void {
|
|
423
|
+
if (!this.indexManager) return;
|
|
424
|
+
|
|
425
|
+
const cycle = this.indexManager.getCycleForDocument(document.uri.toString());
|
|
426
|
+
if (!cycle || cycle.length === 0) return;
|
|
427
|
+
|
|
428
|
+
// Build human-readable cycle display using basenames
|
|
429
|
+
const cycleDisplay = cycle
|
|
430
|
+
.map(uri => {
|
|
431
|
+
const parts = uri.split('/');
|
|
432
|
+
return parts.at(-1) ?? uri;
|
|
433
|
+
})
|
|
434
|
+
.join(' → ');
|
|
435
|
+
|
|
436
|
+
accept('warning', ValidationMessages.IMPORT_CYCLE_DETECTED(cycleDisplay), {
|
|
437
|
+
node: imp,
|
|
438
|
+
property: 'uri',
|
|
439
|
+
codeDescription: buildCodeDescription('language.md', 'imports'),
|
|
440
|
+
data: { code: IssueCodes.ImportCycleDetected }
|
|
441
|
+
});
|
|
442
|
+
}
|
|
388
443
|
}
|
|
389
444
|
|
|
390
445
|
/**
|
|
391
446
|
* Creates validation checks for import statements.
|
|
392
447
|
*
|
|
393
|
-
* Returns async validators that leverage the shared
|
|
448
|
+
* Returns async validators that leverage the shared ManifestManager
|
|
394
449
|
* for cached manifest/lock file reading.
|
|
395
450
|
*/
|
|
396
451
|
export function createImportChecks(services: DomainLangServices): ValidationChecks<DomainLangAstType> {
|