UNPKG

@bolt/build-tools

Version:

Curated collection of front-end build tools in the Bolt Design System.

280 lines (249 loc) 8.57 kB
const path = require('path'); const log = require('@bolt/build-utils/log'); const manifest = require('@bolt/build-utils/manifest'); const timer = require('@bolt/build-utils/timer'); const { getConfig } = require('@bolt/build-utils/config-store'); const events = require('@bolt/build-utils/events'); const webpackTasks = require('./webpack-tasks'); // const criticalcssTasks = require('./criticalcss-tasks'); const internalTasks = require('./internal-tasks'); const iconComponentTasks = require('./icon-component-tasks'); const iconTasks = require('./icon-tasks'); const extraTasks = []; let config; // These tasks are present based on optional conditions like `config.env` and should only be `require`-ed when it's the right env due to each file's setup where it tries to grab specific files - and of course the tasks should only run in the correct `env` as well. async function getExtraTasks() { config = config || (await getConfig()); switch (config.env) { case 'pl': extraTasks.patternLab = require('./pattern-lab-tasks'); break; case 'static': extraTasks.static = require('./static-tasks'); break; case 'pwa': delete require.cache[require.resolve('./api-tasks')]; extraTasks.api = require('./api-tasks'); extraTasks.patternLab = require('./pattern-lab-tasks'); extraTasks.static = require('./static-tasks'); break; } if (config.wwwDir) { extraTasks.server = require('./server-tasks'); } return extraTasks; } async function compileBasedOnEnvironment() { await getExtraTasks(); const promiseTasks = []; switch (config.env) { case 'pl': promiseTasks.push(await extraTasks.patternLab.precompile()); break; case 'static': promiseTasks.push(await extraTasks.static.compile()); break; case 'pwa': promiseTasks.push( new Promise(async (resolve, reject) => { await extraTasks.static.compile(); resolve(); }), ); promiseTasks.push( new Promise(async (resolve, reject) => { await extraTasks.patternLab.compile(true, true).then(async () => { await extraTasks.api.generate().then(async () => { await extraTasks.patternLab.precompile(); }); resolve(); }); }), ); } return promiseTasks; } async function clean(cleanAll = false) { config = config || (await getConfig()); try { let dirs = []; switch (config.env) { case 'static': // If we have a pattern lab site built in a static site, like this folder structure: // - www/ // - build/ // - docs/ // - pattern-lab/ // - build/ // We need to be careful; we want to delete everything in there but a `pattern-lab` folder (yes, that's a hard coded magic string that'll we'll have to update if we change `publicDir` in `config.yml` in pattern lab) // Also when we use `del` (our clean task), we have to explicitly ignore parent directories: https://www.npmjs.com/package/del#beware // On top of that, you can't do `!www/`, you must do `!www` - if you ignore a directory, it MUST NOT have a trailing slash, so we pass it through `path.resolve()` which handles that for us. That can't handle `**` though, but `path.join()` can. dirs = [ path.join(path.resolve(config.wwwDir), '**'), `!${path.resolve(config.wwwDir)}`, `!${path.resolve(config.wwwDir, 'pattern-lab/styleguide')}`, // @todo Remove hard-coded magic string of `pattern-lab` sub folder `!${path.join( path.resolve(config.wwwDir, 'pattern-lab/styleguide'), '**', )}`, ]; break; case 'pl': dirs = [ path.join(config.wwwDir, 'pattern-lab/**'), `!${path.join(config.wwwDir, 'pattern-lab')}`, // don't delete the pl folder itself `!${path.join(config.wwwDir, 'pattern-lab/index.html')}`, // or pl's index.html file `!${path.join(config.wwwDir, 'pattern-lab/styleguide')}`, // or the pl assets `!${path.join(config.wwwDir, 'pattern-lab/styleguide/**')}`, ]; break; case 'pwa': dirs = [ `${path.join( path.resolve(config.wwwDir, 'pattern-lab/patterns'), '**', )}`, ]; break; default: dirs = [config.buildDir]; break; } if (cleanAll === true && config.env !== 'pwa') { dirs = [config.wwwDir]; } await internalTasks.clean(dirs); } catch (error) { log.errorAndExit('Clean failed', error); } } async function serve(buildTime = timer.start()) { config = config || (await getConfig()); await getExtraTasks(); try { const serverTasks = []; // if (config.renderingService) { // serverTasks.push(extraTasks.server.phpServer()); // } if (config.wwwDir) { if (config.webpackDevServer && config.watch !== false) { serverTasks.push(webpackTasks.server()); } else if (config.webpackDevServer === false && config.watch !== false) { serverTasks.push(extraTasks.server.serve()); } } return Promise.all(serverTasks); } catch (error) { log.errorAndExit('Serve failed', error); } } // async function criticalcss() { // try { // const criticalTasks = []; // criticalTasks.push(criticalcssTasks.build()); // return Promise.all(criticalTasks); // } catch (error) { // log.errorAndExit('Critical CSS failed', error); // } // } async function buildPrep(cleanAll = false) { config = config || (await getConfig()); try { await getExtraTasks(); config.prod ? await clean(cleanAll) : ''; await internalTasks.mkDirs(); await manifest.writeBoltManifest(); if ( config.env === 'pl' || config.env === 'static' || config.env === 'pwa' ) { // Do not run in Drupal environment. Lerna is not available there. await require('./api-tasks/bolt-versions').writeBoltVersions(); } await manifest.writeTwigNamespaceFile(); } catch (error) { log.errorAndExit('Build failed', error); } } async function build(shouldReturnTime = false) { const startTime = timer.start(); config = config || (await getConfig()); try { await buildPrep(startTime); // only try to generate / update the SVG manifest for pwa / non-Drupal builds switch (config.env) { case 'pl': case 'static': case 'pwa': // TODO: Remove `iconComponentTasks` when the Icon/Icons Component is removed await iconComponentTasks.build(); await iconTasks.build(); } config.prod || config.watch === false ? await webpackTasks.compile() : ''; config.prod || config.watch === false ? await Promise.all(await compileBasedOnEnvironment()) : ''; await internalTasks.writeMetadata(); if (shouldReturnTime) { return startTime; } else { log.info(`Build completed in ${timer.end(startTime)}.`); } } catch (error) { log.errorAndExit('Build failed', error); } } async function watch() { config = config || (await getConfig()); try { const watchTasks = []; // if webpackDevServer isn't defined or is disabled, use webpack watch mode instead if (!config.webpackDevServer) { watchTasks.push(webpackTasks.watch()); } switch (config.env) { case 'pl': watchTasks.push(extraTasks.patternLab.watch()); break; case 'static': watchTasks.push(extraTasks.static.watch()); break; case 'pwa': watchTasks.push(extraTasks.patternLab.watch()); watchTasks.push(extraTasks.api.watch()); watchTasks.push(extraTasks.static.watch()); // TODO: Remove `iconComponentTasks` when the Icon/Icons Component is removed watchTasks.push(iconComponentTasks.watch()); watchTasks.push(iconTasks.watch()); break; } return Promise.all(watchTasks); } catch (error) { log.errorAndExit('Watch failed', error); } } async function start() { let buildTime; config = config || (await getConfig()); try { if (!config.quick) { buildTime = await build(true); } await Promise.all(await compileBasedOnEnvironment()).then(async () => { await watch(); await serve(buildTime); }); } catch (error) { log.errorAndExit('Start failed', error); } } module.exports = { serve, start, build, buildPrep, watch, clean, // criticalcss, };