diff --git a/app/javascript/editor/editor.js b/app/javascript/editor/editor.js.old similarity index 96% rename from app/javascript/editor/editor.js rename to app/javascript/editor/editor.js.old index d44cdb50..c96fa7f7 100644 --- a/app/javascript/editor/editor.js +++ b/app/javascript/editor/editor.js.old @@ -8,6 +8,8 @@ import { tagNameSetFn } from 'editor/types' +import { setMultimedia } from 'editor/types/multimedia' + const origin = location.origin /* @@ -242,17 +244,18 @@ const hasContent = (element) => { else if (child.hasChildNodes() && hasContent(child)) return true } // TODO: verificar que los elementos tiene contenido - if (element.tagName === "IMG" - || element.tagName === "AUDIO" - || element.tagName === "VIDEO" - || element.tagName === "IFRAME" - || element.tagName === "BR") return true + if (element.tagName === "BR") return true + if (element.parentElement.tagName === "FIGURE" + && (element.dataset.editorMultimediaElement + || element.tagName === "FIGCAPTION") + ) return true return false } /* Limpia el elemento de contenido del editor * Por ahora: * * Cambia el tag de los bloques no reconocidos (ver `elementIsBlock`) + * * Borra
sin multimedia adentro * * Hace lo que hace cleanNode */ const cleanContent = (contentEl) => { @@ -264,11 +267,18 @@ const cleanContent = (contentEl) => { if (child.tagName) { if (elementIsParentBlock(child)) { cleanContent(child) + } else if (child.tagName === "FIGURE") { + if ( + !child.querySelector('*[data-editor-multimedia-element]') + && !child.dataset.editorLoading + ) { + moveChildren(child, child.parentNode, child) + child.parentNode.removeChild(child) + } } else if (!elementIsBlock(child)) { const el = document.createElement("p") moveChildren(child, el, null) - contentEl.insertBefore(el, child) - child.parentNode.removeChild(child) + child.parentNode.replaceChild(el, child) } } else if (child.nodeType === Node.TEXT_NODE) { const el = document.createElement("p") diff --git a/app/javascript/editor/editor.ts b/app/javascript/editor/editor.ts new file mode 100644 index 00000000..92448bb2 --- /dev/null +++ b/app/javascript/editor/editor.ts @@ -0,0 +1,174 @@ +import { storeContent, restoreContent } from 'editor/storage' +import { isDirectChild, moveChildren, safeGetSelection, safeGetRangeAt } from 'editor/utils' +import { types, EditorNode, getValidChildren, getType } from 'editor/types' +import { setupButtons as setupMarksButtons } from 'editor/types/marks' +import { blocks, EditorBlock, setupButtons as setupBlocksButtons } from 'editor/types/blocks' +import { parentBlocks, parentBlockNames } from 'editor/types/parentBlocks' + +export interface Editor { + editorEl: HTMLElement, + toolbarEl: HTMLElement, + contentEl: HTMLElement, + wordAlertEl: HTMLElement, + htmlEl: HTMLTextAreaElement, +} + +// Esta funcion corrije errores que pueden haber como: +// * que un nodo que no tiene 'text' permitido no tenga children (se les +// inserta un allowedChildren[0]) +// * TODO: que haya una imágen sin
o que no esté como bloque (se ponen +// después del bloque en el que están como bloque de por si) +// * TODO: convierte y en y +// Lo hace para que siga la estructura del documento y que no se borren por +// cleanContent luego. +function fixContent (editor: Editor, node: Element = editor.contentEl): void { + const _type = getType(node) + if (!_type) return + + const { typeName, type } = _type + + const sel = safeGetSelection(editor) + const range = sel && safeGetRangeAt(sel) + + if (getValidChildren(node, type).length == 0) { + if (typeof type.handleEmpty !== 'string') { + const el = type.handleEmpty.create() + // mover cosas que pueden haber + // por ejemplo: cuando convertís a un
    , queda texto fuera del li que + // creamos acá + moveChildren(node, el, null) + node.appendChild(el) + if (range?.intersectsNode(node)) + sel?.collapse(el) + } + } + + for (const child of node.childNodes) { + if (!(child instanceof Element)) continue + fixContent(editor, child) + } +} + +// Esta funcion hace que los elementos del editor sigan la estructura. +// TODO: nos falta borrar atributos (style, y básicamente cualquier otra cosa) +// Edge cases: +// * no borramos los
    por que se requieren para que los navegadores +// funcionen bien al escribir. no se deberían mostrar de todas maneras +function cleanContent (editor: Editor, node: Element = editor.contentEl): void { + if (node.tagName === 'BR') return + + const _type = getType(node) + if (!_type) { + node.parentElement?.removeChild(node) + return + } + + const { type } = _type + + for (const child of node.childNodes) { + if (child.nodeType === Node.TEXT_NODE + && type.allowedChildren.indexOf('text') === -1 + ) { + node.removeChild(child) + continue + } + + if (!(child instanceof Element)) continue + + const childType = getType(child) + if (!childType || type.allowedChildren.indexOf(childType.typeName) === -1) { + // XXX: esto extrae las cosas de adentro para que no sea destructivo + moveChildren(child, node, child) + node.removeChild(child) + return + } + + cleanContent(editor, child) + } + + // solo contar children válido para ese nodo + const validChildrenLength = getValidChildren(node, type).length + + const sel = safeGetSelection(editor) + const range = sel && safeGetRangeAt(sel) + if (type.handleEmpty === 'remove' + && validChildrenLength == 0 + //&& (!range || !range.intersectsNode(node)) + ) { + node.parentNode?.removeChild(node) + return + } +} + +function routine (editor: Editor): void { + try { + fixContent(editor) + cleanContent(editor) + storeContent(editor) + + editor.htmlEl.value = editor.contentEl.innerHTML + } catch (error) { + console.error('Hubo un problema corriendo la rutina', editor, error) + } +} + +function setupEditor (editorEl: HTMLElement): void { + // XXX: ¡Esto afecta a todo el documento! ¿Quizás usar un iframe para el editor? + document.execCommand('defaultParagraphSeparator', false, 'p') + + const toolbarEl = editorEl.querySelector('.editor-toolbar') + if (!toolbarEl) throw new Error('No pude encontrar .editor-toolbar') + const contentEl = editorEl.querySelector('.editor-content') + if (!contentEl) throw new Error('No pude encontrar .editor-content') + const wordAlertEl = editorEl.querySelector('.editor-aviso-word') + if (!wordAlertEl) throw new Error('No pude encontrar .editor-aviso-word') + const htmlEl = editorEl.querySelector('textarea') + if (!htmlEl) throw new Error('No pude encontrar el textarea para el HTML') + + const editor: Editor = { + editorEl, + toolbarEl, + contentEl, + wordAlertEl, + htmlEl, + } + console.debug('iniciando editor', editor) + + // Recuperar el contenido si hay algo guardado, si tuviéramos un campo + // de última edición podríamos saber si el artículo fue editado + // después o la versión local es la última. + // + // TODO: Preguntar si se lo quiere recuperar. + restoreContent(editor) + + // Word alert + editor.contentEl.addEventListener('paste', () => { + editor.wordAlertEl.style.display = 'block' + }) + + // Setup routine listeners + const observer = new MutationObserver(() => routine(editor)) + observer.observe(contentEl, { + childList: true, + attributes: true, + subtree: true, + characterData: true, + }) + + document.addEventListener("selectionchange", () => routine(editor)) + + // Setup botones + setupMarksButtons(editor) + setupBlocksButtons(editor) + + // Finally... + routine(editor) +} + +document.addEventListener("turbolinks:load", () => { + for (const editorEl of document.querySelectorAll('.editor')) { + if (!editorEl.querySelector('.editor-toolbar')) continue + + setupEditor(editorEl) + } +}) diff --git a/app/javascript/editor/storage.ts b/app/javascript/editor/storage.ts new file mode 100644 index 00000000..df27d59c --- /dev/null +++ b/app/javascript/editor/storage.ts @@ -0,0 +1,32 @@ +import { Editor } from 'editor/editor' + +/* + * Guarda una copia local de los cambios para poder recuperarlos + * después. + * + * Usamos la URL completa sin anchors. + */ +function getStorageKey (editor: Editor): string { + const keyEl = editor.editorEl.querySelector('[data-target="storage-key"]') + if (!keyEl) throw new Error('No encuentro la llave para guardar los artículos') + return keyEl.value +} + +export function forgetContent (storedKey: string): void { + window.localStorage.removeItem(storedKey) +} + +export function storeContent (editor: Editor): void { + if (editor.contentEl.innerText.trim().length === 0) return + + window.localStorage.setItem(getStorageKey(editor), editor.contentEl.innerHTML) +} + +export function restoreContent (editor: Editor): void { + const content = window.localStorage.getItem(getStorageKey(editor)) + + if (!content) return + if (content.trim().length === 0) return + + editor.contentEl.innerHTML = content +} diff --git a/app/javascript/editor/types.ts b/app/javascript/editor/types.ts new file mode 100644 index 00000000..86b0093e --- /dev/null +++ b/app/javascript/editor/types.ts @@ -0,0 +1,57 @@ +import { marks } from 'editor/types/marks' +import { blocks, li, blockNames, EditorBlock } from 'editor/types/blocks' +import { parentBlocks } from 'editor/types/parentBlocks' + +export interface EditorNode { + selector: string, + // la string es el nombre en la gran lista de types O 'text' + // XXX: esto es un hack para no poner EditorNode dentro de EditorNode, + // quizás podemos hacer que esto sea una función que retorna bool + allowedChildren: string[], + + // * si es 'do-nothing', no hace nada si está vacío (esto es para cuando + // permitís 'text' entonces se puede tipear adentro, ej: párrafo vacío) + // * si es 'remove', sacamos el coso si está vacío. + // ej: strong: { handleNothing: 'remove' } + // * si es un block, insertamos el bloque y movemos la selección ahí + // ej: ul: { handleNothing: li } + handleEmpty: 'do-nothing' | 'remove' | EditorBlock, + + create: () => HTMLElement, +} + +export const types: { [propName: string]: EditorNode } = { + ...marks, + ...blocks, + li, + ...parentBlocks, + contentEl: { + selector: '.editor-content', + allowedChildren: [...blockNames, ...Object.keys(parentBlocks)], + handleEmpty: blocks.paragraph, + create: () => { throw new Error('se intentó crear contentEl') } + }, +} + +export function getType (node: Element): { typeName: string, type: EditorNode } | null { + for (let [typeName, type] of Object.entries(types)) { + if (node.matches(type.selector)) return { typeName, type } + } + return null +} + +export function getValidChildren (node: Element, type: EditorNode): Node[] { + return [...node.childNodes].filter(n => { + // si permite texto y esto es un texto, es válido + if (n.nodeType === Node.TEXT_NODE) + return type.allowedChildren.includes('text') + + // si no es un elemento, no es válido + if (!(n instanceof Element)) + return false + + const t = getType(n) + if (!t) return false + return type.allowedChildren.includes(t.typeName) + }) +} diff --git a/app/javascript/editor/types/blocks.ts b/app/javascript/editor/types/blocks.ts new file mode 100644 index 00000000..e81ab013 --- /dev/null +++ b/app/javascript/editor/types/blocks.ts @@ -0,0 +1,101 @@ +import { Editor } from 'editor/editor' +import { safeGetSelection, safeGetRangeAt, moveChildren } from 'editor/utils' +import { EditorNode, getType } from 'editor/types' +import { markNames } from 'editor/types/marks' +import { parentBlockNames } from 'editor/types/parentBlocks' + +// TODO: implementar multimedia como otro tipo! ya que no puede tener children +// debe ser tratado distinto que los bloques (quizás EditorBlockWithText y +// EditorBlock o algo así) + +export interface EditorBlock extends EditorNode { +} + +export const blockNames = ['paragraph', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'unordered_list', 'ordered_list'] + +function makeBlock (tag: string): EditorBlock { + return { + selector: tag, + allowedChildren: [...markNames, 'text'], + handleEmpty: 'do-nothing', + create: () => { + const el = document.createElement(tag) + return el + //moveChildren(el, newEl, null) + //el.parentNode!.replaceChild(newEl, el) + //// TODO: mantener la selección + //const sel = window.getSelection() + //sel && sel.collapse(newEl, 0) + }, + } +} + +export const li: EditorBlock = makeBlock('li') + +// XXX: si agregás algo acá, agregalo a blockNames +// (y probablemente le quieras hacer un botón en app/views/posts/attributes/_content.haml) +export const blocks: { [propName: string]: EditorBlock } = { + paragraph: makeBlock('p'), + h1: makeBlock('h1'), + h2: makeBlock('h2'), + h3: makeBlock('h3'), + h4: makeBlock('h4'), + h5: makeBlock('h5'), + h6: makeBlock('h6'), + unordered_list: { + ...makeBlock('ul'), + allowedChildren: ['li'], + handleEmpty: li, + }, + ordered_list: { + ...makeBlock('ol'), + allowedChildren: ['li'], + handleEmpty: li, + }, +} + +export function setupButtons (editor: Editor): void { + for (const [ name, type ] of Object.entries(blocks)) { + const buttonEl = editor.toolbarEl.querySelector(`[data-editor-button="block-${name}"]`) + if (!buttonEl) continue + buttonEl.addEventListener("click", event => { + event.preventDefault() + + const sel = safeGetSelection(editor) + if (!sel) return + const range = safeGetRangeAt(sel) + if (!range) return + + let blockEl = sel.anchorNode + while (true) { + if (!blockEl) throw new Error('WTF') + if (!blockEl.parentElement) throw new Error('No pude encontrar contentEl!') + + let type = getType(blockEl.parentElement) + if (!type) throw new Error('La selección está en algo que no es un type!') + + if (type.typeName === 'contentEl' + || parentBlockNames.includes(type.typeName) + ) break + + blockEl = blockEl.parentElement + } + if (!(blockEl instanceof Element)) + throw new Error('La selección no está en un elemento!') + + const parentEl = blockEl.parentElement + if (!parentEl) throw new Error('Inesperado') + + let replacementType = blockEl.matches(type.selector) + ? blocks.paragraph + : type + + const el = replacementType.create() + moveChildren(blockEl, el, null) + parentEl.replaceChild(el, blockEl) + sel.collapse(el) + + return false + }) + } +} diff --git a/app/javascript/editor/types/marks.ts b/app/javascript/editor/types/marks.ts new file mode 100644 index 00000000..560f3f6d --- /dev/null +++ b/app/javascript/editor/types/marks.ts @@ -0,0 +1,87 @@ +import { Editor } from 'editor/editor' +import { EditorNode } from 'editor/types' +import { safeGetSelection, safeGetRangeAt, moveChildren, splitNode } from 'editor/utils' + +export const markNames = ['bold', 'italic', 'deleted', 'underline', 'sub', 'super', 'mark', 'a'] + +function makeMark (name: string, tag: string): EditorNode { + return { + selector: tag, + allowedChildren: [...markNames.filter(n => n !== name), 'text'], + handleEmpty: 'remove', + create: () => document.createElement(tag), + } +} + +// XXX: si agregás algo acá, agregalo a markNames +export const marks: { [propName: string]: EditorNode } = { + bold: makeMark('bold', 'strong'), + italic: makeMark('italic', 'em'), + deleted: makeMark('deleted', 'del'), + underline: makeMark('underline', 'u'), + sub: makeMark('sub', 'sub'), + super: makeMark('super', 'sup'), + mark: makeMark('mark', 'mark'), + link: makeMark('link', 'a'), +} + +function recursiveFilterSelection ( + node: Element, + selection: Selection, + selector: string, +): Element[] { + let output: Element[] = [] + for (const child of [...node.children]) { + if (child.matches(selector) + && selection.containsNode(child) + ) output.push(child) + output = [...output, ...recursiveFilterSelection(child, selection, selector)] + } + return output +} + +export function setupButtons (editor: Editor): void { + for (const [ name, type ] of Object.entries(marks)) { + const buttonEl = editor.toolbarEl.querySelector(`[data-editor-button="mark-${name}"]`) + if (!buttonEl) continue + buttonEl.addEventListener("click", event => { + event.preventDefault() + + const sel = safeGetSelection(editor) + if (!sel) return + const range = safeGetRangeAt(sel) + if (!range) return + + if (!(range.commonAncestorContainer instanceof Element)) + throw new Error(':/') + const existingMarks = recursiveFilterSelection( + range.commonAncestorContainer, + sel, + type.selector, + ) + console.debug('marks encontradas:', existingMarks) + + if (existingMarks.length > 0) { + const mark = existingMarks[0] + if (!mark.parentElement) + throw new Error(':/') + moveChildren(mark, mark.parentElement, mark) + mark.parentElement.removeChild(mark) + } else { + const tagEl = type.create() + + try { + range.surroundContents(tagEl) + } catch (error) { + // TODO: mostrar error + return console.error("No puedo marcar cosas a través de distintos bloques!") + } + + range.insertNode(tagEl) + range.selectNode(tagEl) + } + + return false + }) + } +} diff --git a/app/javascript/editor/types/parentBlocks.ts b/app/javascript/editor/types/parentBlocks.ts new file mode 100644 index 00000000..71b611d1 --- /dev/null +++ b/app/javascript/editor/types/parentBlocks.ts @@ -0,0 +1,33 @@ +import { blockNames, EditorBlock } from 'editor/types/blocks' + +function makeParentBlock (tag: string, create: EditorBlock["create"]): EditorBlock { + return { + selector: tag, + allowedChildren: blockNames, + handleEmpty: 'remove', + create: create, + } +} + +// TODO: añadir blockquote +// XXX: si agregás algo acá, probablemente le quieras hacer un botón +// en app/views/posts/attributes/_content.haml +export const parentBlocks: { [propName: string]: EditorBlock } = { + left: makeParentBlock('div[data-align=left]', () => { + const el = document.createElement('div') + el.dataset.align = 'left' + return el + }), + center: makeParentBlock('div[data-align=center]', () => { + const el = document.createElement('div') + el.dataset.align = 'center' + return el + }), + right: makeParentBlock('div[data-align=right]', () => { + const el = document.createElement('div') + el.dataset.align = 'right' + return el + }), +} + +export const parentBlockNames = Object.keys(parentBlocks) diff --git a/app/javascript/editor/utils.ts b/app/javascript/editor/utils.ts new file mode 100644 index 00000000..e76587b7 --- /dev/null +++ b/app/javascript/editor/utils.ts @@ -0,0 +1,62 @@ +import { Editor } from 'editor/editor' + +export function moveChildren (from: Element, to: Element, toRef: Node | null) { + while (from.firstChild) to.insertBefore(from.firstChild, toRef) +} + +export function isDirectChild (node: Node, supposedChild: Node): boolean { + for (const child of node.childNodes) { + if (child == supposedChild) return true + } + return false +} + +export function safeGetSelection (editor: Editor): Selection | null { + const sel = window.getSelection() + if (!sel) return null + // XXX: no damos la selección si esta fuera o _es_ el contentEl, ¿quizás + // deberíamos mostrar un error? + if ( + !editor.contentEl.contains(sel.anchorNode) + || !editor.contentEl.contains(sel.focusNode) + || sel.anchorNode == editor.contentEl + || sel.focusNode == editor.contentEl + ) return null + return sel +} + +export function safeGetRangeAt (selection: Selection, num = 0): Range | null { + try { + return selection.getRangeAt(num) + } catch (error) { + return null + } +} + +interface SplitNode { + range: Range, + node: Node, +} + +export function splitNode (node: Element, range: Range): [SplitNode, SplitNode] { + const [left, right] = [ + { range: document.createRange(), node: node.cloneNode(false) }, + { range: document.createRange(), node: node.cloneNode(false) }, + ] + + if (node.firstChild) left.range.setStartBefore(node.firstChild) + left.range.setEnd(range.startContainer, range.startOffset) + left.range.surroundContents(left.node) + + right.range.setStart(range.endContainer, range.endOffset) + if (node.lastChild) right.range.setEndAfter(node.lastChild) + right.range.surroundContents(right.node) + + if (!node.parentElement) + throw new Error('No pude separar los nodos por que no tiene parentNode') + + moveChildren(node, node.parentElement, node) + node.parentElement.removeChild(node) + + return [left, right] +} diff --git a/app/views/posts/attributes/_content.haml b/app/views/posts/attributes/_content.haml index e5d327d9..7efba92d 100644 --- a/app/views/posts/attributes/_content.haml +++ b/app/views/posts/attributes/_content.haml @@ -15,26 +15,26 @@ .editor-toolbar .editor-primary-toolbar.scrollbar-black - %button.btn{ data: { button: 'bold' } }= t('editor.bold') - %button.btn{ data: { button: 'italic' } }= t('editor.italic') - %button.btn{ data: { button: 'deleted' } }= t('editor.deleted') - %button.btn{ data: { button: 'underline' } }= t('editor.underline') - %button.btn{ data: { button: 'sup' } }= t('editor.sup') - %button.btn{ data: { button: 'sub' } }= t('editor.sub') - %button.btn{ data: { button: 'mark' } }= t('editor.mark') - %button.btn{ data: { button: 'a' } }= t('editor.a') - %button.btn{ data: { button: 'h1' } }= t('editor.h1') - %button.btn{ data: { button: 'h2' } }= t('editor.h2') - %button.btn{ data: { button: 'h3' } }= t('editor.h3') - %button.btn{ data: { button: 'h4' } }= t('editor.h4') - %button.btn{ data: { button: 'h5' } }= t('editor.h5') - %button.btn{ data: { button: 'h6' } }= t('editor.h6') - %button.btn{ data: { button: 'ul' } }= t('editor.ul') - %button.btn{ data: { button: 'ol' } }= t('editor.ol') - %button.btn{ data: { button: 'left' } }= t('editor.left') - %button.btn{ data: { button: 'center' } }= t('editor.center') - %button.btn{ data: { button: 'right' } }= t('editor.right') - %button.btn{ data: { button: 'multimedia' } }= t('editor.multimedia') + %button.btn{ data: { editor: { button: 'mark-bold' } } }= t('editor.bold') + %button.btn{ data: { editor: { button: 'mark-italic' } } }= t('editor.italic') + %button.btn{ data: { editor: { button: 'mark-deleted' } } }= t('editor.deleted') + %button.btn{ data: { editor: { button: 'mark-underline' } } }= t('editor.underline') + %button.btn{ data: { editor: { button: 'mark-super' } } }= t('editor.super') + %button.btn{ data: { editor: { button: 'mark-sub' } } }= t('editor.sub') + %button.btn{ data: { editor: { button: 'mark-mark' } } }= t('editor.mark') + %button.btn{ data: { editor: { button: 'mark-link' } } }= t('editor.link') + %button.btn{ data: { editor: { button: 'block-h1' } } }= t('editor.h1') + %button.btn{ data: { editor: { button: 'block-h2' } } }= t('editor.h2') + %button.btn{ data: { editor: { button: 'block-h3' } } }= t('editor.h3') + %button.btn{ data: { editor: { button: 'block-h4' } } }= t('editor.h4') + %button.btn{ data: { editor: { button: 'block-h5' } } }= t('editor.h5') + %button.btn{ data: { editor: { button: 'block-h6' } } }= t('editor.h6') + %button.btn{ data: { editor: { button: 'block-unordered_list' } } }= t('editor.ul') + %button.btn{ data: { editor: { button: 'block-ordered_list' } } }= t('editor.ol') + %button.btn{ data: { editor: { button: 'parentBlock-left' } } }= t('editor.left') + %button.btn{ data: { editor: { button: 'parentBlock-center' } } }= t('editor.center') + %button.btn{ data: { editor: { button: 'parentBlock-right' } } }= t('editor.right') + //%button.btn{ data: { editor: { button: 'multimedia' } } }= t('editor.multimedia') -# HAML cringe diff --git a/babel.config.js b/babel.config.js index 4df19493..da4b00af 100644 --- a/babel.config.js +++ b/babel.config.js @@ -34,7 +34,8 @@ module.exports = function(api) { modules: false, exclude: ['transform-typeof-symbol'] } - ] + ], + ['@babel/preset-typescript', { 'allExtensions': true, 'isTSX': true }] ].filter(Boolean), plugins: [ 'babel-plugin-macros', diff --git a/config/webpack/development.js b/config/webpack/development.js index c5edff94..1a709945 100644 --- a/config/webpack/development.js +++ b/config/webpack/development.js @@ -1,5 +1,19 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development' +const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); +const path = require("path"); + const environment = require('./environment') +environment.plugins.append( + "ForkTsCheckerWebpackPlugin", + new ForkTsCheckerWebpackPlugin({ + typescript: { + configFile: path.resolve(__dirname, "../../tsconfig.json"), + }, + // non-async so type checking will block compilation + async: false, + }) +); + module.exports = environment.toWebpackConfig() diff --git a/config/webpacker.yml b/config/webpacker.yml index b19ab110..25519907 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -33,6 +33,8 @@ default: &default - .woff2 extensions: + - .tsx + - .ts - .mjs - .js - .sass diff --git a/package.json b/package.json index 35958268..8f21a6e5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ "zepto": "^1.2.0" }, "devDependencies": { + "@babel/preset-typescript": "^7.12.13", + "fork-ts-checker-webpack-plugin": "^6.1.0", + "typescript": "^4.1.5", "webpack-dev-server": "^3.11.0" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..1cb735ac --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es2015", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/yarn.lock b/yarn.lock index dfab3fb9..96b66c92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,6 +28,13 @@ dependencies: "@babel/highlight" "^7.10.4" +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + "@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.5.tgz#f56db0c4bb1bbbf221b4e81345aab4141e7cb0e9" @@ -64,6 +71,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.12.13": + version "7.12.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.15.tgz#4617b5d0b25cc572474cc1aafee1edeaf9b5368f" + integrity sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ== + dependencies: + "@babel/types" "^7.12.13" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -100,6 +116,17 @@ "@babel/helper-replace-supers" "^7.12.1" "@babel/helper-split-export-declaration" "^7.10.4" +"@babel/helper-create-class-features-plugin@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.13.tgz#0f1707c2eec1a4604f2a22a6fb209854ef2a399a" + integrity sha512-Vs/e9wv7rakKYeywsmEBSRC9KtmE7Px+YBlESekLeJOF0zbGUicGfXSNi3o+tfXSNS48U/7K9mIOOCR79Cl3+Q== + dependencies: + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-member-expression-to-functions" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-create-regexp-features-plugin@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz#18b1302d4677f9dc4740fe8c9ed96680e29d37e8" @@ -134,6 +161,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/helper-get-function-arity@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" @@ -141,6 +177,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-hoist-variables@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" @@ -155,6 +198,13 @@ dependencies: "@babel/types" "^7.12.1" +"@babel/helper-member-expression-to-functions@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz#c5715695b4f8bab32660dbdcdc2341dec7e3df40" + integrity sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-module-imports@^7.12.1": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" @@ -184,6 +234,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" @@ -194,6 +251,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-plugin-utils@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz#174254d0f2424d8aefb4dd48057511247b0a9eeb" + integrity sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA== + "@babel/helper-regex@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" @@ -227,6 +289,16 @@ "@babel/traverse" "^7.12.5" "@babel/types" "^7.12.5" +"@babel/helper-replace-supers@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz#00ec4fb6862546bd3d0aff9aac56074277173121" + integrity sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/helper-simple-access@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" @@ -248,16 +320,33 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== + dependencies: + "@babel/types" "^7.12.13" + "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + "@babel/helper-validator-option@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== +"@babel/helper-validator-option@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f" + integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw== + "@babel/helper-wrap-function@^7.10.4": version "7.12.3" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz#3332339fc4d1fbbf1c27d7958c27d34708e990d9" @@ -295,11 +384,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.10.4", "@babel/parser@^7.12.3", "@babel/parser@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== +"@babel/parser@^7.12.13": + version "7.12.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.15.tgz#2b20de7f0b4b332d9b119dd9c33409c538b8aacf" + integrity sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA== + "@babel/plugin-proposal-async-generator-functions@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" @@ -500,6 +603,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-typescript@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz#9dff111ca64154cef0f4dc52cf843d9f12ce4474" + integrity sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-transform-arrow-functions@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" @@ -754,6 +864,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typescript@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.13.tgz#8bcb5dd79cb8bba690d6920e19992d9228dfed48" + integrity sha512-z1VWskPJxK9tfxoYvePWvzSJC+4pxXr8ArmRm5ofqgi+mwpKg6lvtomkIngBYMJVnKhsFYVysCQLDn//v2RHcg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-typescript" "^7.12.13" + "@babel/plugin-transform-unicode-escapes@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709" @@ -852,6 +971,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-typescript@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.13.tgz#c859c7c075c531d2cc34c2516b214e5d884efe5c" + integrity sha512-gYry7CeXwD2wtw5qHzrtzKaShEhOfTmKb4i0ZxeYBcBosN5VuAudsNbjX7Oj5EAfQ3K4s4HsVMQRRcqGsPvs2A== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-validator-option" "^7.12.11" + "@babel/plugin-transform-typescript" "^7.12.13" + "@babel/runtime@^7.11.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" @@ -868,6 +996,15 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/template@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + "@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095" @@ -883,6 +1020,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" + integrity sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.1", "@babel/types@^7.12.5": version "7.12.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" @@ -892,6 +1044,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" + integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@babel/types@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" @@ -1014,6 +1175,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.4": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" @@ -1265,7 +1431,7 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.2, ajv@^6.12.4, 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== @@ -1322,6 +1488,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1465,6 +1638,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -1930,6 +2108,14 @@ chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -1964,6 +2150,21 @@ chokidar@^3.4.1: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.4.2: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" @@ -2051,12 +2252,19 @@ color-convert@^1.9.0, color-convert@^1.9.1: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + 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= -color-name@^1.0.0: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -2610,6 +2818,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -3255,6 +3468,24 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +fork-ts-checker-webpack-plugin@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.1.0.tgz#7581a6ccd7cbbed9ecce3de64fb1f599d7a2990b" + integrity sha512-xLNufWQ1dfQUdZe48TGQlER/0OkcMnUB6lfbN9Tt13wsYyo+2DwcCbnOaPBo1PoFow/WL8pJPktGIdbJaHxAnw== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -3298,6 +3529,16 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" @@ -3312,6 +3553,11 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-monkey@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.1.tgz#4a82f36944365e619f4454d9fff106553067b781" + integrity sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA== + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -3340,6 +3586,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fstream@^1.0.0, fstream@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" @@ -3515,6 +3766,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" @@ -4309,6 +4565,15 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -4587,6 +4852,13 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memfs@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.0.tgz#f9438e622b5acd1daa8a4ae160c496fdd1325b26" + integrity sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A== + dependencies: + fs-monkey "1.0.1" + memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -6908,6 +7180,15 @@ sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -6970,6 +7251,13 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.2: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -7512,7 +7800,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -7764,6 +8052,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -7831,6 +8124,11 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"