@domainlang/language 0.6.0 → 0.7.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 +1 -1
- package/out/domain-lang-module.js +2 -0
- package/out/domain-lang-module.js.map +1 -1
- package/out/lsp/domain-lang-index-manager.d.ts +69 -1
- package/out/lsp/domain-lang-index-manager.js +173 -5
- package/out/lsp/domain-lang-index-manager.js.map +1 -1
- package/out/lsp/domain-lang-scope-provider.d.ts +67 -0
- package/out/lsp/domain-lang-scope-provider.js +95 -0
- package/out/lsp/domain-lang-scope-provider.js.map +1 -0
- package/out/lsp/domain-lang-workspace-manager.d.ts +25 -0
- package/out/lsp/domain-lang-workspace-manager.js +102 -3
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
- package/out/main.js +114 -19
- package/out/main.js.map +1 -1
- package/out/services/import-resolver.d.ts +29 -6
- package/out/services/import-resolver.js +48 -9
- package/out/services/import-resolver.js.map +1 -1
- package/out/validation/constants.d.ts +13 -0
- package/out/validation/constants.js +18 -0
- package/out/validation/constants.js.map +1 -1
- package/out/validation/import.d.ts +11 -0
- package/out/validation/import.js +62 -2
- package/out/validation/import.js.map +1 -1
- package/out/validation/maps.js +51 -2
- package/out/validation/maps.js.map +1 -1
- package/package.json +1 -1
- package/src/domain-lang-module.ts +2 -0
- package/src/lsp/domain-lang-index-manager.ts +196 -5
- package/src/lsp/domain-lang-scope-provider.ts +134 -0
- package/src/lsp/domain-lang-workspace-manager.ts +128 -3
- package/src/main.ts +153 -22
- package/src/services/import-resolver.ts +60 -9
- package/src/validation/constants.ts +24 -0
- package/src/validation/import.ts +75 -2
- package/src/validation/maps.ts +59 -2
|
@@ -9,12 +9,14 @@ import type { DomainLangServices } from '../domain-lang-module.js';
|
|
|
9
9
|
* the shared WorkspaceManager service with its cached manifest/lock file reading.
|
|
10
10
|
*
|
|
11
11
|
* Checks:
|
|
12
|
+
* - All import URIs resolve to existing files
|
|
12
13
|
* - External imports require manifest + alias
|
|
13
14
|
* - Local path dependencies stay inside workspace
|
|
14
15
|
* - Lock file exists for external dependencies
|
|
15
16
|
*/
|
|
16
17
|
export declare class ImportValidator {
|
|
17
18
|
private readonly workspaceManager;
|
|
19
|
+
private readonly importResolver;
|
|
18
20
|
constructor(services: DomainLangServices);
|
|
19
21
|
/**
|
|
20
22
|
* Validates an import statement asynchronously.
|
|
@@ -32,6 +34,15 @@ export declare class ImportValidator {
|
|
|
32
34
|
* - External: owner/package (requires manifest dependencies)
|
|
33
35
|
*/
|
|
34
36
|
private isExternalImport;
|
|
37
|
+
/**
|
|
38
|
+
* Validates that an import URI resolves to an existing file.
|
|
39
|
+
* Returns true if there was an error (import doesn't resolve).
|
|
40
|
+
*/
|
|
41
|
+
private validateImportResolves;
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a file exists (async).
|
|
44
|
+
*/
|
|
45
|
+
private fileExists;
|
|
35
46
|
/**
|
|
36
47
|
* Gets the normalized dependency configuration for an alias.
|
|
37
48
|
*/
|
package/out/validation/import.js
CHANGED
|
@@ -8,6 +8,7 @@ import { ValidationMessages, buildCodeDescription, IssueCodes } from './constant
|
|
|
8
8
|
* the shared WorkspaceManager service with its cached manifest/lock file reading.
|
|
9
9
|
*
|
|
10
10
|
* Checks:
|
|
11
|
+
* - All import URIs resolve to existing files
|
|
11
12
|
* - External imports require manifest + alias
|
|
12
13
|
* - Local path dependencies stay inside workspace
|
|
13
14
|
* - Lock file exists for external dependencies
|
|
@@ -15,6 +16,7 @@ import { ValidationMessages, buildCodeDescription, IssueCodes } from './constant
|
|
|
15
16
|
export class ImportValidator {
|
|
16
17
|
constructor(services) {
|
|
17
18
|
this.workspaceManager = services.imports.WorkspaceManager;
|
|
19
|
+
this.importResolver = services.imports.ImportResolver;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Validates an import statement asynchronously.
|
|
@@ -32,6 +34,12 @@ export class ImportValidator {
|
|
|
32
34
|
});
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
37
|
+
// First, verify the import resolves to a valid file
|
|
38
|
+
// This catches renamed/moved/deleted files immediately
|
|
39
|
+
const resolveError = await this.validateImportResolves(imp, document, accept);
|
|
40
|
+
if (resolveError) {
|
|
41
|
+
return; // Don't continue with other validations if can't resolve
|
|
42
|
+
}
|
|
35
43
|
if (!this.isExternalImport(imp.uri)) {
|
|
36
44
|
return;
|
|
37
45
|
}
|
|
@@ -92,6 +100,54 @@ export class ImportValidator {
|
|
|
92
100
|
}
|
|
93
101
|
return true;
|
|
94
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Validates that an import URI resolves to an existing file.
|
|
105
|
+
* Returns true if there was an error (import doesn't resolve).
|
|
106
|
+
*/
|
|
107
|
+
async validateImportResolves(imp, document, accept) {
|
|
108
|
+
if (!imp.uri) {
|
|
109
|
+
return true; // Error already reported
|
|
110
|
+
}
|
|
111
|
+
const docDir = path.dirname(document.uri.fsPath);
|
|
112
|
+
try {
|
|
113
|
+
const resolvedUri = await this.importResolver.resolveFrom(docDir, imp.uri);
|
|
114
|
+
// Check if the resolved file actually exists
|
|
115
|
+
const filePath = resolvedUri.fsPath;
|
|
116
|
+
const exists = await this.fileExists(filePath);
|
|
117
|
+
if (!exists) {
|
|
118
|
+
accept('error', ValidationMessages.IMPORT_UNRESOLVED(imp.uri), {
|
|
119
|
+
node: imp,
|
|
120
|
+
property: 'uri',
|
|
121
|
+
codeDescription: buildCodeDescription('language.md', 'imports'),
|
|
122
|
+
data: { code: IssueCodes.ImportUnresolved, uri: imp.uri }
|
|
123
|
+
});
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Resolution failed - report as unresolved import
|
|
130
|
+
accept('error', ValidationMessages.IMPORT_UNRESOLVED(imp.uri), {
|
|
131
|
+
node: imp,
|
|
132
|
+
property: 'uri',
|
|
133
|
+
codeDescription: buildCodeDescription('language.md', 'imports'),
|
|
134
|
+
data: { code: IssueCodes.ImportUnresolved, uri: imp.uri }
|
|
135
|
+
});
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Checks if a file exists (async).
|
|
141
|
+
*/
|
|
142
|
+
async fileExists(filePath) {
|
|
143
|
+
try {
|
|
144
|
+
const stat = await fs.stat(filePath);
|
|
145
|
+
return stat.isFile();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
95
151
|
/**
|
|
96
152
|
* Gets the normalized dependency configuration for an alias.
|
|
97
153
|
*/
|
|
@@ -240,9 +296,13 @@ export function createImportChecks(services) {
|
|
|
240
296
|
return {
|
|
241
297
|
// Langium 4.x supports async validators via MaybePromise<void>
|
|
242
298
|
ImportStatement: async (imp, accept, cancelToken) => {
|
|
243
|
-
|
|
244
|
-
|
|
299
|
+
// Get document from root (Model), not from ImportStatement
|
|
300
|
+
// Langium sets $document only on the root AST node
|
|
301
|
+
const root = imp.$container;
|
|
302
|
+
const document = root?.$document;
|
|
303
|
+
if (!document) {
|
|
245
304
|
return;
|
|
305
|
+
}
|
|
246
306
|
await validator.checkImportPath(imp, accept, document, cancelToken);
|
|
247
307
|
}
|
|
248
308
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/validation/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/validation/import.ts"],"names":[],"mappings":"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"}
|
package/out/validation/maps.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ValidationMessages, buildCodeDescription } from './constants.js';
|
|
1
|
+
import { ValidationMessages, buildCodeDescription, IssueCodes } from './constants.js';
|
|
2
2
|
/**
|
|
3
3
|
* Validates that a context map contains at least one bounded context.
|
|
4
4
|
* Empty context maps are not useful for documentation purposes.
|
|
@@ -15,6 +15,32 @@ function validateContextMapHasContexts(map, accept) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates that MultiReference items in a context map resolve.
|
|
20
|
+
* Langium doesn't report errors for unresolved MultiReference items by default,
|
|
21
|
+
* so we need custom validation to catch these cases.
|
|
22
|
+
*
|
|
23
|
+
* @param map - The context map to validate
|
|
24
|
+
* @param accept - The validation acceptor for reporting issues
|
|
25
|
+
*/
|
|
26
|
+
function validateContextMapReferences(map, accept) {
|
|
27
|
+
if (!map.boundedContexts)
|
|
28
|
+
return;
|
|
29
|
+
for (const multiRef of map.boundedContexts) {
|
|
30
|
+
// A MultiReference has a $refText (the source text) and items (resolved refs)
|
|
31
|
+
// If $refText exists but items is empty, the reference didn't resolve
|
|
32
|
+
const refText = multiRef.$refText;
|
|
33
|
+
if (refText && multiRef.items.length === 0) {
|
|
34
|
+
accept('error', ValidationMessages.UNRESOLVED_REFERENCE('BoundedContext', refText), {
|
|
35
|
+
node: map,
|
|
36
|
+
// Find the CST node for this specific reference
|
|
37
|
+
property: 'boundedContexts',
|
|
38
|
+
index: map.boundedContexts.indexOf(multiRef),
|
|
39
|
+
code: IssueCodes.UnresolvedReference
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
18
44
|
/**
|
|
19
45
|
* Validates that a context map has at least one relationship if it contains multiple contexts.
|
|
20
46
|
* Multiple unrelated contexts should have documented relationships.
|
|
@@ -50,11 +76,34 @@ function validateDomainMapHasDomains(map, accept) {
|
|
|
50
76
|
});
|
|
51
77
|
}
|
|
52
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Validates that MultiReference items in a domain map resolve.
|
|
81
|
+
*
|
|
82
|
+
* @param map - The domain map to validate
|
|
83
|
+
* @param accept - The validation acceptor for reporting issues
|
|
84
|
+
*/
|
|
85
|
+
function validateDomainMapReferences(map, accept) {
|
|
86
|
+
if (!map.domains)
|
|
87
|
+
return;
|
|
88
|
+
for (const multiRef of map.domains) {
|
|
89
|
+
const refText = multiRef.$refText;
|
|
90
|
+
if (refText && multiRef.items.length === 0) {
|
|
91
|
+
accept('error', ValidationMessages.UNRESOLVED_REFERENCE('Domain', refText), {
|
|
92
|
+
node: map,
|
|
93
|
+
property: 'domains',
|
|
94
|
+
index: map.domains.indexOf(multiRef),
|
|
95
|
+
code: IssueCodes.UnresolvedReference
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
53
100
|
export const contextMapChecks = [
|
|
54
101
|
validateContextMapHasContexts,
|
|
102
|
+
validateContextMapReferences,
|
|
55
103
|
validateContextMapHasRelationships
|
|
56
104
|
];
|
|
57
105
|
export const domainMapChecks = [
|
|
58
|
-
validateDomainMapHasDomains
|
|
106
|
+
validateDomainMapHasDomains,
|
|
107
|
+
validateDomainMapReferences
|
|
59
108
|
];
|
|
60
109
|
//# sourceMappingURL=maps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maps.js","sourceRoot":"","sources":["../../src/validation/maps.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"maps.js","sourceRoot":"","sources":["../../src/validation/maps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@domainlang/language",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"displayName": "DomainLang Language",
|
|
5
5
|
"description": "Core language library for DomainLang - parse, validate, and query Domain-Driven Design models programmatically",
|
|
6
6
|
"author": "larsbaunwall",
|
|
@@ -11,6 +11,7 @@ import { DomainLangGeneratedModule, DomainLangGeneratedSharedModule } from './ge
|
|
|
11
11
|
import { registerValidationChecks } from './validation/domain-lang-validator.js';
|
|
12
12
|
import { QualifiedNameProvider } from './lsp/domain-lang-naming.js';
|
|
13
13
|
import { DomainLangScopeComputation } from './lsp/domain-lang-scope.js';
|
|
14
|
+
import { DomainLangScopeProvider } from './lsp/domain-lang-scope-provider.js';
|
|
14
15
|
import { DomainLangFormatter } from './lsp/domain-lang-formatter.js';
|
|
15
16
|
import { DomainLangHoverProvider } from './lsp/hover/domain-lang-hover.js';
|
|
16
17
|
import { DomainLangCompletionProvider } from './lsp/domain-lang-completion.js';
|
|
@@ -64,6 +65,7 @@ export const DomainLangModule: Module<DomainLangServices, PartialLangiumServices
|
|
|
64
65
|
},
|
|
65
66
|
references: {
|
|
66
67
|
ScopeComputation: (services) => new DomainLangScopeComputation(services),
|
|
68
|
+
ScopeProvider: (services) => new DomainLangScopeProvider(services),
|
|
67
69
|
QualifiedNameProvider: () => new QualifiedNameProvider()
|
|
68
70
|
},
|
|
69
71
|
lsp: {
|
|
@@ -17,6 +17,7 @@ import type { Model } from '../generated/ast.js';
|
|
|
17
17
|
* **How it works:**
|
|
18
18
|
* - When a document is indexed, we ensure all its imports are also loaded
|
|
19
19
|
* - Maintains a reverse dependency graph: importedUri → Set<importingUri>
|
|
20
|
+
* - Also tracks import specifiers to detect when file moves affect resolution
|
|
20
21
|
* - Overrides `isAffected()` to also check this graph
|
|
21
22
|
* - This integrates with Langium's native `DocumentBuilder.update()` flow
|
|
22
23
|
*
|
|
@@ -35,6 +36,14 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
35
36
|
*/
|
|
36
37
|
private readonly importDependencies = new Map<string, Set<string>>();
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Maps document URI to its import specifiers and their resolved URIs.
|
|
41
|
+
* Used to detect when file moves could affect import resolution.
|
|
42
|
+
* Key: importing document URI
|
|
43
|
+
* Value: Map of import specifier → resolved URI
|
|
44
|
+
*/
|
|
45
|
+
private readonly documentImportSpecifiers = new Map<string, Map<string, string>>();
|
|
46
|
+
|
|
38
47
|
/**
|
|
39
48
|
* Tracks documents that have had their imports loaded to avoid redundant work.
|
|
40
49
|
* Cleared on workspace config changes.
|
|
@@ -115,7 +124,9 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
115
124
|
|
|
116
125
|
/**
|
|
117
126
|
* Tracks import dependencies for a document.
|
|
118
|
-
* For each import in the document, records
|
|
127
|
+
* For each import in the document, records:
|
|
128
|
+
* 1. That the imported URI is depended upon (for direct change detection)
|
|
129
|
+
* 2. The import specifier used (for file move detection)
|
|
119
130
|
*/
|
|
120
131
|
private async trackImportDependencies(document: LangiumDocument): Promise<void> {
|
|
121
132
|
const importingUri = document.uri.toString();
|
|
@@ -123,6 +134,7 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
123
134
|
// First, remove old dependencies from this document
|
|
124
135
|
// (in case imports changed)
|
|
125
136
|
this.removeDocumentFromDependencies(importingUri);
|
|
137
|
+
this.documentImportSpecifiers.delete(importingUri);
|
|
126
138
|
|
|
127
139
|
// Skip if document isn't ready (no parse result)
|
|
128
140
|
if (document.state < DocumentState.Parsed) {
|
|
@@ -134,6 +146,8 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
134
146
|
return;
|
|
135
147
|
}
|
|
136
148
|
|
|
149
|
+
const specifierMap = new Map<string, string>();
|
|
150
|
+
|
|
137
151
|
for (const imp of model.imports) {
|
|
138
152
|
if (!imp.uri) continue;
|
|
139
153
|
|
|
@@ -141,6 +155,9 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
141
155
|
const resolvedUri = await resolveImportPath(document, imp.uri);
|
|
142
156
|
const importedUri = resolvedUri.toString();
|
|
143
157
|
|
|
158
|
+
// Track the specifier → resolved URI mapping
|
|
159
|
+
specifierMap.set(imp.uri, importedUri);
|
|
160
|
+
|
|
144
161
|
// Add to reverse dependency graph: importedUri → importingUri
|
|
145
162
|
let dependents = this.importDependencies.get(importedUri);
|
|
146
163
|
if (!dependents) {
|
|
@@ -149,9 +166,14 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
149
166
|
}
|
|
150
167
|
dependents.add(importingUri);
|
|
151
168
|
} catch {
|
|
152
|
-
// Import resolution failed -
|
|
169
|
+
// Import resolution failed - still track the specifier with empty resolution
|
|
170
|
+
specifierMap.set(imp.uri, '');
|
|
153
171
|
}
|
|
154
172
|
}
|
|
173
|
+
|
|
174
|
+
if (specifierMap.size > 0) {
|
|
175
|
+
this.documentImportSpecifiers.set(importingUri, specifierMap);
|
|
176
|
+
}
|
|
155
177
|
}
|
|
156
178
|
|
|
157
179
|
/**
|
|
@@ -199,8 +221,10 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
199
221
|
// Load or create the imported document
|
|
200
222
|
const importedDoc = await langiumDocuments.getOrCreateDocument(resolvedUri);
|
|
201
223
|
|
|
202
|
-
// If document is
|
|
203
|
-
|
|
224
|
+
// If document is not yet validated, add to batch for building
|
|
225
|
+
// This ensures all imported documents reach Validated state,
|
|
226
|
+
// preventing "workspace state is already Validated" errors
|
|
227
|
+
if (importedDoc.state < DocumentState.Validated) {
|
|
204
228
|
newDocs.push(importedDoc);
|
|
205
229
|
}
|
|
206
230
|
} catch {
|
|
@@ -208,7 +232,7 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
208
232
|
}
|
|
209
233
|
}
|
|
210
234
|
|
|
211
|
-
// Build any newly discovered documents
|
|
235
|
+
// Build any newly discovered documents to Validated state
|
|
212
236
|
// This triggers indexing which will recursively load their imports
|
|
213
237
|
if (newDocs.length > 0) {
|
|
214
238
|
await documentBuilder.build(newDocs, { validation: true });
|
|
@@ -243,6 +267,7 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
243
267
|
*/
|
|
244
268
|
clearImportDependencies(): void {
|
|
245
269
|
this.importDependencies.clear();
|
|
270
|
+
this.documentImportSpecifiers.clear();
|
|
246
271
|
this.importsLoaded.clear();
|
|
247
272
|
}
|
|
248
273
|
|
|
@@ -253,4 +278,170 @@ export class DomainLangIndexManager extends DefaultIndexManager {
|
|
|
253
278
|
markForReprocessing(uri: string): void {
|
|
254
279
|
this.importsLoaded.delete(uri);
|
|
255
280
|
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Gets all documents that import the given URI.
|
|
284
|
+
* Used to find documents that need rebuilding when a file changes.
|
|
285
|
+
*
|
|
286
|
+
* @param uri - The URI of the changed/deleted file
|
|
287
|
+
* @returns Set of URIs (as strings) of documents that import this file
|
|
288
|
+
*/
|
|
289
|
+
getDependentDocuments(uri: string): Set<string> {
|
|
290
|
+
return this.importDependencies.get(uri) ?? new Set();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Gets the resolved import URIs for a document.
|
|
295
|
+
* Returns only URIs where import resolution succeeded (non-empty resolved URI).
|
|
296
|
+
*
|
|
297
|
+
* @param documentUri - The URI of the document
|
|
298
|
+
* @returns Set of resolved import URIs, or empty set if none
|
|
299
|
+
*/
|
|
300
|
+
getResolvedImports(documentUri: string): Set<string> {
|
|
301
|
+
const specifierMap = this.documentImportSpecifiers.get(documentUri);
|
|
302
|
+
if (!specifierMap) {
|
|
303
|
+
return new Set();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const resolved = new Set<string>();
|
|
307
|
+
for (const resolvedUri of specifierMap.values()) {
|
|
308
|
+
// Only include successfully resolved imports (non-empty string)
|
|
309
|
+
if (resolvedUri) {
|
|
310
|
+
resolved.add(resolvedUri);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return resolved;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Gets all documents that would be affected by changes to the given URIs.
|
|
318
|
+
* This includes direct dependents and transitive dependents.
|
|
319
|
+
*
|
|
320
|
+
* @param changedUris - URIs of changed/deleted files
|
|
321
|
+
* @returns Set of all affected document URIs
|
|
322
|
+
*/
|
|
323
|
+
getAllAffectedDocuments(changedUris: Iterable<string>): Set<string> {
|
|
324
|
+
const affected = new Set<string>();
|
|
325
|
+
const toProcess = [...changedUris];
|
|
326
|
+
|
|
327
|
+
while (toProcess.length > 0) {
|
|
328
|
+
const uri = toProcess.pop();
|
|
329
|
+
if (!uri) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const dependents = this.importDependencies.get(uri);
|
|
333
|
+
if (dependents) {
|
|
334
|
+
for (const dep of dependents) {
|
|
335
|
+
if (!affected.has(dep)) {
|
|
336
|
+
affected.add(dep);
|
|
337
|
+
// Also check transitive dependents
|
|
338
|
+
toProcess.push(dep);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return affected;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Gets documents that have import specifiers which might be affected by file moves.
|
|
349
|
+
*
|
|
350
|
+
* When a file is moved/renamed, import specifiers that previously resolved to it
|
|
351
|
+
* (or could now resolve to it) need to be re-evaluated. This method finds documents
|
|
352
|
+
* whose imports might resolve differently after the file system change.
|
|
353
|
+
*
|
|
354
|
+
* @param changedUris - URIs of changed/deleted/created files
|
|
355
|
+
* @returns Set of document URIs that should be rebuilt
|
|
356
|
+
*/
|
|
357
|
+
getDocumentsWithPotentiallyAffectedImports(changedUris: Iterable<string>): Set<string> {
|
|
358
|
+
const changedPaths = this.extractPathSegments(changedUris);
|
|
359
|
+
return this.findDocumentsMatchingPaths(changedPaths);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Extracts path segments from URIs for fuzzy matching.
|
|
364
|
+
*/
|
|
365
|
+
private extractPathSegments(uris: Iterable<string>): Set<string> {
|
|
366
|
+
const paths = new Set<string>();
|
|
367
|
+
|
|
368
|
+
for (const uri of uris) {
|
|
369
|
+
this.addPathSegmentsFromUri(uri, paths);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return paths;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Adds path segments from a single URI to the set.
|
|
377
|
+
*/
|
|
378
|
+
private addPathSegmentsFromUri(uri: string, paths: Set<string>): void {
|
|
379
|
+
try {
|
|
380
|
+
const url = new URL(uri);
|
|
381
|
+
const pathParts = url.pathname.split('/').filter(p => p.length > 0);
|
|
382
|
+
|
|
383
|
+
// Add filename
|
|
384
|
+
const fileName = pathParts.at(-1);
|
|
385
|
+
if (fileName) {
|
|
386
|
+
paths.add(fileName);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Add parent/filename combination
|
|
390
|
+
if (pathParts.length >= 2) {
|
|
391
|
+
paths.add(pathParts.slice(-2).join('/'));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Add grandparent/parent/filename combination
|
|
395
|
+
if (pathParts.length >= 3) {
|
|
396
|
+
paths.add(pathParts.slice(-3).join('/'));
|
|
397
|
+
}
|
|
398
|
+
} catch {
|
|
399
|
+
// Invalid URI, skip
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Finds documents with import specifiers matching any of the given paths.
|
|
405
|
+
*/
|
|
406
|
+
private findDocumentsMatchingPaths(changedPaths: Set<string>): Set<string> {
|
|
407
|
+
const affected = new Set<string>();
|
|
408
|
+
|
|
409
|
+
for (const [docUri, specifierMap] of this.documentImportSpecifiers) {
|
|
410
|
+
if (this.hasMatchingSpecifierOrResolvedUri(specifierMap, changedPaths)) {
|
|
411
|
+
affected.add(docUri);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return affected;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Checks if any specifier OR its resolved URI matches the changed paths.
|
|
420
|
+
*
|
|
421
|
+
* This handles both regular imports and path aliases:
|
|
422
|
+
* - Regular: `./domains/sales.dlang` matches path `sales.dlang`
|
|
423
|
+
* - Aliased: `@domains/sales.dlang` resolves to `/full/path/domains/sales.dlang`
|
|
424
|
+
* When the file moves, the resolved URI matches but the specifier doesn't
|
|
425
|
+
*
|
|
426
|
+
* We check both to ensure moves of aliased imports trigger revalidation.
|
|
427
|
+
*/
|
|
428
|
+
private hasMatchingSpecifierOrResolvedUri(specifierMap: Map<string, string>, changedPaths: Set<string>): boolean {
|
|
429
|
+
for (const [specifier, resolvedUri] of specifierMap.entries()) {
|
|
430
|
+
const normalizedSpecifier = specifier.replace(/^[.@/]+/, '');
|
|
431
|
+
|
|
432
|
+
for (const changedPath of changedPaths) {
|
|
433
|
+
// Check the raw specifier (handles relative imports)
|
|
434
|
+
if (specifier.includes(changedPath) || changedPath.endsWith(normalizedSpecifier)) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Check the resolved URI (handles path aliases like @domains/...)
|
|
439
|
+
// The resolved URI contains the full file path which matches moved files
|
|
440
|
+
if (resolvedUri?.includes(changedPath)) {
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
256
447
|
}
|