NodeBB/NodeBB

View on GitHub
Gruntfile.js

Summary

Maintainability
A
3 hrs
Test Coverage
'use strict';

const path = require('path');
const nconf = require('nconf');

nconf.argv().env({
    separator: '__',
});
const winston = require('winston');
const { fork } = require('child_process');

const { env } = process;
let worker;

env.NODE_ENV = env.NODE_ENV || 'development';

const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
const prestart = require('./src/prestart');

prestart.loadConfig(configFile);

const db = require('./src/database');

module.exports = function (grunt) {
    const args = [];

    if (!grunt.option('verbose')) {
        args.push('--log-level=info');
        nconf.set('log-level', 'info');
    }
    prestart.setupWinston();

    grunt.initConfig({
        watch: {},
    });

    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', ['watch']);

    grunt.registerTask('init', async function () {
        const done = this.async();
        let plugins = [];
        if (!process.argv.includes('--core')) {
            await db.init();
            plugins = await db.getSortedSetRange('plugins:active', 0, -1);
            addBaseThemes(plugins);
            if (!plugins.includes('nodebb-plugin-composer-default')) {
                plugins.push('nodebb-plugin-composer-default');
            }
            if (!plugins.includes('nodebb-theme-persona')) {
                plugins.push('nodebb-theme-persona');
            }
        }

        const styleUpdated_Client = plugins.map(p => `node_modules/${p}/*.less`)
            .concat(plugins.map(p => `node_modules/${p}/*.css`))
            .concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
            .concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));

        const styleUpdated_Admin = plugins.map(p => `node_modules/${p}/*.less`)
            .concat(plugins.map(p => `node_modules/${p}/*.css`))
            .concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
            .concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));

        const clientUpdated = plugins.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
        const serverUpdated = plugins.map(p => `node_modules/${p}/*.js`)
            .concat(plugins.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));

        const templatesUpdated = plugins.map(p => `node_modules/${p}/+(public|static|templates)/**/*.tpl`);
        const langUpdated = plugins.map(p => `node_modules/${p}/+(public|static|languages)/**/*.json`);

        grunt.config(['watch'], {
            styleUpdated_Client: {
                files: [
                    'public/less/**/*.less',
                    ...styleUpdated_Client,
                ],
                options: {
                    interval: 1000,
                },
            },
            styleUpdated_Admin: {
                files: [
                    'public/less/**/*.less',
                    ...styleUpdated_Admin,
                ],
                options: {
                    interval: 1000,
                },
            },
            clientUpdated: {
                files: [
                    'public/src/**/*.js',
                    'public/vendor/**/*.js',
                    ...clientUpdated,
                    'node_modules/benchpressjs/build/benchpress.js',
                ],
                options: {
                    interval: 1000,
                },
            },
            serverUpdated: {
                files: [
                    'app.js',
                    'install/*.js',
                    'src/**/*.js',
                    'public/src/modules/translator.js',
                    'public/src/modules/helpers.js',
                    'public/src/utils.js',
                    serverUpdated,
                    '!src/upgrades/**',
                ],
                options: {
                    interval: 1000,
                },
            },
            templatesUpdated: {
                files: [
                    'src/views/**/*.tpl',
                    ...templatesUpdated,
                ],
                options: {
                    interval: 1000,
                },
            },
            langUpdated: {
                files: [
                    'public/language/en-GB/*.json',
                    'public/language/en-GB/**/*.json',
                    ...langUpdated,
                ],
                options: {
                    interval: 1000,
                },
            },
        });
        const build = require('./src/meta/build');
        if (!grunt.option('skip')) {
            await build.build(true);
        }
        run();
        done();
    });

    function run() {
        if (worker) {
            worker.kill();
        }

        const execArgv = [];
        const inspect = process.argv.find(a => a.startsWith('--inspect'));

        if (inspect) {
            execArgv.push(inspect);
        }

        worker = fork('app.js', args, {
            env,
            execArgv,
        });
    }

    grunt.task.run('init');

    grunt.event.removeAllListeners('watch');
    grunt.event.on('watch', (action, filepath, target) => {
        let compiling;
        if (target === 'styleUpdated_Client') {
            compiling = 'clientCSS';
        } else if (target === 'styleUpdated_Admin') {
            compiling = 'acpCSS';
        } else if (target === 'clientUpdated') {
            compiling = 'js';
        } else if (target === 'templatesUpdated') {
            compiling = 'tpl';
        } else if (target === 'langUpdated') {
            compiling = 'lang';
        } else if (target === 'serverUpdated') {
            // empty require cache
            const paths = ['./src/meta/build.js', './src/meta/index.js'];
            paths.forEach(p => delete require.cache[require.resolve(p)]);
            return run();
        }

        require('./src/meta/build').build([compiling], (err) => {
            if (err) {
                winston.error(err.stack);
            }
            if (worker) {
                worker.send({ compiling: compiling });
            }
        });
    });
};

function addBaseThemes(plugins) {
    let themeId = plugins.find(p => p.includes('nodebb-theme-'));
    if (!themeId) {
        return plugins;
    }
    let baseTheme;
    do {
        try {
            baseTheme = require(`${themeId}/theme`).baseTheme;
        } catch (err) {
            console.log(err);
        }

        if (baseTheme) {
            plugins.push(baseTheme);
            themeId = baseTheme;
        }
    } while (baseTheme);
    return plugins;
}