feat(tauri) initial work for the CLI (#20)

* feat(tauri) initial work for the CLI

* feat(build) read dist path from env

* feat(tauri) proton.js entry

* feat(tauri) threeshake fs

* feat(injector) check if tauri dir exists

* feat(injector) don't show success message if dir exists

* chore(injector) cleanup fs imports
This commit is contained in:
Lucas Fernandes Nogueira
2019-08-19 18:09:29 -03:00
committed by GitHub
parent 4c2c9d00b9
commit a0f410dcff
18 changed files with 1253 additions and 6 deletions

View File

@@ -3,11 +3,8 @@ extern crate includedir_codegen;
use includedir_codegen::Compression;
use std::env;
static CARGOENV: &str = "cargo:rustc-env=";
fn main() {
let dist_path = format!("{}/../../../../{}", env::var("OUT_DIR").unwrap(), "compiled-web");
println!("{}TAURI_DIST_DIR={}", CARGOENV, dist_path);
let dist_path = env::var("TAURI_DIST_DIR").unwrap();
includedir_codegen::start("ASSETS")
.dir(dist_path, Compression::None)
.build("data.rs")

337
lib/tauri.js Normal file
View File

@@ -0,0 +1,337 @@
<%
if (typeof(confName) === 'undefined') {
confName = 'quasar.conf.js'
}
%>
/**
* THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* Please whitelist these API functions in <% confName %>
*
**/
/**
* @module tauri
* @description This API interface makes powerful interactions available
* to be run on client side applications. They are opt-in features, and
* must be enabled in <% confName %>
*
* Each binding MUST provide these interfaces in order to be compliant,
* and also whitelist them based upon the developer's settings.
*/
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
const uid = function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
<% if (ctx.dev) { %>
/**
* @name __whitelistWarning
* @description Present a stylish warning to the developer that their API
* call has not been whitelisted in <% confName %>
* @param {String} func - function name to warn
* @private
*/
const __whitelistWarning = function (func) {
console.warn('%c[Tauri] Danger \ntauri.' + func + ' not whitelisted 💣\n%c\nAdd to <% confName %>: \n\ntauri: \n whitelist: { \n ' + func + ': true \n\nReference: https://quasar.dev/quasar-cli/creating-tauri-apps/api#' + func , 'background: red; color: white; font-weight: 800; padding: 2px; font-size:1.5em', ' ')
}
<% } %>
/**
* @name __reject
* @description is a private promise used to deflect un-whitelisted tauri API calls
* Its only purpose is to maintain thenable structure in client code without
* breaking the application
* * @type {Promise<any>}
* @private
*/
const __reject = new Promise((reject) => { reject })
export default class Tauri {
<% if (ctx.dev) { %>
/**
* @name invoke
* @description Calls a Quasar Core feature, such as setTitle
* @param {Object} args
*/
<% } %>
static invoke (args) {
Object.freeze(args)
external.invoke(JSON.stringify(args))
}
<% if (ctx.dev) { %>
/**
* @name addEventListener
* @description Add an event listener to Tauri back end
* @param {String} event
* @param {Function} handler
* @param {Boolean} once
*/
<% } %>
static addEventListener(event, handler, once = false) {
this.invoke({
cmd: 'addEventListener',
event,
handler: this.transformCallback(handler, once),
once
})
}
<% if (ctx.dev) { %>
/**
* @name emit
* @description Emits an event to the Tauri back end
* @param {String} event
* @param {Object} payload
*/
<% } %>
static emit(event, payload) {
this.invoke({
cmd: 'emit',
event,
payload
})
}
<% if (ctx.dev) { %>
/**
* @name transformCallback
* @description Registers a callback with a uid
* @param {Function} callback
* @param {Boolean} once
* @returns {*}
*/
<% } %>
static transformCallback (callback, once = true) {
const identifier = Object.freeze(uid())
window[identifier] = (result) => {
if (once) {
delete window[identifier]
}
return callback && callback(result)
}
return identifier
}
<% if (ctx.dev) { %>
/**
* @name promisified
* @description Turns a request into a chainable promise
* @param {Object} args
* @returns {Promise<any>}
*/
<% } %>
static promisified (args) {
return new Promise((resolve, reject) => {
this.invoke({
callback: this.transformCallback(resolve),
error: this.transformCallback(reject),
...args
})
})
}
<% if (ctx.dev) { %>
/**
* @name readTextFile
* @description Accesses a non-binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static readTextFile (path) {
<% if (tauri.whitelist.readTextFile === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'readTextFile', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('readTextFile')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name readBinaryFile
* @description Accesses a binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static readBinaryFile (path) {
<% if (tauri.whitelist.readBinaryFile === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'readBinaryFile', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('readBinaryFile')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name writeFile
* @description Write a file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
*/
<% } %>
static writeFile (cfg) {
Object.freeze(cfg)
<% if (tauri.whitelist.writeFile === true || tauri.whitelist.all === true) { %>
this.invoke({ cmd: 'writeFile', file: cfg.file, contents: cfg.contents })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('writeFile')
<% } %>
return __reject
<% } %> }
<% if (ctx.dev) { %>
/**
* @name listFiles
* @description Get the files in a path.
* Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static listFiles (path) {
<% if (tauri.whitelist.listFiles === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'listFiles', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('listFiles')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name listDirs
* @description Get the directories in a path.
* Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static listDirs (path) {
<% if (tauri.whitelist.listDirs === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'listDirs', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('listDirs')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name setTitle
* @description Set the application's title
* @param {String} title
*/
<% } %>
static setTitle (title) {
<% if (tauri.whitelist.setTitle === true || tauri.whitelist.all === true) { %>
Object.freeze(title)
this.invoke({ cmd: 'setTitle', title })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('setTitle')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name open
* @description Open an URI
* @param {String} uri
*/
<% } %>
static open (uri) {
<% if (tauri.whitelist.open === true || tauri.whitelist.all === true) { %>
Object.freeze(uri)
this.invoke({ cmd: 'open', uri })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('open')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name execute
* @description Execute a program with arguments.
* Permissions based on the app's PID owner
* @param {String} command
* @param {String|Array} args
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static execute (command, args) {
<% if (tauri.whitelist.execute === true || tauri.whitelist.all === true) { %>
Object.freeze(command)
if (typeof args === 'string' || typeof args === 'object') {
Object.freeze(args)
}
return this.promisified({ cmd: 'execute', command, args: typeof (args) === 'string' ? [args] : args })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('execute')
<% } %>
return __reject
<% } %>
}
<% if (ctx.dev) { %>
/**
* @name bridge
* @description Securely pass a message to the backend.
* @example
* this.$q.tauri.bridge('QBP/1/ping/client-1', 'pingback')
* @param {String} command - a compressed, slash-delimited and
* versioned API call to the backend.
* @param {String|Object}payload
* @returns {*|Promise<any>|Promise}
*/
<% } %>
static bridge (command, payload) {
<% if (tauri.whitelist.bridge === true || tauri.whitelist.all === true) { %>
Object.freeze(command)
if (typeof payload === 'string' || typeof payload === 'object') {
Object.freeze(payload)
}
return this.promisified({ cmd: 'bridge', command, payload: typeof (payload) === 'object' ? [payload] : payload })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('bridge')
<% } %>
return __reject
<% } %>
}
}

View File

@@ -2,7 +2,10 @@
"name": "@quasar/tauri",
"version": "1.0.0-alpha.1",
"description": "Multi-binding collection of libraries and templates for building Tauri",
"main": "templates/index.js",
"main": "tauri/lib.js",
"bin": {
"tauri": "tauri/bin/tauri.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
@@ -23,5 +26,18 @@
"node": ">= 10.16.0",
"npm": ">= 6.6.0",
"yarn": ">= 1.17.3"
},
"dependencies": {
"@iarna/toml": "^2.2.3",
"chalk": "^2.4.2",
"chokidar": "^3.0.2",
"cross-spawn": "^6.0.5",
"fast-glob": "^3.0.4",
"fs-extra": "^8.1.0",
"lodash.debounce": "^4.0.8",
"lodash.template": "^4.5.0",
"minimist": "^1.2.0",
"ms": "^2.1.2",
"webpack-merge": "^4.2.1"
}
}

42
tauri/bin/tauri-build.js Normal file
View File

@@ -0,0 +1,42 @@
const
parseArgs = require('minimist'),
{ writeFileSync } = require('fs-extra'),
path = require('path')
const argv = parseArgs(process.argv.slice(2), {
alias: {
h: 'help',
d: 'debug'
},
boolean: ['h', 'd']
})
if (argv.help) {
console.log(`
Description
Tauri build.
Usage
$ tauri build
Options
--help, -h Displays this message
`)
process.exit(0)
}
const appPaths = require('../helpers/app-paths'),
Runner = require('../runner'),
Injector = require('../injector'),
tauri = new Runner(appPaths),
injector = new Injector(appPaths),
tauriConfig = require('../helpers/tauri-config')({
ctx: {
debug: argv.debug
}
})
const {bundle, ...cfg} = tauriConfig.tauri,
cfgDir = injector.configDir()
writeFileSync(path.join(cfgDir, 'config.json'), JSON.stringify(cfg))
writeFileSync(path.join(cfgDir, 'bundle.json'), JSON.stringify(bundle))
require('../helpers/generator')(tauriConfig)
tauri.build(tauriConfig)

44
tauri/bin/tauri-dev.js Normal file
View File

@@ -0,0 +1,44 @@
const
parseArgs = require('minimist'),
path = require('path'),
{ writeFileSync } = require('fs-extra')
const argv = parseArgs(process.argv.slice(2), {
alias: {
h: 'help'
},
boolean: ['h']
})
if (argv.help) {
console.log(`
Description
Tauri dev.
Usage
$ tauri dev
Options
--help, -h Displays this message
`)
process.exit(0)
}
const appPaths = require('../helpers/app-paths'),
Runner = require('../runner'),
Injector = require('../injector'),
tauri = new Runner(appPaths),
injector = new Injector(appPaths),
tauriConfig = require('../helpers/tauri-config')({
ctx: {
debug: true
}
})
const { bundle, ...cfg } = tauriConfig.tauri,
cfgDir = injector.configDir()
writeFileSync(path.join(cfgDir, 'config.json'), JSON.stringify(cfg))
writeFileSync(path.join(cfgDir, 'bundle.json'), JSON.stringify(bundle))
require('../helpers/generator')(tauriConfig)
tauri.run(tauriConfig)

29
tauri/bin/tauri-init.js Normal file
View File

@@ -0,0 +1,29 @@
const
parseArgs = require('minimist'),
appPaths = require('../helpers/app-paths'),
log = require('../helpers/logger')('app:tauri')
const argv = parseArgs(process.argv.slice(2), {
alias: {
h: 'help'
},
boolean: ['h']
})
if (argv.help) {
console.log(`
Description
Inits the Tauri template.
Usage
$ tauri init
Options
--help, -h Displays this message
`)
process.exit(0)
}
const Injector = require('../injector'),
injector = new Injector(appPaths)
if (injector.injectTemplate()) {
log('Tauri template successfully installed')
}

22
tauri/bin/tauri.js Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env node
const cmds = ['init', 'dev', 'build', 'help']
const cmd = process.argv[2]
if (!cmd || cmd === '-h' || cmd === '--help' || cmd === 'help') {
console.log(`
Description
Tauri CLI.
Usage
$ tauri ${cmds.join('|')}
Options
--help, -h Displays this message
`)
process.exit(0)
}
if (cmds.includes(cmd)) {
process.argv.splice(2, 1)
require(`./tauri-${cmd}`)
} else {
console.log(`Invalid command ${cmd}. Use one of ${cmds.join(',')}.`)
}

View File

@@ -0,0 +1,38 @@
const
{ existsSync } = require('fs'),
path = require('path'),
resolve = path.resolve,
join = path.join
function getAppDir() {
let dir = process.cwd()
while (dir.length && dir[dir.length - 1] !== path.sep) {
if (existsSync(join(dir, 'tauri.conf.js'))) {
return dir
}
dir = path.normalize(join(dir, '..'))
}
const
logger = require('./logger')
warn = logger('app:paths', 'red')
warn(`⚠️ Error. This command must be executed inside a Tauri project folder.`)
warn()
process.exit(1)
}
const appDir = getAppDir(),
tauriDir = resolve(appDir, 'src-tauri')
module.exports = {
appDir,
tauriDir,
resolve: {
app: dir => join(appDir, dir),
tauri: dir => join(tauriDir, dir)
}
}

View File

@@ -0,0 +1,13 @@
const compileTemplate = require('lodash.template'),
{ readFileSync, writeFileSync } = require('fs'),
appPaths = require('./app-paths'),
path = require('path')
module.exports = cfg => {
const apiTemplate = readFileSync(path.resolve(__dirname, '../../lib/tauri.js'), 'utf-8')
const apiContent = compileTemplate(apiTemplate)({
...cfg,
confName: 'tauri.conf.js'
})
writeFileSync(appPaths.resolve.tauri('tauri.js'), apiContent, 'utf-8')
}

21
tauri/helpers/logger.js Normal file
View File

@@ -0,0 +1,21 @@
const
ms = require('ms'),
chalk = require('chalk')
let prevTime
module.exports = function (banner, color = 'green') {
return function (msg) {
const
curr = +new Date(),
diff = curr - (prevTime || curr)
prevTime = curr
if (msg) {
console.log(` ${chalk[color](banner)} ${msg} ${chalk.green(`+${ms(diff)}`)}`)
} else {
console.log()
}
}
}

View File

@@ -0,0 +1,15 @@
module.exports = function (fn) {
const cleanup = () => {
try {
fn()
} finally {
process.exit()
}
}
process.on('exit', cleanup)
process.on('SIGINT', cleanup)
process.on('SIGTERM', cleanup)
process.on('SIGHUP', cleanup)
process.on('SIGBREAK', cleanup)
}

62
tauri/helpers/spawn.js Normal file
View File

@@ -0,0 +1,62 @@
const
logger = require('./logger'),
log = logger('app:spawn'),
warn = logger('app:spawn', 'red'),
crossSpawn = require('cross-spawn')
/*
Returns pid, takes onClose
*/
module.exports.spawn = function (cmd, params, cwd, onClose) {
log(`Running "${cmd} ${params.join(' ')}"`)
log()
const runner = crossSpawn(
cmd,
params, {
stdio: 'inherit',
stdout: 'inherit',
stderr: 'inherit',
cwd
}
)
runner.on('close', code => {
log()
if (code) {
log(`Command "${cmd}" failed with exit code: ${code}`)
}
onClose && onClose(code)
})
return runner.pid
}
/*
Returns nothing, takes onFail
*/
module.exports.spawnSync = function (cmd, params, cwd, onFail) {
log(`[sync] Running "${cmd} ${params.join(' ')}"`)
log()
const runner = crossSpawn.sync(
cmd,
params, {
stdio: 'inherit',
stdout: 'inherit',
stderr: 'inherit',
cwd
}
)
if (runner.status || runner.error) {
warn()
warn(`⚠️ Command "${cmd}" failed with exit code: ${runner.status}`)
if (runner.status === null) {
warn(`⚠️ Please globally install "${cmd}"`)
}
onFail && onFail()
process.exit(1)
}
}

View File

@@ -0,0 +1,29 @@
const appPaths = require('./app-paths'),
merge = require('webpack-merge')
module.exports = cfg => {
const tauriConf = require(appPaths.resolve.app('tauri.conf.js'))(cfg.ctx)
const config = merge({
build: {},
ctx: {},
tauri: {
embeddedServer: {
active: true
},
bundle: {
active: true
},
whitelist: {},
window: {
title: require(appPaths.resolve.app('package.json')).productName
},
security: {
csp: 'default-src data: filesystem: ws: http: https: \'unsafe-eval\' \'unsafe-inline\''
}
}
}, tauriConf, cfg)
process.env.TAURI_DIST_DIR = appPaths.resolve.app(config.build.distDir)
return config
}

41
tauri/injector.js Normal file
View File

@@ -0,0 +1,41 @@
const { copySync, renameSync, existsSync, mkdirSync } = require('fs-extra'),
path = require('path')
class TauriInjector {
constructor(appPaths) {
this.appPaths = appPaths
}
configDir() {
return path.resolve(__dirname, '..')
}
injectTemplate() {
if (existsSync(this.appPaths.tauriDir)) {
console.log(`Tauri dir (${this.appPaths.tauriDir}) not empty.`)
return false
}
mkdirSync(this.appPaths.tauriDir)
copySync(path.resolve(__dirname, '../templates/rust'), this.appPaths.tauriDir)
const files = require('fast-glob').sync(['**/_*'], {
cwd: this.appPaths.tauriDir
})
for (const rawPath of files) {
const targetRelativePath = rawPath.split('/').map(name => {
// dotfiles are ignored when published to npm, therefore in templates
// we need to use underscore instead (e.g. "_gitignore")
if (name.charAt(0) === '_' && name.charAt(1) !== '_') {
return `.${name.slice(1)}`
}
if (name.charAt(0) === '_' && name.charAt(1) === '_') {
return `${name.slice(1)}`
}
return name
}).join('/')
renameSync(this.appPaths.resolve.tauri(rawPath), this.appPaths.resolve.tauri(targetRelativePath))
}
return true
}
}
module.exports = TauriInjector

9
tauri/lib.js Normal file
View File

@@ -0,0 +1,9 @@
const TauriRunner = require('./runner'),
TauriInjector = require('./injector'),
path = require('path')
module.exports = {
runner: TauriRunner,
injector: TauriInjector,
apiTemplatePath: path.resolve(__dirname, '../lib/tauri.js')
}

189
tauri/runner.js Normal file
View File

@@ -0,0 +1,189 @@
const
chokidar = require('chokidar'),
debounce = require('lodash.debounce')
const
{ spawn } = require('./helpers/spawn'),
log = require('./helpers/logger')('app:tauri')
onShutdown = require('./helpers/on-shutdown'),
{ readFileSync, writeFileSync } = require('fs-extra')
class TauriRunner {
constructor(appPaths) {
this.appPaths = appPaths
this.pid = 0
this.tauriWatcher = null
onShutdown(() => {
this.stop()
})
}
async run(cfg) {
const
url = cfg.build.APP_URL
if (this.pid) {
if (this.url !== url) {
await this.stop()
} else {
return
}
}
this.__manipulateToml(toml => {
this.__whitelistApi(cfg, toml)
})
this.url = url
const args = ['--url', url]
const startDevTauri = () => {
return this.__runCargoCommand({
cargoArgs: ['run', '--features', 'dev'],
extraArgs: args
})
}
// Start watching for tauri app changes
this.tauriWatcher = chokidar
.watch([
this.appPaths.resolve.tauri('src'),
this.appPaths.resolve.tauri('Cargo.toml'),
this.appPaths.resolve.tauri('build.rs')
], {
watchers: {
chokidar: {
ignoreInitial: true
}
}
})
.on('change', debounce(async () => {
await this.__stopCargo()
startDevTauri()
}, 1000))
return startDevTauri()
}
async build(cfg) {
this.__manipulateToml(toml => {
this.__whitelistApi(cfg, toml)
})
const features = []
if (cfg.tauri.embeddedServer.active) {
features.push('embedded-server')
}
const buildFn = target => this.__runCargoCommand({
cargoArgs: [cfg.tauri.bundle.active ? 'tauri-bundle' : 'build']
.concat(features.length ? ['--features', ...features] : [])
.concat(cfg.ctx.debug ? [] : ['--release'])
.concat(target ? ['--target', target] : [])
})
if (cfg.ctx.debug || !cfg.ctx.targetName) {
// on debug mode or if not arget specified,
// build only for the current platform
return buildFn()
}
const targets = cfg.ctx.target.split(',')
for (const target of targets) {
await buildFn(target)
}
}
stop() {
return new Promise((resolve, reject) => {
this.tauriWatcher && this.tauriWatcher.close()
this.__stopCargo().then(resolve)
})
}
__runCargoCommand({
cargoArgs,
extraArgs
}) {
return new Promise(resolve => {
this.pid = spawn(
'cargo',
extraArgs ?
cargoArgs.concat(['--']).concat(extraArgs) :
cargoArgs,
this.appPaths.tauriDir,
code => {
if (code) {
warn()
warn(`⚠️ [FAIL] Cargo CLI has failed`)
warn()
process.exit(1)
}
if (this.killPromise) {
this.killPromise()
this.killPromise = null
} else { // else it wasn't killed by us
warn()
warn('Cargo process was killed. Exiting...')
warn()
process.exit(0)
}
}
)
resolve()
})
}
__stopCargo() {
const pid = this.pid
if (!pid) {
return Promise.resolve()
}
log('Shutting down tauri process...')
this.pid = 0
return new Promise((resolve, reject) => {
this.killPromise = resolve
process.kill(pid)
})
}
__manipulateToml(callback) {
const toml = require('@iarna/toml'),
tomlPath = this.appPaths.resolve.tauri('Cargo.toml'),
tomlFile = readFileSync(tomlPath),
tomlContents = toml.parse(tomlFile)
callback(tomlContents)
const output = toml.stringify(tomlContents)
writeFileSync(tomlPath, output)
}
__whitelistApi(cfg, tomlContents) {
if (!tomlContents.dependencies.tauri.features) {
tomlContents.dependencies.tauri.features = []
}
if (cfg.tauri.whitelist.all) {
if (!tomlContents.dependencies.tauri.features.includes('all-api')) {
tomlContents.dependencies.tauri.features.push('all-api')
}
} else {
const whitelist = Object.keys(cfg.tauri.whitelist).filter(w => cfg.tauri.whitelist[w] === true)
tomlContents.dependencies.tauri.features = whitelist.concat(tomlContents.dependencies.tauri.features.filter(f => f !== 'api' && cfg.tauri.whitelist[f] !== true))
}
}
}
module.exports = TauriRunner

View File

@@ -7,4 +7,6 @@
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
**/*.rs.bk
tauri.js

341
yarn.lock Normal file
View File

@@ -0,0 +1,341 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@iarna/toml@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.3.tgz#f060bf6eaafae4d56a7dac618980838b0696e2ab"
integrity sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==
"@nodelib/fs.scandir@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.1.tgz#7fa8fed654939e1a39753d286b48b4836d00e0eb"
integrity sha512-NT/skIZjgotDSiXs0WqYhgcuBKhUMgfekCmCGtkUAiLqZdOnrdjmZr9wRl3ll64J9NF79uZ4fk16Dx0yMc/Xbg==
dependencies:
"@nodelib/fs.stat" "2.0.1"
run-parallel "^1.1.9"
"@nodelib/fs.stat@2.0.1", "@nodelib/fs.stat@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.1.tgz#814f71b1167390cfcb6a6b3d9cdeb0951a192c14"
integrity sha512-+RqhBlLn6YRBGOIoVYthsG0J9dfpO79eJyN7BYBkZJtfqrBwf2KK+rD/M/yjZR6WBmIhAgOV7S60eCgaSWtbFw==
"@nodelib/fs.walk@^1.2.1":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.2.tgz#6a6450c5e17012abd81450eb74949a4d970d2807"
integrity sha512-J/DR3+W12uCzAJkw7niXDcqcKBg6+5G5Q/ZpThpGNzAUz70eOR6RV4XnnSN01qHZiVl0eavoxJsBypQoKsV2QQ==
dependencies:
"@nodelib/fs.scandir" "2.1.1"
fastq "^1.6.0"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
anymatch@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09"
integrity sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
binary-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
braces@^3.0.1, braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chokidar@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681"
integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==
dependencies:
anymatch "^3.0.1"
braces "^3.0.2"
glob-parent "^5.0.0"
is-binary-path "^2.1.0"
is-glob "^4.0.1"
normalize-path "^3.0.0"
readdirp "^3.1.1"
optionalDependencies:
fsevents "^2.0.6"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
fast-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602"
integrity sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==
dependencies:
"@nodelib/fs.stat" "^2.0.1"
"@nodelib/fs.walk" "^1.2.1"
glob-parent "^5.0.0"
is-glob "^4.0.1"
merge2 "^1.2.3"
micromatch "^4.0.2"
fastq@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2"
integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==
dependencies:
reusify "^1.0.0"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
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"
fsevents@^2.0.6:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a"
integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==
glob-parent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
dependencies:
is-glob "^4.0.1"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.2"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
is-binary-path@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.template@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
dependencies:
lodash._reinterpolate "^3.0.0"
lodash.templatesettings "^4.0.0"
lodash.templatesettings@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
dependencies:
lodash._reinterpolate "^3.0.0"
lodash@^4.17.5:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
merge2@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3"
integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==
micromatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
dependencies:
braces "^3.0.1"
picomatch "^2.0.5"
minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
ms@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
picomatch@^2.0.4, picomatch@^2.0.5:
version "2.0.7"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
readdirp@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.2.tgz#fa85d2d14d4289920e4671dead96431add2ee78a"
integrity sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==
dependencies:
picomatch "^2.0.4"
reusify@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
run-parallel@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
semver@^5.5.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
webpack-merge@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4"
integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==
dependencies:
lodash "^4.17.5"
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"