| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 | /* eslint-disable indent, no-process-exit *//** * Helper methods for running JSDoc on the command line. * * A few critical notes for anyone who works on this module: * * + The module should really export an instance of `cli`, and `props` should be properties of a * `cli` instance. * * @private */module.exports = (() => {    const app = require('jsdoc/app');    const env = require('jsdoc/env');    const logger = require('jsdoc/util/logger');    const stripBom = require('jsdoc/util/stripbom');    const stripJsonComments = require('strip-json-comments');    const Promise = require('bluebird');    const props = {        docs: [],        packageJson: null,        shouldExitWithError: false,        tmpdir: null    };    const FATAL_ERROR_MESSAGE = 'Exiting JSDoc because an error occurred. See the previous log ' +        'messages for details.';    const cli = {};    // TODO: docs    cli.setVersionInfo = () => {        const fs = require('fs');        const path = require('path');        // allow this to throw--something is really wrong if we can't read our own package file        const info = JSON.parse( stripBom.strip(fs.readFileSync(path.join(env.dirname, 'package.json'),            'utf8')) );        env.version = {            number: info.version,            revision: new Date( parseInt(info.revision, 10) ).toUTCString()        };        return cli;    };    // TODO: docs    cli.loadConfig = () => {        const _ = require('underscore');        const args = require('jsdoc/opts/args');        const Config = require('jsdoc/config');        let config;        const fs = require('jsdoc/fs');        const path = require('jsdoc/path');        let confPath;        let isFile;        const defaultOpts = {            destination: './out/',            encoding: 'utf8'        };        try {            env.opts = args.parse(env.args);        }        catch (e) {            console.error(`${e.message}\n`);            cli.printHelp().then(() => {                cli.exit(1);            });        }        confPath = env.opts.configure || path.join(env.dirname, 'conf.json');        try {            isFile = fs.statSync(confPath).isFile();        }        catch (e) {            isFile = false;        }        if ( !isFile && !env.opts.configure ) {            confPath = path.join(env.dirname, 'conf.json.EXAMPLE');        }        try {            switch ( path.extname(confPath) ) {                case '.js':                    config = require( path.resolve(confPath) ) || {};                    break;                case '.json':                case '.EXAMPLE':                default:                    config = fs.readFileSync(confPath, 'utf8');                    break;            }            env.conf = new Config(config).get();        }        catch (e) {            cli.exit(1, `Cannot parse the config file ${confPath}: ${e}\n${FATAL_ERROR_MESSAGE}`);        }        // look for options on the command line, in the config file, and in the defaults, in that order        env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts);        return cli;    };    // TODO: docs    cli.configureLogger = () => {        function recoverableError() {            props.shouldExitWithError = true;        }        function fatalError() {            cli.exit(1);        }        if (env.opts.debug) {            logger.setLevel(logger.LEVELS.DEBUG);        }        else if (env.opts.verbose) {            logger.setLevel(logger.LEVELS.INFO);        }        if (env.opts.pedantic) {            logger.once('logger:warn', recoverableError);            logger.once('logger:error', fatalError);        }        else {            logger.once('logger:error', recoverableError);        }        logger.once('logger:fatal', fatalError);        return cli;    };    // TODO: docs    cli.logStart = () => {        logger.debug( cli.getVersion() );        logger.debug('Environment info: %j', {            env: {                conf: env.conf,                opts: env.opts            }        });    };    // TODO: docs    cli.logFinish = () => {        let delta;        let deltaSeconds;        if (env.run.finish && env.run.start) {            delta = env.run.finish.getTime() - env.run.start.getTime();        }        if (delta !== undefined) {            deltaSeconds = (delta / 1000).toFixed(2);            logger.info('Finished running in %s seconds.', deltaSeconds);        }    };    // TODO: docs    cli.runCommand = cb => {        let cmd;        const opts = env.opts;        if (opts.help) {            cmd = cli.printHelp;        }        else if (opts.test) {            cmd = cli.runTests;        }        else if (opts.version) {            cmd = cli.printVersion;        }        else {            cmd = cli.main;        }        cmd().then(errorCode => {            if (!errorCode && props.shouldExitWithError) {                errorCode = 1;            }            cb(errorCode);        });    };    // TODO: docs    cli.printHelp = () => {        cli.printVersion();        console.log( `\n${require('jsdoc/opts/args').help()}\n` );        console.log('Visit https://jsdoc.app/ for more information.');        return Promise.resolve(0);    };    // TODO: docs    cli.runTests = () => {        const path = require('jsdoc/path');        const runner = Promise.promisify(require( path.join(env.dirname, 'test/runner') ));        console.log('Running tests...');        return runner();    };    // TODO: docs    cli.getVersion = () => `JSDoc ${env.version.number} (${env.version.revision})`;    // TODO: docs    cli.printVersion = () => {        console.log( cli.getVersion() );        return Promise.resolve(0);    };    // TODO: docs    cli.main = () => {        cli.scanFiles();        if (env.sourceFiles.length === 0) {            console.log('There are no input files to process.');            return Promise.resolve(0);        } else {            return cli.createParser()                .parseFiles()                .processParseResults()                .then(() => {                    env.run.finish = new Date();                    return 0;                });        }    };    function readPackageJson(filepath) {        const fs = require('jsdoc/fs');        try {            return stripJsonComments( fs.readFileSync(filepath, 'utf8') );        }        catch (e) {            logger.error('Unable to read the package file "%s"', filepath);            return null;        }    }    function buildSourceList() {        const Readme = require('jsdoc/readme');        let packageJson;        let readmeHtml;        let sourceFile;        let sourceFiles = env.opts._ ? env.opts._.slice(0) : [];        if (env.conf.source && env.conf.source.include) {            sourceFiles = sourceFiles.concat(env.conf.source.include);        }        // load the user-specified package/README files, if any        if (env.opts.package) {            packageJson = readPackageJson(env.opts.package);        }        if (env.opts.readme) {            readmeHtml = new Readme(env.opts.readme).html;        }        // source files named `package.json` or `README.md` get special treatment, unless the user        // explicitly specified a package and/or README file        for (let i = 0, l = sourceFiles.length; i < l; i++) {            sourceFile = sourceFiles[i];            if ( !env.opts.package && /\bpackage\.json$/i.test(sourceFile) ) {                packageJson = readPackageJson(sourceFile);                sourceFiles.splice(i--, 1);            }            if ( !env.opts.readme && /(\bREADME|\.md)$/i.test(sourceFile) ) {                readmeHtml = new Readme(sourceFile).html;                sourceFiles.splice(i--, 1);            }        }        props.packageJson = packageJson;        env.opts.readme = readmeHtml;        return sourceFiles;    }    // TODO: docs    cli.scanFiles = () => {        const Filter = require('jsdoc/src/filter').Filter;        let filter;        env.opts._ = buildSourceList();        // are there any files to scan and parse?        if (env.conf.source && env.opts._.length) {            filter = new Filter(env.conf.source);            env.sourceFiles = app.jsdoc.scanner.scan(env.opts._,                (env.opts.recurse ? env.conf.recurseDepth : undefined), filter);        }        return cli;    };    function resolvePluginPaths(paths) {        const path = require('jsdoc/path');        const pluginPaths = [];        paths.forEach(plugin => {            const basename = path.basename(plugin);            const dirname = path.dirname(plugin);            const pluginPath = path.getResourcePath(dirname, basename);            if (!pluginPath) {                logger.error('Unable to find the plugin "%s"', plugin);                return;            }            pluginPaths.push( pluginPath );        });        return pluginPaths;    }    cli.createParser = () => {        const handlers = require('jsdoc/src/handlers');        const parser = require('jsdoc/src/parser');        const plugins = require('jsdoc/plugins');        app.jsdoc.parser = parser.createParser(env.conf.parser);        if (env.conf.plugins) {            env.conf.plugins = resolvePluginPaths(env.conf.plugins);            plugins.installPlugins(env.conf.plugins, app.jsdoc.parser);        }        handlers.attachTo(app.jsdoc.parser);        return cli;    };    cli.parseFiles = () => {        const augment = require('jsdoc/augment');        const borrow = require('jsdoc/borrow');        const Package = require('jsdoc/package').Package;        let docs;        let packageDocs;        props.docs = docs = app.jsdoc.parser.parse(env.sourceFiles, env.opts.encoding);        // If there is no package.json, just create an empty package        packageDocs = new Package(props.packageJson);        packageDocs.files = env.sourceFiles || [];        docs.push(packageDocs);        logger.debug('Adding inherited symbols, mixins, and interface implementations...');        augment.augmentAll(docs);        logger.debug('Adding borrowed doclets...');        borrow.resolveBorrows(docs);        logger.debug('Post-processing complete.');        app.jsdoc.parser.fireProcessingComplete(docs);        return cli;    };    cli.processParseResults = () => {        if (env.opts.explain) {            cli.dumpParseResults();            return Promise.resolve();        }        else {            cli.resolveTutorials();            return cli.generateDocs();        }    };    cli.dumpParseResults = () => {        console.log(require('jsdoc/util/dumper').dump(props.docs));        return cli;    };    cli.resolveTutorials = () => {        const resolver = require('jsdoc/tutorial/resolver');        if (env.opts.tutorials) {            resolver.load(env.opts.tutorials);            resolver.resolve();        }        return cli;    };    cli.generateDocs = () => {        const path = require('jsdoc/path');        const resolver = require('jsdoc/tutorial/resolver');        const { taffy } = require('@jsdoc/salty');        let template;        env.opts.template = (() => {            const publish = env.opts.template || 'templates/default';            const templatePath = path.getResourcePath(publish);            // if we didn't find the template, keep the user-specified value so the error message is            // useful            return templatePath || env.opts.template;        })();        try {            template = require(`${env.opts.template}/publish`);        }        catch (e) {            logger.fatal(`Unable to load template: ${e.message}` || e);        }        // templates should include a publish.js file that exports a "publish" function        if (template.publish && typeof template.publish === 'function') {            let publishPromise;            logger.info('Generating output files...');            publishPromise = template.publish(                taffy(props.docs),                env.opts,                resolver.root            );            return Promise.resolve(publishPromise);        }        else {            logger.fatal(`${env.opts.template} does not export a "publish" function. Global "publish" functions are no longer supported.`);        }        return Promise.resolve();    };    // TODO: docs    cli.exit = (exitCode, message) => {        if (exitCode > 0 && message) {            console.error(message);        }        process.on('exit', () => { process.exit(exitCode); });    };    return cli;})();
 |