mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
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>
This commit is contained in:
parent
2df8b85c5f
commit
7686edadb0
@ -9,5 +9,5 @@ set -o pipefail
|
||||
mkdir -p ./build-artifacts/deps-viz/
|
||||
|
||||
# generate viz
|
||||
SESIFY_AUTOGEN=1 npx gulp build:extension:js:background
|
||||
SESIFY_AUTOGEN=1 yarn build scripts:core:prod:background
|
||||
npx sesify-viz --deps sesify/deps-background.json --config sesify/background.json --dest ./build-artifacts/deps-viz/background
|
@ -24,16 +24,9 @@
|
||||
"128": "images/icon-128.png",
|
||||
"512": "images/icon-512.png"
|
||||
},
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webextension@metamask.io",
|
||||
"strict_min_version": "56.0"
|
||||
}
|
||||
},
|
||||
"default_locale": "en",
|
||||
"background": {
|
||||
"scripts": [
|
||||
"chromereload.js",
|
||||
"bg-libs.js",
|
||||
"background.js"
|
||||
],
|
1
app/manifest/brave.json
Normal file
1
app/manifest/brave.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
3
app/manifest/chrome.json
Normal file
3
app/manifest/chrome.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"minimum_chrome_version": "58"
|
||||
}
|
8
app/manifest/firefox.json
Normal file
8
app/manifest/firefox.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webextension@metamask.io",
|
||||
"strict_min_version": "56.0"
|
||||
}
|
||||
}
|
||||
}
|
9
app/manifest/opera.json
Normal file
9
app/manifest/opera.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": [
|
||||
"storage",
|
||||
"tabs",
|
||||
"clipboardWrite",
|
||||
"clipboardRead",
|
||||
"http://localhost:8545/"
|
||||
]
|
||||
}
|
@ -50,7 +50,7 @@ import accountImporter from './account-import-strategies'
|
||||
import getBuyEthUrl from './lib/buy-eth-url'
|
||||
import selectChainId from './lib/select-chain-id'
|
||||
import { Mutex } from 'await-semaphore'
|
||||
import { version } from '../manifest.json'
|
||||
import { version } from '../manifest/_base.json'
|
||||
import ethUtil, { BN } from 'ethereumjs-util'
|
||||
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
|
@ -1,14 +1,10 @@
|
||||
const manifest = require('../app/manifest.json')
|
||||
|
||||
const version = manifest.version
|
||||
|
||||
const fs = require('fs')
|
||||
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 msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`
|
||||
|
||||
console.log(msg)
|
||||
|
150
development/build/display.js
Normal file
150
development/build/display.js
Normal file
@ -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')
|
||||
}
|
||||
}
|
42
development/build/etc.js
Normal file
42
development/build/etc.js
Normal file
@ -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'),
|
||||
)
|
||||
}
|
||||
}
|
91
development/build/index.js
Executable file
91
development/build/index.js
Executable file
@ -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)
|
||||
|
||||
}
|
97
development/build/manifest.js
Normal file
97
development/build/manifest.js
Normal file
@ -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))
|
||||
}
|
362
development/build/scripts.js
Normal file
362
development/build/scripts.js
Normal file
@ -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')
|
||||
}
|
95
development/build/static.js
Normal file
95
development/build/static.js
Normal file
@ -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}`)
|
||||
}))
|
||||
}
|
||||
|
||||
}
|
79
development/build/styles.js
Normal file
79
development/build/styles.js
Normal file
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
101
development/build/task.js
Normal file
101
development/build/task.js
Normal file
@ -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()))
|
||||
}
|
||||
}
|
724
gulpfile.js
724
gulpfile.js
@ -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')
|
||||
}
|
17
package.json
17
package.json
@ -3,12 +3,13 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "gulp dev:extension",
|
||||
"dist": "gulp dist",
|
||||
"start:test": "gulp dev:test",
|
||||
"start": "yarn build dev",
|
||||
"dist": "yarn build prod",
|
||||
"build": "node development/build/index.js",
|
||||
"start:test": "yarn build testDev",
|
||||
"benchmark:chrome": "SELENIUM_BROWSER=chrome node test/e2e/benchmark.js",
|
||||
"benchmark:firefox": "SELENIUM_BROWSER=firefox node test/e2e/benchmark.js",
|
||||
"build:test": "gulp build:test",
|
||||
"build:test": "yarn build test",
|
||||
"test": "yarn test:unit && yarn lint",
|
||||
"dapp": "node development/static-server.js test/e2e/contract-test --port 8080",
|
||||
"dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && node development/static-server.js test/e2e/contract-test --port 8080'",
|
||||
@ -19,7 +20,7 @@
|
||||
"test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\"",
|
||||
"test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive mocha test/unit-global/*",
|
||||
"test:integration": "yarn test:integration:build && yarn test:flat",
|
||||
"test:integration:build": "gulp build:scss",
|
||||
"test:integration:build": "yarn build styles",
|
||||
"test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh",
|
||||
"test:web3:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-web3.sh",
|
||||
"test:web3:firefox": "SELENIUM_BROWSER=firefox test/e2e/run-web3.sh",
|
||||
@ -193,7 +194,7 @@
|
||||
"browserify-derequire": "^1.0.1",
|
||||
"browserify-transform-tools": "^1.7.0",
|
||||
"chai": "^4.1.0",
|
||||
"chalk": "^2.4.2",
|
||||
"chalk": "^3.0.0",
|
||||
"chromedriver": "^79.0.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"coveralls": "^3.0.0",
|
||||
@ -210,8 +211,10 @@
|
||||
"eslint-plugin-mocha": "^6.2.2",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"fancy-log": "^1.3.3",
|
||||
"fast-glob": "^3.2.2",
|
||||
"fetch-mock": "^6.5.2",
|
||||
"file-loader": "^1.1.11",
|
||||
"fs-extra": "^8.1.0",
|
||||
"ganache-cli": "^6.4.4",
|
||||
"ganache-core": "2.8.0",
|
||||
"geckodriver": "^1.19.1",
|
||||
@ -221,7 +224,6 @@
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-debug": "^3.2.0",
|
||||
"gulp-imagemin": "^6.1.0",
|
||||
"gulp-json-editor": "^2.5.4",
|
||||
"gulp-livereload": "4.0.0",
|
||||
"gulp-multi-process": "^1.3.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
@ -252,6 +254,7 @@
|
||||
"proxyquire": "^2.1.3",
|
||||
"qs": "^6.2.0",
|
||||
"qunitjs": "^2.4.1",
|
||||
"randomcolor": "^0.5.4",
|
||||
"react-devtools": "^4.4.0",
|
||||
"react-test-renderer": "^16.12.0",
|
||||
"read-installed": "^4.0.3",
|
||||
|
@ -1,4 +1,4 @@
|
||||
const fs = require('fs').promises
|
||||
const { promises: fs } = require('fs')
|
||||
const Koa = require('koa')
|
||||
const path = require('path')
|
||||
|
||||
|
117
yarn.lock
117
yarn.lock
@ -1888,11 +1888,24 @@
|
||||
"@nodelib/fs.stat" "2.0.2"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
|
||||
integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "2.0.3"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.2", "@nodelib/fs.stat@^2.0.1":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz#2762aea8fe78ea256860182dcb52d61ee4b8fda6"
|
||||
integrity sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==
|
||||
|
||||
"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
|
||||
integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
|
||||
|
||||
"@nodelib/fs.stat@^1.1.2":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||
@ -1906,6 +1919,14 @@
|
||||
"@nodelib/fs.scandir" "2.1.2"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@nodelib/fs.walk@^1.2.3":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
|
||||
integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
|
||||
dependencies:
|
||||
"@nodelib/fs.scandir" "2.1.3"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@protobufjs/utf8@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
@ -7265,7 +7286,7 @@ concurrently@^5.1.0:
|
||||
tree-kill "^1.2.2"
|
||||
yargs "^13.3.0"
|
||||
|
||||
config-chain@^1.1.11, config-chain@^1.1.12:
|
||||
config-chain@^1.1.11:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
|
||||
integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
|
||||
@ -8484,11 +8505,6 @@ deepmerge@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
||||
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
||||
|
||||
deepmerge@^4.2.1:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
default-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f"
|
||||
@ -8739,11 +8755,6 @@ detect-indent@^4.0.0:
|
||||
dependencies:
|
||||
repeating "^2.0.0"
|
||||
|
||||
detect-indent@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd"
|
||||
integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==
|
||||
|
||||
detect-libc@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
@ -9255,16 +9266,6 @@ editions@^1.3.3:
|
||||
resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b"
|
||||
integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==
|
||||
|
||||
editorconfig@^0.15.3:
|
||||
version "0.15.3"
|
||||
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
|
||||
integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==
|
||||
dependencies:
|
||||
commander "^2.19.0"
|
||||
lru-cache "^4.1.5"
|
||||
semver "^5.6.0"
|
||||
sigmund "^1.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@ -11534,6 +11535,18 @@ fast-glob@^3.0.3:
|
||||
merge2 "^1.2.3"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
fast-glob@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d"
|
||||
integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.0"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.2"
|
||||
picomatch "^2.2.1"
|
||||
|
||||
fast-json-parse@^1.0.0, fast-json-parse@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d"
|
||||
@ -12306,6 +12319,15 @@ fs-extra@^8.0.1:
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-minipass@^1.2.5:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
|
||||
@ -12762,7 +12784,7 @@ glob-parent@^3.0.1, glob-parent@^3.1.0:
|
||||
is-glob "^3.1.0"
|
||||
path-dirname "^1.0.0"
|
||||
|
||||
glob-parent@^5.0.0, glob-parent@~5.1.0:
|
||||
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
|
||||
integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==
|
||||
@ -13158,7 +13180,7 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.15:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
graceful-fs@^4.2.2:
|
||||
graceful-fs@^4.2.0, graceful-fs@^4.2.2:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
|
||||
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
|
||||
@ -13299,17 +13321,6 @@ gulp-imagemin@^6.1.0:
|
||||
imagemin-optipng "^7.0.0"
|
||||
imagemin-svgo "^7.0.0"
|
||||
|
||||
gulp-json-editor@^2.5.4:
|
||||
version "2.5.4"
|
||||
resolved "https://registry.yarnpkg.com/gulp-json-editor/-/gulp-json-editor-2.5.4.tgz#b77b46bca22d2dd1ac9f15bbec1eddbe5ef3567c"
|
||||
integrity sha512-3IdMYsSACfLFYipet9Rmpag7PEU059KnR6TWgfuAfz+ftyzN8yaEvf9vXAD5b9K9v711Ymcpqe6vWGQYfQJ/uQ==
|
||||
dependencies:
|
||||
deepmerge "^4.2.1"
|
||||
detect-indent "^6.0.0"
|
||||
js-beautify "^1.10.2"
|
||||
plugin-error "^1.0.1"
|
||||
through2 "^3.0.1"
|
||||
|
||||
gulp-livereload@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-livereload/-/gulp-livereload-4.0.0.tgz#be4a6b01731a93f76f4fb29c9e62e323affe7d03"
|
||||
@ -16412,17 +16423,6 @@ js-base64@^2.1.8:
|
||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
|
||||
integrity sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==
|
||||
|
||||
js-beautify@^1.10.2:
|
||||
version "1.10.3"
|
||||
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.3.tgz#c73fa10cf69d3dfa52d8ed624f23c64c0a6a94c1"
|
||||
integrity sha512-wfk/IAWobz1TfApSdivH5PJ0miIHgDoYb1ugSqHcODPmaYu46rYe5FVuIEkhjg8IQiv6rDNPyhsqbsohI/C2vQ==
|
||||
dependencies:
|
||||
config-chain "^1.1.12"
|
||||
editorconfig "^0.15.3"
|
||||
glob "^7.1.3"
|
||||
mkdirp "~0.5.1"
|
||||
nopt "~4.0.1"
|
||||
|
||||
js-levenshtein@^1.1.3:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
@ -18533,14 +18533,6 @@ lru-cache@^3.2.0:
|
||||
dependencies:
|
||||
pseudomap "^1.0.1"
|
||||
|
||||
lru-cache@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
||||
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
|
||||
dependencies:
|
||||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
@ -18916,6 +18908,11 @@ merge2@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5"
|
||||
integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
|
||||
integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
|
||||
|
||||
merkle-lib@^2.0.10:
|
||||
version "2.0.10"
|
||||
resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326"
|
||||
@ -20097,7 +20094,7 @@ nonce-tracker@^1.0.0:
|
||||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
nopt@^4.0.1, nopt@~4.0.1:
|
||||
nopt@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
|
||||
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
|
||||
@ -21682,7 +21679,7 @@ phantomjs-prebuilt@^2.1.16, phantomjs-prebuilt@~2.1.7:
|
||||
request-progress "^2.0.1"
|
||||
which "^1.2.10"
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.0.5:
|
||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
|
||||
integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==
|
||||
@ -23005,6 +23002,11 @@ randombytes@^2.0.1, randombytes@^2.0.3, randombytes@^2.0.6:
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
randomcolor@^0.5.4:
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/randomcolor/-/randomcolor-0.5.4.tgz#df615b13f25b89ea58c5f8f72647f0a6f07adcc3"
|
||||
integrity sha512-nYd4nmTuuwMFzHL6W+UWR5fNERGZeVauho8mrJDUSXdNDbao4rbrUwhuLgKC/j8VCS5+34Ria8CsTDuBjrIrQA==
|
||||
|
||||
randomfill@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62"
|
||||
@ -25419,11 +25421,6 @@ side-channel@^1.0.2:
|
||||
es-abstract "^1.17.0-next.1"
|
||||
object-inspect "^1.7.0"
|
||||
|
||||
sigmund@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
|
||||
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
|
Loading…
Reference in New Issue
Block a user