UNPKG

15.3 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright Google Inc. All Rights Reserved.
4 *
5 * Use of this source code is governed by an MIT-style license that can be
6 * found in the LICENSE file at https://angular.io/license
7 */
8"use strict";
9var compiler_1 = require('@angular/compiler');
10var tsc_wrapped_1 = require('@angular/tsc-wrapped');
11var fs = require('fs');
12var path = require('path');
13var ts = require('typescript');
14var static_reflector_1 = require('./static_reflector');
15var EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
16var DTS = /\.d\.ts$/;
17var NODE_MODULES = '/node_modules/';
18var IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
19var ReflectorHost = (function () {
20 function ReflectorHost(program, compilerHost, options, context) {
21 this.program = program;
22 this.compilerHost = compilerHost;
23 this.options = options;
24 this.metadataCollector = new tsc_wrapped_1.MetadataCollector();
25 this.typeCache = new Map();
26 this.resolverCache = new Map();
27 // normalize the path so that it never ends with '/'.
28 this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
29 this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
30 this.context = context || new NodeReflectorHostContext(compilerHost);
31 var genPath = path.relative(this.basePath, this.genDir);
32 this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
33 }
34 ReflectorHost.prototype.angularImportLocations = function () {
35 return {
36 coreDecorators: '@angular/core/src/metadata',
37 diDecorators: '@angular/core/src/di/metadata',
38 diMetadata: '@angular/core/src/di/metadata',
39 diOpaqueToken: '@angular/core/src/di/opaque_token',
40 animationMetadata: '@angular/core/src/animation/metadata',
41 provider: '@angular/core/src/di/provider'
42 };
43 };
44 // We use absolute paths on disk as canonical.
45 ReflectorHost.prototype.getCanonicalFileName = function (fileName) { return fileName; };
46 ReflectorHost.prototype.resolve = function (m, containingFile) {
47 m = m.replace(EXT, '');
48 var resolved = ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
49 .resolvedModule;
50 return resolved ? resolved.resolvedFileName : null;
51 };
52 ;
53 ReflectorHost.prototype.normalizeAssetUrl = function (url) {
54 var assetUrl = compiler_1.AssetUrl.parse(url);
55 var path = assetUrl ? assetUrl.packageName + "/" + assetUrl.modulePath : null;
56 return this.getCanonicalFileName(path);
57 };
58 ReflectorHost.prototype.resolveAssetUrl = function (url, containingFile) {
59 var assetUrl = this.normalizeAssetUrl(url);
60 if (assetUrl) {
61 return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
62 }
63 return url;
64 };
65 /**
66 * We want a moduleId that will appear in import statements in the generated code.
67 * These need to be in a form that system.js can load, so absolute file paths don't work.
68 *
69 * The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
70 * `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
71 * existing file.
72 *
73 * | genDir | node_module | rootDir
74 * --------------+----------+-------------+----------
75 * generated | relative | relative | n/a
76 * existing file | n/a | absolute | relative(*)
77 *
78 * NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
79 */
80 ReflectorHost.prototype.getImportPath = function (containingFile, importedFile) {
81 importedFile = this.resolveAssetUrl(importedFile, containingFile);
82 containingFile = this.resolveAssetUrl(containingFile, '');
83 // If a file does not yet exist (because we compile it later), we still need to
84 // assume it exists it so that the `resolve` method works!
85 if (!this.compilerHost.fileExists(importedFile)) {
86 this.context.assumeFileExists(importedFile);
87 }
88 containingFile = this.rewriteGenDirPath(containingFile);
89 var containingDir = path.dirname(containingFile);
90 // drop extension
91 importedFile = importedFile.replace(EXT, '');
92 var nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
93 var importModule = nodeModulesIndex === -1 ?
94 null :
95 importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
96 var isGeneratedFile = IS_GENERATED.test(importedFile);
97 if (isGeneratedFile) {
98 // rewrite to genDir path
99 if (importModule) {
100 // it is generated, therefore we do a relative path to the factory
101 return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
102 }
103 else {
104 // assume that import is also in `genDir`
105 importedFile = this.rewriteGenDirPath(importedFile);
106 return this.dotRelative(containingDir, importedFile);
107 }
108 }
109 else {
110 // user code import
111 if (importModule) {
112 return importModule;
113 }
114 else {
115 if (!this.isGenDirChildOfRootDir) {
116 // assume that they are on top of each other.
117 importedFile = importedFile.replace(this.basePath, this.genDir);
118 }
119 return this.dotRelative(containingDir, importedFile);
120 }
121 }
122 };
123 ReflectorHost.prototype.dotRelative = function (from, to) {
124 var rPath = path.relative(from, to).replace(/\\/g, '/');
125 return rPath.startsWith('.') ? rPath : './' + rPath;
126 };
127 /**
128 * Moves the path into `genDir` folder while preserving the `node_modules` directory.
129 */
130 ReflectorHost.prototype.rewriteGenDirPath = function (filepath) {
131 var nodeModulesIndex = filepath.indexOf(NODE_MODULES);
132 if (nodeModulesIndex !== -1) {
133 // If we are in node_modulse, transplant them into `genDir`.
134 return path.join(this.genDir, filepath.substring(nodeModulesIndex));
135 }
136 else {
137 // pretend that containing file is on top of the `genDir` to normalize the paths.
138 // we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
139 return filepath.replace(this.basePath, this.genDir);
140 }
141 };
142 ReflectorHost.prototype.findDeclaration = function (module, symbolName, containingFile, containingModule) {
143 if (!containingFile || !containingFile.length) {
144 if (module.indexOf('.') === 0) {
145 throw new Error('Resolution of relative paths requires a containing file.');
146 }
147 // Any containing file gives the same result for absolute imports
148 containingFile = path.join(this.basePath, 'index.ts');
149 }
150 try {
151 var assetUrl = this.normalizeAssetUrl(module);
152 if (assetUrl) {
153 module = assetUrl;
154 }
155 var filePath = this.resolve(module, containingFile);
156 if (!filePath) {
157 // If the file cannot be found the module is probably referencing a declared module
158 // for which there is no disambiguating file and we also don't need to track
159 // re-exports. Just use the module name.
160 return this.getStaticSymbol(module, symbolName);
161 }
162 var tc = this.program.getTypeChecker();
163 var sf = this.program.getSourceFile(filePath);
164 if (!sf || !sf.symbol) {
165 // The source file was not needed in the compile but we do need the values from
166 // the corresponding .ts files stored in the .metadata.json file. Check the file
167 // for exports to see if the file is exported.
168 return this.resolveExportedSymbol(filePath, symbolName) ||
169 this.getStaticSymbol(filePath, symbolName);
170 }
171 var symbol = tc.getExportsOfModule(sf.symbol).find(function (m) { return m.name === symbolName; });
172 if (!symbol) {
173 throw new Error("can't find symbol " + symbolName + " exported from module " + filePath);
174 }
175 if (symbol &&
176 symbol.flags & ts.SymbolFlags.Alias) {
177 symbol = tc.getAliasedSymbol(symbol);
178 }
179 var declaration = symbol.getDeclarations()[0];
180 var declarationFile = this.getCanonicalFileName(declaration.getSourceFile().fileName);
181 return this.getStaticSymbol(declarationFile, symbol.getName());
182 }
183 catch (e) {
184 console.error("can't resolve module " + module + " from " + containingFile);
185 throw e;
186 }
187 };
188 /**
189 * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
190 * All types passed to the StaticResolver should be pseudo-types returned by this method.
191 *
192 * @param declarationFile the absolute path of the file where the symbol is declared
193 * @param name the name of the type.
194 */
195 ReflectorHost.prototype.getStaticSymbol = function (declarationFile, name, members) {
196 var memberSuffix = members ? "." + members.join('.') : '';
197 var key = "\"" + declarationFile + "\"." + name + memberSuffix;
198 var result = this.typeCache.get(key);
199 if (!result) {
200 result = new static_reflector_1.StaticSymbol(declarationFile, name, members);
201 this.typeCache.set(key, result);
202 }
203 return result;
204 };
205 ReflectorHost.prototype.getMetadataFor = function (filePath) {
206 if (!this.context.fileExists(filePath)) {
207 // If the file doesn't exists then we cannot return metadata for the file.
208 // This will occur if the user refernced a declared module for which no file
209 // exists for the module (i.e. jQuery or angularjs).
210 return;
211 }
212 if (DTS.test(filePath)) {
213 var metadataPath = filePath.replace(DTS, '.metadata.json');
214 if (this.context.fileExists(metadataPath)) {
215 var metadata = this.readMetadata(metadataPath);
216 return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
217 }
218 }
219 else {
220 var sf = this.program.getSourceFile(filePath);
221 if (!sf) {
222 if (this.context.fileExists(filePath)) {
223 var sourceText = this.context.readFile(filePath);
224 return this.metadataCollector.getMetadata(ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
225 }
226 throw new Error("Source file " + filePath + " not present in program.");
227 }
228 return this.metadataCollector.getMetadata(sf);
229 }
230 };
231 ReflectorHost.prototype.readMetadata = function (filePath) {
232 try {
233 return this.resolverCache.get(filePath) || JSON.parse(this.context.readFile(filePath));
234 }
235 catch (e) {
236 console.error("Failed to read JSON file " + filePath);
237 throw e;
238 }
239 };
240 ReflectorHost.prototype.getResolverMetadata = function (filePath) {
241 var metadata = this.resolverCache.get(filePath);
242 if (!metadata) {
243 metadata = this.getMetadataFor(filePath);
244 this.resolverCache.set(filePath, metadata);
245 }
246 return metadata;
247 };
248 ReflectorHost.prototype.resolveExportedSymbol = function (filePath, symbolName) {
249 var _this = this;
250 var resolveModule = function (moduleName) {
251 var resolvedModulePath = _this.getCanonicalFileName(_this.resolve(moduleName, filePath));
252 if (!resolvedModulePath) {
253 throw new Error("Could not resolve module '" + moduleName + "' relative to file " + filePath);
254 }
255 return resolvedModulePath;
256 };
257 var metadata = this.getResolverMetadata(filePath);
258 if (metadata) {
259 // If we have metadata for the symbol, this is the original exporting location.
260 if (metadata.metadata[symbolName]) {
261 return this.getStaticSymbol(filePath, symbolName);
262 }
263 // If no, try to find the symbol in one of the re-export location
264 if (metadata.exports) {
265 // Try and find the symbol in the list of explicitly re-exported symbols.
266 for (var _i = 0, _a = metadata.exports; _i < _a.length; _i++) {
267 var moduleExport = _a[_i];
268 if (moduleExport.export) {
269 var exportSymbol = moduleExport.export.find(function (symbol) {
270 if (typeof symbol === 'string') {
271 return symbol == symbolName;
272 }
273 else {
274 return symbol.as == symbolName;
275 }
276 });
277 if (exportSymbol) {
278 var symName = symbolName;
279 if (typeof exportSymbol !== 'string') {
280 symName = exportSymbol.name;
281 }
282 return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
283 }
284 }
285 }
286 // Try to find the symbol via export * directives.
287 for (var _b = 0, _c = metadata.exports; _b < _c.length; _b++) {
288 var moduleExport = _c[_b];
289 if (!moduleExport.export) {
290 var resolvedModule = resolveModule(moduleExport.from);
291 var candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
292 if (candidateSymbol)
293 return candidateSymbol;
294 }
295 }
296 }
297 }
298 return null;
299 };
300 return ReflectorHost;
301}());
302exports.ReflectorHost = ReflectorHost;
303var NodeReflectorHostContext = (function () {
304 function NodeReflectorHostContext(host) {
305 this.host = host;
306 this.assumedExists = {};
307 }
308 NodeReflectorHostContext.prototype.fileExists = function (fileName) {
309 return this.assumedExists[fileName] || this.host.fileExists(fileName);
310 };
311 NodeReflectorHostContext.prototype.directoryExists = function (directoryName) {
312 try {
313 return fs.statSync(directoryName).isDirectory();
314 }
315 catch (e) {
316 return false;
317 }
318 };
319 NodeReflectorHostContext.prototype.readFile = function (fileName) { return fs.readFileSync(fileName, 'utf8'); };
320 NodeReflectorHostContext.prototype.assumeFileExists = function (fileName) { this.assumedExists[fileName] = true; };
321 return NodeReflectorHostContext;
322}());
323exports.NodeReflectorHostContext = NodeReflectorHostContext;
324//# sourceMappingURL=reflector_host.js.map
\No newline at end of file