require('require-yaml'); const gulp = require('gulp'); const PluginError = require('plugin-error'); const argv = require('minimist')(process.argv.slice(2)); const log = require('fancy-log'); const Myt = require('@verdnatura/myt/myt'); const Run = require('@verdnatura/myt/myt-run'); const Start = require('@verdnatura/myt/myt-start'); // Configuration let isWindows = /^win/.test(process.platform); if (argv.NODE_ENV) process.env.NODE_ENV = argv.NODE_ENV; let langs = ['es', 'en']; let srcDir = './front'; let modulesDir = './modules'; let buildDir = 'dist'; let backSources = [ '!node_modules', '!loopback/locale/*.json', 'loopback', 'modules/*/back/**', 'modules/*/back/*', 'back', 'print' ]; // Development const localesRoutes = gulp.parallel(locales, routes); localesRoutes.description = `Builds locales and routes`; const front = gulp.series(clean, gulp.parallel(localesRoutes, watch, webpackDevServer)); front.description = `Starts frontend service`; function backOnly(done) { let app = require(`./loopback/server/server`); app.start(); app.on('started', done); } backOnly.description = `Starts backend service`; function backWatch(done) { const nodemon = require('gulp-nodemon'); // XXX: Workaround to avoid nodemon bug // https://github.com/remy/nodemon/issues/1346 let commands = ['node --tls-min-v1.0 --inspect ./node_modules/gulp/bin/gulp.js']; if (!isWindows) commands.unshift('sleep 1'); nodemon({ exec: commands.join(' && '), ext: 'js html css json', args: ['backOnly'], watch: backSources, done: done }); } backWatch.description = `Starts backend in watcher mode`; const back = gulp.series(dockerStart, backWatch); back.description = `Starts backend and database service`; const defaultTask = gulp.parallel(front, back); defaultTask.description = `Starts all application services`; function install() { const install = require('gulp-install'); const print = require('gulp-print'); let npmArgs = []; if (argv.ci) npmArgs = ['--no-audit', '--prefer-offline']; let packageFiles = ['front/package.json', 'print/package.json']; return gulp.src(packageFiles) .pipe(print(filepath => { return `Installing packages in ${filepath}`; })) .pipe(install({npm: npmArgs})); } install.description = `Installs node dependencies in all directories`; const i = gulp.series(install); i.description = `Alias for the 'install' task`; // Deployment const build = gulp.series(clean, gulp.parallel(localesRoutes, webpack)); build.description = `Generates binaries and configuration files`; function clean() { const del = require('del'); const files = [ `${buildDir}/*` ]; return del(files, {force: true}); } clean.description = `Cleans all files generated by the 'build' task`; // Webpack function webpack(done) { const webpackCompile = require('webpack'); const merge = require('webpack-merge'); let wpConfig = require('./webpack.config.js'); wpConfig = merge(wpConfig, {}); let compiler = webpackCompile(wpConfig); compiler.run(function(err, stats) { if (err) throw new PluginError('webpack', err); log('[webpack]', stats.toString(wpConfig.stats)); done(); }); } webpack.description = `Transpiles application into files`; function webpackDevServer(done) { const webpack = require('webpack'); const merge = require('webpack-merge'); const WebpackDevServer = require('webpack-dev-server'); let wpConfig = require('./webpack.config.js'); wpConfig = merge(wpConfig, {}); let devServer = wpConfig.devServer; for (let entryName in wpConfig.entry) { let entry = wpConfig.entry[entryName]; if (!Array.isArray(entry)) entry = [entry]; let wdsAssets = [ `webpack-dev-server/client?http://localhost:${devServer.port}/`, `webpack/hot/dev-server` ]; wpConfig.entry[entryName] = wdsAssets.concat(entry); } let compiler = webpack(wpConfig); new WebpackDevServer(compiler, wpConfig.devServer) .listen(devServer.port, devServer.host, function(err) { if (err) throw new PluginError('webpack-dev-server', err); // XXX: Keep the server alive or continue? done(); }); } webpackDevServer.description = `Transpiles application into memory`; // Locale let localeFiles = [ `${srcDir}/**/locale/*.yml`, `${modulesDir}/*/front/**/locale/*.yml` ]; /** * Mixes all locale files into one JSON file per module and language. It looks * recursively in all project directories for locale folders with per language * yaml translation files. * * @return {Stream} The merged gulp streams */ function locales() { const mergeJson = require('gulp-merge-json'); const gulpFile = require('gulp-file'); const yaml = require('gulp-yaml'); const merge = require('merge-stream'); const fs = require('fs-extra'); let streams = []; let localePaths = []; let modules = fs.readdirSync(modulesDir); for (let mod of modules) localePaths[mod] = `${modulesDir}/${mod}`; let baseMods = ['core', 'salix']; for (let mod of baseMods) localePaths[mod] = `${srcDir}/${mod}`; for (let mod in localePaths) { let path = localePaths[mod]; for (let lang of langs) { let localeFiles = `${path}/**/locale/${lang}.yml`; streams.push(gulp.src(localeFiles) .pipe(yaml()) .pipe(mergeJson({fileName: `${lang}.json`})) .pipe(gulp.dest(`${buildDir}/locale/${mod}`))); } } for (let mod in localePaths) { for (let lang of langs) { let file = `${buildDir}/locale/${mod}/${lang}.json`; if (fs.existsSync(file)) continue; streams.push(gulpFile('en.json', '{}', {src: true}) .pipe(gulp.dest(`${buildDir}/locale/${mod}`))); } } return merge(streams); } locales.description = `Generates client locale files`; // Routes let routeFiles = `${modulesDir}/*/front/routes.json`; function routes() { const concat = require('gulp-concat'); const wrap = require('gulp-wrap'); return gulp.src(routeFiles) .pipe(concat('routes.js', {newLine: ','})) .pipe(wrap('var routes = [<%=contents%>\n];')) .pipe(gulp.dest(buildDir)); } routes.description = 'Merges all module routes file into one file'; // Watch function watch(done) { gulp.watch(routeFiles, gulp.series(routes)); gulp.watch(localeFiles, gulp.series(locales)); done(); } watch.description = `Watches for changes in routes and locale files`; // Docker async function dockerStart() { const myt = new Myt(); await myt.init({workspace: __dirname}); await myt.run(Start); await myt.deinit(); } dockerStart.description = `Starts the DB container`; async function docker() { const myt = new Myt(); await myt.init({workspace: __dirname}); await myt.run(Run); await myt.deinit(); } docker.description = `Builds and starts the DB container`; module.exports = { default: defaultTask, front, back, backOnly, backWatch, i, install, build, clean, webpack, webpackDevServer, routes, locales, localesRoutes, watch, dockerStart, docker };