@loj-lang/rdsl-compiler 0.5.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 +23 -0
- package/dist/cache-signature.d.ts +2 -0
- package/dist/cache-signature.d.ts.map +1 -0
- package/dist/cache-signature.js +17 -0
- package/dist/cache-signature.js.map +1 -0
- package/dist/codegen.d.ts +37 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +6394 -0
- package/dist/codegen.js.map +1 -0
- package/dist/dependency-graph.d.ts +23 -0
- package/dist/dependency-graph.d.ts.map +1 -0
- package/dist/dependency-graph.js +516 -0
- package/dist/dependency-graph.js.map +1 -0
- package/dist/expr.d.ts +24 -0
- package/dist/expr.d.ts.map +1 -0
- package/dist/expr.js +359 -0
- package/dist/expr.js.map +1 -0
- package/dist/flow-proof.d.ts +68 -0
- package/dist/flow-proof.d.ts.map +1 -0
- package/dist/flow-proof.js +487 -0
- package/dist/flow-proof.js.map +1 -0
- package/dist/host-files.d.ts +27 -0
- package/dist/host-files.d.ts.map +1 -0
- package/dist/host-files.js +441 -0
- package/dist/host-files.js.map +1 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +948 -0
- package/dist/index.js.map +1 -0
- package/dist/ir.d.ts +451 -0
- package/dist/ir.d.ts.map +1 -0
- package/dist/ir.js +13 -0
- package/dist/ir.js.map +1 -0
- package/dist/manifest.d.ts +104 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +635 -0
- package/dist/manifest.js.map +1 -0
- package/dist/node-inspect.d.ts +23 -0
- package/dist/node-inspect.d.ts.map +1 -0
- package/dist/node-inspect.js +475 -0
- package/dist/node-inspect.js.map +1 -0
- package/dist/normalize.d.ts +101 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +1771 -0
- package/dist/normalize.js.map +1 -0
- package/dist/page-table-block.d.ts +69 -0
- package/dist/page-table-block.d.ts.map +1 -0
- package/dist/page-table-block.js +241 -0
- package/dist/page-table-block.js.map +1 -0
- package/dist/parser.d.ts +262 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +1335 -0
- package/dist/parser.js.map +1 -0
- package/dist/project-paths.d.ts +6 -0
- package/dist/project-paths.d.ts.map +1 -0
- package/dist/project-paths.js +84 -0
- package/dist/project-paths.js.map +1 -0
- package/dist/relation-projection.d.ts +60 -0
- package/dist/relation-projection.d.ts.map +1 -0
- package/dist/relation-projection.js +121 -0
- package/dist/relation-projection.js.map +1 -0
- package/dist/rules-proof.d.ts +95 -0
- package/dist/rules-proof.d.ts.map +1 -0
- package/dist/rules-proof.js +537 -0
- package/dist/rules-proof.js.map +1 -0
- package/dist/source-files.d.ts +9 -0
- package/dist/source-files.d.ts.map +1 -0
- package/dist/source-files.js +27 -0
- package/dist/source-files.js.map +1 -0
- package/dist/style-proof.d.ts +70 -0
- package/dist/style-proof.d.ts.map +1 -0
- package/dist/style-proof.js +640 -0
- package/dist/style-proof.js.map +1 -0
- package/dist/validator.d.ts +51 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +2487 -0
- package/dist/validator.js.map +1 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactDSL Compiler — Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Pipeline: Source → Parse → Normalize → Validate → Generate → Manifest
|
|
5
|
+
*/
|
|
6
|
+
import { parse } from './parser.js';
|
|
7
|
+
import { normalize } from './normalize.js';
|
|
8
|
+
import { validate } from './validator.js';
|
|
9
|
+
import { generate } from './codegen.js';
|
|
10
|
+
import { buildDependencyGraph, diffDependencyGraphs } from './dependency-graph.js';
|
|
11
|
+
import { buildManifestArtifacts } from './manifest.js';
|
|
12
|
+
import { dirnameProjectPath, normalizeProjectPath, resolveProjectPath, } from './project-paths.js';
|
|
13
|
+
import { CANONICAL_RDSL_SOURCE_SUFFIX, describeRdslSourceSuffixes, isRdslImportPath, isRdslSourceFile, } from './source-files.js';
|
|
14
|
+
export { CANONICAL_RDSL_SOURCE_SUFFIX, LEGACY_RDSL_SOURCE_SUFFIX, RDSL_SOURCE_SUFFIXES, describeRdslSourceSuffixes, isRdslImportPath, isRdslSourceFile, stripRdslSourceSuffix, } from './source-files.js';
|
|
15
|
+
const projectCacheInternals = new WeakMap();
|
|
16
|
+
const PROJECT_CACHE_SNAPSHOT_VERSION = '0.1.1';
|
|
17
|
+
const COMPILE_RESULT_SIGNATURE_VERSION = '0.1.1';
|
|
18
|
+
export function compile(source, fileName = `app${CANONICAL_RDSL_SOURCE_SUFFIX}`) {
|
|
19
|
+
const entryFile = normalizeProjectPath(fileName);
|
|
20
|
+
return compileProject({
|
|
21
|
+
entryFile,
|
|
22
|
+
readFile(requestedFile) {
|
|
23
|
+
if (normalizeProjectPath(requestedFile) !== entryFile) {
|
|
24
|
+
throw new Error(`Imported file not available in single-file compile: ${requestedFile}`);
|
|
25
|
+
}
|
|
26
|
+
return source;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export { buildRulesManifestFileName, countRulesEntries, compileRulesSource, isRulesSourceFile, } from './rules-proof.js';
|
|
31
|
+
export { buildStyleManifestFileName, compileStyleSource, emitReactStyleCss, isStyleSourceFile, } from './style-proof.js';
|
|
32
|
+
export function compileProject(options) {
|
|
33
|
+
if (options.cache && options.changedFiles) {
|
|
34
|
+
invalidateProjectCache(options.cache, options.changedFiles);
|
|
35
|
+
}
|
|
36
|
+
const loaded = loadProject(options);
|
|
37
|
+
if (loaded.errors.length > 0) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
files: [],
|
|
41
|
+
errors: loaded.errors,
|
|
42
|
+
warnings: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const signature = options.cache
|
|
46
|
+
? createProjectSignature(options.cache, loaded)
|
|
47
|
+
: null;
|
|
48
|
+
const hasExternalInvalidations = options.cache
|
|
49
|
+
? hasNonDslInvalidations(options.cache, loaded)
|
|
50
|
+
: false;
|
|
51
|
+
const cachedResult = signature && options.cache
|
|
52
|
+
&& !hasExternalInvalidations
|
|
53
|
+
? getCachedCompileResult(options.cache, loaded.entryFile, signature)
|
|
54
|
+
: undefined;
|
|
55
|
+
if (cachedResult) {
|
|
56
|
+
clearResolvedInvalidations(options.cache, loaded);
|
|
57
|
+
return cachedResult;
|
|
58
|
+
}
|
|
59
|
+
const cachedCodegen = options.cache
|
|
60
|
+
&& !hasExternalInvalidations
|
|
61
|
+
? getCodegenCacheSnapshot(options.cache, loaded.entryFile)
|
|
62
|
+
: undefined;
|
|
63
|
+
const cachedDependencyGraph = options.cache
|
|
64
|
+
&& !hasExternalInvalidations
|
|
65
|
+
? getDependencyGraphSnapshot(options.cache, loaded.entryFile)
|
|
66
|
+
: undefined;
|
|
67
|
+
const cachedManifest = options.cache
|
|
68
|
+
&& !hasExternalInvalidations
|
|
69
|
+
? getManifestCacheSnapshot(options.cache, loaded.entryFile)
|
|
70
|
+
: undefined;
|
|
71
|
+
const cachedNormalize = options.cache
|
|
72
|
+
&& !hasExternalInvalidations
|
|
73
|
+
? getNormalizeCacheSnapshot(options.cache, loaded.entryFile)
|
|
74
|
+
: undefined;
|
|
75
|
+
const cachedValidate = options.cache
|
|
76
|
+
&& !hasExternalInvalidations
|
|
77
|
+
? getValidateCacheSnapshot(options.cache, loaded.entryFile)
|
|
78
|
+
: undefined;
|
|
79
|
+
const compiled = compileAst(loaded.ast, loaded.entryFile, options.projectRoot ? normalizeProjectPath(options.projectRoot) : dirnameProjectPath(loaded.entryFile), loaded.sourceFiles, loaded.moduleGraph, options.readFile, cachedNormalize, cachedValidate, cachedCodegen, cachedDependencyGraph, cachedManifest);
|
|
80
|
+
cacheDependencyGraphSnapshot(options.cache, loaded.entryFile, compiled.dependencyGraphSnapshot);
|
|
81
|
+
cacheNormalizeSnapshot(options.cache, loaded.entryFile, compiled.normalizeCacheSnapshot);
|
|
82
|
+
cacheValidateSnapshot(options.cache, loaded.entryFile, compiled.validationCacheSnapshot);
|
|
83
|
+
cacheCodegenSnapshot(options.cache, loaded.entryFile, compiled.codegenCacheSnapshot);
|
|
84
|
+
cacheManifestSnapshot(options.cache, loaded.entryFile, compiled.manifestCacheSnapshot);
|
|
85
|
+
cacheCompileResult(options.cache, loaded.entryFile, signature, compiled.result);
|
|
86
|
+
clearResolvedInvalidations(options.cache, loaded);
|
|
87
|
+
return compiled.result;
|
|
88
|
+
}
|
|
89
|
+
function compileAst(ast, entryFile, projectRoot, sourceFiles, moduleGraph, readFile, normalizeCache, validationCache, codegenCache, dependencyGraphCache, manifestCache) {
|
|
90
|
+
const errors = [];
|
|
91
|
+
const warnings = [];
|
|
92
|
+
// Phase 1: Normalize (Raw AST → IR)
|
|
93
|
+
const { ir, errors: normalizeErrors, cacheSnapshot: nextNormalizeCache, } = normalize(ast, {
|
|
94
|
+
cache: normalizeCache,
|
|
95
|
+
projectRoot,
|
|
96
|
+
readFile(fileName) {
|
|
97
|
+
try {
|
|
98
|
+
return readFile(fileName);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
for (const err of normalizeErrors) {
|
|
106
|
+
errors.push({ phase: 'normalize', message: err.message, nodeId: err.nodeId });
|
|
107
|
+
}
|
|
108
|
+
const dependencyGraph = buildDependencyGraph(ir);
|
|
109
|
+
const dependencyDiff = diffDependencyGraphs(dependencyGraphCache, dependencyGraph);
|
|
110
|
+
// Phase 2: Validate (semantic checks)
|
|
111
|
+
const { errors: validationErrors, cacheSnapshot: nextValidationCache, } = validate(ir, {
|
|
112
|
+
cache: validationCache,
|
|
113
|
+
affectedNodeIds: dependencyDiff.affectedNodeIds,
|
|
114
|
+
});
|
|
115
|
+
for (const err of validationErrors) {
|
|
116
|
+
if (err.severity === 'error') {
|
|
117
|
+
errors.push({ phase: 'validate', message: err.message, nodeId: err.nodeId });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
warnings.push({ phase: 'validate', message: err.message, nodeId: err.nodeId });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (errors.length > 0) {
|
|
124
|
+
return {
|
|
125
|
+
result: { success: false, ir, files: [], errors, warnings },
|
|
126
|
+
dependencyGraphSnapshot: dependencyGraph,
|
|
127
|
+
normalizeCacheSnapshot: nextNormalizeCache,
|
|
128
|
+
validationCacheSnapshot: nextValidationCache,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Phase 3: Code Generation
|
|
132
|
+
const { files, cacheSnapshot } = generate(ir, {
|
|
133
|
+
cache: codegenCache,
|
|
134
|
+
affectedNodeIds: dependencyDiff.affectedNodeIds,
|
|
135
|
+
});
|
|
136
|
+
const manifests = buildManifestArtifacts(ir, files, entryFile, sourceFiles, moduleGraph, {
|
|
137
|
+
cache: manifestCache,
|
|
138
|
+
dependencyGraph,
|
|
139
|
+
affectedRootIds: dependencyDiff.affectedRootIds,
|
|
140
|
+
readHostFile(path) {
|
|
141
|
+
try {
|
|
142
|
+
return readFile(path);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
try {
|
|
146
|
+
return readFile(resolveProjectPath(dirnameProjectPath(entryFile), path));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const semanticManifest = manifests.semanticManifest;
|
|
155
|
+
const traceManifest = manifests.traceManifest;
|
|
156
|
+
const manifest = manifests.manifestJson;
|
|
157
|
+
return {
|
|
158
|
+
result: {
|
|
159
|
+
success: true,
|
|
160
|
+
ir,
|
|
161
|
+
files,
|
|
162
|
+
errors,
|
|
163
|
+
warnings,
|
|
164
|
+
semanticManifest,
|
|
165
|
+
traceManifest,
|
|
166
|
+
manifest,
|
|
167
|
+
},
|
|
168
|
+
dependencyGraphSnapshot: dependencyGraph,
|
|
169
|
+
normalizeCacheSnapshot: nextNormalizeCache,
|
|
170
|
+
validationCacheSnapshot: nextValidationCache,
|
|
171
|
+
codegenCacheSnapshot: cacheSnapshot,
|
|
172
|
+
manifestCacheSnapshot: manifests.cacheSnapshot,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export function createProjectCache() {
|
|
176
|
+
const cache = {
|
|
177
|
+
files: new Map(),
|
|
178
|
+
graphs: new Map(),
|
|
179
|
+
invalidatedFiles: new Set(),
|
|
180
|
+
};
|
|
181
|
+
projectCacheInternals.set(cache, {
|
|
182
|
+
entries: new Map(),
|
|
183
|
+
results: new Map(),
|
|
184
|
+
dependency: new Map(),
|
|
185
|
+
normalize: new Map(),
|
|
186
|
+
validate: new Map(),
|
|
187
|
+
codegen: new Map(),
|
|
188
|
+
manifest: new Map(),
|
|
189
|
+
coldEntries: new Set(),
|
|
190
|
+
});
|
|
191
|
+
return cache;
|
|
192
|
+
}
|
|
193
|
+
export function serializeProjectCache(cache) {
|
|
194
|
+
const internals = getProjectCacheInternals(cache);
|
|
195
|
+
return {
|
|
196
|
+
version: PROJECT_CACHE_SNAPSHOT_VERSION,
|
|
197
|
+
files: Object.fromEntries(cache.files.entries()),
|
|
198
|
+
graphs: Object.fromEntries(cache.graphs.entries()),
|
|
199
|
+
invalidatedFiles: Array.from(cache.invalidatedFiles),
|
|
200
|
+
internals: {
|
|
201
|
+
entries: Object.fromEntries(Array.from(internals.entries.entries()).map(([entryFile, graph]) => [
|
|
202
|
+
entryFile,
|
|
203
|
+
serializeCachedProjectGraph(graph),
|
|
204
|
+
])),
|
|
205
|
+
results: Object.fromEntries(internals.results.entries()),
|
|
206
|
+
dependency: Object.fromEntries(internals.dependency.entries()),
|
|
207
|
+
normalize: Object.fromEntries(internals.normalize.entries()),
|
|
208
|
+
validate: Object.fromEntries(internals.validate.entries()),
|
|
209
|
+
codegen: Object.fromEntries(internals.codegen.entries()),
|
|
210
|
+
manifest: Object.fromEntries(internals.manifest.entries()),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export function restoreProjectCache(snapshot, options = {}) {
|
|
215
|
+
const cache = createProjectCache();
|
|
216
|
+
if (snapshot.version !== PROJECT_CACHE_SNAPSHOT_VERSION) {
|
|
217
|
+
return cache;
|
|
218
|
+
}
|
|
219
|
+
const revalidateSources = options.revalidateSources !== false;
|
|
220
|
+
const internals = getProjectCacheInternals(cache);
|
|
221
|
+
const fileEntries = Object.entries(snapshot.files);
|
|
222
|
+
cache.files = new Map(fileEntries.map(([fileName, unit]) => [normalizeProjectPath(fileName), unit]));
|
|
223
|
+
cache.graphs = new Map(Object.entries(snapshot.graphs).map(([entryFile, graph]) => [normalizeProjectPath(entryFile), graph]));
|
|
224
|
+
cache.invalidatedFiles = new Set(revalidateSources
|
|
225
|
+
? fileEntries.map(([fileName]) => normalizeProjectPath(fileName))
|
|
226
|
+
: snapshot.invalidatedFiles.map((fileName) => normalizeProjectPath(fileName)));
|
|
227
|
+
internals.entries = new Map(Object.entries(snapshot.internals.entries).map(([entryFile, graph]) => [
|
|
228
|
+
normalizeProjectPath(entryFile),
|
|
229
|
+
deserializeCachedProjectGraph(graph),
|
|
230
|
+
]));
|
|
231
|
+
internals.results = new Map(Object.entries(snapshot.internals.results).map(([entryFile, result]) => [
|
|
232
|
+
normalizeProjectPath(entryFile),
|
|
233
|
+
result,
|
|
234
|
+
]));
|
|
235
|
+
internals.dependency = new Map(Object.entries(snapshot.internals.dependency).map(([entryFile, dependency]) => [
|
|
236
|
+
normalizeProjectPath(entryFile),
|
|
237
|
+
dependency,
|
|
238
|
+
]));
|
|
239
|
+
internals.normalize = new Map(Object.entries(snapshot.internals.normalize).map(([entryFile, normalizeCache]) => [
|
|
240
|
+
normalizeProjectPath(entryFile),
|
|
241
|
+
normalizeCache,
|
|
242
|
+
]));
|
|
243
|
+
internals.validate = new Map(Object.entries(snapshot.internals.validate).map(([entryFile, validateCache]) => [
|
|
244
|
+
normalizeProjectPath(entryFile),
|
|
245
|
+
validateCache,
|
|
246
|
+
]));
|
|
247
|
+
internals.codegen = new Map(Object.entries(snapshot.internals.codegen).map(([entryFile, codegenCache]) => [
|
|
248
|
+
normalizeProjectPath(entryFile),
|
|
249
|
+
codegenCache,
|
|
250
|
+
]));
|
|
251
|
+
internals.manifest = new Map(Object.entries(snapshot.internals.manifest).map(([entryFile, manifestCache]) => [
|
|
252
|
+
normalizeProjectPath(entryFile),
|
|
253
|
+
manifestCache,
|
|
254
|
+
]));
|
|
255
|
+
internals.coldEntries = new Set(revalidateSources
|
|
256
|
+
? Object.keys(snapshot.internals.entries).map((entryFile) => normalizeProjectPath(entryFile))
|
|
257
|
+
: []);
|
|
258
|
+
return cache;
|
|
259
|
+
}
|
|
260
|
+
export function invalidateProjectCache(cache, fileNames) {
|
|
261
|
+
if (!fileNames) {
|
|
262
|
+
cache.files.clear();
|
|
263
|
+
cache.graphs.clear();
|
|
264
|
+
cache.invalidatedFiles.clear();
|
|
265
|
+
const internals = getProjectCacheInternals(cache);
|
|
266
|
+
internals.entries.clear();
|
|
267
|
+
internals.results.clear();
|
|
268
|
+
internals.dependency.clear();
|
|
269
|
+
internals.normalize.clear();
|
|
270
|
+
internals.validate.clear();
|
|
271
|
+
internals.codegen.clear();
|
|
272
|
+
internals.manifest.clear();
|
|
273
|
+
internals.coldEntries.clear();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
for (const fileName of fileNames) {
|
|
277
|
+
cache.invalidatedFiles.add(normalizeProjectPath(fileName));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function loadProject(options) {
|
|
281
|
+
const entryFile = normalizeProjectPath(options.entryFile);
|
|
282
|
+
const cachedGraph = options.cache ? getCachedProjectGraph(options.cache, entryFile) : undefined;
|
|
283
|
+
if (cachedGraph) {
|
|
284
|
+
const cachedLoad = loadProjectFromGraphCache(cachedGraph, options);
|
|
285
|
+
if (cachedLoad) {
|
|
286
|
+
return cachedLoad;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const loaded = loadProjectFresh(entryFile, options, cachedGraph);
|
|
290
|
+
cacheLoadedProject(options.cache, loaded);
|
|
291
|
+
return loaded;
|
|
292
|
+
}
|
|
293
|
+
function loadProjectFresh(entryFile, options, previousGraph) {
|
|
294
|
+
const errors = [];
|
|
295
|
+
const moduleGraph = {};
|
|
296
|
+
const sourceUnits = new Map();
|
|
297
|
+
const sourceFiles = [];
|
|
298
|
+
const scanDirectories = new Set();
|
|
299
|
+
const discovered = new Set();
|
|
300
|
+
const visiting = [];
|
|
301
|
+
const visited = new Set();
|
|
302
|
+
visitProjectFile(entryFile, entryFile, options, errors, sourceUnits, moduleGraph, sourceFiles, scanDirectories, discovered, visiting, visited);
|
|
303
|
+
return finalizeLoadedProject(entryFile, sourceFiles, Array.from(scanDirectories).sort((left, right) => left.localeCompare(right)), moduleGraph, sourceUnits, errors, previousGraph);
|
|
304
|
+
}
|
|
305
|
+
function loadProjectFromGraphCache(cachedGraph, options) {
|
|
306
|
+
const cache = options.cache;
|
|
307
|
+
if (!cache) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
if (getProjectCacheInternals(cache).coldEntries.has(cachedGraph.entryFile)) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
if (cache.invalidatedFiles.has(cachedGraph.entryFile)) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
const changedScannedFiles = Array.from(cache.invalidatedFiles).filter((fileName) => (!cachedGraph.sourceFiles.includes(fileName) &&
|
|
317
|
+
isPathWithinScanDirectories(fileName, cachedGraph.scanDirectories)));
|
|
318
|
+
if (changedScannedFiles.length > 0) {
|
|
319
|
+
const loaded = loadProjectFresh(cachedGraph.entryFile, options, cachedGraph);
|
|
320
|
+
cacheLoadedProject(cache, loaded);
|
|
321
|
+
return loaded;
|
|
322
|
+
}
|
|
323
|
+
const changedProjectFiles = cachedGraph.sourceFiles.filter((fileName) => cache.invalidatedFiles.has(fileName));
|
|
324
|
+
if (changedProjectFiles.length === 0) {
|
|
325
|
+
return materializeLoadedProject(cachedGraph);
|
|
326
|
+
}
|
|
327
|
+
const errors = [];
|
|
328
|
+
const sourceUnits = new Map(cachedGraph.sourceUnits);
|
|
329
|
+
for (const fileName of changedProjectFiles) {
|
|
330
|
+
const ast = readAndParseFile(fileName, options.readFile, errors, cache, findImportChain(cachedGraph.moduleGraph, cachedGraph.entryFile, fileName));
|
|
331
|
+
if (!ast) {
|
|
332
|
+
sourceUnits.delete(fileName);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (fileName !== cachedGraph.entryFile) {
|
|
336
|
+
validateModuleAst(fileName, ast, errors, findImportChain(cachedGraph.moduleGraph, cachedGraph.entryFile, fileName));
|
|
337
|
+
}
|
|
338
|
+
sourceUnits.set(fileName, ast);
|
|
339
|
+
}
|
|
340
|
+
const importsChanged = errors.length === 0 && changedProjectFiles.some((fileName) => (didFileImportsChange(fileName, sourceUnits.get(fileName) ?? null, cachedGraph, options.listFiles, errors)));
|
|
341
|
+
if (errors.length === 0 && (cachedGraph.hasErrors || importsChanged)) {
|
|
342
|
+
const loaded = loadProjectFresh(cachedGraph.entryFile, options, cachedGraph);
|
|
343
|
+
cacheLoadedProject(cache, loaded);
|
|
344
|
+
return loaded;
|
|
345
|
+
}
|
|
346
|
+
if (errors.length === 0 &&
|
|
347
|
+
!cachedGraph.hasErrors &&
|
|
348
|
+
changedProjectFiles.every((fileName) => sourceUnits.get(fileName) === cachedGraph.sourceUnits.get(fileName))) {
|
|
349
|
+
return materializeLoadedProject(cachedGraph);
|
|
350
|
+
}
|
|
351
|
+
const loaded = finalizeLoadedProject(cachedGraph.entryFile, cachedGraph.sourceFiles, cachedGraph.scanDirectories, cachedGraph.moduleGraph, sourceUnits, errors, cachedGraph);
|
|
352
|
+
cacheLoadedProject(cache, loaded);
|
|
353
|
+
return loaded;
|
|
354
|
+
}
|
|
355
|
+
function finalizeLoadedProject(entryFile, sourceFiles, scanDirectories, moduleGraph, sourceUnits, errors, previousGraph) {
|
|
356
|
+
const normalizedSourceFiles = [...sourceFiles];
|
|
357
|
+
const normalizedScanDirectories = [...scanDirectories];
|
|
358
|
+
const normalizedModuleGraph = cloneModuleGraph(moduleGraph);
|
|
359
|
+
const clonedErrors = errors.map(cloneCompileError);
|
|
360
|
+
return {
|
|
361
|
+
ast: clonedErrors.length > 0
|
|
362
|
+
? createEmptyAst()
|
|
363
|
+
: mergeRawAsts(entryFile, normalizedSourceFiles, sourceUnits, previousGraph),
|
|
364
|
+
entryFile,
|
|
365
|
+
sourceFiles: normalizedSourceFiles,
|
|
366
|
+
scanDirectories: normalizedScanDirectories,
|
|
367
|
+
moduleGraph: normalizedModuleGraph,
|
|
368
|
+
errors: clonedErrors,
|
|
369
|
+
sourceUnits: new Map(sourceUnits),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function materializeLoadedProject(cachedGraph) {
|
|
373
|
+
return {
|
|
374
|
+
ast: cachedGraph.ast,
|
|
375
|
+
entryFile: cachedGraph.entryFile,
|
|
376
|
+
sourceFiles: [...cachedGraph.sourceFiles],
|
|
377
|
+
scanDirectories: [...cachedGraph.scanDirectories],
|
|
378
|
+
moduleGraph: cloneModuleGraph(cachedGraph.moduleGraph),
|
|
379
|
+
errors: cachedGraph.errors.map(cloneCompileError),
|
|
380
|
+
sourceUnits: new Map(cachedGraph.sourceUnits),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function cacheLoadedProject(cache, loaded) {
|
|
384
|
+
if (!cache)
|
|
385
|
+
return;
|
|
386
|
+
const cachedGraph = {
|
|
387
|
+
entryFile: loaded.entryFile,
|
|
388
|
+
sourceFiles: [...loaded.sourceFiles],
|
|
389
|
+
scanDirectories: [...loaded.scanDirectories],
|
|
390
|
+
moduleGraph: cloneModuleGraph(loaded.moduleGraph),
|
|
391
|
+
hasErrors: loaded.errors.length > 0,
|
|
392
|
+
ast: loaded.ast,
|
|
393
|
+
errors: loaded.errors.map(cloneCompileError),
|
|
394
|
+
sourceUnits: new Map(loaded.sourceUnits),
|
|
395
|
+
};
|
|
396
|
+
getProjectCacheInternals(cache).entries.set(loaded.entryFile, cachedGraph);
|
|
397
|
+
getProjectCacheInternals(cache).coldEntries.delete(loaded.entryFile);
|
|
398
|
+
cache.graphs.set(loaded.entryFile, {
|
|
399
|
+
entryFile: cachedGraph.entryFile,
|
|
400
|
+
sourceFiles: [...cachedGraph.sourceFiles],
|
|
401
|
+
scanDirectories: [...cachedGraph.scanDirectories],
|
|
402
|
+
moduleGraph: cloneModuleGraph(cachedGraph.moduleGraph),
|
|
403
|
+
hasErrors: cachedGraph.hasErrors,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function getCachedProjectGraph(cache, entryFile) {
|
|
407
|
+
return getProjectCacheInternals(cache).entries.get(entryFile);
|
|
408
|
+
}
|
|
409
|
+
function getProjectCacheInternals(cache) {
|
|
410
|
+
let internals = projectCacheInternals.get(cache);
|
|
411
|
+
if (!internals) {
|
|
412
|
+
internals = {
|
|
413
|
+
entries: new Map(),
|
|
414
|
+
results: new Map(),
|
|
415
|
+
dependency: new Map(),
|
|
416
|
+
normalize: new Map(),
|
|
417
|
+
validate: new Map(),
|
|
418
|
+
codegen: new Map(),
|
|
419
|
+
manifest: new Map(),
|
|
420
|
+
coldEntries: new Set(),
|
|
421
|
+
};
|
|
422
|
+
projectCacheInternals.set(cache, internals);
|
|
423
|
+
}
|
|
424
|
+
return internals;
|
|
425
|
+
}
|
|
426
|
+
function getCachedCompileResult(cache, entryFile, signature) {
|
|
427
|
+
const snapshot = getProjectCacheInternals(cache).results.get(entryFile);
|
|
428
|
+
if (!snapshot || snapshot.signature !== signature) {
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
return snapshot.result;
|
|
432
|
+
}
|
|
433
|
+
function cacheCompileResult(cache, entryFile, signature, result) {
|
|
434
|
+
if (!cache || !signature)
|
|
435
|
+
return;
|
|
436
|
+
getProjectCacheInternals(cache).results.set(entryFile, {
|
|
437
|
+
signature,
|
|
438
|
+
result,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function getNormalizeCacheSnapshot(cache, entryFile) {
|
|
442
|
+
return getProjectCacheInternals(cache).normalize.get(entryFile);
|
|
443
|
+
}
|
|
444
|
+
function getDependencyGraphSnapshot(cache, entryFile) {
|
|
445
|
+
return getProjectCacheInternals(cache).dependency.get(entryFile);
|
|
446
|
+
}
|
|
447
|
+
function cacheDependencyGraphSnapshot(cache, entryFile, snapshot) {
|
|
448
|
+
if (!cache || !snapshot)
|
|
449
|
+
return;
|
|
450
|
+
getProjectCacheInternals(cache).dependency.set(entryFile, snapshot);
|
|
451
|
+
}
|
|
452
|
+
function cacheNormalizeSnapshot(cache, entryFile, snapshot) {
|
|
453
|
+
if (!cache || !snapshot)
|
|
454
|
+
return;
|
|
455
|
+
getProjectCacheInternals(cache).normalize.set(entryFile, snapshot);
|
|
456
|
+
}
|
|
457
|
+
function getValidateCacheSnapshot(cache, entryFile) {
|
|
458
|
+
return getProjectCacheInternals(cache).validate.get(entryFile);
|
|
459
|
+
}
|
|
460
|
+
function cacheValidateSnapshot(cache, entryFile, snapshot) {
|
|
461
|
+
if (!cache || !snapshot)
|
|
462
|
+
return;
|
|
463
|
+
getProjectCacheInternals(cache).validate.set(entryFile, snapshot);
|
|
464
|
+
}
|
|
465
|
+
function getCodegenCacheSnapshot(cache, entryFile) {
|
|
466
|
+
return getProjectCacheInternals(cache).codegen.get(entryFile);
|
|
467
|
+
}
|
|
468
|
+
function cacheCodegenSnapshot(cache, entryFile, snapshot) {
|
|
469
|
+
if (!cache || !snapshot)
|
|
470
|
+
return;
|
|
471
|
+
getProjectCacheInternals(cache).codegen.set(entryFile, snapshot);
|
|
472
|
+
}
|
|
473
|
+
function getManifestCacheSnapshot(cache, entryFile) {
|
|
474
|
+
return getProjectCacheInternals(cache).manifest.get(entryFile);
|
|
475
|
+
}
|
|
476
|
+
function cacheManifestSnapshot(cache, entryFile, snapshot) {
|
|
477
|
+
if (!cache || !snapshot)
|
|
478
|
+
return;
|
|
479
|
+
getProjectCacheInternals(cache).manifest.set(entryFile, snapshot);
|
|
480
|
+
}
|
|
481
|
+
function createProjectSignature(cache, loaded) {
|
|
482
|
+
const parts = [COMPILE_RESULT_SIGNATURE_VERSION, loaded.entryFile];
|
|
483
|
+
for (const fileName of loaded.sourceFiles) {
|
|
484
|
+
const unit = cache.files.get(fileName);
|
|
485
|
+
if (!unit) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
parts.push(fileName, hashSourceText(unit.sourceText));
|
|
489
|
+
}
|
|
490
|
+
for (const directory of loaded.scanDirectories) {
|
|
491
|
+
parts.push(`dir:${directory}`);
|
|
492
|
+
}
|
|
493
|
+
const graphEntries = Object.entries(loaded.moduleGraph)
|
|
494
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
495
|
+
.map(([fileName, imports]) => `${fileName}->${imports.join(',')}`);
|
|
496
|
+
parts.push(...graphEntries);
|
|
497
|
+
return parts.join('|');
|
|
498
|
+
}
|
|
499
|
+
function hasNonDslInvalidations(cache, loaded) {
|
|
500
|
+
return Array.from(cache.invalidatedFiles).some((fileName) => (!loaded.sourceFiles.includes(fileName) &&
|
|
501
|
+
!isPathWithinScanDirectories(fileName, loaded.scanDirectories)));
|
|
502
|
+
}
|
|
503
|
+
function clearResolvedInvalidations(cache, loaded) {
|
|
504
|
+
if (!cache) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
for (const fileName of Array.from(cache.invalidatedFiles)) {
|
|
508
|
+
if (loaded.sourceFiles.includes(fileName) ||
|
|
509
|
+
isPathWithinScanDirectories(fileName, loaded.scanDirectories)) {
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
cache.invalidatedFiles.delete(fileName);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function hashSourceText(sourceText) {
|
|
516
|
+
let hash = 2166136261;
|
|
517
|
+
for (let index = 0; index < sourceText.length; index += 1) {
|
|
518
|
+
hash ^= sourceText.charCodeAt(index);
|
|
519
|
+
hash = Math.imul(hash, 16777619);
|
|
520
|
+
}
|
|
521
|
+
return `${sourceText.length}:${hash >>> 0}`;
|
|
522
|
+
}
|
|
523
|
+
function cloneModuleGraph(moduleGraph) {
|
|
524
|
+
const clone = {};
|
|
525
|
+
for (const [fileName, imports] of Object.entries(moduleGraph)) {
|
|
526
|
+
clone[fileName] = [...imports];
|
|
527
|
+
}
|
|
528
|
+
return clone;
|
|
529
|
+
}
|
|
530
|
+
function cloneCompileError(error) {
|
|
531
|
+
return { ...error };
|
|
532
|
+
}
|
|
533
|
+
function serializeCachedProjectGraph(graph) {
|
|
534
|
+
return {
|
|
535
|
+
entryFile: graph.entryFile,
|
|
536
|
+
sourceFiles: [...graph.sourceFiles],
|
|
537
|
+
scanDirectories: [...graph.scanDirectories],
|
|
538
|
+
moduleGraph: cloneModuleGraph(graph.moduleGraph),
|
|
539
|
+
hasErrors: graph.hasErrors,
|
|
540
|
+
ast: graph.ast,
|
|
541
|
+
errors: graph.errors.map(cloneCompileError),
|
|
542
|
+
sourceUnits: Object.fromEntries(graph.sourceUnits.entries()),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function deserializeCachedProjectGraph(graph) {
|
|
546
|
+
return {
|
|
547
|
+
entryFile: normalizeProjectPath(graph.entryFile),
|
|
548
|
+
sourceFiles: graph.sourceFiles.map((fileName) => normalizeProjectPath(fileName)),
|
|
549
|
+
scanDirectories: graph.scanDirectories.map((directory) => normalizeProjectPath(directory)),
|
|
550
|
+
moduleGraph: cloneModuleGraph(graph.moduleGraph),
|
|
551
|
+
hasErrors: graph.hasErrors,
|
|
552
|
+
ast: graph.ast,
|
|
553
|
+
errors: graph.errors.map(cloneCompileError),
|
|
554
|
+
sourceUnits: new Map(Object.entries(graph.sourceUnits).map(([fileName, ast]) => [normalizeProjectPath(fileName), ast])),
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function readAndParseFile(fileName, readFile, errors, cache, importChain) {
|
|
558
|
+
const normalizedFile = normalizeProjectPath(fileName);
|
|
559
|
+
const previous = cache?.files.get(normalizedFile);
|
|
560
|
+
const cached = cache && !cache.invalidatedFiles.has(normalizedFile)
|
|
561
|
+
? cache.files.get(normalizedFile)
|
|
562
|
+
: undefined;
|
|
563
|
+
if (cached) {
|
|
564
|
+
for (const error of cached.errors) {
|
|
565
|
+
errors.push({ ...error });
|
|
566
|
+
}
|
|
567
|
+
return cached.ast;
|
|
568
|
+
}
|
|
569
|
+
let source;
|
|
570
|
+
try {
|
|
571
|
+
source = readFile(normalizedFile);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
const unit = {
|
|
575
|
+
ast: null,
|
|
576
|
+
errors: [{
|
|
577
|
+
phase: 'parse',
|
|
578
|
+
message: appendImportChain(`Failed to read ${normalizedFile}: ${error instanceof Error ? error.message : String(error)}`, importChain),
|
|
579
|
+
file: normalizedFile,
|
|
580
|
+
}],
|
|
581
|
+
sourceText: '',
|
|
582
|
+
};
|
|
583
|
+
if (cache) {
|
|
584
|
+
cache.files.set(normalizedFile, unit);
|
|
585
|
+
cache.invalidatedFiles.delete(normalizedFile);
|
|
586
|
+
}
|
|
587
|
+
for (const issue of unit.errors) {
|
|
588
|
+
errors.push({ ...issue });
|
|
589
|
+
}
|
|
590
|
+
return unit.ast;
|
|
591
|
+
}
|
|
592
|
+
if (previous && previous.sourceText === source) {
|
|
593
|
+
if (cache) {
|
|
594
|
+
cache.invalidatedFiles.delete(normalizedFile);
|
|
595
|
+
}
|
|
596
|
+
for (const error of previous.errors) {
|
|
597
|
+
errors.push({ ...error });
|
|
598
|
+
}
|
|
599
|
+
return previous.ast;
|
|
600
|
+
}
|
|
601
|
+
const unit = parseProjectUnit(normalizedFile, source, importChain);
|
|
602
|
+
if (cache) {
|
|
603
|
+
cache.files.set(normalizedFile, unit);
|
|
604
|
+
cache.invalidatedFiles.delete(normalizedFile);
|
|
605
|
+
}
|
|
606
|
+
for (const error of unit.errors) {
|
|
607
|
+
errors.push({ ...error });
|
|
608
|
+
}
|
|
609
|
+
return unit.ast;
|
|
610
|
+
}
|
|
611
|
+
function parseProjectUnit(fileName, source, importChain) {
|
|
612
|
+
const { ast, errors: parseErrors } = parse(source, fileName);
|
|
613
|
+
const unitErrors = [];
|
|
614
|
+
for (const err of parseErrors) {
|
|
615
|
+
unitErrors.push({
|
|
616
|
+
phase: 'parse',
|
|
617
|
+
message: appendImportChain(err.message, importChain),
|
|
618
|
+
file: fileName,
|
|
619
|
+
line: err.line,
|
|
620
|
+
col: err.col,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
ast: parseErrors.length === 0 ? ast : null,
|
|
625
|
+
errors: unitErrors,
|
|
626
|
+
sourceText: source,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function validateModuleAst(fileName, ast, errors, importChain) {
|
|
630
|
+
if (ast.app) {
|
|
631
|
+
errors.push({
|
|
632
|
+
phase: 'validate',
|
|
633
|
+
message: appendImportChain(`Module file must not contain app: ${fileName}`, importChain),
|
|
634
|
+
file: fileName,
|
|
635
|
+
line: ast.app.sourceSpan?.startLine,
|
|
636
|
+
col: ast.app.sourceSpan?.startCol,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
if (ast.compiler) {
|
|
640
|
+
errors.push({
|
|
641
|
+
phase: 'validate',
|
|
642
|
+
message: appendImportChain(`Module file must not contain compiler: ${fileName}`, importChain),
|
|
643
|
+
file: fileName,
|
|
644
|
+
line: ast.compiler.sourceSpan?.startLine,
|
|
645
|
+
col: ast.compiler.sourceSpan?.startCol,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function resolveImports(fileName, imports, listFiles, errors, importChain) {
|
|
650
|
+
const seen = new Set();
|
|
651
|
+
const resolvedImports = [];
|
|
652
|
+
const scanDirectories = new Set();
|
|
653
|
+
for (const rawImport of imports) {
|
|
654
|
+
if (!rawImport.startsWith('./') && !rawImport.startsWith('../')) {
|
|
655
|
+
errors.push({
|
|
656
|
+
phase: 'parse',
|
|
657
|
+
message: appendImportChain(`Import path "${rawImport}" must be relative (starting with ./ or ../)`, importChain),
|
|
658
|
+
file: fileName,
|
|
659
|
+
});
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (!isRdslImportPath(rawImport)) {
|
|
663
|
+
errors.push({
|
|
664
|
+
phase: 'parse',
|
|
665
|
+
message: appendImportChain(`Import path "${rawImport}" must reference a ${describeRdslSourceSuffixes()} file or a directory ending with /`, importChain),
|
|
666
|
+
file: fileName,
|
|
667
|
+
});
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (rawImport.endsWith('/')) {
|
|
671
|
+
const directory = resolveImportPath(fileName, rawImport);
|
|
672
|
+
const expandedFiles = expandDirectoryImport(fileName, rawImport, directory, listFiles, errors, importChain);
|
|
673
|
+
scanDirectories.add(directory);
|
|
674
|
+
for (const resolved of expandedFiles) {
|
|
675
|
+
if (resolved === fileName) {
|
|
676
|
+
errors.push({
|
|
677
|
+
phase: 'validate',
|
|
678
|
+
message: appendImportChain(`File cannot import itself through directory "${rawImport}"`, importChain),
|
|
679
|
+
file: fileName,
|
|
680
|
+
});
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
if (seen.has(resolved)) {
|
|
684
|
+
errors.push({
|
|
685
|
+
phase: 'validate',
|
|
686
|
+
message: appendImportChain(`Duplicate imported path "${rawImport}" resolves to "${resolved}"`, importChain),
|
|
687
|
+
file: fileName,
|
|
688
|
+
});
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
seen.add(resolved);
|
|
692
|
+
resolvedImports.push(resolved);
|
|
693
|
+
}
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const resolved = resolveImportPath(fileName, rawImport);
|
|
697
|
+
if (resolved === fileName) {
|
|
698
|
+
errors.push({
|
|
699
|
+
phase: 'validate',
|
|
700
|
+
message: appendImportChain(`File cannot import itself: ${rawImport}`, importChain),
|
|
701
|
+
file: fileName,
|
|
702
|
+
});
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (seen.has(resolved)) {
|
|
706
|
+
errors.push({
|
|
707
|
+
phase: 'validate',
|
|
708
|
+
message: appendImportChain(`Duplicate imported path "${rawImport}" resolves to "${resolved}"`, importChain),
|
|
709
|
+
file: fileName,
|
|
710
|
+
});
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
seen.add(resolved);
|
|
714
|
+
resolvedImports.push(resolved);
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
files: resolvedImports,
|
|
718
|
+
scanDirectories: Array.from(scanDirectories).sort((left, right) => left.localeCompare(right)),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function expandDirectoryImport(fileName, rawImport, directory, listFiles, errors, importChain) {
|
|
722
|
+
if (!listFiles) {
|
|
723
|
+
errors.push({
|
|
724
|
+
phase: 'parse',
|
|
725
|
+
message: appendImportChain(`Directory import "${rawImport}" requires listFiles support in the project loader`, importChain),
|
|
726
|
+
file: fileName,
|
|
727
|
+
});
|
|
728
|
+
return [];
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
return listFiles(directory)
|
|
732
|
+
.map((entry) => normalizeProjectPath(entry))
|
|
733
|
+
.filter((entry) => dirnameProjectPath(entry) === directory && isRdslSourceFile(entry))
|
|
734
|
+
.sort((left, right) => left.localeCompare(right));
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
errors.push({
|
|
738
|
+
phase: 'parse',
|
|
739
|
+
message: appendImportChain(`Failed to read import directory "${rawImport}": ${error instanceof Error ? error.message : String(error)}`, importChain),
|
|
740
|
+
file: fileName,
|
|
741
|
+
});
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function visitProjectFile(fileName, entryFile, options, errors, sourceUnits, moduleGraph, sourceFiles, scanDirectories, discovered, visiting, visited) {
|
|
746
|
+
const normalizedFile = normalizeProjectPath(fileName);
|
|
747
|
+
if (!discovered.has(normalizedFile)) {
|
|
748
|
+
discovered.add(normalizedFile);
|
|
749
|
+
sourceFiles.push(normalizedFile);
|
|
750
|
+
}
|
|
751
|
+
if (visited.has(normalizedFile)) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
visiting.push(normalizedFile);
|
|
755
|
+
const importChain = [...visiting];
|
|
756
|
+
const ast = readAndParseFile(normalizedFile, options.readFile, errors, options.cache, importChain);
|
|
757
|
+
if (!ast) {
|
|
758
|
+
moduleGraph[normalizedFile] ??= [];
|
|
759
|
+
visiting.pop();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (normalizedFile !== entryFile) {
|
|
763
|
+
validateModuleAst(normalizedFile, ast, errors, importChain);
|
|
764
|
+
}
|
|
765
|
+
sourceUnits.set(normalizedFile, ast);
|
|
766
|
+
const resolvedImports = resolveImports(normalizedFile, ast.imports, options.listFiles, errors, importChain);
|
|
767
|
+
for (const directory of resolvedImports.scanDirectories) {
|
|
768
|
+
scanDirectories.add(directory);
|
|
769
|
+
}
|
|
770
|
+
moduleGraph[normalizedFile] = resolvedImports.files;
|
|
771
|
+
for (const importedFile of resolvedImports.files) {
|
|
772
|
+
const cycleIndex = visiting.indexOf(importedFile);
|
|
773
|
+
if (cycleIndex >= 0) {
|
|
774
|
+
errors.push({
|
|
775
|
+
phase: 'validate',
|
|
776
|
+
message: `Import cycle detected: ${formatImportChain([
|
|
777
|
+
...visiting.slice(0, cycleIndex),
|
|
778
|
+
...visiting.slice(cycleIndex),
|
|
779
|
+
importedFile,
|
|
780
|
+
])}`,
|
|
781
|
+
file: normalizedFile,
|
|
782
|
+
});
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
visitProjectFile(importedFile, entryFile, options, errors, sourceUnits, moduleGraph, sourceFiles, scanDirectories, discovered, visiting, visited);
|
|
786
|
+
}
|
|
787
|
+
visiting.pop();
|
|
788
|
+
visited.add(normalizedFile);
|
|
789
|
+
}
|
|
790
|
+
function didFileImportsChange(fileName, ast, cachedGraph, listFiles, errors) {
|
|
791
|
+
if (!ast) {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
const previousImports = cachedGraph.moduleGraph[fileName] ?? [];
|
|
795
|
+
if (ast.imports.length === 0 && previousImports.length === 0) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
const errorCount = errors.length;
|
|
799
|
+
const nextImports = resolveImports(fileName, ast.imports, listFiles, errors, findImportChain(cachedGraph.moduleGraph, cachedGraph.entryFile, fileName));
|
|
800
|
+
if (errors.length > errorCount) {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
if (previousImports.length !== nextImports.files.length) {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
return previousImports.some((importPath, index) => importPath !== nextImports.files[index]);
|
|
807
|
+
}
|
|
808
|
+
function findImportChain(moduleGraph, entryFile, targetFile) {
|
|
809
|
+
if (entryFile === targetFile) {
|
|
810
|
+
return [entryFile];
|
|
811
|
+
}
|
|
812
|
+
const queue = [entryFile];
|
|
813
|
+
const parents = new Map([[entryFile, null]]);
|
|
814
|
+
while (queue.length > 0) {
|
|
815
|
+
const current = queue.shift();
|
|
816
|
+
for (const imported of moduleGraph[current] ?? []) {
|
|
817
|
+
if (parents.has(imported)) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
parents.set(imported, current);
|
|
821
|
+
if (imported === targetFile) {
|
|
822
|
+
return materializeImportChain(parents, targetFile);
|
|
823
|
+
}
|
|
824
|
+
queue.push(imported);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return [targetFile];
|
|
828
|
+
}
|
|
829
|
+
function materializeImportChain(parents, targetFile) {
|
|
830
|
+
const chain = [];
|
|
831
|
+
let current = targetFile;
|
|
832
|
+
while (current) {
|
|
833
|
+
chain.push(current);
|
|
834
|
+
current = parents.get(current);
|
|
835
|
+
}
|
|
836
|
+
return chain.reverse();
|
|
837
|
+
}
|
|
838
|
+
function appendImportChain(message, importChain) {
|
|
839
|
+
if (!importChain || importChain.length <= 1) {
|
|
840
|
+
return message;
|
|
841
|
+
}
|
|
842
|
+
return `${message} (import chain: ${formatImportChain(importChain)})`;
|
|
843
|
+
}
|
|
844
|
+
function formatImportChain(importChain) {
|
|
845
|
+
return importChain.join(' -> ');
|
|
846
|
+
}
|
|
847
|
+
function isPathWithinScanDirectories(fileName, scanDirectories) {
|
|
848
|
+
const normalizedFile = normalizeProjectPath(fileName);
|
|
849
|
+
return scanDirectories.some((directory) => (normalizedFile.startsWith(`${directory}/`) &&
|
|
850
|
+
isRdslSourceFile(normalizedFile)));
|
|
851
|
+
}
|
|
852
|
+
function mergeRawAsts(entryFile, sourceFiles, sourceUnits, previousGraph) {
|
|
853
|
+
const entryAst = sourceUnits.get(entryFile) ?? createEmptyAst();
|
|
854
|
+
const reusableGraph = previousGraph && !previousGraph.hasErrors ? previousGraph : undefined;
|
|
855
|
+
const previousAst = reusableGraph?.ast;
|
|
856
|
+
const previousEntryAst = reusableGraph?.sourceUnits.get(entryFile);
|
|
857
|
+
const app = previousAst && previousEntryAst?.app === entryAst.app
|
|
858
|
+
? previousAst.app
|
|
859
|
+
: entryAst.app;
|
|
860
|
+
const compiler = previousAst && previousEntryAst?.compiler === entryAst.compiler
|
|
861
|
+
? previousAst.compiler
|
|
862
|
+
: entryAst.compiler;
|
|
863
|
+
const imports = previousAst && previousEntryAst?.imports === entryAst.imports
|
|
864
|
+
? previousAst.imports
|
|
865
|
+
: [...entryAst.imports];
|
|
866
|
+
const models = previousAst && canReuseMergedRawAstCategory(sourceFiles, reusableGraph.sourceFiles, sourceUnits, reusableGraph.sourceUnits, 'models')
|
|
867
|
+
? previousAst.models
|
|
868
|
+
: mergeRawAstCategory(sourceFiles, sourceUnits, 'models');
|
|
869
|
+
const resources = previousAst && canReuseMergedRawAstCategory(sourceFiles, reusableGraph.sourceFiles, sourceUnits, reusableGraph.sourceUnits, 'resources')
|
|
870
|
+
? previousAst.resources
|
|
871
|
+
: mergeRawAstCategory(sourceFiles, sourceUnits, 'resources');
|
|
872
|
+
const readModels = previousAst && canReuseMergedRawAstCategory(sourceFiles, reusableGraph.sourceFiles, sourceUnits, reusableGraph.sourceUnits, 'readModels')
|
|
873
|
+
? previousAst.readModels
|
|
874
|
+
: mergeRawAstCategory(sourceFiles, sourceUnits, 'readModels');
|
|
875
|
+
const pages = previousAst && canReuseMergedRawAstCategory(sourceFiles, reusableGraph.sourceFiles, sourceUnits, reusableGraph.sourceUnits, 'pages')
|
|
876
|
+
? previousAst.pages
|
|
877
|
+
: mergeRawAstCategory(sourceFiles, sourceUnits, 'pages');
|
|
878
|
+
if (previousAst &&
|
|
879
|
+
previousAst.app === app &&
|
|
880
|
+
previousAst.compiler === compiler &&
|
|
881
|
+
previousAst.imports === imports &&
|
|
882
|
+
previousAst.models === models &&
|
|
883
|
+
previousAst.resources === resources &&
|
|
884
|
+
previousAst.readModels === readModels &&
|
|
885
|
+
previousAst.pages === pages) {
|
|
886
|
+
return previousAst;
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
app,
|
|
890
|
+
compiler,
|
|
891
|
+
imports,
|
|
892
|
+
models,
|
|
893
|
+
resources,
|
|
894
|
+
readModels,
|
|
895
|
+
pages,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function createEmptyAst() {
|
|
899
|
+
return {
|
|
900
|
+
imports: [],
|
|
901
|
+
models: [],
|
|
902
|
+
resources: [],
|
|
903
|
+
readModels: [],
|
|
904
|
+
pages: [],
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
function resolveImportPath(fromFile, rawImport) {
|
|
908
|
+
const baseDir = dirnameProjectPath(fromFile);
|
|
909
|
+
return resolveProjectPath(baseDir, rawImport);
|
|
910
|
+
}
|
|
911
|
+
function canReuseMergedRawAstCategory(sourceFiles, previousSourceFiles, sourceUnits, previousSourceUnits, key) {
|
|
912
|
+
const currentContributors = getRawAstContributors(sourceFiles, sourceUnits, key);
|
|
913
|
+
const previousContributors = getRawAstContributors(previousSourceFiles, previousSourceUnits, key);
|
|
914
|
+
if (currentContributors.length !== previousContributors.length) {
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
return currentContributors.every((fileName, index) => (fileName === previousContributors[index] &&
|
|
918
|
+
getRawAstCollection(sourceUnits.get(fileName), key) === getRawAstCollection(previousSourceUnits.get(fileName), key)));
|
|
919
|
+
}
|
|
920
|
+
function getRawAstContributors(sourceFiles, sourceUnits, key) {
|
|
921
|
+
return sourceFiles.filter((fileName) => (getRawAstCollection(sourceUnits.get(fileName), key)?.length ?? 0) > 0);
|
|
922
|
+
}
|
|
923
|
+
function mergeRawAstCategory(sourceFiles, sourceUnits, key) {
|
|
924
|
+
const merged = [];
|
|
925
|
+
for (const fileName of sourceFiles) {
|
|
926
|
+
const entries = getRawAstCollection(sourceUnits.get(fileName), key);
|
|
927
|
+
if (!entries || entries.length === 0)
|
|
928
|
+
continue;
|
|
929
|
+
merged.push(...entries);
|
|
930
|
+
}
|
|
931
|
+
return merged;
|
|
932
|
+
}
|
|
933
|
+
function getRawAstCollection(ast, key) {
|
|
934
|
+
if (!ast) {
|
|
935
|
+
return [];
|
|
936
|
+
}
|
|
937
|
+
return ast[key];
|
|
938
|
+
}
|
|
939
|
+
// Re-exports for direct usage
|
|
940
|
+
export { parse } from './parser.js';
|
|
941
|
+
export { normalize } from './normalize.js';
|
|
942
|
+
export { validate } from './validator.js';
|
|
943
|
+
export { generate } from './codegen.js';
|
|
944
|
+
export { parseExpr } from './expr.js';
|
|
945
|
+
export { buildSemanticManifest, buildTraceManifest, findTraceNode, listManifestHostFilesForNode, listTraceRegionsForNode, resolveTraceLocation, } from './manifest.js';
|
|
946
|
+
export { collectHostFiles, listHostFilesForNode, listMaterializedHostFiles } from './host-files.js';
|
|
947
|
+
export { inspectSemanticNode, semanticNodeInspectionToLines } from './node-inspect.js';
|
|
948
|
+
//# sourceMappingURL=index.js.map
|