compodoc/ngd

View on GitHub
src/modules/compiler/dist/compiler.js

Summary

Maintainability
F
5 days
Test Coverage
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Compiler = void 0;
var path = require("path");
var ts = require("typescript");
var ngd_core_1 = require("@compodoc/ngd-core");
var utils_1 = require("./utils");
var Compiler = /** @class */ (function () {
    function Compiler(files, options) {
        this.__cache = {};
        this.__nsModule = {};
        this.unknown = '???';
        this.files = files;
        var transpileOptions = {
            target: ts.ScriptTarget.ES5,
            module: ts.ModuleKind.CommonJS,
            tsconfigDirectory: options.tsconfigDirectory,
        };
        this.program = ts.createProgram(this.files, transpileOptions, (0, ngd_core_1.compilerHost)(transpileOptions));
        // silent this instance of the logger
        ngd_core_1.logger.setVerbose(options.silent);
    }
    Compiler.prototype.getDependencies = function () {
        var _this = this;
        var deps = [];
        var sourceFiles = this.program.getSourceFiles() || [];
        sourceFiles.map(function (file) {
            var filePath = file.fileName;
            if (path.extname(filePath) === '.ts') {
                if (filePath.lastIndexOf('.d.ts') === -1 && filePath.lastIndexOf('spec.ts') === -1) {
                    ngd_core_1.logger.info('parsing', filePath);
                    try {
                        _this.getSourceFileDecorators(file, deps);
                    }
                    catch (e) {
                        ngd_core_1.logger.trace(e, file.fileName);
                    }
                }
            }
            return deps;
        });
        return deps;
    };
    Compiler.prototype.getSourceFileDecorators = function (srcFile, outputSymbols) {
        var _this = this;
        ts.forEachChild(srcFile, function (node) {
            (0, utils_1.getNodeDecorators)(node);
            var decorators = (0, utils_1.getNodeDecorators)(node) || [];
            if ((0, utils_1.nodeHasDecorator)(node)) {
                var visitNode = function (visitedNode, index) {
                    var name = _this.getSymboleName(node);
                    var deps = {};
                    var metadata = decorators[decorators.length - 1];
                    var props = _this.findProps(visitedNode);
                    if (_this.isModule(metadata)) {
                        deps = {
                            name: name,
                            file: srcFile.fileName.split('/').splice(-3).join('/'),
                            providers: _this.getModuleProviders(props),
                            declarations: _this.getModuleDeclations(props),
                            imports: _this.getModuleImports(props),
                            exports: _this.getModuleExports(props),
                            bootstrap: _this.getModuleBootstrap(props),
                            __raw: props,
                        };
                        outputSymbols.push(deps);
                    }
                    else if (_this.isComponent(metadata)) {
                        deps = {
                            name: name,
                            file: srcFile.fileName.split('/').splice(-3).join('/'),
                            selector: _this.getComponentSelector(props),
                            providers: _this.getComponentProviders(props),
                            templateUrl: _this.getComponentTemplateUrl(props),
                            styleUrls: _this.getComponentStyleUrls(props),
                            __raw: props,
                        };
                    }
                    _this.debug(deps);
                    _this.__cache[name] = deps;
                };
                var filterByDecorators = function (node) {
                    if (node.expression && node.expression.expression) {
                        return /(NgModule|Component)/.test(node.expression.expression.text);
                    }
                    return false;
                };
                decorators.filter(filterByDecorators).forEach(visitNode);
            }
            else {
                // process.stdout.write('.');
            }
        });
    };
    Compiler.prototype.debug = function (deps) {
        ngd_core_1.logger.debug('debug', "".concat(deps.name, ":"));
        ['imports', 'exports', 'declarations', 'providers', 'bootstrap'].forEach(function (symbols) {
            if (deps[symbols] && deps[symbols].length > 0) {
                ngd_core_1.logger.debug('', "- ".concat(symbols, ":"));
                deps[symbols]
                    .map(function (i) { return i.name; })
                    .forEach(function (d) {
                    ngd_core_1.logger.debug('', "\t- ".concat(d));
                });
            }
        });
    };
    Compiler.prototype.isComponent = function (metadata) {
        return metadata.expression.expression.text === 'Component';
    };
    Compiler.prototype.isModule = function (metadata) {
        return metadata.expression.expression.text === 'NgModule';
    };
    Compiler.prototype.getSymboleName = function (node) {
        return node.name.text;
    };
    Compiler.prototype.getComponentSelector = function (props) {
        return this.getSymbolDeps(props, 'selector').pop();
    };
    Compiler.prototype.getModuleProviders = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'providers').map(function (providerName) {
            return _this.parseDeepIndentifier(providerName);
        });
    };
    Compiler.prototype.findProps = function (visitedNode) {
        return visitedNode.expression.arguments.pop().properties;
    };
    Compiler.prototype.getModuleDeclations = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'declarations').map(function (name) {
            var component = _this.findComponentSelectorByName(name);
            if (component) {
                return component;
            }
            return _this.parseDeepIndentifier(name);
        });
    };
    Compiler.prototype.getModuleImports = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'imports').map(function (name) {
            return _this.parseDeepIndentifier(name);
        });
    };
    Compiler.prototype.getModuleExports = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'exports').map(function (name) {
            return _this.parseDeepIndentifier(name);
        });
    };
    Compiler.prototype.getModuleBootstrap = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'bootstrap').map(function (name) {
            return _this.parseDeepIndentifier(name);
        });
    };
    Compiler.prototype.getComponentProviders = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'providers').map(function (name) {
            return _this.parseDeepIndentifier(name);
        });
    };
    Compiler.prototype.getComponentDirectives = function (props) {
        var _this = this;
        return this.getSymbolDeps(props, 'directives').map(function (name) {
            var identifier = _this.parseDeepIndentifier(name);
            identifier.selector = _this.findComponentSelectorByName(name);
            identifier.label = '';
            return identifier;
        });
    };
    Compiler.prototype.parseDeepIndentifier = function (name) {
        var nsModule = name.split('.');
        if (nsModule.length > 1) {
            // cache deps with the same namespace (i.e Shared.*)
            if (this.__nsModule[nsModule[0]]) {
                this.__nsModule[nsModule[0]].push(name);
            }
            else {
                this.__nsModule[nsModule[0]] = [name];
            }
            return {
                ns: nsModule[0],
                name: name,
            };
        }
        return {
            name: name,
        };
    };
    Compiler.prototype.getComponentTemplateUrl = function (props) {
        return this.sanitizeUrls(this.getSymbolDeps(props, 'templateUrl'));
    };
    Compiler.prototype.getComponentStyleUrls = function (props) {
        return this.sanitizeUrls(this.getSymbolDeps(props, 'styleUrls'));
    };
    Compiler.prototype.sanitizeUrls = function (urls) {
        return urls.map(function (url) { return url.replace('./', ''); });
    };
    Compiler.prototype.getSymbolDeps = function (props, type) {
        var _this = this;
        var deps = props.filter(function (node) {
            return node.name.text === type;
        });
        var parseSymbolText = function (text) {
            if (text.indexOf('/') !== -1) {
                text = text.split('/').pop();
            }
            return [text];
        };
        var buildIdentifierName = function (node, name) {
            if (name === void 0) { name = ''; }
            if (node.expression) {
                name = name ? ".".concat(name) : name;
                var nodeName = _this.unknown;
                if (node.name) {
                    nodeName = node.name.text;
                }
                else if (node.text) {
                    nodeName = node.text;
                }
                else if (node.expression) {
                    if (node.expression.text) {
                        nodeName = node.expression.text;
                    }
                    else if (node.expression.elements) {
                        if (node.expression.kind === ts.SyntaxKind.ArrayLiteralExpression) {
                            nodeName = node.expression.elements.map(function (el) { return el.text; }).join(', ');
                            nodeName = "[".concat(nodeName, "]");
                        }
                    }
                }
                if (node.kind === ts.SyntaxKind.SpreadElement) {
                    return "...".concat(nodeName);
                }
                return "".concat(buildIdentifierName(node.expression, nodeName)).concat(name);
            }
            return "".concat(node.text, ".").concat(name);
        };
        var parseProviderConfiguration = function (o) {
            // parse expressions such as:
            // { provide: APP_BASE_HREF, useValue: '/' },
            // or
            // { provide: 'Date', useFactory: (d1, d2) => new Date(), deps: ['d1', 'd2'] }
            var _genProviderName = [];
            var _providerProps = [];
            (o.properties || []).forEach(function (prop) {
                var identifier = prop.initializer.text;
                if (prop.initializer.kind === ts.SyntaxKind.StringLiteral) {
                    identifier = "'".concat(identifier, "'");
                }
                // lambda function (i.e useFactory)
                if (prop.initializer.body) {
                    var params = (prop.initializer.parameters || []).map(function (params) { return params.name.text; });
                    identifier = "(".concat(params.join(', '), ") => {}");
                }
                // factory deps array
                else if (prop.initializer.elements) {
                    var elements = (prop.initializer.elements || []).map(function (n) {
                        if (n.kind === ts.SyntaxKind.StringLiteral) {
                            return "'".concat(n.text, "'");
                        }
                        return n.text;
                    });
                    identifier = "[".concat(elements.join(', '), "]");
                }
                _providerProps.push([
                    // i.e provide
                    prop.name.text,
                    // i.e OpaqueToken or 'StringToken'
                    identifier,
                ].join(': '));
            });
            return "{ ".concat(_providerProps.join(', '), " }");
        };
        var parseSymbolElements = function (o) {
            // parse expressions such as: AngularFireModule.initializeApp(firebaseConfig)
            if (o.arguments) {
                var className = buildIdentifierName(o.expression);
                // function arguments could be really complexe. There are so
                // many use cases that we can't handle. Just print "args" to indicate
                // that we have arguments.
                var functionArgs = o.arguments.length > 0 ? 'args' : '';
                var text = "".concat(className, "(").concat(functionArgs, ")");
                return text;
            }
            // parse expressions such as: Shared.Module
            else if (o.expression) {
                var identifier = buildIdentifierName(o);
                return identifier;
            }
            return o.text ? o.text : parseProviderConfiguration(o);
        };
        var parseSymbols = function (node) {
            var text = node.initializer.text;
            if (text) {
                return parseSymbolText(text);
            }
            else if (node.initializer.expression) {
                var identifier = parseSymbolElements(node.initializer);
                return [identifier];
            }
            else if (node.initializer.elements) {
                return node.initializer.elements.map(parseSymbolElements);
            }
        };
        return deps.map(parseSymbols).pop() || [];
    };
    Compiler.prototype.findComponentSelectorByName = function (name) {
        return this.__cache[name];
    };
    return Compiler;
}());
exports.Compiler = Compiler;