// // build task definitions // // run any task with "yarn build ${taskName}" // const path = require('path'); const livereload = require('gulp-livereload'); const minimist = require('minimist'); const { sync: globby } = require('globby'); const { createTask, composeSeries, composeParallel, runTask, } = require('./task'); const createManifestTasks = require('./manifest'); const createScriptTasks = require('./scripts'); const createStyleTasks = require('./styles'); const createStaticAssetTasks = require('./static'); const createEtcTasks = require('./etc'); const { BuildType, getBrowserVersionMap } = require('./utils'); // Packages required dynamically via browserify configuration in dependencies // Required for LavaMoat policy generation require('loose-envify'); require('@babel/plugin-proposal-object-rest-spread'); require('@babel/plugin-transform-runtime'); require('@babel/plugin-proposal-class-properties'); require('@babel/plugin-proposal-optional-chaining'); require('@babel/plugin-proposal-nullish-coalescing-operator'); require('@babel/preset-env'); require('@babel/preset-react'); require('@babel/core'); // ESLint-related require('@babel/eslint-parser'); require('@babel/eslint-plugin'); require('@metamask/eslint-config'); require('@metamask/eslint-config-nodejs'); require('eslint'); require('eslint-config-prettier'); require('eslint-import-resolver-node'); require('eslint-plugin-import'); require('eslint-plugin-node'); require('eslint-plugin-prettier'); require('eslint-plugin-react'); require('eslint-plugin-react-hooks'); defineAndRunBuildTasks(); function defineAndRunBuildTasks() { const { buildType, entryTask, isLavaMoat, policyOnly, shouldIncludeLockdown, shouldLintFenceFiles, skipStats, } = parseArgv(); const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera']; const browserVersionMap = getBrowserVersionMap(browserPlatforms); const ignoredFiles = getIgnoredFiles(buildType); const staticTasks = createStaticAssetTasks({ livereload, browserPlatforms, shouldIncludeLockdown, buildType, }); const manifestTasks = createManifestTasks({ browserPlatforms, browserVersionMap, buildType, }); const styleTasks = createStyleTasks({ livereload }); const scriptTasks = createScriptTasks({ browserPlatforms, buildType, ignoredFiles, isLavaMoat, livereload, policyOnly, shouldLintFenceFiles, }); const { clean, reload, zip } = createEtcTasks({ livereload, browserPlatforms, buildType, }); // build for development (livereload) createTask( 'dev', composeSeries( clean, styleTasks.dev, composeParallel( scriptTasks.dev, staticTasks.dev, manifestTasks.dev, reload, ), ), ); // build for test development (livereload) createTask( 'testDev', composeSeries( clean, styleTasks.dev, composeParallel( scriptTasks.testDev, staticTasks.dev, manifestTasks.testDev, reload, ), ), ); // build for prod release createTask( 'prod', composeSeries( clean, styleTasks.prod, composeParallel(scriptTasks.prod, staticTasks.prod, manifestTasks.prod), zip, ), ); // build just production scripts, for LavaMoat policy generation purposes createTask('scripts:prod', scriptTasks.prod); // build for CI testing createTask( 'test', composeSeries( clean, styleTasks.prod, composeParallel(scriptTasks.test, staticTasks.prod, manifestTasks.test), zip, ), ); // special build for minimal CI testing createTask('styles', styleTasks.prod); // Finally, start the build process by running the entry task. runTask(entryTask, { skipStats }); } function parseArgv() { const NamedArgs = { BuildType: 'build-type', LintFenceFiles: 'lint-fence-files', Lockdown: 'lockdown', PolicyOnly: 'policy-only', SkipStats: 'skip-stats', }; const argv = minimist(process.argv.slice(2), { boolean: [ NamedArgs.LintFenceFiles, NamedArgs.Lockdown, NamedArgs.PolicyOnly, NamedArgs.SkipStats, ], string: [NamedArgs.BuildType], default: { [NamedArgs.BuildType]: BuildType.main, [NamedArgs.LintFenceFiles]: true, [NamedArgs.Lockdown]: true, [NamedArgs.PolicyOnly]: false, [NamedArgs.SkipStats]: false, }, }); if (argv._.length !== 1) { throw new Error( `Metamask build: Expected a single positional argument, but received "${argv._.length}" arguments.`, ); } const entryTask = argv._[0]; if (!entryTask) { throw new Error('MetaMask build: No entry task specified.'); } const buildType = argv[NamedArgs.BuildType]; if (!(buildType in BuildType)) { throw new Error(`MetaMask build: Invalid build type: "${buildType}"`); } // Manually default this to `false` for dev builds only. const shouldLintFenceFiles = process.argv.includes( `--${NamedArgs.LintFenceFiles}`, ) ? argv[NamedArgs.LintFenceFiles] : !/dev/iu.test(entryTask); const policyOnly = argv[NamedArgs.PolicyOnly]; return { buildType, entryTask, isLavaMoat: process.argv[0].includes('lavamoat'), policyOnly, shouldIncludeLockdown: argv[NamedArgs.Lockdown], shouldLintFenceFiles, skipStats: argv[NamedArgs.SkipStats], }; } /** * Gets the files to be ignored by the current build, if any. * * @param {string} buildType - The type of the current build. * @returns {string[] | null} The array of files to be ignored by the current * build, or `null` if no files are to be ignored. */ function getIgnoredFiles(currentBuildType) { const excludedFiles = Object.values(BuildType) // This filter removes "main" and the current build type. The files of any // build types that remain in the array will be excluded. "main" is the // default build type, and has no files that are excluded from other builds. .filter( (buildType) => buildType !== BuildType.main && buildType !== currentBuildType, ) // Compute globs targeting files for exclusion for each excluded build // type. .reduce((excludedGlobs, excludedBuildType) => { return excludedGlobs.concat([ `../../app/**/${excludedBuildType}/**`, `../../shared/**/${excludedBuildType}/**`, `../../ui/**/${excludedBuildType}/**`, ]); }, []) // This creates absolute paths of the form: // PATH_TO_REPOSITORY_ROOT/app/**/${excludedBuildType}/** .map((pathGlob) => path.resolve(__dirname, pathGlob)); return globby(excludedFiles); }