@compodoc/compodoc
Version:
The missing documentation tool for your Angular application
1,239 lines (1,111 loc) • 123 kB
text/typescript
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,