Build system refactor (#8140)
* build - start static asset task cleanup * build - simplify manifest tasks * build - refactor + rename some tasks * build - various cleanups * manifest - fix ref from controller * build - drop gulp for simple async tasks * build - breakout gulpfile into multiple files * build - rename some tasks * build - use task fn refs instead of string names * build - bundle all scripts first, except for contentscript * build - improve task timeline * deps - update lock * build - improve task time printout * build/scripts - remove intermediate named task * build - use 'yarn build' for task entry points * build - properly run tasks via runTask for timeline display * development/announcer - fix manifest path + clean * build - lint fix * build - make all defined tasks possible entry points * build/task - properly report errors during task * ci - fix sesify/lavamoat-viz build command * build/scripts - run each bundle in separate processes * lint fix * build - forward childProcess logs to console * build/task - fix parallel/series stream end event * build/scripts refactor contentscript+inpage into a single task * build/static - use the fs for 150x speedup zomg * lint fix * build/static - fix css copy * Update development/build/scripts.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/scripts.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/index.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * deps - remove redundant mkdirp * deps - remove unused pumpify * deps - remove redundant merge-deep * deps - prefer is-stream of isstream * deps - remove clone for lodash.cloneDeep * clean - remove commented code * build/static - use fs.copy + fast-glob instead of linux cp for better platform support * build/manifest - standardize task naming * build/display - clean - remove unused code * bugfix - fix fs.promises import * build - create "clean" as named task for use as entrypoint * build/static - fix for copying dirs * Update development/build/task.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/display.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/display.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/display.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * build - use task refs, tasks only return promises not streams, etc * lint fi bad merge + lint * build - one last cleanup + refactor * build - add comments introducing file * build/manifest - fix bug + subtasks dont beed to be named * Update package.json Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * build/task - remove unused fn * Update package.json Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/styles.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> * Update development/build/styles.js Co-Authored-By: Mark Stacey <markjstacey@gmail.com> Co-authored-by: Mark Stacey <markjstacey@gmail.com>feature/default_network_editable
parent
2df8b85c5f
commit
7686edadb0
@ -0,0 +1 @@ |
|||||||
|
{} |
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"minimum_chrome_version": "58" |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"applications": { |
||||||
|
"gecko": { |
||||||
|
"id": "webextension@metamask.io", |
||||||
|
"strict_min_version": "56.0" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"permissions": [ |
||||||
|
"storage", |
||||||
|
"tabs", |
||||||
|
"clipboardWrite", |
||||||
|
"clipboardRead", |
||||||
|
"http://localhost:8545/" |
||||||
|
] |
||||||
|
} |
@ -1,14 +1,10 @@ |
|||||||
const manifest = require('../app/manifest.json') |
|
||||||
|
|
||||||
const version = manifest.version |
|
||||||
|
|
||||||
const fs = require('fs') |
const fs = require('fs') |
||||||
const path = require('path') |
const path = require('path') |
||||||
|
const { version } = require('../app/manifest/_base.json') |
||||||
|
|
||||||
const changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md')).toString() |
const changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md'), 'utf8') |
||||||
|
|
||||||
const log = changelog.split(version)[1].split('##')[0].trim() |
const log = changelog.split(version)[1].split('##')[0].trim() |
||||||
|
|
||||||
const msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}` |
const msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}` |
||||||
|
|
||||||
console.log(msg) |
console.log(msg) |
||||||
|
@ -0,0 +1,150 @@ |
|||||||
|
const randomColor = require('randomcolor') |
||||||
|
const chalk = require('chalk') |
||||||
|
|
||||||
|
module.exports = { setupTaskDisplay, displayChart } |
||||||
|
|
||||||
|
const SYMBOLS = { |
||||||
|
Empty: '', |
||||||
|
Space: ' ', |
||||||
|
Full: '█', |
||||||
|
SevenEighths: '▉', |
||||||
|
ThreeQuarters: '▊', |
||||||
|
FiveEighths: '▋', |
||||||
|
Half: '▌', |
||||||
|
ThreeEighths: '▍', |
||||||
|
Quarter: '▎', |
||||||
|
Eighth: '▏', |
||||||
|
RightHalf: '▐', |
||||||
|
RightEigth: '▕', |
||||||
|
} |
||||||
|
|
||||||
|
function setupTaskDisplay (taskEvents) { |
||||||
|
const taskData = [] |
||||||
|
taskEvents.on('start', ([name]) => { |
||||||
|
console.log(`Starting '${name}'...`) |
||||||
|
}) |
||||||
|
taskEvents.on('end', ([name, start, end]) => { |
||||||
|
taskData.push([name, start, end]) |
||||||
|
console.log(`Finished '${name}'`) |
||||||
|
}) |
||||||
|
taskEvents.on('complete', () => { |
||||||
|
displayChart(taskData) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function displayChart (data) { |
||||||
|
// sort tasks by start time
|
||||||
|
data.sort((a, b,) => a[1] - b[1]) |
||||||
|
|
||||||
|
// get bounds
|
||||||
|
const first = Math.min(...data.map((entry) => entry[1])) |
||||||
|
const last = Math.max(...data.map((entry) => entry[2])) |
||||||
|
|
||||||
|
// get colors
|
||||||
|
const colors = randomColor({ count: data.length }) |
||||||
|
|
||||||
|
// some heading before the bars
|
||||||
|
console.log(`\nbuild completed. task timeline:`) |
||||||
|
|
||||||
|
// build bars for bounds
|
||||||
|
data.map((entry, index) => { |
||||||
|
const [label, start, end] = entry |
||||||
|
const [start2, end2] = [start, end].map((value) => adjust(value, first, last, 40)) |
||||||
|
const barString = barBuilder(start2, end2) |
||||||
|
const color = colors[index] |
||||||
|
const coloredBarString = colorize(color, barString) |
||||||
|
const duration = ((end - start) / 1e3).toFixed(1) |
||||||
|
console.log(coloredBarString, `${label} ${duration}s`) |
||||||
|
}) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function colorize (color, string) { |
||||||
|
const colorizer = (typeof chalk[color] === 'function') ? chalk[color] : chalk.hex(color) |
||||||
|
return colorizer(string) |
||||||
|
} |
||||||
|
|
||||||
|
// scale number within bounds
|
||||||
|
function adjust (value, first, last, size) { |
||||||
|
const length = last - first |
||||||
|
const result = (value - first) / length * size |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// draw bars
|
||||||
|
function barBuilder (start, end) { |
||||||
|
const [spaceInt, spaceRest] = splitNumber(start) |
||||||
|
const barBodyLength = end - spaceInt |
||||||
|
let [barInt, barRest] = splitNumber(barBodyLength) |
||||||
|
// We are handling zero value as a special case
|
||||||
|
// to print at least something on the screen
|
||||||
|
if (barInt === 0 && barRest === 0) { |
||||||
|
barInt = 0 |
||||||
|
barRest = 0.001 |
||||||
|
} |
||||||
|
|
||||||
|
const spaceFull = SYMBOLS.Space.repeat(spaceInt) |
||||||
|
const spacePartial = getSymbolNormalRight(spaceRest) |
||||||
|
const barFull = SYMBOLS.Full.repeat(barInt) |
||||||
|
const barPartial = getSymbolNormal(barRest) |
||||||
|
|
||||||
|
return `${spaceFull}${spacePartial}${barFull}${barPartial}` |
||||||
|
} |
||||||
|
|
||||||
|
// get integer and remainder
|
||||||
|
function splitNumber (value = 0) { |
||||||
|
const [int, rest = '0'] = value.toString().split('.') |
||||||
|
const int2 = parseInt(int, 10) |
||||||
|
const rest2 = parseInt(rest, 10) / Math.pow(10, rest.length) |
||||||
|
return [int2, rest2] |
||||||
|
} |
||||||
|
|
||||||
|
// get partial block char for value (left-adjusted)
|
||||||
|
function getSymbolNormal (value) { |
||||||
|
// round to closest supported value
|
||||||
|
const possibleValues = [0, 1 / 8, 1 / 4, 3 / 8, 1 / 2, 5 / 8, 3 / 4, 7 / 8, 1] |
||||||
|
const rounded = possibleValues.reduce((prev, curr) => { |
||||||
|
return (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev) |
||||||
|
}) |
||||||
|
|
||||||
|
if (rounded === 0) { |
||||||
|
return SYMBOLS.Empty |
||||||
|
} else if (rounded === 1 / 8) { |
||||||
|
return SYMBOLS.Eighth |
||||||
|
} else if (rounded === 1 / 4) { |
||||||
|
return SYMBOLS.Quarter |
||||||
|
} else if (rounded === 3 / 8) { |
||||||
|
return SYMBOLS.ThreeEighths |
||||||
|
} else if (rounded === 1 / 2) { |
||||||
|
return SYMBOLS.Half |
||||||
|
} else if (rounded === 5 / 8) { |
||||||
|
return SYMBOLS.FiveEighths |
||||||
|
} else if (rounded === 3 / 4) { |
||||||
|
return SYMBOLS.ThreeQuarters |
||||||
|
} else if (rounded === 7 / 8) { |
||||||
|
return SYMBOLS.SevenEighths |
||||||
|
} else { |
||||||
|
return SYMBOLS.Full |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// get partial block char for value (right-adjusted)
|
||||||
|
function getSymbolNormalRight (value) { |
||||||
|
// round to closest supported value (not much :/)
|
||||||
|
const possibleValues = [0, 1 / 2, 7 / 8, 1] |
||||||
|
const rounded = possibleValues.reduce((prev, curr) => { |
||||||
|
return (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev) |
||||||
|
}) |
||||||
|
|
||||||
|
if (rounded === 0) { |
||||||
|
return SYMBOLS.Full |
||||||
|
} else if (rounded === 1 / 2) { |
||||||
|
return SYMBOLS.RightHalf |
||||||
|
} else if (rounded === 7 / 8) { |
||||||
|
return SYMBOLS.RightEigth |
||||||
|
} else if (rounded === 1) { |
||||||
|
return SYMBOLS.Space |
||||||
|
} else { |
||||||
|
throw new Error('getSymbolNormalRight got unexpected result') |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
const gulp = require('gulp') |
||||||
|
const gulpZip = require('gulp-zip') |
||||||
|
const del = require('del') |
||||||
|
const { promises: fs } = require('fs') |
||||||
|
const pify = require('pify') |
||||||
|
const pump = pify(require('pump')) |
||||||
|
const baseManifest = require('../../app/manifest/_base.json') |
||||||
|
const { createTask, composeParallel } = require('./task') |
||||||
|
|
||||||
|
module.exports = createEtcTasks |
||||||
|
|
||||||
|
|
||||||
|
function createEtcTasks ({ browserPlatforms, livereload }) { |
||||||
|
|
||||||
|
const clean = createTask('clean', async function clean () { |
||||||
|
await del(['./dist/*']) |
||||||
|
await Promise.all(browserPlatforms.map(async (platform) => { |
||||||
|
await fs.mkdir(`./dist/${platform}`, { recursive: true }) |
||||||
|
})) |
||||||
|
}) |
||||||
|
|
||||||
|
const reload = createTask('reload', function devReload () { |
||||||
|
livereload.listen({ port: 35729 }) |
||||||
|
}) |
||||||
|
|
||||||
|
// zip tasks for distribution
|
||||||
|
const zip = createTask('zip', composeParallel( |
||||||
|
...browserPlatforms.map((platform) => createZipTask(platform)) |
||||||
|
)) |
||||||
|
|
||||||
|
return { clean, reload, zip } |
||||||
|
} |
||||||
|
|
||||||
|
function createZipTask (target) { |
||||||
|
return async () => { |
||||||
|
await pump( |
||||||
|
gulp.src(`dist/${target}/**`), |
||||||
|
gulpZip(`metamask-${target}-${baseManifest.version}.zip`), |
||||||
|
gulp.dest('builds'), |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
//
|
||||||
|
// build task definitions
|
||||||
|
//
|
||||||
|
// run any task with "yarn build ${taskName}"
|
||||||
|
//
|
||||||
|
|
||||||
|
const livereload = require('gulp-livereload') |
||||||
|
const { createTask, composeSeries, composeParallel, detectAndRunEntryTask } = require('./task') |
||||||
|
const createManifestTasks = require('./manifest') |
||||||
|
const createScriptTasks = require('./scripts') |
||||||
|
const createStyleTasks = require('./styles') |
||||||
|
const createStaticAssetTasks = require('./static') |
||||||
|
const createEtcTasks = require('./etc') |
||||||
|
|
||||||
|
const browserPlatforms = [ |
||||||
|
'firefox', |
||||||
|
'chrome', |
||||||
|
'brave', |
||||||
|
'opera', |
||||||
|
] |
||||||
|
|
||||||
|
defineAllTasks() |
||||||
|
detectAndRunEntryTask() |
||||||
|
|
||||||
|
function defineAllTasks () { |
||||||
|
|
||||||
|
const staticTasks = createStaticAssetTasks({ livereload, browserPlatforms }) |
||||||
|
const manifestTasks = createManifestTasks({ browserPlatforms }) |
||||||
|
const styleTasks = createStyleTasks({ livereload }) |
||||||
|
const scriptTasks = createScriptTasks({ livereload, browserPlatforms }) |
||||||
|
const { clean, reload, zip } = createEtcTasks({ livereload, browserPlatforms }) |
||||||
|
|
||||||
|
// 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 for CI testing
|
||||||
|
createTask('test', |
||||||
|
composeSeries( |
||||||
|
clean, |
||||||
|
styleTasks.prod, |
||||||
|
composeParallel( |
||||||
|
scriptTasks.test, |
||||||
|
staticTasks.prod, |
||||||
|
manifestTasks.test, |
||||||
|
), |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
// special build for minimal CI testing
|
||||||
|
createTask('styles', styleTasks.prod) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
const { promises: fs } = require('fs') |
||||||
|
const { merge, cloneDeep } = require('lodash') |
||||||
|
|
||||||
|
const baseManifest = require('../../app/manifest/_base.json') |
||||||
|
|
||||||
|
const { createTask, composeSeries } = require('./task') |
||||||
|
|
||||||
|
module.exports = createManifestTasks |
||||||
|
|
||||||
|
|
||||||
|
const scriptsToExcludeFromBackgroundDevBuild = { |
||||||
|
'bg-libs.js': true, |
||||||
|
} |
||||||
|
|
||||||
|
function createManifestTasks ({ browserPlatforms }) { |
||||||
|
|
||||||
|
// merge base manifest with per-platform manifests
|
||||||
|
const prepPlatforms = async () => { |
||||||
|
return Promise.all(browserPlatforms.map(async (platform) => { |
||||||
|
const platformModifications = await readJson(`${__dirname}/../../app/manifest/${platform}.json`) |
||||||
|
const result = merge(cloneDeep(baseManifest), platformModifications) |
||||||
|
const dir = `./dist/${platform}` |
||||||
|
await fs.mkdir(dir, { recursive: true }) |
||||||
|
await writeJson(result, `${dir}/manifest.json`) |
||||||
|
})) |
||||||
|
} |
||||||
|
|
||||||
|
// dev: remove bg-libs, add chromereload, add perms
|
||||||
|
const envDev = createTaskForModifyManifestForEnvironment((manifest) => { |
||||||
|
const scripts = manifest.background.scripts.filter((scriptName) => !scriptsToExcludeFromBackgroundDevBuild[scriptName]) |
||||||
|
scripts.push('chromereload.js') |
||||||
|
manifest.background = { |
||||||
|
...manifest.background, |
||||||
|
scripts, |
||||||
|
} |
||||||
|
manifest.permissions = [...manifest.permissions, 'webRequestBlocking'] |
||||||
|
}) |
||||||
|
|
||||||
|
// testDev: remove bg-libs, add perms
|
||||||
|
const envTestDev = createTaskForModifyManifestForEnvironment((manifest) => { |
||||||
|
const scripts = manifest.background.scripts.filter((scriptName) => !scriptsToExcludeFromBackgroundDevBuild[scriptName]) |
||||||
|
scripts.push('chromereload.js') |
||||||
|
manifest.background = { |
||||||
|
...manifest.background, |
||||||
|
scripts, |
||||||
|
} |
||||||
|
manifest.permissions = [...manifest.permissions, 'webRequestBlocking', 'http://localhost/*'] |
||||||
|
}) |
||||||
|
|
||||||
|
// test: add permissions
|
||||||
|
const envTest = createTaskForModifyManifestForEnvironment((manifest) => { |
||||||
|
manifest.permissions = [...manifest.permissions, 'webRequestBlocking', 'http://localhost/*'] |
||||||
|
}) |
||||||
|
|
||||||
|
// high level manifest tasks
|
||||||
|
const dev = createTask('manifest:dev', composeSeries( |
||||||
|
prepPlatforms, |
||||||
|
envDev, |
||||||
|
)) |
||||||
|
|
||||||
|
const testDev = createTask('manifest:testDev', composeSeries( |
||||||
|
prepPlatforms, |
||||||
|
envTestDev, |
||||||
|
)) |
||||||
|
|
||||||
|
const test = createTask('manifest:test', composeSeries( |
||||||
|
prepPlatforms, |
||||||
|
envTest, |
||||||
|
)) |
||||||
|
|
||||||
|
const prod = createTask('manifest:prod', prepPlatforms) |
||||||
|
|
||||||
|
return { prod, dev, testDev, test } |
||||||
|
|
||||||
|
// helper for modifying each platform's manifest.json in place
|
||||||
|
function createTaskForModifyManifestForEnvironment (transformFn) { |
||||||
|
return () => { |
||||||
|
return Promise.all(browserPlatforms.map(async (platform) => { |
||||||
|
const path = `./dist/${platform}/manifest.json` |
||||||
|
const manifest = await readJson(path) |
||||||
|
transformFn(manifest) |
||||||
|
await writeJson(manifest, path) |
||||||
|
})) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// helper for reading and deserializing json from fs
|
||||||
|
async function readJson (path) { |
||||||
|
return JSON.parse(await fs.readFile(path, 'utf8')) |
||||||
|
} |
||||||
|
|
||||||
|
// helper for serializing and writing json to fs
|
||||||
|
async function writeJson (obj, path) { |
||||||
|
return fs.writeFile(path, JSON.stringify(obj, null, 2)) |
||||||
|
} |
@ -0,0 +1,362 @@ |
|||||||
|
const fs = require('fs') |
||||||
|
const gulp = require('gulp') |
||||||
|
const watch = require('gulp-watch') |
||||||
|
const source = require('vinyl-source-stream') |
||||||
|
const buffer = require('vinyl-buffer') |
||||||
|
const log = require('fancy-log') |
||||||
|
const { assign } = require('lodash') |
||||||
|
const watchify = require('watchify') |
||||||
|
const browserify = require('browserify') |
||||||
|
const envify = require('envify/custom') |
||||||
|
const sourcemaps = require('gulp-sourcemaps') |
||||||
|
const sesify = require('sesify') |
||||||
|
const terser = require('gulp-terser-js') |
||||||
|
const pify = require('pify') |
||||||
|
const endOfStream = pify(require('end-of-stream')) |
||||||
|
const { makeStringTransform } = require('browserify-transform-tools') |
||||||
|
|
||||||
|
|
||||||
|
const { createTask, composeParallel, composeSeries, runInChildProcess } = require('./task') |
||||||
|
const packageJSON = require('../../package.json') |
||||||
|
|
||||||
|
module.exports = createScriptTasks |
||||||
|
|
||||||
|
|
||||||
|
const dependencies = Object.keys((packageJSON && packageJSON.dependencies) || {}) |
||||||
|
const materialUIDependencies = ['@material-ui/core'] |
||||||
|
const reactDepenendencies = dependencies.filter((dep) => dep.match(/react/)) |
||||||
|
const d3Dependencies = ['c3', 'd3'] |
||||||
|
|
||||||
|
const externalDependenciesMap = { |
||||||
|
background: [ |
||||||
|
'3box', |
||||||
|
], |
||||||
|
ui: [ |
||||||
|
...materialUIDependencies, ...reactDepenendencies, ...d3Dependencies, |
||||||
|
], |
||||||
|
} |
||||||
|
|
||||||
|
function createScriptTasks ({ browserPlatforms, livereload }) { |
||||||
|
|
||||||
|
// internal tasks
|
||||||
|
const core = { |
||||||
|
// dev tasks (live reload)
|
||||||
|
dev: createTasksForBuildJsExtension({ taskPrefix: 'scripts:core:dev', devMode: true }), |
||||||
|
testDev: createTasksForBuildJsExtension({ taskPrefix: 'scripts:core:test-live', devMode: true, testing: true }), |
||||||
|
// built for CI tests
|
||||||
|
test: createTasksForBuildJsExtension({ taskPrefix: 'scripts:core:test', testing: true }), |
||||||
|
// production
|
||||||
|
prod: createTasksForBuildJsExtension({ taskPrefix: 'scripts:core:prod' }), |
||||||
|
} |
||||||
|
const deps = { |
||||||
|
background: createTasksForBuildJsDeps({ filename: 'bg-libs', key: 'background' }), |
||||||
|
ui: createTasksForBuildJsDeps({ filename: 'ui-libs', key: 'ui' }), |
||||||
|
} |
||||||
|
|
||||||
|
// high level tasks
|
||||||
|
|
||||||
|
const prod = composeParallel( |
||||||
|
deps.background, |
||||||
|
deps.ui, |
||||||
|
core.prod, |
||||||
|
) |
||||||
|
|
||||||
|
const dev = core.dev |
||||||
|
const testDev = core.testDev |
||||||
|
|
||||||
|
const test = composeParallel( |
||||||
|
deps.background, |
||||||
|
deps.ui, |
||||||
|
core.test, |
||||||
|
) |
||||||
|
|
||||||
|
return { prod, dev, testDev, test } |
||||||
|
|
||||||
|
|
||||||
|
function createTasksForBuildJsDeps ({ key, filename }) { |
||||||
|
return createTask(`scripts:deps:${key}`, bundleTask({ |
||||||
|
label: filename, |
||||||
|
filename: `${filename}.js`, |
||||||
|
buildLib: true, |
||||||
|
dependenciesToBundle: externalDependenciesMap[key], |
||||||
|
devMode: false, |
||||||
|
})) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function createTasksForBuildJsExtension ({ taskPrefix, devMode, testing }) { |
||||||
|
const standardBundles = [ |
||||||
|
'background', |
||||||
|
'ui', |
||||||
|
'phishing-detect', |
||||||
|
] |
||||||
|
|
||||||
|
const standardSubtasks = standardBundles.map((filename) => { |
||||||
|
return createTask(`${taskPrefix}:${filename}`, |
||||||
|
createBundleTaskForBuildJsExtensionNormal({ filename, devMode, testing }) |
||||||
|
) |
||||||
|
}) |
||||||
|
// inpage must be built before contentscript
|
||||||
|
// because inpage bundle result is included inside contentscript
|
||||||
|
const contentscriptSubtask = createTask(`${taskPrefix}:contentscript`, |
||||||
|
createTaskForBuildJsExtensionContentscript({ devMode, testing }) |
||||||
|
) |
||||||
|
|
||||||
|
// task for initiating livereload
|
||||||
|
const initiateLiveReload = async () => { |
||||||
|
if (devMode) { |
||||||
|
// trigger live reload when the bundles are updated
|
||||||
|
// this is not ideal, but overcomes the limitations:
|
||||||
|
// - run from the main process (not child process tasks)
|
||||||
|
// - after the first build has completed (thus the timeout)
|
||||||
|
// - build tasks never "complete" when run with livereload + child process
|
||||||
|
setTimeout(() => { |
||||||
|
watch('./dist/*/*.js', (event) => { |
||||||
|
livereload.changed(event.path) |
||||||
|
}) |
||||||
|
}, 75e3) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// make each bundle run in a separate process
|
||||||
|
const allSubtasks = [...standardSubtasks, contentscriptSubtask].map((subtask) => runInChildProcess(subtask)) |
||||||
|
// const allSubtasks = [...standardSubtasks, contentscriptSubtask].map(subtask => (subtask))
|
||||||
|
// make a parent task that runs each task in a child thread
|
||||||
|
return composeParallel(initiateLiveReload, ...allSubtasks) |
||||||
|
} |
||||||
|
|
||||||
|
function createBundleTaskForBuildJsExtensionNormal ({ filename, devMode, testing }) { |
||||||
|
return bundleTask({ |
||||||
|
label: filename, |
||||||
|
filename: `${filename}.js`, |
||||||
|
filepath: `./app/scripts/${filename}.js`, |
||||||
|
externalDependencies: devMode ? undefined : externalDependenciesMap[filename], |
||||||
|
devMode, |
||||||
|
testing, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function createTaskForBuildJsExtensionContentscript ({ devMode, testing }) { |
||||||
|
const inpage = 'inpage' |
||||||
|
const contentscript = 'contentscript' |
||||||
|
return composeSeries( |
||||||
|
bundleTask({ |
||||||
|
label: inpage, |
||||||
|
filename: `${inpage}.js`, |
||||||
|
filepath: `./app/scripts/${inpage}.js`, |
||||||
|
externalDependencies: devMode ? undefined : externalDependenciesMap[inpage], |
||||||
|
devMode, |
||||||
|
testing, |
||||||
|
}), |
||||||
|
bundleTask({ |
||||||
|
label: contentscript, |
||||||
|
filename: `${contentscript}.js`, |
||||||
|
filepath: `./app/scripts/${contentscript}.js`, |
||||||
|
externalDependencies: devMode ? undefined : externalDependenciesMap[contentscript], |
||||||
|
devMode, |
||||||
|
testing, |
||||||
|
}) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function bundleTask (opts) { |
||||||
|
let bundler |
||||||
|
|
||||||
|
return performBundle |
||||||
|
|
||||||
|
async function performBundle () { |
||||||
|
// initialize bundler if not available yet
|
||||||
|
// dont create bundler until task is actually run
|
||||||
|
if (!bundler) { |
||||||
|
bundler = generateBundler(opts, performBundle) |
||||||
|
// output build logs to terminal
|
||||||
|
bundler.on('log', log) |
||||||
|
} |
||||||
|
|
||||||
|
let buildStream = bundler.bundle() |
||||||
|
|
||||||
|
// handle errors
|
||||||
|
buildStream.on('error', (err) => { |
||||||
|
beep() |
||||||
|
if (opts.devMode) { |
||||||
|
console.warn(err.stack) |
||||||
|
} else { |
||||||
|
throw err |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
// process bundles
|
||||||
|
buildStream = buildStream |
||||||
|
// convert bundle stream to gulp vinyl stream
|
||||||
|
.pipe(source(opts.filename)) |
||||||
|
// buffer file contents (?)
|
||||||
|
.pipe(buffer()) |
||||||
|
|
||||||
|
// Initialize Source Maps
|
||||||
|
buildStream = buildStream |
||||||
|
// loads map from browserify file
|
||||||
|
.pipe(sourcemaps.init({ loadMaps: true })) |
||||||
|
|
||||||
|
// Minification
|
||||||
|
if (!opts.devMode) { |
||||||
|
buildStream = buildStream |
||||||
|
.pipe(terser({ |
||||||
|
mangle: { |
||||||
|
reserved: [ 'MetamaskInpageProvider' ], |
||||||
|
}, |
||||||
|
})) |
||||||
|
} |
||||||
|
|
||||||
|
// Finalize Source Maps
|
||||||
|
if (opts.devMode) { |
||||||
|
// Use inline source maps for development due to Chrome DevTools bug
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=931675
|
||||||
|
buildStream = buildStream |
||||||
|
.pipe(sourcemaps.write()) |
||||||
|
} else { |
||||||
|
buildStream = buildStream |
||||||
|
.pipe(sourcemaps.write('../sourcemaps')) |
||||||
|
} |
||||||
|
|
||||||
|
// write completed bundles
|
||||||
|
browserPlatforms.forEach((platform) => { |
||||||
|
const dest = `./dist/${platform}` |
||||||
|
buildStream = buildStream.pipe(gulp.dest(dest)) |
||||||
|
}) |
||||||
|
|
||||||
|
await endOfStream(buildStream) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function configureBundleForSesify ({ |
||||||
|
browserifyOpts, |
||||||
|
bundleName, |
||||||
|
}) { |
||||||
|
// add in sesify args for better globalRef usage detection
|
||||||
|
Object.assign(browserifyOpts, sesify.args) |
||||||
|
|
||||||
|
// ensure browserify uses full paths
|
||||||
|
browserifyOpts.fullPaths = true |
||||||
|
|
||||||
|
// record dependencies used in bundle
|
||||||
|
fs.mkdirSync('./sesify', { recursive: true }) |
||||||
|
browserifyOpts.plugin.push(['deps-dump', { |
||||||
|
filename: `./sesify/deps-${bundleName}.json`, |
||||||
|
}]) |
||||||
|
|
||||||
|
const sesifyConfigPath = `./sesify/${bundleName}.json` |
||||||
|
|
||||||
|
// add sesify plugin
|
||||||
|
browserifyOpts.plugin.push([sesify, { |
||||||
|
writeAutoConfig: sesifyConfigPath, |
||||||
|
}]) |
||||||
|
|
||||||
|
// remove html comments that SES is alergic to
|
||||||
|
const removeHtmlComment = makeStringTransform('remove-html-comment', { excludeExtension: ['.json'] }, (content, _, cb) => { |
||||||
|
const result = content.split('-->').join('-- >') |
||||||
|
cb(null, result) |
||||||
|
}) |
||||||
|
browserifyOpts.transform.push([removeHtmlComment, { global: true }]) |
||||||
|
} |
||||||
|
|
||||||
|
function generateBundler (opts, performBundle) { |
||||||
|
const browserifyOpts = assign({}, watchify.args, { |
||||||
|
plugin: [], |
||||||
|
transform: [], |
||||||
|
debug: true, |
||||||
|
fullPaths: opts.devMode, |
||||||
|
}) |
||||||
|
|
||||||
|
const bundleName = opts.filename.split('.')[0] |
||||||
|
|
||||||
|
// activate sesify
|
||||||
|
const activateAutoConfig = Boolean(process.env.SESIFY_AUTOGEN) |
||||||
|
// const activateSesify = activateAutoConfig
|
||||||
|
const activateSesify = activateAutoConfig && ['background'].includes(bundleName) |
||||||
|
if (activateSesify) { |
||||||
|
configureBundleForSesify({ browserifyOpts, bundleName }) |
||||||
|
} |
||||||
|
|
||||||
|
if (!activateSesify) { |
||||||
|
browserifyOpts.plugin.push('browserify-derequire') |
||||||
|
} |
||||||
|
|
||||||
|
if (!opts.buildLib) { |
||||||
|
if (opts.devMode && opts.filename === 'ui.js') { |
||||||
|
browserifyOpts['entries'] = ['./development/require-react-devtools.js', opts.filepath] |
||||||
|
} else { |
||||||
|
browserifyOpts['entries'] = [opts.filepath] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let bundler = browserify(browserifyOpts) |
||||||
|
.transform('babelify') |
||||||
|
// Transpile any dependencies using the object spread/rest operator
|
||||||
|
// because it is incompatible with `esprima`, which is used by `envify`
|
||||||
|
// See https://github.com/jquery/esprima/issues/1927
|
||||||
|
.transform('babelify', { |
||||||
|
only: [ |
||||||
|
'./**/node_modules/libp2p', |
||||||
|
], |
||||||
|
global: true, |
||||||
|
plugins: ['@babel/plugin-proposal-object-rest-spread'], |
||||||
|
}) |
||||||
|
.transform('brfs') |
||||||
|
|
||||||
|
if (opts.buildLib) { |
||||||
|
bundler = bundler.require(opts.dependenciesToBundle) |
||||||
|
} |
||||||
|
|
||||||
|
if (opts.externalDependencies) { |
||||||
|
bundler = bundler.external(opts.externalDependencies) |
||||||
|
} |
||||||
|
|
||||||
|
let environment |
||||||
|
if (opts.devMode) { |
||||||
|
environment = 'development' |
||||||
|
} else if (opts.testing) { |
||||||
|
environment = 'testing' |
||||||
|
} else if (process.env.CIRCLE_BRANCH === 'master') { |
||||||
|
environment = 'production' |
||||||
|
} else if (/^Version-v(\d+)[.](\d+)[.](\d+)/.test(process.env.CIRCLE_BRANCH)) { |
||||||
|
environment = 'release-candidate' |
||||||
|
} else if (process.env.CIRCLE_BRANCH === 'develop') { |
||||||
|
environment = 'staging' |
||||||
|
} else if (process.env.CIRCLE_PULL_REQUEST) { |
||||||
|
environment = 'pull-request' |
||||||
|
} else { |
||||||
|
environment = 'other' |
||||||
|
} |
||||||
|
|
||||||
|
// Inject variables into bundle
|
||||||
|
bundler.transform(envify({ |
||||||
|
METAMASK_DEBUG: opts.devMode, |
||||||
|
METAMASK_ENVIRONMENT: environment, |
||||||
|
NODE_ENV: opts.devMode ? 'development' : 'production', |
||||||
|
IN_TEST: opts.testing ? 'true' : false, |
||||||
|
PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '', |
||||||
|
PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '', |
||||||
|
}), { |
||||||
|
global: true, |
||||||
|
}) |
||||||
|
|
||||||
|
// Live reload - minimal rebundle on change
|
||||||
|
if (opts.devMode) { |
||||||
|
bundler = watchify(bundler) |
||||||
|
// on any file update, re-runs the bundler
|
||||||
|
bundler.on('update', () => { |
||||||
|
performBundle() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return bundler |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function beep () { |
||||||
|
process.stdout.write('\x07') |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
const fs = require('fs-extra') |
||||||
|
const path = require('path') |
||||||
|
const watch = require('gulp-watch') |
||||||
|
const glob = require('fast-glob') |
||||||
|
|
||||||
|
const { createTask, composeSeries } = require('./task') |
||||||
|
|
||||||
|
module.exports = createStaticAssetTasks |
||||||
|
|
||||||
|
|
||||||
|
const copyTargets = [ |
||||||
|
{ |
||||||
|
src: `./app/_locales/`, |
||||||
|
dest: `_locales`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./app/images/`, |
||||||
|
dest: `images`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./node_modules/eth-contract-metadata/images/`, |
||||||
|
dest: `images/contract`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./app/fonts/`, |
||||||
|
dest: `fonts`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./app/vendor/`, |
||||||
|
dest: `vendor`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./ui/app/css/output/`, |
||||||
|
pattern: `*.css`, |
||||||
|
dest: ``, |
||||||
|
}, |
||||||
|
{ |
||||||
|
src: `./app/`, |
||||||
|
pattern: `*.html`, |
||||||
|
dest: ``, |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
const copyTargetsDev = [ |
||||||
|
...copyTargets, |
||||||
|
{ |
||||||
|
src: './app/scripts/', |
||||||
|
pattern: '/chromereload.js', |
||||||
|
dest: ``, |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
function createStaticAssetTasks ({ livereload, browserPlatforms }) { |
||||||
|
|
||||||
|
const prod = createTask('static:prod', composeSeries(...copyTargets.map((target) => { |
||||||
|
return async function copyStaticAssets () { |
||||||
|
await performCopy(target) |
||||||
|
} |
||||||
|
}))) |
||||||
|
const dev = createTask('static:dev', composeSeries(...copyTargetsDev.map((target) => { |
||||||
|
return async function copyStaticAssets () { |
||||||
|
await setupLiveCopy(target) |
||||||
|
} |
||||||
|
}))) |
||||||
|
|
||||||
|
return { dev, prod } |
||||||
|
|
||||||
|
async function setupLiveCopy (target) { |
||||||
|
const pattern = target.pattern || '/**/*' |
||||||
|
watch(target.src + pattern, (event) => { |
||||||
|
livereload.changed(event.path) |
||||||
|
performCopy(target) |
||||||
|
}) |
||||||
|
await performCopy(target) |
||||||
|
} |
||||||
|
|
||||||
|
async function performCopy (target) { |
||||||
|
await Promise.all(browserPlatforms.map(async (platform) => { |
||||||
|
if (target.pattern) { |
||||||
|
await copyGlob(target.src, `${target.src}${target.pattern}`, `./dist/${platform}/${target.dest}`) |
||||||
|
} else { |
||||||
|
await copyGlob(target.src, `${target.src}`, `./dist/${platform}/${target.dest}`) |
||||||
|
} |
||||||
|
})) |
||||||
|
} |
||||||
|
|
||||||
|
async function copyGlob (baseDir, srcGlob, dest) { |
||||||
|
const sources = await glob(srcGlob, { onlyFiles: false }) |
||||||
|
await Promise.all(sources.map(async (src) => { |
||||||
|
const relativePath = path.relative(baseDir, src) |
||||||
|
await fs.copy(src, `${dest}${relativePath}`) |
||||||
|
})) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
const pify = require('pify') |
||||||
|
const gulp = require('gulp') |
||||||
|
const sass = require('gulp-sass') |
||||||
|
sass.compiler = require('node-sass') |
||||||
|
const autoprefixer = require('gulp-autoprefixer') |
||||||
|
const gulpStylelint = require('gulp-stylelint') |
||||||
|
const watch = require('gulp-watch') |
||||||
|
const sourcemaps = require('gulp-sourcemaps') |
||||||
|
const rtlcss = require('gulp-rtlcss') |
||||||
|
const rename = require('gulp-rename') |
||||||
|
const pump = pify(require('pump')) |
||||||
|
const { createTask } = require('./task') |
||||||
|
|
||||||
|
|
||||||
|
// scss compilation and autoprefixing tasks
|
||||||
|
module.exports = createStyleTasks |
||||||
|
|
||||||
|
|
||||||
|
function createStyleTasks ({ livereload }) { |
||||||
|
|
||||||
|
const prod = createTask('styles:prod', createScssBuildTask({ |
||||||
|
src: 'ui/app/css/index.scss', |
||||||
|
dest: 'ui/app/css/output', |
||||||
|
devMode: false, |
||||||
|
})) |
||||||
|
|
||||||
|
const dev = createTask('styles:dev', createScssBuildTask({ |
||||||
|
src: 'ui/app/css/index.scss', |
||||||
|
dest: 'ui/app/css/output', |
||||||
|
devMode: true, |
||||||
|
pattern: 'ui/app/**/*.scss', |
||||||
|
})) |
||||||
|
|
||||||
|
const lint = createTask('lint-scss', function () { |
||||||
|
return gulp |
||||||
|
.src('ui/app/css/itcss/**/*.scss') |
||||||
|
.pipe(gulpStylelint({ |
||||||
|
reporters: [ |
||||||
|
{ formatter: 'string', console: true }, |
||||||
|
], |
||||||
|
fix: true, |
||||||
|
})) |
||||||
|
}) |
||||||
|
|
||||||
|
return { prod, dev, lint } |
||||||
|
|
||||||
|
|
||||||
|
function createScssBuildTask ({ src, dest, devMode, pattern }) { |
||||||
|
return async function () { |
||||||
|
if (devMode) { |
||||||
|
watch(pattern, async (event) => { |
||||||
|
await buildScss(devMode) |
||||||
|
livereload.changed(event.path) |
||||||
|
}) |
||||||
|
} |
||||||
|
await buildScss(devMode) |
||||||
|
} |
||||||
|
|
||||||
|
async function buildScss (devMode) { |
||||||
|
await pump(...[ |
||||||
|
// pre-process
|
||||||
|
gulp.src(src), |
||||||
|
devMode && sourcemaps.init(), |
||||||
|
sass().on('error', sass.logError), |
||||||
|
devMode && sourcemaps.write(), |
||||||
|
autoprefixer(), |
||||||
|
// standard
|
||||||
|
gulp.dest(dest), |
||||||
|
// right-to-left
|
||||||
|
rtlcss(), |
||||||
|
rename({ suffix: '-rtl' }), |
||||||
|
devMode && sourcemaps.write(), |
||||||
|
gulp.dest(dest), |
||||||
|
].filter(Boolean)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
const EventEmitter = require('events') |
||||||
|
const { spawn } = require('child_process') |
||||||
|
|
||||||
|
const tasks = {} |
||||||
|
const taskEvents = new EventEmitter() |
||||||
|
|
||||||
|
module.exports = { detectAndRunEntryTask, tasks, taskEvents, createTask, runTask, composeSeries, composeParallel, runInChildProcess } |
||||||
|
|
||||||
|
const { setupTaskDisplay } = require('./display') |
||||||
|
|
||||||
|
|
||||||
|
function detectAndRunEntryTask () { |
||||||
|
// get requested task name and execute
|
||||||
|
const taskName = process.argv[2] |
||||||
|
if (!taskName) { |
||||||
|
throw new Error(`MetaMask build: No task name specified`) |
||||||
|
} |
||||||
|
const skipStats = process.argv[3] === '--skip-stats' |
||||||
|
|
||||||
|
runTask(taskName, { skipStats }) |
||||||
|
} |
||||||
|
|
||||||
|
async function runTask (taskName, { skipStats } = {}) { |
||||||
|
if (!(taskName in tasks)) { |
||||||
|
throw new Error(`MetaMask build: Unrecognized task name "${taskName}"`) |
||||||
|
} |
||||||
|
if (!skipStats) { |
||||||
|
setupTaskDisplay(taskEvents) |
||||||
|
console.log(`running task "${taskName}"...`) |
||||||
|
} |
||||||
|
try { |
||||||
|
await tasks[taskName]() |
||||||
|
} catch (err) { |
||||||
|
console.error(`MetaMask build: Encountered an error while running task "${taskName}".`) |
||||||
|
console.error(err) |
||||||
|
process.exit(1) |
||||||
|
} |
||||||
|
taskEvents.emit('complete') |
||||||
|
} |
||||||
|
|
||||||
|
function createTask (taskName, taskFn) { |
||||||
|
if (taskName in tasks) { |
||||||
|
throw new Error(`MetaMask build: task "${taskName}" already exists. Refusing to redefine`) |
||||||
|
} |
||||||
|
const task = instrumentForTaskStats(taskName, taskFn) |
||||||
|
task.taskName = taskName |
||||||
|
tasks[taskName] = task |
||||||
|
return task |
||||||
|
} |
||||||
|
|
||||||
|
function runInChildProcess (task) { |
||||||
|
const taskName = typeof task === 'string' ? task : task.taskName |
||||||
|
if (!taskName) { |
||||||
|
throw new Error(`MetaMask build: runInChildProcess unable to identify task name`) |
||||||
|
} |
||||||
|
return instrumentForTaskStats(taskName, async () => { |
||||||
|
const childProcess = spawn('yarn', ['build', taskName, '--skip-stats']) |
||||||
|
// forward logs to main process
|
||||||
|
// skip the first stdout event (announcing the process command)
|
||||||
|
childProcess.stdout.once('data', () => { |
||||||
|
childProcess.stdout.on('data', (data) => process.stdout.write(`${taskName}: ${data}`)) |
||||||
|
}) |
||||||
|
childProcess.stderr.on('data', (data) => process.stderr.write(`${taskName}: ${data}`)) |
||||||
|
// await end of process
|
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
childProcess.once('close', (errCode) => { |
||||||
|
if (errCode !== 0) { |
||||||
|
reject(new Error(`MetaMask build: runInChildProcess for task "${taskName}" encountered an error`)) |
||||||
|
return |
||||||
|
} |
||||||
|
resolve() |
||||||
|
}) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function instrumentForTaskStats (taskName, asyncFn) { |
||||||
|
return async () => { |
||||||
|
const start = Date.now() |
||||||
|
taskEvents.emit('start', [taskName, start]) |
||||||
|
await asyncFn() |
||||||
|
const end = Date.now() |
||||||
|
taskEvents.emit('end', [taskName, start, end]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function composeSeries (...subtasks) { |
||||||
|
return async () => { |
||||||
|
const realTasks = subtasks |
||||||
|
for (const subtask of realTasks) { |
||||||
|
await subtask() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function composeParallel (...subtasks) { |
||||||
|
return async () => { |
||||||
|
const realTasks = subtasks |
||||||
|
await Promise.all(realTasks.map((subtask) => subtask())) |
||||||
|
} |
||||||
|
} |
@ -1,724 +0,0 @@ |
|||||||
const fs = require('fs') |
|
||||||
const watchify = require('watchify') |
|
||||||
const browserify = require('browserify') |
|
||||||
const envify = require('envify/custom') |
|
||||||
const gulp = require('gulp') |
|
||||||
const source = require('vinyl-source-stream') |
|
||||||
const buffer = require('vinyl-buffer') |
|
||||||
const log = require('fancy-log') |
|
||||||
const watch = require('gulp-watch') |
|
||||||
const sourcemaps = require('gulp-sourcemaps') |
|
||||||
const jsoneditor = require('gulp-json-editor') |
|
||||||
const zip = require('gulp-zip') |
|
||||||
const { assign } = require('lodash') |
|
||||||
const livereload = require('gulp-livereload') |
|
||||||
const del = require('del') |
|
||||||
const manifest = require('./app/manifest.json') |
|
||||||
const sass = require('gulp-sass') |
|
||||||
const autoprefixer = require('gulp-autoprefixer') |
|
||||||
const gulpStylelint = require('gulp-stylelint') |
|
||||||
const terser = require('gulp-terser-js') |
|
||||||
const pify = require('pify') |
|
||||||
const rtlcss = require('gulp-rtlcss') |
|
||||||
const rename = require('gulp-rename') |
|
||||||
const gulpMultiProcess = require('gulp-multi-process') |
|
||||||
const endOfStream = pify(require('end-of-stream')) |
|
||||||
const sesify = require('sesify') |
|
||||||
const imagemin = require('gulp-imagemin') |
|
||||||
const { makeStringTransform } = require('browserify-transform-tools') |
|
||||||
|
|
||||||
const packageJSON = require('./package.json') |
|
||||||
|
|
||||||
sass.compiler = require('node-sass') |
|
||||||
|
|
||||||
const dependencies = Object.keys((packageJSON && packageJSON.dependencies) || {}) |
|
||||||
const materialUIDependencies = ['@material-ui/core'] |
|
||||||
const reactDepenendencies = dependencies.filter((dep) => dep.match(/react/)) |
|
||||||
const d3Dependencies = ['c3', 'd3'] |
|
||||||
|
|
||||||
const externalDependenciesMap = { |
|
||||||
background: [ |
|
||||||
'3box', |
|
||||||
], |
|
||||||
ui: [ |
|
||||||
...materialUIDependencies, ...reactDepenendencies, ...d3Dependencies, |
|
||||||
], |
|
||||||
} |
|
||||||
|
|
||||||
function gulpParallel (...args) { |
|
||||||
return function spawnGulpChildProcess (cb) { |
|
||||||
return gulpMultiProcess(args, cb, true) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const browserPlatforms = [ |
|
||||||
'firefox', |
|
||||||
'chrome', |
|
||||||
'brave', |
|
||||||
'opera', |
|
||||||
] |
|
||||||
const commonPlatforms = [ |
|
||||||
// browser extensions
|
|
||||||
...browserPlatforms, |
|
||||||
] |
|
||||||
|
|
||||||
// browser reload
|
|
||||||
|
|
||||||
gulp.task('dev:reload', function () { |
|
||||||
livereload.listen({ |
|
||||||
port: 35729, |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
// copy universal
|
|
||||||
|
|
||||||
const copyTaskNames = [] |
|
||||||
const copyDevTaskNames = [] |
|
||||||
|
|
||||||
createCopyTasks('locales', { |
|
||||||
source: './app/_locales/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}/_locales`), |
|
||||||
}) |
|
||||||
createCopyTasks('images', { |
|
||||||
source: './app/images/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}/images`), |
|
||||||
}) |
|
||||||
createCopyTasks('contractImages', { |
|
||||||
source: './node_modules/eth-contract-metadata/images/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}/images/contract`), |
|
||||||
}) |
|
||||||
createCopyTasks('fonts', { |
|
||||||
source: './app/fonts/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}/fonts`), |
|
||||||
}) |
|
||||||
createCopyTasks('vendor', { |
|
||||||
source: './app/vendor/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}/vendor`), |
|
||||||
}) |
|
||||||
createCopyTasks('css', { |
|
||||||
source: './ui/app/css/output/', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}`), |
|
||||||
}) |
|
||||||
createCopyTasks('reload', { |
|
||||||
devOnly: true, |
|
||||||
source: './app/scripts/', |
|
||||||
pattern: '/chromereload.js', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}`), |
|
||||||
}) |
|
||||||
createCopyTasks('html', { |
|
||||||
source: './app/', |
|
||||||
pattern: '/*.html', |
|
||||||
destinations: commonPlatforms.map((platform) => `./dist/${platform}`), |
|
||||||
}) |
|
||||||
|
|
||||||
// copy extension
|
|
||||||
|
|
||||||
createCopyTasks('manifest', { |
|
||||||
source: './app/', |
|
||||||
pattern: '/*.json', |
|
||||||
destinations: browserPlatforms.map((platform) => `./dist/${platform}`), |
|
||||||
}) |
|
||||||
|
|
||||||
function createCopyTasks (label, opts) { |
|
||||||
if (!opts.devOnly) { |
|
||||||
const copyTaskName = `copy:${label}` |
|
||||||
copyTask(copyTaskName, opts) |
|
||||||
copyTaskNames.push(copyTaskName) |
|
||||||
} |
|
||||||
const copyDevTaskName = `dev:copy:${label}` |
|
||||||
copyTask(copyDevTaskName, Object.assign({ devMode: true }, opts)) |
|
||||||
copyDevTaskNames.push(copyDevTaskName) |
|
||||||
} |
|
||||||
|
|
||||||
function copyTask (taskName, opts) { |
|
||||||
const source = opts.source |
|
||||||
const destination = opts.destination |
|
||||||
const destinations = opts.destinations || [destination] |
|
||||||
const pattern = opts.pattern || '/**/*' |
|
||||||
const devMode = opts.devMode |
|
||||||
|
|
||||||
return gulp.task(taskName, function () { |
|
||||||
if (devMode) { |
|
||||||
watch(source + pattern, (event) => { |
|
||||||
livereload.changed(event.path) |
|
||||||
performCopy() |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
return performCopy() |
|
||||||
}) |
|
||||||
|
|
||||||
function performCopy () { |
|
||||||
// stream from source
|
|
||||||
let stream = gulp.src(source + pattern, { base: source }) |
|
||||||
|
|
||||||
// copy to destinations
|
|
||||||
destinations.forEach(function (destination) { |
|
||||||
stream = stream.pipe(gulp.dest(destination)) |
|
||||||
}) |
|
||||||
|
|
||||||
return stream |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// manifest tinkering
|
|
||||||
|
|
||||||
gulp.task('manifest:chrome', function () { |
|
||||||
return gulp.src('./dist/chrome/manifest.json') |
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
delete json.applications |
|
||||||
json.minimum_chrome_version = '58' |
|
||||||
return json |
|
||||||
})) |
|
||||||
.pipe(gulp.dest('./dist/chrome', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task('manifest:opera', function () { |
|
||||||
return gulp.src('./dist/opera/manifest.json') |
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
json.permissions = [ |
|
||||||
'storage', |
|
||||||
'tabs', |
|
||||||
'clipboardWrite', |
|
||||||
'clipboardRead', |
|
||||||
'http://localhost:8545/', |
|
||||||
] |
|
||||||
return json |
|
||||||
})) |
|
||||||
.pipe(gulp.dest('./dist/opera', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task('manifest:production', function () { |
|
||||||
return gulp.src([ |
|
||||||
'./dist/firefox/manifest.json', |
|
||||||
'./dist/chrome/manifest.json', |
|
||||||
'./dist/brave/manifest.json', |
|
||||||
'./dist/opera/manifest.json', |
|
||||||
], { base: './dist/' }) |
|
||||||
|
|
||||||
// Exclude chromereload script in production:
|
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
json.background.scripts = json.background.scripts.filter((script) => { |
|
||||||
return !script.includes('chromereload') |
|
||||||
}) |
|
||||||
return json |
|
||||||
})) |
|
||||||
|
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task('manifest:testing', function () { |
|
||||||
return gulp.src([ |
|
||||||
'./dist/firefox/manifest.json', |
|
||||||
'./dist/chrome/manifest.json', |
|
||||||
], { base: './dist/' }) |
|
||||||
|
|
||||||
// Exclude chromereload script in production:
|
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
json.permissions = [...json.permissions, 'webRequestBlocking', 'http://localhost/*'] |
|
||||||
return json |
|
||||||
})) |
|
||||||
|
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
const scriptsToExcludeFromBackgroundDevBuild = { |
|
||||||
'bg-libs.js': true, |
|
||||||
} |
|
||||||
|
|
||||||
gulp.task('manifest:testing-local', function () { |
|
||||||
return gulp.src([ |
|
||||||
'./dist/firefox/manifest.json', |
|
||||||
'./dist/chrome/manifest.json', |
|
||||||
], { base: './dist/' }) |
|
||||||
|
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
json.background = { |
|
||||||
...json.background, |
|
||||||
scripts: json.background.scripts.filter((scriptName) => !scriptsToExcludeFromBackgroundDevBuild[scriptName]), |
|
||||||
} |
|
||||||
json.permissions = [...json.permissions, 'webRequestBlocking', 'http://localhost/*'] |
|
||||||
return json |
|
||||||
})) |
|
||||||
|
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
|
|
||||||
gulp.task('manifest:dev', function () { |
|
||||||
return gulp.src([ |
|
||||||
'./dist/firefox/manifest.json', |
|
||||||
'./dist/chrome/manifest.json', |
|
||||||
], { base: './dist/' }) |
|
||||||
|
|
||||||
.pipe(jsoneditor(function (json) { |
|
||||||
json.background = { |
|
||||||
...json.background, |
|
||||||
scripts: json.background.scripts.filter((scriptName) => !scriptsToExcludeFromBackgroundDevBuild[scriptName]), |
|
||||||
} |
|
||||||
json.permissions = [...json.permissions, 'webRequestBlocking'] |
|
||||||
return json |
|
||||||
})) |
|
||||||
|
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task('optimize:images', function () { |
|
||||||
return gulp.src('./dist/**/images/**', { base: './dist/' }) |
|
||||||
.pipe(imagemin()) |
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true })) |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task('copy', |
|
||||||
gulp.series( |
|
||||||
gulp.parallel(...copyTaskNames), |
|
||||||
'manifest:production', |
|
||||||
'manifest:chrome', |
|
||||||
'manifest:opera' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('dev:copy', |
|
||||||
gulp.series( |
|
||||||
gulp.parallel(...copyDevTaskNames), |
|
||||||
'manifest:dev', |
|
||||||
'manifest:chrome', |
|
||||||
'manifest:opera' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('test:copy', |
|
||||||
gulp.series( |
|
||||||
gulp.parallel(...copyDevTaskNames), |
|
||||||
'manifest:chrome', |
|
||||||
'manifest:opera', |
|
||||||
'manifest:testing-local' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
// scss compilation and autoprefixing tasks
|
|
||||||
|
|
||||||
gulp.task('build:scss', createScssBuildTask({ |
|
||||||
src: 'ui/app/css/index.scss', |
|
||||||
dest: 'ui/app/css/output', |
|
||||||
devMode: false, |
|
||||||
})) |
|
||||||
|
|
||||||
gulp.task('dev:scss', createScssBuildTask({ |
|
||||||
src: 'ui/app/css/index.scss', |
|
||||||
dest: 'ui/app/css/output', |
|
||||||
devMode: true, |
|
||||||
pattern: 'ui/app/**/*.scss', |
|
||||||
})) |
|
||||||
|
|
||||||
function createScssBuildTask ({ src, dest, devMode, pattern }) { |
|
||||||
return function () { |
|
||||||
if (devMode) { |
|
||||||
watch(pattern, async (event) => { |
|
||||||
const stream = buildScss() |
|
||||||
await endOfStream(stream) |
|
||||||
livereload.changed(event.path) |
|
||||||
}) |
|
||||||
return buildScssWithSourceMaps() |
|
||||||
} |
|
||||||
return buildScss() |
|
||||||
} |
|
||||||
|
|
||||||
function buildScssWithSourceMaps () { |
|
||||||
return gulp.src(src) |
|
||||||
.pipe(sourcemaps.init()) |
|
||||||
.pipe(sass().on('error', sass.logError)) |
|
||||||
.pipe(sourcemaps.write()) |
|
||||||
.pipe(autoprefixer()) |
|
||||||
.pipe(gulp.dest(dest)) |
|
||||||
.pipe(rtlcss()) |
|
||||||
.pipe(rename({ suffix: '-rtl' })) |
|
||||||
.pipe(sourcemaps.write()) |
|
||||||
.pipe(gulp.dest(dest)) |
|
||||||
} |
|
||||||
|
|
||||||
function buildScss () { |
|
||||||
return gulp.src(src) |
|
||||||
.pipe(sass().on('error', sass.logError)) |
|
||||||
.pipe(autoprefixer()) |
|
||||||
.pipe(gulp.dest(dest)) |
|
||||||
.pipe(rtlcss()) |
|
||||||
.pipe(rename({ suffix: '-rtl' })) |
|
||||||
.pipe(gulp.dest(dest)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
gulp.task('lint-scss', function () { |
|
||||||
return gulp |
|
||||||
.src('ui/app/css/itcss/**/*.scss') |
|
||||||
.pipe(gulpStylelint({ |
|
||||||
reporters: [ |
|
||||||
{ formatter: 'string', console: true }, |
|
||||||
], |
|
||||||
fix: true, |
|
||||||
})) |
|
||||||
}) |
|
||||||
|
|
||||||
// build js
|
|
||||||
|
|
||||||
const buildJsFiles = [ |
|
||||||
'inpage', |
|
||||||
'contentscript', |
|
||||||
'background', |
|
||||||
'ui', |
|
||||||
'phishing-detect', |
|
||||||
] |
|
||||||
|
|
||||||
// bundle tasks
|
|
||||||
createTasksForBuildJsDeps({ filename: 'bg-libs', key: 'background' }) |
|
||||||
createTasksForBuildJsDeps({ filename: 'ui-libs', key: 'ui' }) |
|
||||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'dev:extension:js', devMode: true }) |
|
||||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'dev:test-extension:js', devMode: true, testing: 'true' }) |
|
||||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js' }) |
|
||||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:test:extension:js', testing: 'true' }) |
|
||||||
|
|
||||||
function createTasksForBuildJsDeps ({ key, filename }) { |
|
||||||
const destinations = browserPlatforms.map((platform) => `./dist/${platform}`) |
|
||||||
|
|
||||||
const bundleTaskOpts = Object.assign({ |
|
||||||
buildSourceMaps: true, |
|
||||||
sourceMapDir: '../sourcemaps', |
|
||||||
minifyBuild: true, |
|
||||||
devMode: false, |
|
||||||
}) |
|
||||||
|
|
||||||
gulp.task(`build:extension:js:deps:${key}`, bundleTask(Object.assign({ |
|
||||||
label: filename, |
|
||||||
filename: `${filename}.js`, |
|
||||||
destinations, |
|
||||||
buildLib: true, |
|
||||||
dependenciesToBundle: externalDependenciesMap[key], |
|
||||||
}, bundleTaskOpts))) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function createTasksForBuildJsExtension ({ buildJsFiles, taskPrefix, devMode, testing, bundleTaskOpts = {} }) { |
|
||||||
// inpage must be built before all other scripts:
|
|
||||||
const rootDir = './app/scripts' |
|
||||||
const nonInpageFiles = buildJsFiles.filter((file) => file !== 'inpage') |
|
||||||
const buildPhase1 = ['inpage'] |
|
||||||
const buildPhase2 = nonInpageFiles |
|
||||||
const destinations = browserPlatforms.map((platform) => `./dist/${platform}`) |
|
||||||
bundleTaskOpts = Object.assign({ |
|
||||||
buildSourceMaps: true, |
|
||||||
sourceMapDir: '../sourcemaps', |
|
||||||
minifyBuild: !devMode, |
|
||||||
buildWithFullPaths: devMode, |
|
||||||
watch: devMode, |
|
||||||
devMode, |
|
||||||
testing, |
|
||||||
}, bundleTaskOpts) |
|
||||||
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1, buildPhase2 }) |
|
||||||
} |
|
||||||
|
|
||||||
function createTasksForBuildJs ({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 = [], buildPhase2 = [] }) { |
|
||||||
// bundle task for each file
|
|
||||||
const jsFiles = [].concat(buildPhase1, buildPhase2) |
|
||||||
jsFiles.forEach((jsFile) => { |
|
||||||
gulp.task(`${taskPrefix}:${jsFile}`, bundleTask(Object.assign({ |
|
||||||
label: jsFile, |
|
||||||
filename: `${jsFile}.js`, |
|
||||||
filepath: `${rootDir}/${jsFile}.js`, |
|
||||||
externalDependencies: bundleTaskOpts.devMode ? undefined : externalDependenciesMap[jsFile], |
|
||||||
destinations, |
|
||||||
}, bundleTaskOpts))) |
|
||||||
}) |
|
||||||
// compose into larger task
|
|
||||||
const subtasks = [] |
|
||||||
subtasks.push(gulp.parallel(buildPhase1.map((file) => `${taskPrefix}:${file}`))) |
|
||||||
if (buildPhase2.length) { |
|
||||||
subtasks.push(gulp.parallel(buildPhase2.map((file) => `${taskPrefix}:${file}`))) |
|
||||||
} |
|
||||||
|
|
||||||
gulp.task(taskPrefix, gulp.series(subtasks)) |
|
||||||
} |
|
||||||
|
|
||||||
// clean dist
|
|
||||||
|
|
||||||
gulp.task('clean', function clean () { |
|
||||||
return del(['./dist/*']) |
|
||||||
}) |
|
||||||
|
|
||||||
// zip tasks for distribution
|
|
||||||
gulp.task('zip:chrome', zipTask('chrome')) |
|
||||||
gulp.task('zip:firefox', zipTask('firefox')) |
|
||||||
gulp.task('zip:opera', zipTask('opera')) |
|
||||||
gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:opera')) |
|
||||||
|
|
||||||
// high level tasks
|
|
||||||
|
|
||||||
gulp.task('dev:test', |
|
||||||
gulp.series( |
|
||||||
'clean', |
|
||||||
'dev:scss', |
|
||||||
gulp.parallel( |
|
||||||
'dev:test-extension:js', |
|
||||||
'test:copy', |
|
||||||
'dev:reload' |
|
||||||
) |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('dev:extension', |
|
||||||
gulp.series( |
|
||||||
'clean', |
|
||||||
'dev:scss', |
|
||||||
gulp.parallel( |
|
||||||
'dev:extension:js', |
|
||||||
'dev:copy', |
|
||||||
'dev:reload' |
|
||||||
) |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('build', |
|
||||||
gulp.series( |
|
||||||
'clean', |
|
||||||
'build:scss', |
|
||||||
gulpParallel( |
|
||||||
'build:extension:js:deps:background', |
|
||||||
'build:extension:js:deps:ui', |
|
||||||
'build:extension:js', |
|
||||||
'copy' |
|
||||||
), |
|
||||||
'optimize:images' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('build:test', |
|
||||||
gulp.series( |
|
||||||
'clean', |
|
||||||
'build:scss', |
|
||||||
gulpParallel( |
|
||||||
'build:extension:js:deps:background', |
|
||||||
'build:extension:js:deps:ui', |
|
||||||
'build:test:extension:js', |
|
||||||
'copy' |
|
||||||
), |
|
||||||
'manifest:testing' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
gulp.task('dist', |
|
||||||
gulp.series( |
|
||||||
'build', |
|
||||||
'zip' |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
// task generators
|
|
||||||
|
|
||||||
function zipTask (target) { |
|
||||||
return () => { |
|
||||||
return gulp.src(`dist/${target}/**`) |
|
||||||
.pipe(zip(`metamask-${target}-${manifest.version}.zip`)) |
|
||||||
.pipe(gulp.dest('builds')) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function generateBundler (opts, performBundle) { |
|
||||||
const browserifyOpts = assign({}, watchify.args, { |
|
||||||
plugin: [], |
|
||||||
transform: [], |
|
||||||
debug: opts.buildSourceMaps, |
|
||||||
fullPaths: opts.buildWithFullPaths, |
|
||||||
}) |
|
||||||
|
|
||||||
const bundleName = opts.filename.split('.')[0] |
|
||||||
|
|
||||||
// activate sesify
|
|
||||||
const activateAutoConfig = Boolean(process.env.SESIFY_AUTOGEN) |
|
||||||
// const activateSesify = activateAutoConfig
|
|
||||||
const activateSesify = activateAutoConfig && ['background'].includes(bundleName) |
|
||||||
if (activateSesify) { |
|
||||||
configureBundleForSesify({ browserifyOpts, bundleName }) |
|
||||||
} |
|
||||||
|
|
||||||
if (!activateSesify) { |
|
||||||
browserifyOpts.plugin.push('browserify-derequire') |
|
||||||
} |
|
||||||
|
|
||||||
if (!opts.buildLib) { |
|
||||||
if (opts.devMode && opts.filename === 'ui.js') { |
|
||||||
browserifyOpts['entries'] = ['./development/require-react-devtools.js', opts.filepath] |
|
||||||
} else { |
|
||||||
browserifyOpts['entries'] = [opts.filepath] |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
let bundler = browserify(browserifyOpts) |
|
||||||
.transform('babelify') |
|
||||||
// Transpile any dependencies using the object spread/rest operator
|
|
||||||
// because it is incompatible with `esprima`, which is used by `envify`
|
|
||||||
// See https://github.com/jquery/esprima/issues/1927
|
|
||||||
.transform('babelify', { |
|
||||||
only: [ |
|
||||||
'./**/node_modules/libp2p', |
|
||||||
], |
|
||||||
global: true, |
|
||||||
plugins: ['@babel/plugin-proposal-object-rest-spread'], |
|
||||||
}) |
|
||||||
.transform('brfs') |
|
||||||
|
|
||||||
if (opts.buildLib) { |
|
||||||
bundler = bundler.require(opts.dependenciesToBundle) |
|
||||||
} |
|
||||||
|
|
||||||
if (opts.externalDependencies) { |
|
||||||
bundler = bundler.external(opts.externalDependencies) |
|
||||||
} |
|
||||||
|
|
||||||
let environment |
|
||||||
if (opts.devMode) { |
|
||||||
environment = 'development' |
|
||||||
} else if (opts.testing) { |
|
||||||
environment = 'testing' |
|
||||||
} else if (process.env.CIRCLE_BRANCH === 'master') { |
|
||||||
environment = 'production' |
|
||||||
} else if (/^Version-v(\d+)[.](\d+)[.](\d+)/.test(process.env.CIRCLE_BRANCH)) { |
|
||||||
environment = 'release-candidate' |
|
||||||
} else if (process.env.CIRCLE_BRANCH === 'develop') { |
|
||||||
environment = 'staging' |
|
||||||
} else if (process.env.CIRCLE_PULL_REQUEST) { |
|
||||||
environment = 'pull-request' |
|
||||||
} else { |
|
||||||
environment = 'other' |
|
||||||
} |
|
||||||
|
|
||||||
// Inject variables into bundle
|
|
||||||
bundler.transform(envify({ |
|
||||||
METAMASK_DEBUG: opts.devMode, |
|
||||||
METAMASK_ENVIRONMENT: environment, |
|
||||||
NODE_ENV: opts.devMode ? 'development' : 'production', |
|
||||||
IN_TEST: opts.testing, |
|
||||||
PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '', |
|
||||||
PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '', |
|
||||||
}), { |
|
||||||
global: true, |
|
||||||
}) |
|
||||||
|
|
||||||
if (opts.watch) { |
|
||||||
bundler = watchify(bundler) |
|
||||||
// on any file update, re-runs the bundler
|
|
||||||
bundler.on('update', async (ids) => { |
|
||||||
const stream = performBundle() |
|
||||||
await endOfStream(stream) |
|
||||||
livereload.changed(`${ids}`) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
return bundler |
|
||||||
} |
|
||||||
|
|
||||||
function bundleTask (opts) { |
|
||||||
let bundler |
|
||||||
|
|
||||||
return performBundle |
|
||||||
|
|
||||||
function performBundle () { |
|
||||||
// initialize bundler if not available yet
|
|
||||||
// dont create bundler until task is actually run
|
|
||||||
if (!bundler) { |
|
||||||
bundler = generateBundler(opts, performBundle) |
|
||||||
// output build logs to terminal
|
|
||||||
bundler.on('log', log) |
|
||||||
} |
|
||||||
|
|
||||||
let buildStream = bundler.bundle() |
|
||||||
|
|
||||||
// handle errors
|
|
||||||
buildStream.on('error', (err) => { |
|
||||||
beep() |
|
||||||
if (opts.watch) { |
|
||||||
console.warn(err.stack) |
|
||||||
} else { |
|
||||||
throw err |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// process bundles
|
|
||||||
buildStream = buildStream |
|
||||||
// convert bundle stream to gulp vinyl stream
|
|
||||||
.pipe(source(opts.filename)) |
|
||||||
// buffer file contents (?)
|
|
||||||
.pipe(buffer()) |
|
||||||
|
|
||||||
// Initialize Source Maps
|
|
||||||
if (opts.buildSourceMaps) { |
|
||||||
buildStream = buildStream |
|
||||||
// loads map from browserify file
|
|
||||||
.pipe(sourcemaps.init({ loadMaps: true })) |
|
||||||
} |
|
||||||
|
|
||||||
// Minification
|
|
||||||
if (opts.minifyBuild) { |
|
||||||
buildStream = buildStream |
|
||||||
.pipe(terser({ |
|
||||||
mangle: { |
|
||||||
reserved: [ 'MetamaskInpageProvider' ], |
|
||||||
}, |
|
||||||
})) |
|
||||||
} |
|
||||||
|
|
||||||
// Finalize Source Maps
|
|
||||||
if (opts.buildSourceMaps) { |
|
||||||
if (opts.devMode) { |
|
||||||
// Use inline source maps for development due to Chrome DevTools bug
|
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=931675
|
|
||||||
buildStream = buildStream |
|
||||||
.pipe(sourcemaps.write()) |
|
||||||
} else { |
|
||||||
buildStream = buildStream |
|
||||||
.pipe(sourcemaps.write(opts.sourceMapDir)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// write completed bundles
|
|
||||||
opts.destinations.forEach((dest) => { |
|
||||||
buildStream = buildStream.pipe(gulp.dest(dest)) |
|
||||||
}) |
|
||||||
|
|
||||||
return buildStream |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function configureBundleForSesify ({ |
|
||||||
browserifyOpts, |
|
||||||
bundleName, |
|
||||||
}) { |
|
||||||
// add in sesify args for better globalRef usage detection
|
|
||||||
Object.assign(browserifyOpts, sesify.args) |
|
||||||
|
|
||||||
// ensure browserify uses full paths
|
|
||||||
browserifyOpts.fullPaths = true |
|
||||||
|
|
||||||
// record dependencies used in bundle
|
|
||||||
fs.mkdirSync('./sesify', { recursive: true }) |
|
||||||
browserifyOpts.plugin.push(['deps-dump', { |
|
||||||
filename: `./sesify/deps-${bundleName}.json`, |
|
||||||
}]) |
|
||||||
|
|
||||||
const sesifyConfigPath = `./sesify/${bundleName}.json` |
|
||||||
|
|
||||||
// add sesify plugin
|
|
||||||
browserifyOpts.plugin.push([sesify, { |
|
||||||
writeAutoConfig: sesifyConfigPath, |
|
||||||
}]) |
|
||||||
|
|
||||||
// remove html comments that SES is alergic to
|
|
||||||
const removeHtmlComment = makeStringTransform('remove-html-comment', { excludeExtension: ['.json'] }, (content, _, cb) => { |
|
||||||
const result = content.split('-->').join('-- >') |
|
||||||
cb(null, result) |
|
||||||
}) |
|
||||||
browserifyOpts.transform.push([removeHtmlComment, { global: true }]) |
|
||||||
} |
|
||||||
|
|
||||||
function beep () { |
|
||||||
process.stdout.write('\x07') |
|
||||||
} |
|
Loading…
Reference in new issue