Project

General

Profile

1
"use strict";
2
/**
3
 * @license
4
 * Copyright Google Inc. All Rights Reserved.
5
 *
6
 * Use of this source code is governed by an MIT-style license that can be
7
 * found in the LICENSE file at https://angular.io/license
8
 */
9
var __extends = (this && this.__extends) || (function () {
10
    var extendStatics = Object.setPrototypeOf ||
11
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
12
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
13
    return function (d, b) {
14
        extendStatics(d, b);
15
        function __() { this.constructor = d; }
16
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
17
    };
18
})();
19
Object.defineProperty(exports, "__esModule", { value: true });
20
var tsc_wrapped_1 = require("@angular/tsc-wrapped");
21
var fs = require("fs");
22
var path = require("path");
23
var ts = require("typescript");
24
var EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
25
var DTS = /\.d\.ts$/;
26
var NODE_MODULES = '/node_modules/';
27
var IS_GENERATED = /\.(ngfactory|ngstyle|ngsummary)$/;
28
var GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
29
var GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
30
var SHALLOW_IMPORT = /^((\w|-)+|(@(\w|-)+(\/(\w|-)+)+))$/;
31
var CompilerHost = (function () {
32
    function CompilerHost(program, options, context, collectorOptions) {
33
        var _this = this;
34
        this.program = program;
35
        this.options = options;
36
        this.context = context;
37
        this.metadataCollector = new tsc_wrapped_1.MetadataCollector();
38
        this.resolverCache = new Map();
39
        this.flatModuleIndexCache = new Map();
40
        this.flatModuleIndexNames = new Set();
41
        this.flatModuleIndexRedirectNames = new Set();
42
        this.moduleFileNames = new Map();
43
        // normalize the path so that it never ends with '/'.
44
        this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
45
        this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
46
        var genPath = path.relative(this.basePath, this.genDir);
47
        this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
48
        this.resolveModuleNameHost = Object.create(this.context);
49
        // When calling ts.resolveModuleName,
50
        // additional allow checks for .d.ts files to be done based on
51
        // checks for .ngsummary.json files,
52
        // so that our codegen depends on fewer inputs and requires to be called
53
        // less often.
54
        // This is needed as we use ts.resolveModuleName in reflector_host
55
        // and it should be able to resolve summary file names.
56
        this.resolveModuleNameHost.fileExists = function (fileName) {
57
            if (_this.context.fileExists(fileName)) {
58
                return true;
59
            }
60
            if (DTS.test(fileName)) {
61
                var base = fileName.substring(0, fileName.length - 5);
62
                return _this.context.fileExists(base + '.ngsummary.json');
63
            }
64
            return false;
65
        };
66
    }
67
    // We use absolute paths on disk as canonical.
68
    CompilerHost.prototype.getCanonicalFileName = function (fileName) { return fileName; };
69
    CompilerHost.prototype.moduleNameToFileName = function (m, containingFile) {
70
        var key = m + ':' + (containingFile || '');
71
        var result = this.moduleFileNames.get(key) || null;
72
        if (!result) {
73
            if (!containingFile || !containingFile.length) {
74
                if (m.indexOf('.') === 0) {
75
                    throw new Error('Resolution of relative paths requires a containing file.');
76
                }
77
                // Any containing file gives the same result for absolute imports
78
                containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
79
            }
80
            m = m.replace(EXT, '');
81
            var resolved = ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
82
                .resolvedModule;
83
            result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
84
            this.moduleFileNames.set(key, result);
85
        }
86
        return result;
87
    };
88
    /**
89
     * We want a moduleId that will appear in import statements in the generated code.
90
     * These need to be in a form that system.js can load, so absolute file paths don't work.
91
     *
92
     * The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
93
     * `genDir`, `node_module` or `basePath`.  The `importedFile` is either a generated file or
94
     * existing file.
95
     *
96
     *               | genDir   | node_module |  rootDir
97
     * --------------+----------+-------------+----------
98
     * generated     | relative |   relative  |   n/a
99
     * existing file |   n/a    |   absolute  |  relative(*)
100
     *
101
     * NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
102
     */
103
    CompilerHost.prototype.fileNameToModuleName = function (importedFile, containingFile) {
104
        // If a file does not yet exist (because we compile it later), we still need to
105
        // assume it exists it so that the `resolve` method works!
106
        if (importedFile !== containingFile && !this.context.fileExists(importedFile)) {
107
            this.context.assumeFileExists(importedFile);
108
        }
109
        containingFile = this.rewriteGenDirPath(containingFile);
110
        var containingDir = path.dirname(containingFile);
111
        // drop extension
112
        importedFile = importedFile.replace(EXT, '');
113
        var nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
114
        var importModule = nodeModulesIndex === -1 ?
115
            null :
116
            importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
117
        var isGeneratedFile = IS_GENERATED.test(importedFile);
118
        if (isGeneratedFile) {
119
            // rewrite to genDir path
120
            if (importModule) {
121
                // it is generated, therefore we do a relative path to the factory
122
                return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
123
            }
124
            else {
125
                // assume that import is also in `genDir`
126
                importedFile = this.rewriteGenDirPath(importedFile);
127
                return this.dotRelative(containingDir, importedFile);
128
            }
129
        }
130
        else {
131
            // user code import
132
            if (importModule) {
133
                return importModule;
134
            }
135
            else {
136
                if (!this.isGenDirChildOfRootDir) {
137
                    // assume that they are on top of each other.
138
                    importedFile = importedFile.replace(this.basePath, this.genDir);
139
                }
140
                if (SHALLOW_IMPORT.test(importedFile)) {
141
                    return importedFile;
142
                }
143
                return this.dotRelative(containingDir, importedFile);
144
            }
145
        }
146
    };
147
    CompilerHost.prototype.dotRelative = function (from, to) {
148
        var rPath = path.relative(from, to).replace(/\\/g, '/');
149
        return rPath.startsWith('.') ? rPath : './' + rPath;
150
    };
151
    /**
152
     * Moves the path into `genDir` folder while preserving the `node_modules` directory.
153
     */
154
    CompilerHost.prototype.rewriteGenDirPath = function (filepath) {
155
        var nodeModulesIndex = filepath.indexOf(NODE_MODULES);
156
        if (nodeModulesIndex !== -1) {
157
            // If we are in node_modulse, transplant them into `genDir`.
158
            return path.join(this.genDir, filepath.substring(nodeModulesIndex));
159
        }
160
        else {
161
            // pretend that containing file is on top of the `genDir` to normalize the paths.
162
            // we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
163
            return filepath.replace(this.basePath, this.genDir);
164
        }
165
    };
166
    CompilerHost.prototype.getSourceFile = function (filePath) {
167
        var sf = this.program.getSourceFile(filePath);
168
        if (!sf) {
169
            if (this.context.fileExists(filePath)) {
170
                var sourceText = this.context.readFile(filePath);
171
                return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
172
            }
173
            throw new Error("Source file " + filePath + " not present in program.");
174
        }
175
        return sf;
176
    };
177
    CompilerHost.prototype.getMetadataFor = function (filePath) {
178
        if (!this.context.fileExists(filePath)) {
179
            // If the file doesn't exists then we cannot return metadata for the file.
180
            // This will occur if the user referenced a declared module for which no file
181
            // exists for the module (i.e. jQuery or angularjs).
182
            return;
183
        }
184
        if (DTS.test(filePath)) {
185
            var metadataPath = filePath.replace(DTS, '.metadata.json');
186
            if (this.context.fileExists(metadataPath)) {
187
                return this.readMetadata(metadataPath, filePath);
188
            }
189
            else {
190
                // If there is a .d.ts file but no metadata file we need to produce a
191
                // v3 metadata from the .d.ts file as v3 includes the exports we need
192
                // to resolve symbols.
193
                return [this.upgradeVersion1Metadata({ '__symbolic': 'module', 'version': 1, 'metadata': {} }, filePath)];
194
            }
195
        }
196
        var sf = this.getSourceFile(filePath);
197
        var metadata = this.metadataCollector.getMetadata(sf);
198
        return metadata ? [metadata] : [];
199
    };
200
    CompilerHost.prototype.readMetadata = function (filePath, dtsFilePath) {
201
        var metadatas = this.resolverCache.get(filePath);
202
        if (metadatas) {
203
            return metadatas;
204
        }
205
        try {
206
            var metadataOrMetadatas = JSON.parse(this.context.readFile(filePath));
207
            var metadatas_1 = metadataOrMetadatas ?
208
                (Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
209
                [];
210
            var v1Metadata = metadatas_1.find(function (m) { return m.version === 1; });
211
            var v3Metadata = metadatas_1.find(function (m) { return m.version === 3; });
212
            if (!v3Metadata && v1Metadata) {
213
                metadatas_1.push(this.upgradeVersion1Metadata(v1Metadata, dtsFilePath));
214
            }
215
            this.resolverCache.set(filePath, metadatas_1);
216
            return metadatas_1;
217
        }
218
        catch (e) {
219
            console.error("Failed to read JSON file " + filePath);
220
            throw e;
221
        }
222
    };
223
    CompilerHost.prototype.upgradeVersion1Metadata = function (v1Metadata, dtsFilePath) {
224
        // patch up v1 to v3 by merging the metadata with metadata collected from the d.ts file
225
        // as the only difference between the versions is whether all exports are contained in
226
        // the metadata and the `extends` clause.
227
        var v3Metadata = { '__symbolic': 'module', 'version': 3, 'metadata': {} };
228
        if (v1Metadata.exports) {
229
            v3Metadata.exports = v1Metadata.exports;
230
        }
231
        for (var prop in v1Metadata.metadata) {
232
            v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
233
        }
234
        var exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
235
        if (exports) {
236
            for (var prop in exports.metadata) {
237
                if (!v3Metadata.metadata[prop]) {
238
                    v3Metadata.metadata[prop] = exports.metadata[prop];
239
                }
240
            }
241
            if (exports.exports) {
242
                v3Metadata.exports = exports.exports;
243
            }
244
        }
245
        return v3Metadata;
246
    };
247
    CompilerHost.prototype.loadResource = function (filePath) {
248
        if (this.context.readResource)
249
            return this.context.readResource(filePath);
250
        return this.context.readFile(filePath);
251
    };
252
    CompilerHost.prototype.loadSummary = function (filePath) {
253
        if (this.context.fileExists(filePath)) {
254
            return this.context.readFile(filePath);
255
        }
256
        return null;
257
    };
258
    CompilerHost.prototype.getOutputFileName = function (sourceFilePath) {
259
        return sourceFilePath.replace(EXT, '') + '.d.ts';
260
    };
261
    CompilerHost.prototype.isSourceFile = function (filePath) {
262
        var excludeRegex = this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
263
        if (excludeRegex.test(filePath)) {
264
            return false;
265
        }
266
        if (DTS.test(filePath)) {
267
            // Check for a bundle index.
268
            if (this.hasBundleIndex(filePath)) {
269
                var normalFilePath = path.normalize(filePath);
270
                return this.flatModuleIndexNames.has(normalFilePath) ||
271
                    this.flatModuleIndexRedirectNames.has(normalFilePath);
272
            }
273
        }
274
        return true;
275
    };
276
    CompilerHost.prototype.calculateEmitPath = function (filePath) {
277
        // Write codegen in a directory structure matching the sources.
278
        var root = this.options.basePath;
279
        for (var _i = 0, _a = this.options.rootDirs || []; _i < _a.length; _i++) {
280
            var eachRootDir = _a[_i];
281
            if (this.options.trace) {
282
                console.error("Check if " + filePath + " is under rootDirs element " + eachRootDir);
283
            }
284
            if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
285
                root = eachRootDir;
286
            }
287
        }
288
        // transplant the codegen path to be inside the `genDir`
289
        var relativePath = path.relative(root, filePath);
290
        while (relativePath.startsWith('..' + path.sep)) {
291
            // Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
292
            // into `genDir`.
293
            relativePath = relativePath.substr(3);
294
        }
295
        return path.join(this.options.genDir, relativePath);
296
    };
297
    CompilerHost.prototype.hasBundleIndex = function (filePath) {
298
        var _this = this;
299
        var checkBundleIndex = function (directory) {
300
            var result = _this.flatModuleIndexCache.get(directory);
301
            if (result == null) {
302
                if (path.basename(directory) == 'node_module') {
303
                    // Don't look outside the node_modules this package is installed in.
304
                    result = false;
305
                }
306
                else {
307
                    // A bundle index exists if the typings .d.ts file has a metadata.json that has an
308
                    // importAs.
309
                    try {
310
                        var packageFile = path.join(directory, 'package.json');
311
                        if (_this.context.fileExists(packageFile)) {
312
                            // Once we see a package.json file, assume false until it we find the bundle index.
313
                            result = false;
314
                            var packageContent = JSON.parse(_this.context.readFile(packageFile));
315
                            if (packageContent.typings) {
316
                                var typings = path.normalize(path.join(directory, packageContent.typings));
317
                                if (DTS.test(typings)) {
318
                                    var metadataFile = typings.replace(DTS, '.metadata.json');
319
                                    if (_this.context.fileExists(metadataFile)) {
320
                                        var metadata = JSON.parse(_this.context.readFile(metadataFile));
321
                                        if (metadata.flatModuleIndexRedirect) {
322
                                            _this.flatModuleIndexRedirectNames.add(typings);
323
                                            // Note: don't set result = true,
324
                                            // as this would mark this folder
325
                                            // as having a bundleIndex too early without
326
                                            // filling the bundleIndexNames.
327
                                        }
328
                                        else if (metadata.importAs) {
329
                                            _this.flatModuleIndexNames.add(typings);
330
                                            result = true;
331
                                        }
332
                                    }
333
                                }
334
                            }
335
                        }
336
                        else {
337
                            var parent_1 = path.dirname(directory);
338
                            if (parent_1 != directory) {
339
                                // Try the parent directory.
340
                                result = checkBundleIndex(parent_1);
341
                            }
342
                            else {
343
                                result = false;
344
                            }
345
                        }
346
                    }
347
                    catch (e) {
348
                        // If we encounter any errors assume we this isn't a bundle index.
349
                        result = false;
350
                    }
351
                }
352
                _this.flatModuleIndexCache.set(directory, result);
353
            }
354
            return result;
355
        };
356
        return checkBundleIndex(path.dirname(filePath));
357
    };
358
    return CompilerHost;
359
}());
360
exports.CompilerHost = CompilerHost;
361
var CompilerHostContextAdapter = (function () {
362
    function CompilerHostContextAdapter() {
363
        this.assumedExists = {};
364
    }
365
    CompilerHostContextAdapter.prototype.assumeFileExists = function (fileName) { this.assumedExists[fileName] = true; };
366
    return CompilerHostContextAdapter;
367
}());
368
exports.CompilerHostContextAdapter = CompilerHostContextAdapter;
369
var ModuleResolutionHostAdapter = (function (_super) {
370
    __extends(ModuleResolutionHostAdapter, _super);
371
    function ModuleResolutionHostAdapter(host) {
372
        var _this = _super.call(this) || this;
373
        _this.host = host;
374
        if (host.directoryExists) {
375
            _this.directoryExists = function (directoryName) { return host.directoryExists(directoryName); };
376
        }
377
        return _this;
378
    }
379
    ModuleResolutionHostAdapter.prototype.fileExists = function (fileName) {
380
        return this.assumedExists[fileName] || this.host.fileExists(fileName);
381
    };
382
    ModuleResolutionHostAdapter.prototype.readFile = function (fileName) { return this.host.readFile(fileName); };
383
    ModuleResolutionHostAdapter.prototype.readResource = function (s) {
384
        if (!this.host.fileExists(s)) {
385
            // TODO: We should really have a test for error cases like this!
386
            throw new Error("Compilation failed. Resource file not found: " + s);
387
        }
388
        return Promise.resolve(this.host.readFile(s));
389
    };
390
    return ModuleResolutionHostAdapter;
391
}(CompilerHostContextAdapter));
392
exports.ModuleResolutionHostAdapter = ModuleResolutionHostAdapter;
393
var NodeCompilerHostContext = (function (_super) {
394
    __extends(NodeCompilerHostContext, _super);
395
    function NodeCompilerHostContext() {
396
        return _super !== null && _super.apply(this, arguments) || this;
397
    }
398
    NodeCompilerHostContext.prototype.fileExists = function (fileName) {
399
        return this.assumedExists[fileName] || fs.existsSync(fileName);
400
    };
401
    NodeCompilerHostContext.prototype.directoryExists = function (directoryName) {
402
        try {
403
            return fs.statSync(directoryName).isDirectory();
404
        }
405
        catch (e) {
406
            return false;
407
        }
408
    };
409
    NodeCompilerHostContext.prototype.readFile = function (fileName) { return fs.readFileSync(fileName, 'utf8'); };
410
    NodeCompilerHostContext.prototype.readResource = function (s) {
411
        if (!this.fileExists(s)) {
412
            // TODO: We should really have a test for error cases like this!
413
            throw new Error("Compilation failed. Resource file not found: " + s);
414
        }
415
        return Promise.resolve(this.readFile(s));
416
    };
417
    return NodeCompilerHostContext;
418
}(CompilerHostContextAdapter));
419
exports.NodeCompilerHostContext = NodeCompilerHostContext;
420
//# sourceMappingURL=compiler_host.js.map
(5-5/30)