1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.DependencyResolverFactory = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const inversify_1 = require("inversify");
|
6 | const ts = require("typescript");
|
7 | const tsutils_1 = require("tsutils");
|
8 | const utils_1 = require("../utils");
|
9 | const bind_decorator_1 = require("bind-decorator");
|
10 | const path = require("path");
|
11 | let DependencyResolverFactory = class DependencyResolverFactory {
|
12 | create(host, program) {
|
13 | return new DependencyResolverImpl(host, program);
|
14 | }
|
15 | };
|
16 | DependencyResolverFactory = tslib_1.__decorate([
|
17 | inversify_1.injectable()
|
18 | ], DependencyResolverFactory);
|
19 | exports.DependencyResolverFactory = DependencyResolverFactory;
|
20 | class DependencyResolverImpl {
|
21 | constructor(host, program) {
|
22 | var _a, _b;
|
23 | this.host = host;
|
24 | this.program = program;
|
25 | this.dependencies = new Map();
|
26 | this.fileToProjectReference = undefined;
|
27 | this.fileMetadata = new Map();
|
28 | this.compilerOptions = this.program.getCompilerOptions();
|
29 | this.useSourceOfProjectReferenceRedirect = ((_b = (_a = this.host).useSourceOfProjectReferenceRedirect) === null || _b === void 0 ? void 0 : _b.call(_a)) === true &&
|
30 | !this.compilerOptions.disableSourceOfProjectReferenceRedirect;
|
31 | this.state = undefined;
|
32 | }
|
33 | update(program, updatedFile) {
|
34 | this.state = undefined;
|
35 | this.dependencies.delete(updatedFile);
|
36 | this.fileMetadata.delete(updatedFile);
|
37 | this.program = program;
|
38 | }
|
39 | buildState() {
|
40 | const affectsGlobalScope = [];
|
41 | const ambientModules = new Map();
|
42 | const patternAmbientModules = new Map();
|
43 | const moduleAugmentationsTemp = new Map();
|
44 | for (const file of this.program.getSourceFiles()) {
|
45 | const meta = this.getFileMetaData(file.fileName);
|
46 | if (meta.affectsGlobalScope)
|
47 | affectsGlobalScope.push(file.fileName);
|
48 | for (const ambientModule of meta.ambientModules) {
|
49 | const map = meta.isExternalModule
|
50 | ? moduleAugmentationsTemp
|
51 | : ambientModule.includes('*')
|
52 | ? patternAmbientModules
|
53 | : ambientModules;
|
54 | addToList(map, ambientModule, file.fileName);
|
55 | }
|
56 | }
|
57 | const moduleAugmentations = new Map();
|
58 | for (const [module, files] of moduleAugmentationsTemp) {
|
59 |
|
60 | const ambientModuleAffectingFiles = ambientModules.get(module);
|
61 | if (ambientModuleAffectingFiles !== undefined) {
|
62 | ambientModuleAffectingFiles.push(...files);
|
63 | continue;
|
64 | }
|
65 | for (const file of files) {
|
66 | const resolved = this.getExternalReferences(file).get(module);
|
67 |
|
68 | if (resolved != null) {
|
69 | addToList(moduleAugmentations, resolved, file);
|
70 | }
|
71 | else {
|
72 |
|
73 | const matchingPattern = getBestMatchingPattern(module, patternAmbientModules.keys());
|
74 | if (matchingPattern !== undefined)
|
75 | addToList(patternAmbientModules, matchingPattern, file);
|
76 | }
|
77 | }
|
78 | }
|
79 | return {
|
80 | affectsGlobalScope,
|
81 | ambientModules,
|
82 | moduleAugmentations,
|
83 | patternAmbientModules,
|
84 | };
|
85 | }
|
86 | getFilesAffectingGlobalScope() {
|
87 | var _a;
|
88 | return ((_a = this.state) !== null && _a !== void 0 ? _a : (this.state = this.buildState())).affectsGlobalScope;
|
89 | }
|
90 | getDependencies(file) {
|
91 | var _a;
|
92 | const result = new Map();
|
93 | if (this.program.getSourceFile(file) === undefined)
|
94 | return result;
|
95 | (_a = this.state) !== null && _a !== void 0 ? _a : (this.state = this.buildState());
|
96 | {
|
97 | const augmentations = this.state.moduleAugmentations.get(file);
|
98 | if (augmentations !== undefined)
|
99 | result.set('\0', augmentations);
|
100 | }
|
101 | for (const [identifier, resolved] of this.getExternalReferences(file)) {
|
102 | const filesAffectingAmbientModule = this.state.ambientModules.get(identifier);
|
103 | if (filesAffectingAmbientModule !== undefined) {
|
104 | result.set(identifier, filesAffectingAmbientModule);
|
105 | }
|
106 | else if (resolved !== null) {
|
107 | const list = [resolved];
|
108 | const augmentations = this.state.moduleAugmentations.get(resolved);
|
109 | if (augmentations !== undefined)
|
110 | list.push(...augmentations);
|
111 | result.set(identifier, list);
|
112 | }
|
113 | else {
|
114 | const pattern = getBestMatchingPattern(identifier, this.state.patternAmbientModules.keys());
|
115 | if (pattern !== undefined) {
|
116 | result.set(identifier, this.state.patternAmbientModules.get(pattern));
|
117 | }
|
118 | else {
|
119 | result.set(identifier, null);
|
120 | }
|
121 | }
|
122 | }
|
123 | const meta = this.fileMetadata.get(file);
|
124 | if (!meta.isExternalModule)
|
125 | for (const ambientModule of meta.ambientModules)
|
126 | result.set(ambientModule, this.state[ambientModule.includes('*') ? 'patternAmbientModules' : 'ambientModules'].get(ambientModule));
|
127 | return result;
|
128 | }
|
129 | getFileMetaData(fileName) {
|
130 | return utils_1.resolveCachedResult(this.fileMetadata, fileName, this.collectMetaDataForFile);
|
131 | }
|
132 | getExternalReferences(fileName) {
|
133 | return utils_1.resolveCachedResult(this.dependencies, fileName, this.collectExternalReferences);
|
134 | }
|
135 | collectMetaDataForFile(fileName) {
|
136 | return collectFileMetadata(this.program.getSourceFile(fileName));
|
137 | }
|
138 | collectExternalReferences(fileName) {
|
139 | var _a;
|
140 |
|
141 | const sourceFile = this.program.getSourceFile(fileName);
|
142 | const references = new Set(tsutils_1.findImports(sourceFile, tsutils_1.ImportKind.All, false).map(({ text }) => text));
|
143 | if (ts.isExternalModule(sourceFile))
|
144 | for (const augmentation of this.getFileMetaData(fileName).ambientModules)
|
145 | references.add(augmentation);
|
146 | const result = new Map();
|
147 | if (references.size === 0)
|
148 | return result;
|
149 | (_a = this.fileToProjectReference) !== null && _a !== void 0 ? _a : (this.fileToProjectReference = createProjectReferenceMap(this.program.getResolvedProjectReferences()));
|
150 | const arr = Array.from(references);
|
151 | const resolved = this.host.resolveModuleNames(arr, fileName, undefined, this.fileToProjectReference.get(fileName), this.compilerOptions);
|
152 | for (let i = 0; i < resolved.length; ++i) {
|
153 | const current = resolved[i];
|
154 | if (current === undefined) {
|
155 | result.set(arr[i], null);
|
156 | }
|
157 | else {
|
158 | const projectReference = this.useSourceOfProjectReferenceRedirect
|
159 | ? this.fileToProjectReference.get(current.resolvedFileName)
|
160 | : undefined;
|
161 | if (projectReference === undefined) {
|
162 | result.set(arr[i], current.resolvedFileName);
|
163 | }
|
164 | else if (projectReference.commandLine.options.outFile) {
|
165 |
|
166 | result.set(arr[i], projectReference.commandLine.fileNames[0]);
|
167 | }
|
168 | else {
|
169 | result.set(arr[i], getSourceOfProjectReferenceRedirect(current.resolvedFileName, projectReference));
|
170 | }
|
171 | }
|
172 | }
|
173 | return result;
|
174 | }
|
175 | }
|
176 | tslib_1.__decorate([
|
177 | bind_decorator_1.default,
|
178 | tslib_1.__metadata("design:type", Function),
|
179 | tslib_1.__metadata("design:paramtypes", [String]),
|
180 | tslib_1.__metadata("design:returntype", void 0)
|
181 | ], DependencyResolverImpl.prototype, "collectMetaDataForFile", null);
|
182 | tslib_1.__decorate([
|
183 | bind_decorator_1.default,
|
184 | tslib_1.__metadata("design:type", Function),
|
185 | tslib_1.__metadata("design:paramtypes", [String]),
|
186 | tslib_1.__metadata("design:returntype", Map)
|
187 | ], DependencyResolverImpl.prototype, "collectExternalReferences", null);
|
188 | function getBestMatchingPattern(moduleName, patternAmbientModules) {
|
189 |
|
190 | let longestMatchLength = -1;
|
191 | let longestMatch;
|
192 | for (const pattern of patternAmbientModules) {
|
193 | if (moduleName.length < pattern.length - 1)
|
194 | continue;
|
195 | const index = pattern.indexOf('*');
|
196 | if (index > longestMatchLength &&
|
197 | moduleName.startsWith(pattern.substring(0, index)) &&
|
198 | moduleName.endsWith(pattern.substring(index + 1))) {
|
199 | longestMatchLength = index;
|
200 | longestMatch = pattern;
|
201 | }
|
202 | }
|
203 | return longestMatch;
|
204 | }
|
205 | function createProjectReferenceMap(references) {
|
206 | const result = new Map();
|
207 | for (const ref of utils_1.iterateProjectReferences(references))
|
208 | for (const file of utils_1.getOutputFileNamesOfProjectReference(ref))
|
209 | result.set(file, ref);
|
210 | return result;
|
211 | }
|
212 | function addToList(map, key, value) {
|
213 | const arr = map.get(key);
|
214 | if (arr === undefined) {
|
215 | map.set(key, [value]);
|
216 | }
|
217 | else {
|
218 | arr.push(value);
|
219 | }
|
220 | }
|
221 | function collectFileMetadata(sourceFile) {
|
222 | let affectsGlobalScope;
|
223 | const ambientModules = new Set();
|
224 | const isExternalModule = ts.isExternalModule(sourceFile);
|
225 | for (const statement of sourceFile.statements) {
|
226 | if (statement.flags & ts.NodeFlags.GlobalAugmentation) {
|
227 | affectsGlobalScope = true;
|
228 | }
|
229 | else if (tsutils_1.isModuleDeclaration(statement) && statement.name.kind === ts.SyntaxKind.StringLiteral) {
|
230 | ambientModules.add(statement.name.text);
|
231 | if (!isExternalModule && !affectsGlobalScope && statement.body !== undefined) {
|
232 |
|
233 | for (const s of statement.body.statements) {
|
234 | if (s.flags & ts.NodeFlags.GlobalAugmentation) {
|
235 | affectsGlobalScope = true;
|
236 | break;
|
237 | }
|
238 | }
|
239 | }
|
240 | }
|
241 | else if (tsutils_1.isNamespaceExportDeclaration(statement)) {
|
242 | affectsGlobalScope = true;
|
243 | }
|
244 | else if (affectsGlobalScope === undefined) {
|
245 | affectsGlobalScope = !isExternalModule;
|
246 | }
|
247 | }
|
248 | return { ambientModules, isExternalModule, affectsGlobalScope: affectsGlobalScope === true };
|
249 | }
|
250 | function getSourceOfProjectReferenceRedirect(outputFileName, ref) {
|
251 | const options = ref.commandLine.options;
|
252 | const projectDirectory = path.dirname(ref.sourceFile.fileName);
|
253 | const origin = utils_1.unixifyPath(path.resolve(options.rootDir || projectDirectory, path.relative(options.declarationDir || options.outDir || projectDirectory, outputFileName.slice(0, -5))));
|
254 | for (const extension of ['.ts', '.tsx', '.js', '.jsx']) {
|
255 | const name = origin + extension;
|
256 | if (ref.commandLine.fileNames.includes(name))
|
257 | return name;
|
258 | }
|
259 |
|
260 | return outputFileName;
|
261 | }
|
262 |
|
\ | No newline at end of file |