diff --git a/package.json b/package.json index 70ad5d0e..fb23c423 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "babel-preset-react": "^6.24.1", "babel-preset-stage-3": "^6.24.1", "babel-runtime": "^6.26.0", + "copy-webpack-plugin": "^5.1.1", "css-loader": "^2.1.1", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", @@ -66,6 +67,7 @@ "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1", + "worker-loader": "^3.0.8", "yaml-validator": "^2.0.0" }, "dependencies": { diff --git a/src/app/app.js b/src/app/app.js index 4906b19e..721ef693 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -3,7 +3,6 @@ import { render } from "react-dom"; import store from "./store/index"; import { Provider } from "react-redux"; import Layout from "./components/_layout"; -// import Index from "./components/index"; import Index from "./components/editor"; import "bootstrap"; @@ -16,24 +15,28 @@ export default class App extends Component { super(props); this.handleLoading = this.handleLoading.bind(this); this.state = { - loadingRemote: false + loadingRemote: false, }; } handleLoading(isLoading) { - this.setState({loadingRemote: isLoading}) + this.setState({ loadingRemote: isLoading }); } render() { return ( {this.state.loadingRemote && ( -
-
-

Validazione in corso

- +
+
+

Validazione in corso

+ +
-
)} diff --git a/src/app/components/editor.js b/src/app/components/editor.js index e348b140..40c839e5 100644 --- a/src/app/components/editor.js +++ b/src/app/components/editor.js @@ -1,6 +1,6 @@ import React, { Component, Fragment } from "react"; import { connect } from "react-redux"; -import { initialize, submit, SubmissionError } from "redux-form"; +import { initialize, submit, SubmissionError, stopSubmit } from "redux-form"; import { notify } from "../store/notifications"; import { setVersions } from "../store/cache"; import { APP_FORM } from "../contents/constants"; @@ -41,7 +41,8 @@ const mapDispatchToProps = dispatch => { initialize: (name, data) => dispatch(initialize(name, data)), submit: name => dispatch(submit(name)), notify: data => dispatch(notify(data)), - setVersions: data => dispatch(setVersions(data)) + setVersions: data => dispatch(setVersions(data)), + setValidationErrors: errors => dispatch(stopSubmit(APP_FORM ,errors)) }; }; @@ -80,7 +81,7 @@ class Index extends Component { } async componentDidMount() { - await this.initData(); + // await this.initData(); this.switchLang("it"); this.switchCountry("it"); @@ -145,50 +146,12 @@ class Index extends Component { if (country) this.switchCountry(country); } - // eslint-disable-next-line no-unused-vars - generate(formValues) { - let lastGen = moment(); - this.setState({ loading: true, lastGen }); - //has state - let { values, country, elements } = this.state; - //values[currentLanguage] = formValues; - let obj = ft.transform(values, country, elements); - - // let errors = await fv.validatePubliccodeYml(obj); - // if (errors) alert(errors); - - //SET TIMESTAMP - this.showResults(obj); - } - - - - validateExt(response) { - let r = response.json(); - if (response.ok) { - return r; - } else { - //status for validation error - if (response.status == 422) { - return r.then(() => { - console.log('validation not ok'); - throw new SubmissionError(r); - }); - } else { - //other response failure, try to use internal validator then. - console.error('some network failure occured'); - throw new Error('generic erorr') - } - } - } - /** * * @param {form data} formValues */ validateAndGenerate(formValues) { let lastGen = moment(); - // let errors = {}; this.setState({ loading: true, lastGen }); this.props.onLoadingRemote(true); @@ -196,8 +159,6 @@ class Index extends Component { let { values, country, elements, languages } = this.state; let currentLanguage = languages ? languages[0] : null; - // console.log(formValues, values); - values[currentLanguage] = formValues; let obj = ft.transform(values, country, elements); @@ -208,100 +169,75 @@ class Index extends Component { // Object.assign(obj, staticFieldsJson) // something weird occur. // needs to investigate further - obj['publiccodeYmlVersion'] = '0.2'; - - return postDataForValidation(obj) - .then(this.validateExt) - .then(v => { - // everything fine - // console.log(v); - - this.setState({ loading: false }); - this.props.onLoadingRemote(false); - // removing empty object - // which caused a object {} in yaml results - return this.showResults(this.removeEmpty(v)); - }) - .catch(e => { - if (e instanceof SubmissionError) { - return e.errors.then(r => { - let errorObj = {}; - r.map(x => { - //replacing all string with * language - let key = x.Key.replace(/\/\*\//gi, '_'); - - //replacing separator section from field - key = key.replace(/\//gi, '_'); //replace / with _ - - //BUG - //removing language - //this issue is well known: editor do not validate multi language - //pc since its fields are not named following a lang sintax - key = key.replace(/_it_/gi, '_'); //replace _it_ with _ - - //description appear when a language is not set - //avoided for the moment - if (key != 'description') - errorObj[key] = x.Reason; - }); - // console.log(errorObj); - - //errors are now taken from state, see line 507 for details - // this.props.form[APP_FORM].submitErrors = errorObj - - //errors are in state now - //but in sidebar are rendered from form.submitErrors - //state there is not updated - this.setState({ - errors: errorObj, - loading: false - }) - this.props.onLoadingRemote(false); + obj.publiccodeYmlVersion = '0.2'; + // hack to get all description subfield validated + if(!obj.description) { + obj.description = {}; + languages.map(x => obj.description[x] = {}); + } - throw new SubmissionError(errorObj); - }) - } else { - //generic error use internal validator - console.error('Generic error with remote validation, using local instead', e); - - //BUG - //not working at the moment - //need to figure out why _error subkeys - //cause a crash - //this will cause a wrong validation for subkeys - let errorObj = this.validate(formValues); - let err = {}; - Object.keys(errorObj).forEach(x => { - if (!errorObj[x]._error) - err[x] = errorObj[x]; - }); - console.log(err); + const validatorWorker = postDataForValidation(obj); + validatorWorker.onmessage = (e) => { + if (e && e.data && e.data.validator) { + const validator = JSON.parse(e.data.validator); + console.log(validator); + if (validator.status === "ok") { + this.setState({ loading: false }); + this.props.onLoadingRemote(false); + return this.showResults(obj); + } else { + let errors = Object.fromEntries( + validator.errors.map((x) => { + const key = x.key.replace(/\.it\./gi, "_").replace(/\./gi, "_"); + return [[key], x.description]; + }) + ); + console.log(errors); this.setState({ + errors, loading: false - }) + }); this.props.onLoadingRemote(false); - - if (Object.keys(err).length === 0 && err.constructor === Object) { - this.showResults(obj); - } else { - this.setState({ - errors: err - }) - throw new SubmissionError(err); - } + this.props.setValidationErrors(errors) } - }); + } else { + this.useLocalValidation(formValues, obj); + } + } } - removeEmpty(obj) { - // looking forward to replace with bind() - const that = this; - Object.keys(obj).forEach(function (key) { - (Object.keys(obj[key]).length === 0 && obj[key].constructor === Object) && delete obj[key] || - (obj[key] && typeof obj[key] === 'object') && that.removeEmpty(obj[key]) + // fallback validation + useLocalValidation(formValues, obj) { + //generic error use internal validator + console.error('Generic error with remote validation, using local instead'); + + //BUG + //not working at the moment + //need to figure out why _error subkeys + //cause a crash + //this will cause a wrong validation for subkeys + let errorObj = this.validate(formValues); + let err = {}; + Object.keys(errorObj).forEach(x => { + if (!errorObj[x]._error) + err[x] = errorObj[x]; }); - return obj; + console.log(err); + + this.setState({ + loading: false + }) + this.props.onLoadingRemote(false); + + if (Object.keys(err).length === 0 && err.constructor === Object) { + this.showResults(obj); + } else { + this.setState({ + errors: err + }) + throw new SubmissionError(err); + } } showResults(values) { @@ -334,7 +270,6 @@ class Index extends Component { yamlLoaded = false; } - this.props.notify({ type, title, msg, millis }); //this.scrollToError(errors) this.setState({ yaml, yamlLoaded }); @@ -359,7 +294,6 @@ class Index extends Component { errors = Object.assign(required, objs_n_arrays); console.log(contents, errors); - //UPDATE STATE values[currentLanguage] = contents; this.setState({ @@ -528,7 +462,7 @@ class Index extends Component { } else { console.warn("inviewport"); } - this.setState({ activeSection: activeSection }); + this.setState({ activeSection }); } render() { @@ -568,7 +502,7 @@ class Index extends Component { // onSubmit={this.generate.bind(this)} onSubmit={this.validateAndGenerate.bind(this)} data={blocks} - // validate={this.validate.bind(this)} + // asyncValidate={this.validateWasm.bind(this)} country={country} switchCountry={this.switchCountry.bind(this)} errors={errors} diff --git a/src/app/components/editorForm.js b/src/app/components/editorForm.js index d4780e5f..6fc157f3 100644 --- a/src/app/components/editorForm.js +++ b/src/app/components/editorForm.js @@ -10,19 +10,6 @@ import img_accordion_open from "../../asset/img/accordion-open.svg"; import img_accordion_closed from "../../asset/img/accordion-closed.svg"; import { getFieldByTitle } from "../contents/data"; -// eslint-disable-next-line no-unused-vars -const renderBlocksSimple = blocks => { - return blocks.map((block, i) => ( -
-
-
{block.index}
-
{block.title}
-
-
{renderBlockItems(block.items, i)}
-
- )); -}; - const renderBlockItems = (items, id) => { return items.map((item, i) => { // getField(item); @@ -86,12 +73,8 @@ const renderBlocks = ( }; const EditForm = props => { - /* eslint-disable no-unused-vars */ const { handleSubmit, - pristine, - reset, - submitting, data, errors, activeSection, @@ -100,7 +83,6 @@ const EditForm = props => { allFields, submitFailed } = props; - /* eslint-enable no-unused-vars */ let countryProps = { country, switchCountry }; diff --git a/src/app/components/languageSwitcher.js b/src/app/components/languageSwitcher.js index 8bf851a4..6d1a9ebf 100644 --- a/src/app/components/languageSwitcher.js +++ b/src/app/components/languageSwitcher.js @@ -1,6 +1,5 @@ import React, { Component } from "react"; import available_languages from "../contents/langs"; -//const available_languages = ["ita", "eng", "fra", "zho"]; import CloseButton from "./CloseButton"; export default class languageSwitcher extends Component { diff --git a/src/app/utils/calls.js b/src/app/utils/calls.js index 8483b097..7e8c04f5 100644 --- a/src/app/utils/calls.js +++ b/src/app/utils/calls.js @@ -1,65 +1,46 @@ -import { validatorUrl, validatorRemoteUrl } from "../contents/constants"; +import ValidatorWorker from "worker-loader!../validator/validator_worker.js"; +import { validatorRemoteUrl } from "../contents/constants"; -export const getReleases = versionsUrl => { +export const getReleases = (versionsUrl) => { return fetch(versionsUrl) - .then(res => res.json()) - .then(data => data.filter(d => d.type == "dir")) - .then(data => data.map(d => d.name)); + .then((res) => res.json()) + .then((data) => data.filter((d) => d.type == "dir")) + .then((data) => data.map((d) => d.name)); }; -export const passRemoteURLToValidator = yamlURL => { +export const passRemoteURLToValidator = (yamlURL) => { const encodedYamlURL = encodeURIComponent(yamlURL); const paramsString = `url=${encodedYamlURL}`; const myHeaders = new Headers({ - 'Accept': 'application/x-yaml', - 'Content-Type': 'application/x-yaml' + Accept: "application/x-yaml", + "Content-Type": "application/x-yaml", }); const url = validatorRemoteUrl; const myInit = { - method: 'POST', + method: "POST", headers: myHeaders, - mode: 'cors', - cache: 'default', + mode: "cors", + cache: "default", }; - if (url == '') - return Promise.reject(new Error('No validator URL specified')); + if (url == "") return Promise.reject(new Error("No validator URL specified")); - return fetch(`${url}?${paramsString}`, myInit) - .then(res => { - // 422 should pass as it indicates a failed validation - if (! res.ok && res.status != 422) { - throw new Error(`fetch(${url}) returned ${res.status}`); - } - return res.text() - }); -}; - -export const postDataForValidation = data => { - var myHeaders = new Headers({ - 'Accept': 'application/json', - 'Content-Type': 'application/json' + return fetch(`${url}?${paramsString}`, myInit).then((res) => { + // 422 should pass as it indicates a failed validation + if (!res.ok && res.status != 422) { + throw new Error(`fetch(${url}) returned ${res.status}`); + } + return res.text(); }); - const url = validatorUrl; - - var myInit = { - method: 'POST', - headers: myHeaders, - mode: 'cors', - cache: 'default', - body: JSON.stringify(data) - }; +}; - if (url == '') - return Promise.reject(new Error('No validator URL specified')); +export const postDataForValidation = (data) => { + const validator = new ValidatorWorker(); + validator.postMessage(data); + console.log(data); - return fetch(url, myInit) - .then(res => { - if (! res.ok && res.status != 422) { - throw new Error(`fetch(${url}) returned ${res.status}`); - } - return res - }); + return validator; }; + diff --git a/src/app/validator/validator_wasm.js b/src/app/validator/validator_wasm.js new file mode 100644 index 00000000..d3680626 --- /dev/null +++ b/src/app/validator/validator_wasm.js @@ -0,0 +1,12 @@ +export const validatorWasm = (data, cb) => { + const path = "validator-wasm/wasm_glue.wasm"; + // eslint-disable-next-line no-undef + const go = new Go(); + // eslint-disable-next-line no-undef + WebAssembly.instantiateStreaming(fetch(path), go.importObject).then((obj) => { + go.run(obj.instance); + console.log(data); + // eslint-disable-next-line no-undef + return cb(IsPublicCodeYmlValid(JSON.stringify(data))); + }); +}; diff --git a/src/app/validator/validator_worker.js b/src/app/validator/validator_worker.js new file mode 100644 index 00000000..3c0ce931 --- /dev/null +++ b/src/app/validator/validator_worker.js @@ -0,0 +1,13 @@ +import { validatorWasm } from "./validator_wasm"; +// eslint-disable-next-line no-undef +importScripts("../../../validator-wasm/wasm_exec.js"); + +onmessage = async (e) => { + try { + validatorWasm(e.data, (validator) => { + postMessage({validator}); + }); + } catch (e) { + postMessage({ error: e.message }); + } +}; diff --git a/validator-wasm/wasm_exec.js b/validator-wasm/wasm_exec.js new file mode 100644 index 00000000..8501ae7c --- /dev/null +++ b/validator-wasm/wasm_exec.js @@ -0,0 +1,588 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + const fs = require("fs"); + if (Object.keys(fs) !== 0) { + global.fs = fs; + } + } + + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!global.process) { + global.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!global.crypto) { + const nodeCrypto = require("crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder) { + global.TextEncoder = require("util").TextEncoder; + } + + if (!global.TextDecoder) { + global.TextDecoder = require("util").TextDecoder; + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + global.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime1() (sec int64, nsec int32) + "runtime.walltime1": (sp) => { + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [global, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + go.argv = process.argv.slice(2); + go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); + go.exit = process.exit; + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})(); diff --git a/validator-wasm/wasm_glue.go b/validator-wasm/wasm_glue.go new file mode 100644 index 00000000..ee99bf89 --- /dev/null +++ b/validator-wasm/wasm_glue.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "syscall/js" + + "github.com/italia/publiccode-parser-go" +) + +// Message type for data serialized +type Message struct { + Status string `json:"status"` + Errors interface{} `json:"errors,omitempty"` +} + +// IsPublicCodeYmlValid return a boolean value +// whether the publiccode provided is valid +// or not +func IsPublicCodeYmlValid(this js.Value, args []js.Value) interface{} { + parser := publiccode.NewParser() + parser.DisableNetwork = true + + err := parser.Parse([]byte(args[0].String())) + if err != nil { + var message = Message{Status: "ko", Errors: err} + out, jsonerr := json.Marshal(message) + if jsonerr != nil { + return jsonerr + } + return string(out) + } + var message = Message{Status: "ok", Errors: nil} + out, _ := json.Marshal(message) + return string(out) +} + +func main() { + done := make(chan struct{}, 0) + js.Global().Set("IsPublicCodeYmlValid", js.FuncOf(IsPublicCodeYmlValid)) + <-done +} diff --git a/validator-wasm/wasm_glue.wasm b/validator-wasm/wasm_glue.wasm new file mode 100755 index 00000000..698500a9 Binary files /dev/null and b/validator-wasm/wasm_glue.wasm differ diff --git a/webpack.config.js b/webpack.config.js index e34407c5..84edca82 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,20 +2,20 @@ const path = require("path"); const fs = require("fs"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const devMode = process.env.NODE_ENV !== 'production' -const autoprefixer = require("autoprefixer"); +const devMode = process.env.NODE_ENV !== "production"; const webpack = require("webpack"); +const copyWebpackPlugin = require("copy-webpack-plugin"); + const paths = { DIST: path.resolve(__dirname, "dist"), SRC: path.resolve(__dirname, "src"), - JS: path.resolve(__dirname, "src/app") + JS: path.resolve(__dirname, "src/app"), }; -module.exports = env => { +module.exports = () => { let stage = "development"; let env_file = "./.env"; - if (fs.existsSync(env_file)) { require("dotenv").config({ path: env_file }); } @@ -24,7 +24,7 @@ module.exports = env => { entry: path.join(paths.JS, "app.js"), output: { path: paths.DIST, - filename: "app.bundle.js" + filename: "app.bundle.js", }, plugins: [ new webpack.DefinePlugin({ @@ -32,8 +32,10 @@ module.exports = env => { REPOSITORY: JSON.stringify(process.env.REPOSITORY), ELASTIC_URL: JSON.stringify(process.env.ELASTIC_URL), VALIDATOR_URL: JSON.stringify(process.env.VALIDATOR_URL), - VALIDATOR_REMOTE_URL: JSON.stringify(process.env.VALIDATOR_REMOTE_URL) - } + VALIDATOR_REMOTE_URL: JSON.stringify( + process.env.VALIDATOR_REMOTE_URL + ), + }, }), new HtmlWebpackPlugin({ template: path.join(paths.SRC, "index.html"), @@ -42,55 +44,56 @@ module.exports = env => { minifyCSS: true, minifyJS: true, removeComments: true, - useShortDoctype: true + useShortDoctype: true, }, - favicon: './src/asset/img/favicon-32x32.png' + favicon: "./src/asset/img/favicon-32x32.png", }), new MiniCssExtractPlugin({ - filename: devMode ? '[name].css' : '[name].[hash].css', - chunkFilename: devMode ? '[id].css': '[id].[hash].css', - }) + filename: devMode ? "[name].css" : "[name].[hash].css", + chunkFilename: devMode ? "[id].css" : "[id].[hash].css", + }), + new copyWebpackPlugin([{ from: "validator-wasm", to: "validator-wasm" }]), ], module: { rules: [ { enforce: "pre", test: /\.s(c)ss/, - loader: "import-glob-loader" + loader: "import-glob-loader", }, { test: /\.(js|jsx)$/, exclude: /node_modules/, - use: ["babel-loader"] // + use: ["babel-loader"], // }, { - test: /\.(sa|sc|c)ss$/, - use: [ - devMode ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', - 'postcss-loader', - 'sass-loader', - ], + test: /\.(sa|sc|c)ss$/, + use: [ + devMode ? "style-loader" : MiniCssExtractPlugin.loader, + "css-loader", + "postcss-loader", + "sass-loader", + ], }, { test: /\.(png|jpg|gif)$/, - use: ["url-loader"] + use: ["url-loader"], }, { test: /\.(woff|woff2|eot|ttf|otf|svg)$/, - use: ["file-loader"] - } - ] + use: ["file-loader"], + }, + ], }, resolve: { modules: [path.resolve(__dirname, "src"), "node_modules"], extensions: [".js", ".jsx", ".json", ".yml"], alias: { - 'cldr$': 'cldrjs', - 'cldr': 'cldrjs/dist/cldr' - } - } + cldr$: "cldrjs", + cldr: "cldrjs/dist/cldr", + }, + }, }; }; diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 1f07c826..60a481ff 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -2,16 +2,16 @@ const path = require("path"); const fs = require("fs"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const devMode = process.env.NODE_ENV !== 'production' -const autoprefixer = require("autoprefixer"); +const devMode = process.env.NODE_ENV !== "production"; const webpack = require("webpack"); +const copyWebpackPlugin = require("copy-webpack-plugin"); const paths = { DIST: path.resolve(__dirname, "dist"), SRC: path.resolve(__dirname, "src"), - JS: path.resolve(__dirname, "src/app") + JS: path.resolve(__dirname, "src/app"), }; -module.exports = env => { +module.exports = () => { let stage = "production"; let env_file = "./.env"; @@ -23,7 +23,7 @@ module.exports = env => { entry: path.join(paths.JS, "app.js"), output: { path: paths.DIST, - filename: "app.bundle.js" + filename: "app.bundle.js", }, plugins: [ new webpack.DefinePlugin({ @@ -31,8 +31,10 @@ module.exports = env => { REPOSITORY: JSON.stringify(process.env.REPOSITORY), ELASTIC_URL: JSON.stringify(process.env.ELASTIC_URL), VALIDATOR_URL: JSON.stringify(process.env.VALIDATOR_URL), - VALIDATOR_REMOTE_URL: JSON.stringify(process.env.VALIDATOR_REMOTE_URL) - } + VALIDATOR_REMOTE_URL: JSON.stringify( + process.env.VALIDATOR_REMOTE_URL + ), + }, }), new HtmlWebpackPlugin({ template: path.join(paths.SRC, "index.html"), @@ -41,55 +43,56 @@ module.exports = env => { minifyCSS: true, minifyJS: true, removeComments: true, - useShortDoctype: true + useShortDoctype: true, }, - favicon: './src/asset/img/favicon-32x32.png' + favicon: "./src/asset/img/favicon-32x32.png", }), new MiniCssExtractPlugin({ - filename: devMode ? '[name].css' : '[name].[hash].css', - chunkFilename: devMode ? '[id].css': '[id].[hash].css', - }) + filename: devMode ? "[name].css" : "[name].[hash].css", + chunkFilename: devMode ? "[id].css" : "[id].[hash].css", + }), + new copyWebpackPlugin([{ from: "validator-wasm", to: "validator-wasm" }]), ], module: { rules: [ { enforce: "pre", test: /\.s(c)ss/, - loader: "import-glob-loader" + loader: "import-glob-loader", }, { test: /\.(js|jsx)$/, exclude: /node_modules/, - use: ["babel-loader"] // + use: ["babel-loader"], }, { - test: /\.(sa|sc|c)ss$/, - use: [ - devMode ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', - 'postcss-loader', - 'sass-loader', - ], + test: /\.(sa|sc|c)ss$/, + use: [ + devMode ? "style-loader" : MiniCssExtractPlugin.loader, + "css-loader", + "postcss-loader", + "sass-loader", + ], }, { test: /\.(png|jpg|gif)$/, - use: ["url-loader"] + use: ["url-loader"], }, { test: /\.(woff|woff2|eot|ttf|otf|svg)$/, - use: ["file-loader"] - } - ] + use: ["file-loader"], + }, + ], }, resolve: { modules: [path.resolve(__dirname, "src"), "node_modules"], extensions: [".js", ".jsx", ".json", ".yml"], alias: { - 'cldr$': 'cldrjs', - 'cldr': 'cldrjs/dist/cldr' - } - } + cldr$: "cldrjs", + cldr: "cldrjs/dist/cldr", + }, + }, }; }; diff --git a/yarn.lock b/yarn.lock index ab848e1c..85eed9f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -118,6 +118,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.6": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -371,6 +376,11 @@ ajv-keywords@^3.1.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + ajv@^5.0.0, ajv@^5.1.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -391,6 +401,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.5.5: version "6.7.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96" @@ -1740,6 +1760,27 @@ cacache@^11.3.2: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^12.0.3: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2246,6 +2287,24 @@ copy-to-clipboard@^3.0.8: dependencies: toggle-selection "^1.0.6" +copy-webpack-plugin@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2" + integrity sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ== + dependencies: + cacache "^12.0.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.1" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + webpack-log "^2.0.0" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -2639,6 +2698,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + discontinuous-range@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" @@ -2869,6 +2935,11 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3394,6 +3465,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3552,7 +3628,7 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" -find-cache-dir@^2.0.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -3963,6 +4039,18 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -4359,6 +4447,11 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -4460,6 +4553,11 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4728,7 +4826,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, 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== @@ -5446,6 +5544,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -5613,6 +5718,15 @@ loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2 emojis-list "^2.0.0" json5 "^1.0.1" +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -6681,7 +6795,7 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" -p-limit@^2.2.0: +p-limit@^2.2.0, p-limit@^2.2.1: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -6912,6 +7026,13 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -7357,7 +7478,7 @@ randomatic@^3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -8132,6 +8253,15 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + scroll-into-view-if-needed@^2.2.16: version "2.2.20" resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.20.tgz#3a46847a72233a3af9770e55df450f2a7f2e2a0e" @@ -8210,6 +8340,13 @@ serialize-javascript@^1.7.0: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -9624,6 +9761,14 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +worker-loader@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37" + integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"