UNPKG

@interaktiv/dia-scripts

Version:

CLI toolbox with common scripts for most sort of projects at DIA

202 lines (165 loc) 5.46 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); const path = require('path'); const chalk = require('chalk'); const chokidar = require('chokidar'); const fs = require('fs-extra'); const ignore = require('ignore'); const uuid = require('uuid'); const { fromRoot } = require('../../utils'); const { runDeploy } = require('./runDeploy'); const runCssCompile = require('./runCssCompile'); const DEPLOY_IGNORE = fromRoot('.deployignore'); const DEFAULT_SRC_DIR = fromRoot('force-app/main/default/'); /** * Object that queues the deployment tasks * * @type {Object} */ const deploymentQueue = { list: [], add(task) { this.list = [...this.list, { task, uuid: uuid.v1() }]; this.walk(); }, walk() { const items = this.list.filter(item => typeof item.task === 'function'); const next = () => { const item = items.pop(); const itemIndex = this.list.findIndex(itm => itm.uuid === item.uuid); if (itemIndex > -1) { this.list = [...this.list.slice(0, itemIndex), ...this.list.slice(itemIndex + 1)]; } item.task(item.uuid); if (items.length) next(); }; next(); } }; // eslint-disable-next-line max-lines-per-function module.exports = function ({ argv }) { const sourceDir = argv.sourceDir || DEFAULT_SRC_DIR; /** * Watcher function for scss files. Transpiles them to css files * * @private * @param {string} file The path to the changed file */ const deploymentWrapper = filename => { return (file => { return async () => { const result = await runDeploy({ argv: (0, _extends2.default)({}, argv, { _: [...argv._, path.join(sourceDir, file)] }) }); if (result.status === 0) { console.log(chalk.green('Deployment successful ✅')); } else { console.error(chalk.red('Deployment failed ❌')); } }; })(filename); }; /** * Tries to deploy a file through sfdx cli * * @private * @param {String} filename The file path to be deployed */ const deploy = filename => { deploymentQueue.add(deploymentWrapper(filename)); }; /** * Default file handler. Directly deploys file. * * @private * @param {string} file The path to the file that changed */ // Mapping of file extensions related to handler funcs // Can be files, dir, array or glob const deploymentHookMap = { '**/*.scss': async file => { console.info(chalk.cyan('Compiling SCSS')); try { // Deployment should be done via the other file watcher that is watching // for `.css` file changes too await runCssCompile({ argv, file: path.join(sourceDir, file) }); } catch (error) { console.log(chalk.red(`SASS Error:\n${error.formatted || error}`)); } }, '**/*.+(js|css|html|xml|cls|class|trg|trigger|apx|apex)': file => { deploy(file); } }; /** * Handles watcher event. * Calls discrete handler for file types. * * If file type is not supported, show error message. * * @private * @param {Object} hook={} The event of the watcher * @param {Function} hook.handler The handler to execute on file change * @param {string} hook.paths The file / dir / glob to watch for changes * @param {string} file The path of the file that changed * @param {Object<Ignore>} ignoreInstance An instance of ignore() */ // eslint-disable-next-line default-param-last const watcherFassade = ({ handler, paths } = {}, file, ignoreInstance) => { // Show info message console.info(`\n${chalk.blue('File changed:')} ${file} ℹ️`); if (typeof handler !== 'function') { console.warn(chalk.yellow(`\nNo handler found for "${paths}", skipping file "${file}"`)); return; } if (ignoreInstance && ignoreInstance.ignores(path.join(file))) { console.info(`\n${chalk.blue('File ignored:')} ${file} ⚠️`); return; } handler(file); }; // Register watchers let watcherList = []; const readyQueue = Object.keys(deploymentHookMap) // Remove falsey keys .filter(Boolean) // Remove falsey values .filter(paths => deploymentHookMap[paths]) // Convert to hook config obj .map(paths => ({ paths, handler: deploymentHookMap[paths] })) // Instantiate watcher and add listener .map(hook => new Promise((resolve, reject) => { const ignoreInstance = ignore(); if (fs.existsSync(DEPLOY_IGNORE)) { ignoreInstance.add(fs.readFileSync(DEPLOY_IGNORE).toString()); } const watcher = chokidar.watch(hook.paths, { cwd: sourceDir, ignoreInitial: true }); watcherList = [...watcherList, watcher]; watcher.on('ready', () => { console.info(`\nWatching: ${hook.paths} on ${sourceDir}`); resolve(watcher); }).on('error', reject).on('add', filePath => watcherFassade(hook, filePath, ignoreInstance)).on('change', filePath => watcherFassade(hook, filePath, ignoreInstance)); })); // Add cleanup handling on `process.exit()` process.on('exit', async () => { await Promise.all([watcherList.filter(Boolean).filter(watcher => typeof watcher.close === 'function').map(watcher => watcher.close())]); }); return Promise.all(readyQueue); };