|
| 1 | +const argv = require('minimist')(process.argv.slice(2)); |
| 2 | +const debounce = require('../common/debounce'); |
| 3 | +const { |
| 4 | + buildBinaries, |
| 5 | + buildExtensionCss, |
| 6 | + buildExtensionJs, |
| 7 | + buildNpmBinaries, |
| 8 | + buildNpmCss, |
| 9 | + declareExtension, |
| 10 | + getBentoBuildFilename, |
| 11 | + getExtensionsFromArg, |
| 12 | +} = require('./extension-helpers'); |
| 13 | +const {bentoBundles, verifyBentoBundles} = require('../compile/bundles.config'); |
| 14 | +const {endBuildStep, watchDebounceDelay} = require('./helpers'); |
| 15 | +const {getBentoName} = require('./bento-helpers'); |
| 16 | +const {log} = require('../common/logging'); |
| 17 | +const {mkdirSync} = require('fs'); |
| 18 | +const {red} = require('kleur/colors'); |
| 19 | +const {watch} = require('chokidar'); |
| 20 | + |
| 21 | +// All declared components. |
| 22 | +const COMPONENTS = {}; |
| 23 | + |
| 24 | +/** |
| 25 | + * Initializes all components from build-system/compile/bundles.config.bento.json |
| 26 | + * if not already done and populates the given components object. |
| 27 | + * @param {Object} componentsObject |
| 28 | + */ |
| 29 | +function maybeInitializeBentoComponents(componentsObject) { |
| 30 | + if (Object.keys(componentsObject).length > 0) { |
| 31 | + return; |
| 32 | + } |
| 33 | + verifyBentoBundles(); |
| 34 | + bentoBundles.forEach((c) => { |
| 35 | + declareExtension(c.name, c.version, c.options, componentsObject); |
| 36 | + }); |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * Process the command line arguments --nocomponents, --components, and |
| 41 | + * --components_from and return a list of the referenced components. |
| 42 | + * |
| 43 | + * @param {boolean} preBuild Used for lazy building of components. |
| 44 | + * @return {!Array<string>} |
| 45 | + */ |
| 46 | +function getBentoComponentsToBuild(preBuild = false) { |
| 47 | + const componentsToBuild = new Set(); |
| 48 | + if (argv.extensions) { |
| 49 | + if (typeof argv.extensions !== 'string') { |
| 50 | + log(red('ERROR:'), 'Missing list of components.'); |
| 51 | + process.exit(1); |
| 52 | + } |
| 53 | + const explicitComponents = argv.extensions.replace(/\s/g, '').split(','); |
| 54 | + explicitComponents.forEach((component) => componentsToBuild.add(component)); |
| 55 | + } |
| 56 | + if (argv.extensions_from) { |
| 57 | + const componentsFrom = getExtensionsFromArg(argv.extensions_from); |
| 58 | + componentsFrom.forEach((component) => componentsToBuild.add(component)); |
| 59 | + } |
| 60 | + if ( |
| 61 | + !preBuild && |
| 62 | + !argv.nocomponents && |
| 63 | + !argv.extensions && |
| 64 | + !argv.extensions_from && |
| 65 | + !argv.core_runtime_only |
| 66 | + ) { |
| 67 | + const allComponents = Object.values(COMPONENTS).map((c) => c.name); |
| 68 | + allComponents.forEach((component) => componentsToBuild.add(component)); |
| 69 | + } |
| 70 | + return Array.from(componentsToBuild); |
| 71 | +} |
| 72 | + |
| 73 | +/** |
| 74 | + * Watches for non-JS changes within an components directory to trigger |
| 75 | + * recompilation. |
| 76 | + * |
| 77 | + * @param {string} componentsDir |
| 78 | + * @param {string} name |
| 79 | + * @param {string} version |
| 80 | + * @param {boolean} hasCss |
| 81 | + * @param {?Object} options |
| 82 | + * @return {Promise<void>} |
| 83 | + */ |
| 84 | +async function watchBentoComponent( |
| 85 | + componentsDir, |
| 86 | + name, |
| 87 | + version, |
| 88 | + hasCss, |
| 89 | + options |
| 90 | +) { |
| 91 | + /** |
| 92 | + * Steps to run when a watched file is modified. |
| 93 | + */ |
| 94 | + function watchFunc() { |
| 95 | + buildBentoComponent(name, version, hasCss, { |
| 96 | + ...options, |
| 97 | + continueOnError: true, |
| 98 | + isRebuild: true, |
| 99 | + watch: false, |
| 100 | + }); |
| 101 | + } |
| 102 | + |
| 103 | + const cssDeps = `${componentsDir}/**/*.css`; |
| 104 | + const ignored = /dist/; //should not watch npm dist folders. |
| 105 | + watch([cssDeps], {ignored}).on( |
| 106 | + 'change', |
| 107 | + debounce(watchFunc, watchDebounceDelay) |
| 108 | + ); |
| 109 | +} |
| 110 | + |
| 111 | +/** |
| 112 | + * Copies components from |
| 113 | + * src/bento/components/$name/$name.js |
| 114 | + * to |
| 115 | + * dist/v0/$name-$version.js |
| 116 | + * |
| 117 | + * Optionally copies the CSS at components/$name/$version/$name.css into |
| 118 | + * a generated JS file that can be required from the components as |
| 119 | + * `import {CSS} from '../../../build/$name-0.1.css';` |
| 120 | + * |
| 121 | + * @param {string} name Name of the extension. Must be the sub directory in |
| 122 | + * the components directory and the name of the JS and optional CSS file. |
| 123 | + * @param {string} version Version of the extension. Must be identical to |
| 124 | + * the sub directory inside the extension directory |
| 125 | + * @param {boolean} hasCss Whether there is a CSS file for this extension. |
| 126 | + * @param {?Object} options |
| 127 | + * @param {!Array=} extraGlobs |
| 128 | + * @return {!Promise<void|void[]>} |
| 129 | + */ |
| 130 | +async function buildBentoComponent( |
| 131 | + name, |
| 132 | + version, |
| 133 | + hasCss, |
| 134 | + options = {}, |
| 135 | + extraGlobs |
| 136 | +) { |
| 137 | + options.extraGlobs = extraGlobs; |
| 138 | + options.npm = true; |
| 139 | + options.bento = true; |
| 140 | + |
| 141 | + if (options.compileOnlyCss && !hasCss) { |
| 142 | + return; |
| 143 | + } |
| 144 | + const componentsDir = `src/bento/components/${name}/${version}`; |
| 145 | + if (options.watch) { |
| 146 | + await watchBentoComponent(componentsDir, name, version, hasCss, options); |
| 147 | + } |
| 148 | + |
| 149 | + /** @type {Promise<void>[]} */ |
| 150 | + const promises = []; |
| 151 | + if (hasCss) { |
| 152 | + mkdirSync('build/css', {recursive: true}); |
| 153 | + promises.push(buildExtensionCss(componentsDir, name, version, options)); |
| 154 | + if (options.compileOnlyCss) { |
| 155 | + return Promise.all(promises); |
| 156 | + } |
| 157 | + } |
| 158 | + promises.push(buildNpmBinaries(componentsDir, name, options)); |
| 159 | + promises.push(buildNpmCss(componentsDir, options)); |
| 160 | + if (options.binaries) { |
| 161 | + promises.push(buildBinaries(componentsDir, options.binaries, options)); |
| 162 | + } |
| 163 | + if (options.isRebuild) { |
| 164 | + return Promise.all(promises); |
| 165 | + } |
| 166 | + |
| 167 | + const bentoName = getBentoName(name); |
| 168 | + promises.push( |
| 169 | + buildExtensionJs(componentsDir, bentoName, { |
| 170 | + ...options, |
| 171 | + wrapper: 'none', |
| 172 | + filename: await getBentoBuildFilename( |
| 173 | + componentsDir, |
| 174 | + bentoName, |
| 175 | + 'standalone', |
| 176 | + options |
| 177 | + ), |
| 178 | + // Include extension directory since our entrypoint may be elsewhere. |
| 179 | + extraGlobs: [...(options.extraGlobs || []), `${componentsDir}/**/*.js`], |
| 180 | + }) |
| 181 | + ); |
| 182 | + return Promise.all(promises); |
| 183 | +} |
| 184 | + |
| 185 | +/** |
| 186 | + * Build all the Bento components |
| 187 | + * |
| 188 | + * @param {!Object} options |
| 189 | + * @return {!Promise<void>} |
| 190 | + */ |
| 191 | +async function buildBentoComponents(options) { |
| 192 | + const startTime = Date.now(); |
| 193 | + maybeInitializeBentoComponents(COMPONENTS); |
| 194 | + const toBuild = getBentoComponentsToBuild(); |
| 195 | + const results = Object.values(COMPONENTS) |
| 196 | + .filter( |
| 197 | + (component) => options.compileOnlyCss || toBuild.includes(component.name) |
| 198 | + ) |
| 199 | + .map((component) => |
| 200 | + buildBentoComponent( |
| 201 | + component.name, |
| 202 | + component.version, |
| 203 | + component.hasCss, |
| 204 | + {...options, ...component}, |
| 205 | + component.extraGlobs |
| 206 | + ) |
| 207 | + ); |
| 208 | + |
| 209 | + await Promise.all(results); |
| 210 | + if (!options.compileOnlyCss && results.length > 0) { |
| 211 | + endBuildStep( |
| 212 | + options.minify ? 'Minified all' : 'Compiled all', |
| 213 | + 'components', |
| 214 | + startTime |
| 215 | + ); |
| 216 | + } |
| 217 | +} |
| 218 | + |
| 219 | +module.exports = { |
| 220 | + buildBentoComponents, |
| 221 | + buildComponent: buildBentoComponent, |
| 222 | + getBentoComponentsToBuild, |
| 223 | + maybeInitializeBentoComponents, |
| 224 | +}; |
0 commit comments