UNPKG

@compodoc/compodoc

Version:

The missing documentation tool for your Angular application

1,239 lines (1,111 loc) 123 kB
import * as fs from 'fs-extra'; import * as LiveServer from '@compodoc/live-server'; import * as _ from 'lodash'; import * as path from 'path'; import { SyntaxKind } from 'ts-morph'; const chokidar = require('chokidar'); const { marked } = require('marked'); const traverse = require('traverse'); const crypto = require('crypto'); const babel = require('@babel/core'); import { logger } from '../utils/logger'; import Configuration from './configuration'; import DependenciesEngine from './engines/dependencies.engine'; import ExportEngine from './engines/export.engine'; import FileEngine from './engines/file.engine'; import HtmlEngine from './engines/html.engine'; import I18nEngine from './engines/i18n.engine'; import MarkdownEngine, { markdownReadedDatas } from './engines/markdown.engine'; import NgdEngine from './engines/ngd.engine'; import SearchEngine from './engines/search.engine'; import { AngularDependencies } from './compiler/angular-dependencies'; import { AngularJSDependencies } from './compiler/angularjs-dependencies'; import AngularVersionUtil from '../utils/angular-version.util'; import { COMPODOC_CONSTANTS } from '../utils/constants'; import { COMPODOC_DEFAULTS } from '../utils/defaults'; import { promiseSequential } from '../utils/promise-sequential'; import RouterParserUtil from '../utils/router-parser.util'; import { cleanNameWithoutSpaceAndToLowerCase, cleanSourcesForWatch, findMainSourceFolder } from '../utils/utils'; import { AdditionalNode } from './interfaces/additional-node.interface'; import { CoverageData } from './interfaces/coverageData.interface'; import { LiveServerConfiguration } from './interfaces/live-server-configuration.interface'; const cwd = process.cwd(); let startTime = new Date(); let generationPromiseResolve; let generationPromiseReject; const generationPromise = new Promise((resolve, reject) => { generationPromiseResolve = resolve; generationPromiseReject = reject; }); export class Application { /** * Files processed during initial scanning */ public files: Array<string>; /** * Files processed during watch scanning */ public updatedFiles: Array<string>; /** * Files changed during watch scanning */ public watchChangedFiles: Array<string> = []; /** * Boolean for watching status * @type {boolean} */ public isWatching: boolean = false; /** * Store package.json data */ private packageJsonData = {}; /** * Create a new compodoc application instance. * * @param options An object containing the options that should be used. */ constructor(options?: Object) { for (let option in options) { if (typeof Configuration.mainData[option] !== 'undefined') { Configuration.mainData[option] = options[option]; } // For documentationMainName, process it outside the loop, for handling conflict with pages name if (option === 'name') { Configuration.mainData.documentationMainName = options[option]; } // For documentationMainName, process it outside the loop, for handling conflict with pages name if (option === 'silent') { logger.silent = false; } } } /** * Start compodoc process */ protected generate(): Promise<{}> { process.on('unhandledRejection', this.unhandledRejectionListener); process.on('uncaughtException', this.uncaughtExceptionListener); I18nEngine.init(Configuration.mainData.language); if ( Configuration.mainData.output.charAt(Configuration.mainData.output.length - 1) !== '/' ) { Configuration.mainData.output += '/'; } if (Configuration.mainData.exportFormat !== COMPODOC_DEFAULTS.exportFormat) { this.processPackageJson(); } else { HtmlEngine.init(Configuration.mainData.templates).then(() => this.processPackageJson()); } return generationPromise; } private endCallback() { process.removeListener('unhandledRejection', this.unhandledRejectionListener); process.removeListener('uncaughtException', this.uncaughtExceptionListener); } private unhandledRejectionListener(err, p) { console.log('Unhandled Rejection at:', p, 'reason:', err); logger.error( 'Sorry, but there was a problem during parsing or generation of the documentation. Please fill an issue on github. (https://github.com/compodoc/compodoc/issues/new)' ); // tslint:disable-line process.exit(1); } private uncaughtExceptionListener(err) { logger.error(err); logger.error( 'Sorry, but there was a problem during parsing or generation of the documentation. Please fill an issue on github. (https://github.com/compodoc/compodoc/issues/new)' ); // tslint:disable-line process.exit(1); } /** * Start compodoc documentation coverage */ protected testCoverage() { this.getDependenciesData(); } /** * Store files for initial processing * @param {Array<string>} files Files found during source folder and tsconfig scan */ public setFiles(files: Array<string>) { this.files = files; } /** * Store files for watch processing * @param {Array<string>} files Files found during source folder and tsconfig scan */ public setUpdatedFiles(files: Array<string>) { this.updatedFiles = files; } /** * Return a boolean indicating presence of one TypeScript file in updatedFiles list * @return {boolean} Result of scan */ public hasWatchedFilesTSFiles(): boolean { let result = false; _.forEach(this.updatedFiles, file => { if (path.extname(file) === '.ts') { result = true; } }); return result; } /** * Return a boolean indicating presence of one root markdown files in updatedFiles list * @return {boolean} Result of scan */ public hasWatchedFilesRootMarkdownFiles(): boolean { let result = false; _.forEach(this.updatedFiles, file => { if (path.extname(file) === '.md' && path.dirname(file) === cwd) { result = true; } }); return result; } /** * Clear files for watch processing */ public clearUpdatedFiles(): void { this.updatedFiles = []; this.watchChangedFiles = []; } private processPackageJson(): void { logger.info('Searching package.json file'); FileEngine.get(cwd + path.sep + 'package.json').then( packageData => { let parsedData = JSON.parse(packageData); this.packageJsonData = parsedData; if ( typeof parsedData.name !== 'undefined' && Configuration.mainData.documentationMainName === COMPODOC_DEFAULTS.title ) { Configuration.mainData.documentationMainName = parsedData.name + ' documentation'; } if (typeof parsedData.description !== 'undefined') { Configuration.mainData.documentationMainDescription = parsedData.description; } Configuration.mainData.angularVersion = AngularVersionUtil.getAngularVersionOfProject(parsedData); logger.info('package.json file found'); if (!Configuration.mainData.disableDependencies) { if (typeof parsedData.dependencies !== 'undefined') { this.processPackageDependencies(parsedData.dependencies); } if (typeof parsedData.peerDependencies !== 'undefined') { this.processPackagePeerDependencies(parsedData.peerDependencies); } } if (!Configuration.mainData.disableProperties) { const propertiesToCheck = [ 'version', 'description', 'keywords', 'homepage', 'bugs', 'license', 'repository', 'author' ]; let hasOneOfCheckedProperties = false; propertiesToCheck.forEach(prop => { if (prop in parsedData) { hasOneOfCheckedProperties = true; Configuration.mainData.packageProperties[prop] = parsedData[prop]; } }); if (hasOneOfCheckedProperties) { Configuration.addPage({ name: 'properties', id: 'packageProperties', context: 'package-properties', depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } } this.processMarkdowns().then( () => { this.getDependenciesData(); }, errorMessage => { logger.error(errorMessage); process.exit(1); } ); }, errorMessage => { logger.error(errorMessage); logger.error('Continuing without package.json file'); this.processMarkdowns().then( () => { this.getDependenciesData(); }, errorMessage1 => { logger.error(errorMessage1); process.exit(1); } ); } ); } private processPackagePeerDependencies(dependencies): void { logger.info('Processing package.json peerDependencies'); Configuration.mainData.packagePeerDependencies = dependencies; if (!Configuration.hasPage('dependencies')) { Configuration.addPage({ name: 'dependencies', id: 'packageDependencies', context: 'package-dependencies', depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } } private processPackageDependencies(dependencies): void { logger.info('Processing package.json dependencies'); Configuration.mainData.packageDependencies = dependencies; Configuration.addPage({ name: 'dependencies', id: 'packageDependencies', context: 'package-dependencies', depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } private processMarkdowns(): Promise<any> { logger.info( 'Searching README.md, CHANGELOG.md, CONTRIBUTING.md, LICENSE.md, TODO.md files' ); return new Promise((resolve, reject) => { let i = 0; let markdowns = ['readme', 'changelog', 'contributing', 'license', 'todo']; let numberOfMarkdowns = 5; let loop = () => { if (i < numberOfMarkdowns) { MarkdownEngine.getTraditionalMarkdown(markdowns[i].toUpperCase()).then( (readmeData: markdownReadedDatas) => { Configuration.addPage({ name: markdowns[i] === 'readme' ? 'index' : markdowns[i], context: 'getting-started', id: 'getting-started', markdown: readmeData.markdown, data: readmeData.rawData, depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); if (markdowns[i] === 'readme') { Configuration.mainData.readme = true; Configuration.addPage({ name: 'overview', id: 'overview', context: 'overview', pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } else { Configuration.mainData.markdowns.push({ name: markdowns[i], uppername: markdowns[i].toUpperCase(), depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } logger.info(`${markdowns[i].toUpperCase()}.md file found`); i++; loop(); }, errorMessage => { logger.warn(errorMessage); logger.warn(`Continuing without ${markdowns[i].toUpperCase()}.md file`); if (markdowns[i] === 'readme') { Configuration.addPage({ name: 'index', id: 'index', context: 'overview', depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); } i++; loop(); } ); } else { resolve(); } }; loop(); }); } private rebuildRootMarkdowns(): void { logger.info( 'Regenerating README.md, CHANGELOG.md, CONTRIBUTING.md, LICENSE.md, TODO.md pages' ); let actions = []; Configuration.resetRootMarkdownPages(); actions.push(() => { return this.processMarkdowns(); }); promiseSequential(actions) .then(res => { this.processPages(); this.clearUpdatedFiles(); }) .catch(errorMessage => { logger.error(errorMessage); }); } /** * Get dependency data for small group of updated files during watch process */ private getMicroDependenciesData(): void { logger.info('Get diff dependencies data'); let dependenciesClass: AngularDependencies | AngularJSDependencies = AngularDependencies; Configuration.mainData.angularProject = true; if (this.detectAngularJSProjects()) { logger.info('AngularJS project detected'); Configuration.mainData.angularProject = false; Configuration.mainData.angularJSProject = true; dependenciesClass = AngularJSDependencies; } let crawler = new dependenciesClass( this.updatedFiles, { tsconfigDirectory: path.dirname(Configuration.mainData.tsconfig) }, Configuration, RouterParserUtil ); let dependenciesData = crawler.getDependencies(); DependenciesEngine.update(dependenciesData); this.prepareJustAFewThings(dependenciesData); } /** * Rebuild external documentation during watch process */ private rebuildExternalDocumentation(): void { logger.info('Rebuild external documentation'); let actions = []; Configuration.resetAdditionalPages(); if (Configuration.mainData.includes !== '') { actions.push(() => { return this.prepareExternalIncludes(); }); } promiseSequential(actions) .then(res => { this.processPages(); this.clearUpdatedFiles(); }) .catch(errorMessage => { logger.error(errorMessage); }); } private detectAngularJSProjects() { let result = false; if (typeof this.packageJsonData.dependencies !== 'undefined') { if (typeof this.packageJsonData.dependencies.angular !== 'undefined') { result = true; } else { let countJSFiles = 0; this.files.forEach(file => { if (path.extname(file) === '.js') { countJSFiles += 1; } }); let percentOfJSFiles = (countJSFiles * 100) / this.files.length; if (percentOfJSFiles >= 75) { result = true; } } } return false; } private getDependenciesData(): void { logger.info('Get dependencies data'); /** * AngularJS detection strategy : * - if in package.json * - if 75% of scanned files are *.js files */ let dependenciesClass: AngularDependencies | AngularJSDependencies = AngularDependencies; Configuration.mainData.angularProject = true; if (this.detectAngularJSProjects()) { logger.info('AngularJS project detected'); Configuration.mainData.angularProject = false; Configuration.mainData.angularJSProject = true; dependenciesClass = AngularJSDependencies; } let crawler = new dependenciesClass( this.files, { tsconfigDirectory: path.dirname(Configuration.mainData.tsconfig) }, Configuration, RouterParserUtil ); let dependenciesData = crawler.getDependencies(); DependenciesEngine.init(dependenciesData); Configuration.mainData.routesLength = RouterParserUtil.routesLength(); this.printStatistics(); this.prepareEverything(); } private prepareJustAFewThings(diffCrawledData): void { let actions = []; Configuration.resetPages(); if (!Configuration.mainData.disableRoutesGraph) { actions.push(() => this.prepareRoutes()); } if (diffCrawledData.components.length > 0) { actions.push(() => this.prepareComponents()); } if (diffCrawledData.controllers.length > 0) { actions.push(() => this.prepareControllers()); } if (diffCrawledData.entities.length > 0) { actions.push(() => this.prepareEntities()); } if (diffCrawledData.modules.length > 0) { actions.push(() => this.prepareModules()); } if (diffCrawledData.directives.length > 0) { actions.push(() => this.prepareDirectives()); } if (diffCrawledData.injectables.length > 0) { actions.push(() => this.prepareInjectables()); } if (diffCrawledData.interceptors.length > 0) { actions.push(() => this.prepareInterceptors()); } if (diffCrawledData.guards.length > 0) { actions.push(() => this.prepareGuards()); } if (diffCrawledData.pipes.length > 0) { actions.push(() => this.preparePipes()); } if (diffCrawledData.classes.length > 0) { actions.push(() => this.prepareClasses()); } if (diffCrawledData.interfaces.length > 0) { actions.push(() => this.prepareInterfaces()); } if ( diffCrawledData.miscellaneous.variables.length > 0 || diffCrawledData.miscellaneous.functions.length > 0 || diffCrawledData.miscellaneous.typealiases.length > 0 || diffCrawledData.miscellaneous.enumerations.length > 0 ) { actions.push(() => this.prepareMiscellaneous()); } if (!Configuration.mainData.disableCoverage) { actions.push(() => this.prepareCoverage()); } promiseSequential(actions) .then(res => { if (Configuration.mainData.exportFormat !== COMPODOC_DEFAULTS.exportFormat) { if ( COMPODOC_DEFAULTS.exportFormatsSupported.indexOf( Configuration.mainData.exportFormat ) > -1 ) { logger.info( `Generating documentation in export format ${Configuration.mainData.exportFormat}` ); ExportEngine.export( Configuration.mainData.output, Configuration.mainData ).then(() => { generationPromiseResolve(); this.endCallback(); logger.info( 'Documentation generated in ' + Configuration.mainData.output + ' in ' + this.getElapsedTime() + ' seconds' ); if (Configuration.mainData.serve) { logger.info( `Serving documentation from ${Configuration.mainData.output} at http://${Configuration.mainData.hostname}:${Configuration.mainData.port}` ); this.runWebServer(Configuration.mainData.output); } }); } else { logger.warn(`Exported format not supported`); } } else { this.processGraphs(); this.clearUpdatedFiles(); } }) .catch(errorMessage => { logger.error(errorMessage); }); } private printStatistics() { logger.info('-------------------'); logger.info('Project statistics '); if (DependenciesEngine.modules.length > 0) { logger.info(`- files : ${this.files.length}`); } if (DependenciesEngine.modules.length > 0) { logger.info(`- module : ${DependenciesEngine.modules.length}`); } if (DependenciesEngine.components.length > 0) { logger.info(`- component : ${DependenciesEngine.components.length}`); } if (DependenciesEngine.controllers.length > 0) { logger.info(`- controller : ${DependenciesEngine.controllers.length}`); } if (DependenciesEngine.entities.length > 0) { logger.info(`- entity : ${DependenciesEngine.entities.length}`); } if (DependenciesEngine.directives.length > 0) { logger.info(`- directive : ${DependenciesEngine.directives.length}`); } if (DependenciesEngine.injectables.length > 0) { logger.info(`- injectable : ${DependenciesEngine.injectables.length}`); } if (DependenciesEngine.interceptors.length > 0) { logger.info(`- injector : ${DependenciesEngine.interceptors.length}`); } if (DependenciesEngine.guards.length > 0) { logger.info(`- guard : ${DependenciesEngine.guards.length}`); } if (DependenciesEngine.pipes.length > 0) { logger.info(`- pipe : ${DependenciesEngine.pipes.length}`); } if (DependenciesEngine.classes.length > 0) { logger.info(`- class : ${DependenciesEngine.classes.length}`); } if (DependenciesEngine.interfaces.length > 0) { logger.info(`- interface : ${DependenciesEngine.interfaces.length}`); } if (Configuration.mainData.routesLength > 0) { logger.info(`- route : ${Configuration.mainData.routesLength}`); } logger.info('-------------------'); } private prepareEverything() { let actions = []; actions.push(() => { return this.prepareComponents(); }); actions.push(() => { return this.prepareModules(); }); if (DependenciesEngine.directives.length > 0) { actions.push(() => { return this.prepareDirectives(); }); } if (DependenciesEngine.controllers.length > 0) { actions.push(() => { return this.prepareControllers(); }); } if (DependenciesEngine.entities.length > 0) { actions.push(() => { return this.prepareEntities(); }); } if (DependenciesEngine.injectables.length > 0) { actions.push(() => { return this.prepareInjectables(); }); } if (DependenciesEngine.interceptors.length > 0) { actions.push(() => { return this.prepareInterceptors(); }); } if (DependenciesEngine.guards.length > 0) { actions.push(() => { return this.prepareGuards(); }); } if ( DependenciesEngine.routes && DependenciesEngine.routes.children.length > 0 && !Configuration.mainData.disableRoutesGraph ) { actions.push(() => { return this.prepareRoutes(); }); } if (DependenciesEngine.pipes.length > 0) { actions.push(() => { return this.preparePipes(); }); } if (DependenciesEngine.classes.length > 0) { actions.push(() => { return this.prepareClasses(); }); } if (DependenciesEngine.interfaces.length > 0) { actions.push(() => { return this.prepareInterfaces(); }); } if ( DependenciesEngine.miscellaneous.variables.length > 0 || DependenciesEngine.miscellaneous.functions.length > 0 || DependenciesEngine.miscellaneous.typealiases.length > 0 || DependenciesEngine.miscellaneous.enumerations.length > 0 ) { actions.push(() => { return this.prepareMiscellaneous(); }); } if (!Configuration.mainData.disableCoverage) { actions.push(() => { return this.prepareCoverage(); }); } if (Configuration.mainData.unitTestCoverage !== '') { actions.push(() => { return this.prepareUnitTestCoverage(); }); } if (Configuration.mainData.includes !== '') { actions.push(() => { return this.prepareExternalIncludes(); }); } promiseSequential(actions) .then(res => { if (Configuration.mainData.exportFormat !== COMPODOC_DEFAULTS.exportFormat) { if ( COMPODOC_DEFAULTS.exportFormatsSupported.indexOf( Configuration.mainData.exportFormat ) > -1 ) { logger.info( `Generating documentation in export format ${Configuration.mainData.exportFormat}` ); ExportEngine.export( Configuration.mainData.output, Configuration.mainData ).then(() => { generationPromiseResolve(); this.endCallback(); logger.info( 'Documentation generated in ' + Configuration.mainData.output + ' in ' + this.getElapsedTime() + ' seconds' ); if (Configuration.mainData.serve) { logger.info( `Serving documentation from ${Configuration.mainData.output} at http://${Configuration.mainData.hostname}:${Configuration.mainData.port}` ); this.runWebServer(Configuration.mainData.output); } }); } else { logger.warn(`Exported format not supported`); } } else { this.processGraphs(); } }) .catch(errorMessage => { logger.error(errorMessage); process.exit(1); }); } private getIncludedPathForFile(file) { return path.join(Configuration.mainData.includes, file); } private prepareExternalIncludes() { logger.info('Adding external markdown files'); // Scan include folder for files detailed in summary.json // For each file, add to Configuration.mainData.additionalPages // Each file will be converted to html page, inside COMPODOC_DEFAULTS.additionalEntryPath return new Promise((resolve, reject) => { FileEngine.get(this.getIncludedPathForFile('summary.json')).then( summaryData => { logger.info('Additional documentation: summary.json file found'); const parsedSummaryData = JSON.parse(summaryData); let that = this; let lastLevelOnePage = undefined; traverse(parsedSummaryData).forEach(function () { // tslint:disable-next-line:no-invalid-this if (this.notRoot && typeof this.node === 'object') { // tslint:disable-next-line:no-invalid-this let rawPath = this.path; // tslint:disable-next-line:no-invalid-this let additionalNode: AdditionalNode = this.node; let file = additionalNode.file; let title = additionalNode.title; let finalPath = Configuration.mainData.includesFolder; let finalDepth = rawPath.filter(el => { return !isNaN(parseInt(el, 10)); }); if (typeof file !== 'undefined' && typeof title !== 'undefined') { const url = cleanNameWithoutSpaceAndToLowerCase(title); /** * Id created with title + file path hash, seems to be hypothetically unique here */ const id = crypto .createHash('sha512') .update(title + file) .digest('hex'); // tslint:disable-next-line:no-invalid-this this.node.id = id; let lastElementRootTree = undefined; finalDepth.forEach(el => { let elementTree = typeof lastElementRootTree === 'undefined' ? parsedSummaryData : lastElementRootTree; if (typeof elementTree.children !== 'undefined') { elementTree = elementTree.children[el]; } else { elementTree = elementTree[el]; } finalPath += '/' + cleanNameWithoutSpaceAndToLowerCase(elementTree.title); lastElementRootTree = elementTree; }); finalPath = finalPath.replace('/' + url, ''); let markdownFile = MarkdownEngine.getTraditionalMarkdownSync( that.getIncludedPathForFile(file) ); if (finalDepth.length > 5) { logger.error('Only 5 levels of depth are supported'); } else { let _page = { name: title, id: id, filename: url, context: 'additional-page', path: finalPath, additionalPage: markdownFile, depth: finalDepth.length, childrenLength: additionalNode.children ? additionalNode.children.length : 0, children: [], lastChild: false, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.INTERNAL }; if (finalDepth.length === 1) { lastLevelOnePage = _page; } if (finalDepth.length > 1) { // store all child pages of the last root level 1 page inside it lastLevelOnePage.children.push(_page); } else { Configuration.addAdditionalPage(_page); } } } } }); resolve(); }, errorMessage => { logger.error(errorMessage); reject('Error during Additional documentation generation'); } ); }); } public prepareModules(someModules?): Promise<any> { logger.info('Prepare modules'); let i = 0; let _modules = someModules ? someModules : DependenciesEngine.getModules(); return new Promise((resolve, reject) => { Configuration.mainData.modules = _modules.map(ngModule => { ngModule.compodocLinks = { components: [], controllers: [], directives: [], injectables: [], pipes: [] }; ['declarations', 'bootstrap', 'imports', 'exports', 'controllers'].forEach( metadataType => { ngModule[metadataType] = ngModule[metadataType].filter(metaDataItem => { switch (metaDataItem.type) { case 'directive': return DependenciesEngine.getDirectives().some(directive => { let selectedDirective; if (typeof metaDataItem.id !== 'undefined') { selectedDirective = (directive as any).id === metaDataItem.id; } else { selectedDirective = (directive as any).name === metaDataItem.name; } if ( selectedDirective && !ngModule.compodocLinks.directives.includes(directive) ) { ngModule.compodocLinks.directives.push(directive); } return selectedDirective; }); case 'component': return DependenciesEngine.getComponents().some(component => { let selectedComponent; if (typeof metaDataItem.id !== 'undefined') { selectedComponent = (component as any).id === metaDataItem.id; } else { selectedComponent = (component as any).name === metaDataItem.name; } if ( selectedComponent && !ngModule.compodocLinks.components.includes(component) ) { ngModule.compodocLinks.components.push(component); } return selectedComponent; }); case 'controller': return DependenciesEngine.getControllers().some(controller => { let selectedController; if (typeof metaDataItem.id !== 'undefined') { selectedController = (controller as any).id === metaDataItem.id; } else { selectedController = (controller as any).name === metaDataItem.name; } if ( selectedController && !ngModule.compodocLinks.controllers.includes(controller) ) { ngModule.compodocLinks.controllers.push(controller); } return selectedController; }); case 'module': return DependenciesEngine.getModules().some( module => (module as any).name === metaDataItem.name ); case 'pipe': return DependenciesEngine.getPipes().some(pipe => { let selectedPipe; if (typeof metaDataItem.id !== 'undefined') { selectedPipe = (pipe as any).id === metaDataItem.id; } else { selectedPipe = (pipe as any).name === metaDataItem.name; } if ( selectedPipe && !ngModule.compodocLinks.pipes.includes(pipe) ) { ngModule.compodocLinks.pipes.push(pipe); } return selectedPipe; }); default: return true; } }); } ); ngModule.providers = ngModule.providers.filter(provider => { return ( DependenciesEngine.getInjectables().some(injectable => { let selectedInjectable = (injectable as any).name === provider.name; if ( selectedInjectable && !ngModule.compodocLinks.injectables.includes(injectable) ) { ngModule.compodocLinks.injectables.push(injectable); } return selectedInjectable; }) || DependenciesEngine.getInterceptors().some( interceptor => (interceptor as any).name === provider.name ) ); }); // Try fixing type undefined for each providers _.forEach(ngModule.providers, provider => { if ( DependenciesEngine.getInjectables().find( injectable => (injectable as any).name === provider.name ) ) { provider.type = 'injectable'; } if ( DependenciesEngine.getInterceptors().find( interceptor => (interceptor as any).name === provider.name ) ) { provider.type = 'interceptor'; } }); // Order things ngModule.compodocLinks.components = _.sortBy(ngModule.compodocLinks.components, [ 'name' ]); ngModule.compodocLinks.controllers = _.sortBy(ngModule.compodocLinks.controllers, [ 'name' ]); ngModule.compodocLinks.directives = _.sortBy(ngModule.compodocLinks.directives, [ 'name' ]); ngModule.compodocLinks.injectables = _.sortBy(ngModule.compodocLinks.injectables, [ 'name' ]); ngModule.compodocLinks.pipes = _.sortBy(ngModule.compodocLinks.pipes, ['name']); ngModule.declarations = _.sortBy(ngModule.declarations, ['name']); ngModule.entryComponents = _.sortBy(ngModule.entryComponents, ['name']); ngModule.providers = _.sortBy(ngModule.providers, ['name']); ngModule.imports = _.sortBy(ngModule.imports, ['name']); ngModule.exports = _.sortBy(ngModule.exports, ['name']); return ngModule; }); Configuration.addPage({ name: 'modules', id: 'modules', context: 'modules', depth: 0, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT }); let len = Configuration.mainData.modules.length; let loop = () => { if (i < len) { if ( MarkdownEngine.hasNeighbourReadmeFile( Configuration.mainData.modules[i].file ) ) { logger.info( ` ${Configuration.mainData.modules[i].name} has a README file, include it` ); let readme = MarkdownEngine.readNeighbourReadmeFile( Configuration.mainData.modules[i].file ); Configuration.mainData.modules[i].readme = marked(readme); } Configuration.addPage({ path: 'modules', name: Configuration.mainData.modules[i].name, id: Configuration.mainData.modules[i].id, navTabs: this.getNavTabs(Configuration.mainData.modules[i]), context: 'module', module: Configuration.mainData.modules[i], depth: 1, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.INTERNAL }); i++; loop(); } else { resolve(); } }; loop(); }); } public preparePipes = (somePipes?) => { logger.info('Prepare pipes'); Configuration.mainData.pipes = somePipes ? somePipes : DependenciesEngine.getPipes(); return new Promise((resolve, reject) => { let i = 0; let len = Configuration.mainData.pipes.length; let loop = () => { if (i < len) { let pipe = Configuration.mainData.pipes[i]; if (MarkdownEngine.hasNeighbourReadmeFile(pipe.file)) { logger.info(` ${pipe.name} has a README file, include it`); let readme = MarkdownEngine.readNeighbourReadmeFile(pipe.file); pipe.readme = marked(readme); } let page = { path: 'pipes', name: pipe.name, id: pipe.id, navTabs: this.getNavTabs(pipe), context: 'pipe', pipe: pipe, depth: 1, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.INTERNAL }; if (pipe.isDuplicate) { page.name += '-' + pipe.duplicateId; } Configuration.addPage(page); i++; loop(); } else { resolve(); } }; loop(); }); }; public prepareClasses = (someClasses?) => { logger.info('Prepare classes'); Configuration.mainData.classes = someClasses ? someClasses : DependenciesEngine.getClasses(); return new Promise((resolve, reject) => { let i = 0; let len = Configuration.mainData.classes.length; let loop = () => { if (i < len) { let classe = Configuration.mainData.classes[i]; if (MarkdownEngine.hasNeighbourReadmeFile(classe.file)) { logger.info(` ${classe.name} has a README file, include it`); let readme = MarkdownEngine.readNeighbourReadmeFile(classe.file); classe.readme = marked(readme); } let page = { path: 'classes', name: classe.name, id: classe.id, navTabs: this.getNavTabs(classe), context: 'class', class: classe, depth: 1, pageType: COMPODOC_DEFAULTS.PAGE_TYPES.INTERNAL }; if (classe.isDuplicate) { page.name += '-' + classe.duplicateId; } Configuration.addPage(page); i++; loop(); } else { resolve(); } }; loop(); }); }; public prepareInterfaces(someInterfaces?) { logger.info('Prepare interfaces'); Configuration.mainData.interfaces = someInterfaces ? someInterfaces : DependenciesEngine.getInterfaces(); return new Promise((resolve, reject) => { let i = 0; let len = Configuration.mainData.interfaces.length; let loop = () => { if (i < len) { let interf = Configuration.mainData.interfaces[i]; if (MarkdownEngine.hasNeighbourReadmeFile(interf.file)) { logger.info(` ${interf.name} has a README file, include it`); let readme = MarkdownEngine.readNeighbourReadmeFile(interf.file); interf.readme = marked(readme); } let page = { path: 'interfaces', name: interf.name,