Migrate to TypeScript partially

This commit is contained in:
Pig Fang 2019-03-17 21:09:46 +08:00
parent 3b033be048
commit 5d80b8e06a
29 changed files with 355 additions and 163 deletions

View File

@ -5,6 +5,7 @@ module.exports = api => ({
useBuiltIns: false,
loose: true,
}],
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
@ -19,6 +20,7 @@ module.exports = api => ({
['@babel/preset-env', {
targets: { esmodules: true },
}],
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
@ -29,6 +31,7 @@ module.exports = api => ({
['@babel/preset-env', {
targets: { node: 'current' },
}],
'@babel/preset-typescript',
],
plugins: [
'babel-plugin-dynamic-import-node',

View File

@ -12,8 +12,8 @@
"scripts": {
"dev": "webpack-dev-server",
"build": "rimraf public/app && webpack --mode=production",
"lint": "eslint --ext=.js,.vue -f=beauty .",
"test": "jest",
"lint": "eslint --ext=.js,.vue,.ts -f=beauty .",
"test": "tsc -p . && jest",
"codecov": "codecov -F js"
},
"dependencies": {
@ -36,8 +36,13 @@
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.3.4",
"@babel/preset-env": "^7.3.4",
"@babel/preset-typescript": "^7.3.3",
"@gplane/tsconfig": "^1.0.0",
"@types/jest": "^24.0.11",
"@types/jquery": "^3.3.9",
"@types/toastr": "^2.1.36",
"@typescript-eslint/eslint-plugin": "^1.4.2",
"@typescript-eslint/parser": "^1.4.2",
"@vue/test-utils": "^1.0.0-beta.29",
"autoprefixer": "^9.5.0",
"babel-eslint": "^10.0.1",
@ -65,6 +70,7 @@
"style-loader": "^0.23.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"typescript": "^3.3.3333",
"url-loader": "^1.1.2",
"vue-jest": "^4.0.0-beta.2",
"vue-loader": "^15.7.0",
@ -130,18 +136,37 @@
"prefer-object-spread": 0,
"import/no-unresolved": 0,
"project/linebreak-between-tests": 2
}
},
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"no-undef": 0,
"no-unused-vars": 0,
"no-invalid-this": 0
}
}
]
},
"jest": {
"resetMocks": true,
"timers": "fake",
"transform": {
"^.+\\.js$": "babel-jest",
".*\\.(t|j)s$": "babel-jest",
".*\\.vue$": "vue-jest"
},
"moduleFileExtensions": [
"js",
"vue",
"ts",
"json",
"node"
],
@ -158,6 +183,6 @@
"<rootDir>/resources/assets/tests/setup",
"<rootDir>/resources/assets/tests/utils"
],
"testRegex": "resources/assets/tests/.*\\.(spec|test)\\.js$"
"testRegex": "resources/assets/tests/.*\\.(spec|test)\\.(t|j)s$"
}
}

View File

@ -1,13 +1,13 @@
import { init } from './net'
export async function checkForUpdates() {
export async function checkForUpdates(): Promise<void> {
const response = await fetch(`${blessing.base_url}/admin/update/check`, init)
if (response.ok) {
const data = await response.json()
if (data.available) {
document.querySelector(`[href="${blessing.base_url}/admin/update"]`)
.innerHTML += `
const el = document.querySelector(`[href="${blessing.base_url}/admin/update"]`)
if (data.available && el) {
el.innerHTML += `
<span class="pull-right-container">
<span class="label label-primary pull-right">v${data.latest}</span>
</span>`
@ -15,14 +15,14 @@ export async function checkForUpdates() {
}
}
export async function checkForPluginUpdates() {
export async function checkForPluginUpdates(): Promise<void> {
const response = await fetch(`${blessing.base_url}/admin/plugins/market/check`, init)
if (response.ok) {
const data = await response.json()
if (data.available) {
document.querySelector(`[href="${blessing.base_url}/admin/plugins/market"]`)
.innerHTML += `
const el = document.querySelector(`[href="${blessing.base_url}/admin/plugins/market"]`)
if (data.available && el) {
el.innerHTML += `
<span class="pull-right-container">
<span class="label label-success pull-right">${data.plugins.length}</span>
</span>`
@ -30,5 +30,7 @@ export async function checkForPluginUpdates() {
}
}
window.checkForUpdates = checkForUpdates
window.checkForPluginUpdates = checkForPluginUpdates
Object.assign(window, {
checkForUpdates,
checkForPluginUpdates,
})

View File

@ -1,20 +0,0 @@
/** @type {{ [name: string]: Function[] }} */
const bus = Object.create(null)
/**
* @param {string} eventName
* @param {Function} listener
*/
export function on(eventName, listener) {
(bus[eventName] || (bus[eventName] = [])).push(listener)
}
/**
* @param {string} eventName
* @param {any} payload
*/
export function emit(eventName, payload) {
bus[eventName] && bus[eventName].forEach(listener => listener(payload))
}
blessing.event = { on, emit }

View File

@ -0,0 +1,11 @@
const bus: { [name: string]: CallableFunction[] } = Object.create(null)
export function on(eventName: string, listener: CallableFunction) {
(bus[eventName] || (bus[eventName] = [])).push(listener)
}
export function emit(eventName: string, payload?: any) {
bus[eventName] && bus[eventName].forEach(listener => listener(payload))
}
blessing.event = { on, emit }

View File

@ -1,27 +1,26 @@
import Vue from 'vue'
/**
* Translate according to given key.
*
* @param {string} key
* @param {object} parameters
* @return {string}
*/
export function trans(key, parameters = Object.create(null)) {
export function trans(key: string, parameters = Object.create(null)): string {
const segments = key.split('.')
let temp = blessing.i18n || Object.create(null)
let temp = (blessing.i18n || Object.create(null)) as { [k: string]: string | { [k: string]: string } }
let result = ''
for (const segment of segments) {
if (!temp[segment]) {
return key
}
temp = temp[segment]
const middle = temp[segment]
if (typeof middle === 'string') {
result = middle
} else {
temp = middle
}
}
Object.keys(parameters)
.forEach(slot => (temp = temp.replace(`:${slot}`, parameters[slot])))
.forEach(slot => (result = result.replace(`:${slot}`, parameters[slot])))
return temp
return result
}
Vue.use(_Vue => {
@ -41,4 +40,5 @@ Vue.use(_Vue => {
}
})
})
window.trans = trans

View File

@ -15,7 +15,7 @@ export async function logout() {
}
const { msg } = await post('/auth/logout')
setTimeout(() => (window.location = blessing.base_url), 1000)
setTimeout(() => (window.location.href = blessing.base_url), 1000)
swal({ type: 'success', text: msg })
}

View File

@ -4,30 +4,28 @@ import { queryStringify } from './utils'
import { showAjaxError } from './notify'
class HTTPError extends Error {
constructor(message, response) {
response: Response
constructor(message: string, response: Response) {
super(message)
this.response = response
}
}
const empty = Object.create(null)
/** @type Request */
export const init = {
export const init: RequestInit = {
credentials: 'same-origin',
headers: {
headers: new Headers({
Accept: 'application/json',
},
}),
}
function retrieveToken() {
const csrfField = document.querySelector('meta[name="csrf-token"]')
return csrfField && csrfField.content
const csrfField: HTMLMetaElement | null = document.querySelector('meta[name="csrf-token"]')
return (csrfField && csrfField.content) || ''
}
/**
* @param {Request} request
*/
export async function walkFetch(request) {
export async function walkFetch(request: Request): Promise<any> {
request.headers.set('X-CSRF-TOKEN', retrieveToken())
try {
@ -45,7 +43,7 @@ export async function walkFetch(request) {
}
}
export function get(url, params = empty) {
export function get(url: string, params = empty): Promise<any> {
emit('beforeFetch', {
method: 'GET',
url,
@ -57,7 +55,7 @@ export function get(url, params = empty) {
return walkFetch(new Request(`${blessing.base_url}${url}${qs && `?${qs}`}`, init))
}
export function post(url, data = empty) {
export function post(url: string, data = empty): Promise<any> {
emit('beforeFetch', {
method: 'POST',
url,
@ -77,10 +75,9 @@ export function post(url, data = empty) {
}
Vue.use(_Vue => {
_Vue.prototype.$http = {
get,
post,
}
Object.defineProperty(_Vue.prototype, '$http', {
get: () => ({ get, post }),
})
})
blessing.fetch = { get, post }

View File

@ -2,31 +2,23 @@
import $ from 'jquery'
import Swal from 'sweetalert2'
import toastr from 'toastr'
import { ModalOptions } from '../shims'
import { trans } from './i18n'
/**
* Show modal if error occured when sending an ajax request.
*
* @param {Error} error
* @return {void}
*/
export function showAjaxError(error) {
export function showAjaxError(error: Error): void {
showModal(error.message.replace(/\n/g, '<br>'), trans('general.fatalError'), 'danger')
}
/**
* Show a bootstrap modal.
*
* @param {string} msg Modal content
* @param {string} title Modal title
* @param {string} type Modal type, default|info|success|warning|error
* @param {object} options All $.fn.modal options, plus { btnText, callback, destroyOnClose }
* @return {void}
*/
export function showModal(msg, title = 'Message', type = 'default', options = {}) {
export function showModal(
msg: string, title = 'Message',
type = 'default',
options: ModalOptions = {}
): void {
const btnType = type === 'default' ? 'btn-primary' : 'btn-outline'
const btnText = options.btnText || 'OK'
const onClick = options.callback === undefined ? 'data-dismiss="modal"' : `onclick="${options.callback}"`
const onClick = options.callback === undefined
? 'data-dismiss="modal"'
: `onclick="${options.callback}"`
const destroyOnClose = options.destroyOnClose !== false
const dom = `
@ -51,9 +43,9 @@ export function showModal(msg, title = 'Message', type = 'default', options = {}
$(dom)
.on('hidden.bs.modal', /* istanbul ignore next */ function modal() {
// eslint-disable-next-line no-invalid-this
destroyOnClose && $(this).remove()
})
// @ts-ignore
.modal(options)
}
@ -62,13 +54,9 @@ const swalInstance = Swal.mixin({
cancelButtonText: trans('general.cancel'),
})
/**
* @param {import('sweetalert2').SweetAlertOptions} options
*/
export function swal(options) {
export function swal(options: import('sweetalert2').SweetAlertOptions) {
return swalInstance.fire(options)
}
window.toastr = toastr
window.swal = swal
blessing.notify = { showModal }
Object.assign(window, { toastr, swal })
Object.assign(blessing, { notify: { showModal } })

View File

@ -1,40 +0,0 @@
/**
* @param {Function} func
* @param {number} delay
*/
export function debounce(func, delay) {
let timer
return () => {
clearTimeout(timer)
timer = setTimeout(func, delay)
}
}
/**
* Get parameters in query string with key.
*
* @param {string} key
* @param {string} defaultValue
* @return {string}
*/
export function queryString(key, defaultValue) {
const result = location.search.match(new RegExp(`[?&]${key}=([^&]+)`, 'i'))
if (result === null || result.length < 1) {
return defaultValue
}
return result[1]
}
/**
* Serialize data to URL query string
*
* @param {object} data
* @returns {string}
*/
export function queryStringify(params) {
return Object
.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
}

View File

@ -0,0 +1,23 @@
export function debounce(func: CallableFunction, delay: number) {
let timer: number
return () => {
clearTimeout(timer)
timer = setTimeout(func, delay)
}
}
export function queryString(key: string, defaultValue: string = ''): string {
const result = location.search.match(new RegExp(`[?&]${key}=([^&]+)`, 'i'))
if (result === null || result.length < 1) {
return defaultValue
}
return result[1]
}
export function queryStringify(params: { [key: string]: string }): string {
return Object
.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
}

View File

@ -1,7 +1,9 @@
/* eslint-disable camelcase */
import Vue from 'vue'
import * as JQuery from 'jquery'
declare global {
var blessing: {
let blessing: {
base_url: string
debug: boolean
env: string
@ -48,3 +50,9 @@ declare module 'vue/types/vue' {
$route: string[]
}
}
interface ModalOptions {
btnText?: string,
callback?: CallableFunction,
destroyOnClose?: boolean
}

View File

@ -4,3 +4,4 @@ env:
rules:
no-empty-function: 0
func-names: 0
no-extra-parens: 0

View File

@ -16,19 +16,17 @@ test('check for BS updates', async () => {
json: () => Promise.resolve({ available: true, latest: '4.0.0' }),
})
document.body.innerHTML = `
<a href="/admin/update"></a>
`
document.body.innerHTML = '<a href="/admin/update"></a>'
await checkForUpdates()
expect(window.fetch).toBeCalledWith('/admin/update/check', init)
expect(document.querySelector('a').innerHTML).toBe('')
expect(document.querySelector('a')!.innerHTML).toBe('')
await checkForUpdates()
expect(document.querySelector('a').innerHTML).toBe('')
expect(document.querySelector('a')!.innerHTML).toBe('')
await checkForUpdates()
expect(document.querySelector('a').innerHTML).toContain('4.0.0')
expect(document.querySelector('a')!.innerHTML).toContain('4.0.0')
})
test('check for plugins updates', async () => {
@ -43,17 +41,29 @@ test('check for plugins updates', async () => {
json: () => Promise.resolve({ available: true, plugins: [{}] }),
})
document.body.innerHTML = `
<a href="/admin/plugins/market"></a>
`
document.body.innerHTML = '<a href="/admin/plugins/market"></a>'
await checkForPluginUpdates()
expect(window.fetch).toBeCalledWith('/admin/plugins/market/check', init)
expect(document.querySelector('a').innerHTML).toBe('')
expect(document.querySelector('a')!.innerHTML).toBe('')
await checkForPluginUpdates()
expect(document.querySelector('a').innerHTML).toBe('')
expect(document.querySelector('a')!.innerHTML).toBe('')
await checkForPluginUpdates()
expect(document.querySelector('a').innerHTML).toContain('1')
expect(document.querySelector('a')!.innerHTML).toContain('1')
})
test('do not update anything if element not found', async () => {
window.fetch = jest.fn()
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ available: true, latest: '4.0.0' }),
})
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ available: true, plugins: [{}] }),
})
await Promise.all([checkForUpdates, checkForPluginUpdates])
})

View File

@ -1,9 +1,5 @@
import * as emitter from '@/js/event'
test('mount variable to global', () => {
expect(window.bsEmitter).toBeFrozen()
})
test('add listener and emit event', () => {
const mockA = jest.fn()
const mockB = jest.fn()

View File

@ -1,13 +1,18 @@
import Vue from 'vue'
import * as net from '@/js/net'
import { on } from '@/js/event'
import { showAjaxError } from '@/js/notify'
jest.mock('@/js/notify')
window.Request = function Request(url, init) {
this.url = url
Object.keys(init).forEach(key => (this[key] = init[key]))
this.headers = new Map(Object.entries(init.headers))
;(window as Window & { Request: any }).Request = class {
headers: Map<string, string>
constructor(public url: string, init: Request) {
this.url = url
Object.assign(this, init)
this.headers = new Map(Object.entries(init.headers))
}
}
test('the GET method', async () => {
@ -96,23 +101,28 @@ test('low level fetch', async () => {
text: () => Promise.resolve('text'),
})
const request = { headers: new Map() }
const request: RequestInit = { headers: new Headers() }
const stub = jest.fn()
on('fetchError', stub)
await net.walkFetch(request)
await net.walkFetch(request as Request)
expect(showAjaxError.mock.calls[0][0]).toBeInstanceOf(Error)
expect(showAjaxError.mock.calls[0][0]).toHaveProperty('message', 'network')
expect(stub).toBeCalledWith(expect.any(Error))
await net.walkFetch(request)
await net.walkFetch(request as Request)
expect(showAjaxError.mock.calls[1][0]).toBeInstanceOf(Error)
expect(stub.mock.calls[1][0]).toHaveProperty('message', '404')
expect(stub.mock.calls[1][0]).toHaveProperty('response')
await net.walkFetch(request)
await net.walkFetch(request as Request)
expect(json).toBeCalled()
expect(await net.walkFetch(request)).toBe('text')
expect(await net.walkFetch(request as Request)).toBe('text')
})
test('inject to Vue instance', () => {
expect(typeof Vue.prototype.$http.get).toBe('function')
expect(typeof Vue.prototype.$http.post).toBe('function')
})

View File

@ -10,6 +10,7 @@ jest.mock('sweetalert2', () => ({
}))
test('show AJAX error', () => {
// @ts-ignore
$.fn.modal = function () {
document.body.innerHTML = this.html()
}

15
resources/assets/tests/js/types.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
class Request {
url: string
headers: Map<string, string>
}
interface Window {
trans(key: string, parameters: object): string
blessing: {
i18n: object
}
fetch: jest.Mock
}

View File

@ -14,7 +14,7 @@ test('debounce', () => {
test('queryString', () => {
history.pushState({}, 'page', `${location.href}?key=value`)
expect(utils.queryString('key')).toBe('value')
expect(utils.queryString('a')).toBeUndefined()
expect(utils.queryString('a')).toBe('')
expect(utils.queryString('a', 'b')).toBe('b')
})

View File

@ -8,6 +8,12 @@ window.blessing = {
version: '4.0.0',
}
window.Headers = class extends Map {
constructor(headers = {}) {
super(Object.entries(headers))
}
}
const noop = () => undefined
// eslint-disable-next-line no-console
Object.keys(console).forEach(method => (console[method] = noop))

View File

@ -0,0 +1,18 @@
import * as net from '../../src/js/net'
export const init = {} as typeof net.init
export const walkFetch = {} as jest.Mock<
ReturnType<typeof net.walkFetch>,
Parameters<typeof net.walkFetch>
>
export const get = {} as jest.Mock<
ReturnType<typeof net.get>,
Parameters<typeof net.get>
>
export const post = {} as jest.Mock<
ReturnType<typeof net.post>,
Parameters<typeof net.post>
>

View File

@ -0,0 +1,16 @@
import * as notify from '../../src/js/notify'
export const showAjaxError = {} as jest.Mock<
ReturnType<typeof notify.showAjaxError>,
Parameters<typeof notify.showAjaxError>
>
export const showModal = {} as jest.Mock<
ReturnType<typeof notify.showModal>,
Parameters<typeof notify.showModal>
>
export const swal = {} as jest.Mock<
ReturnType<typeof notify.swal>,
Parameters<typeof notify.swal>
>

22
tsconfig.json Normal file
View File

@ -0,0 +1,22 @@
{
"extends": "@gplane/tsconfig",
"compilerOptions": {
"declaration": false,
"allowJs": true,
"target": "es2015",
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/js/*": [
"./resources/assets/tests/ts-shims/*",
"./resources/assets/src/js/*"
]
}
},
"exclude": [
"node_modules",
"vendor"
]
}

View File

@ -36,7 +36,7 @@ const config = {
module: {
rules: [
{
test: /\.js$/,
test: /\.(t|j)s$/,
exclude: /node_modules/,
use: ['cache-loader', 'babel-loader'],
},
@ -103,8 +103,7 @@ const config = {
flatten: true,
},
{
from:
'node_modules/admin-lte/dist/css/skins/_all-skins.min.css',
from: 'node_modules/admin-lte/dist/css/skins/_all-skins.min.css',
to: 'skins',
flatten: true,
},
@ -114,7 +113,7 @@ const config = {
]),
],
resolve: {
extensions: ['.js', '.vue', '.json'],
extensions: ['.js', '.ts', '.vue', '.json'],
},
devtool: devMode ? 'cheap-module-eval-source-map' : false,
devServer: {

103
yarn.lock
View File

@ -347,6 +347,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-typescript@^7.2.0":
version "7.3.3"
resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-arrow-functions@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
@ -548,6 +555,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-typescript@^7.3.2":
version "7.3.2"
resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.3.2.tgz#59a7227163e55738842f043d9e5bd7c040447d96"
integrity sha512-Pvco0x0ZSCnexJnshMfaibQ5hnK8aUHSvjCQhC1JR8eeg+iBwt0AtCO7gWxJ358zZevuf9wPSO5rv+WJcbHPXQ==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-typescript" "^7.2.0"
"@babel/plugin-transform-unicode-regex@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b"
@ -605,6 +620,14 @@
js-levenshtein "^1.1.3"
semver "^5.3.0"
"@babel/preset-typescript@^7.3.3":
version "7.3.3"
resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a"
integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-typescript" "^7.3.2"
"@babel/runtime@^7.3.4":
version "7.3.4"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
@ -736,6 +759,11 @@
version "5.7.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.7.2.tgz#1498c3eb78ee7c78c5488418707de90aaf58d5d7"
"@gplane/tsconfig@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@gplane/tsconfig/-/tsconfig-1.0.0.tgz#c7e6d56ece3d045abe46d00aece990d8412440f0"
integrity sha512-x0Lxt+oBXGVK8qYYLQvohFYQZlt9KgeVfkGAEXWOq38Gxu5KdcsGzPvyWPMhz5Og/U2pWXbjw4+rN4Qa7CPiWQ==
"@jest/console@^24.3.0":
version "24.3.0"
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.3.0.tgz#7bd920d250988ba0bf1352c4493a48e1cb97671e"
@ -923,6 +951,13 @@
dependencies:
"@types/jest-diff" "*"
"@types/jquery@*":
version "3.3.29"
resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd"
integrity sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ==
dependencies:
"@types/sizzle" "*"
"@types/jquery@^3.3.9":
version "3.3.9"
resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.9.tgz#91f2aaf5c1e91fd79598fc00ecb4504d78b51fd7"
@ -932,16 +967,55 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.3.tgz#7c6b0f8eaf16ae530795de2ad1b85d34bf2f5c58"
integrity sha512-wp6IOGu1lxsfnrD+5mX6qwSwWuqsdkKKxTN4aQc4wByHAKZJf9/D4KXPQ1POUjEbnCP5LMggB0OEFNY9OTsMqg==
"@types/sizzle@*":
version "2.3.2"
resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/toastr@^2.1.36":
version "2.1.36"
resolved "https://registry.npmjs.org/@types/toastr/-/toastr-2.1.36.tgz#6b692ec384147324cffe13892e9e5b26bd1e69e7"
integrity sha512-mz4eBVCNrH0AyGpt+6DLl/xE+CPrSqMfQs/cMTAGJl8W9W375DNOTYOon1GsuSJqVU1QqZ4jCuARzo4TNQNrDg==
dependencies:
"@types/jquery" "*"
"@types/yargs@^12.0.2", "@types/yargs@^12.0.9":
version "12.0.9"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
"@typescript-eslint/eslint-plugin@^1.4.2":
version "1.4.2"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.4.2.tgz#370bc32022d1cc884a5dcf62624ef2024182769d"
integrity sha512-6WInypy/cK4rM1dirKbD5p7iFW28DbSRKT/+PGn+DYzBWEvHq5KnZAqQ5cX25JBc0qMkFxJNxNfBbFXJyyzVcw==
dependencies:
"@typescript-eslint/parser" "1.4.2"
"@typescript-eslint/typescript-estree" "1.4.2"
requireindex "^1.2.0"
tsutils "^3.7.0"
"@typescript-eslint/parser@1.4.2", "@typescript-eslint/parser@^1.4.2":
version "1.4.2"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.4.2.tgz#acfdee2019958a41d308d768e53ded975ef90ce8"
integrity sha512-OqLkY9295DXXaWToItUv3olO2//rmzh6Th6Sc7YjFFEpEuennsm5zhygLLvHZjPxPlzrQgE8UDaOPurDylaUuw==
dependencies:
"@typescript-eslint/typescript-estree" "1.4.2"
eslint-scope "^4.0.0"
eslint-visitor-keys "^1.0.0"
"@typescript-eslint/typescript-estree@1.4.2":
version "1.4.2"
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.4.2.tgz#b16bc36c9a4748a7fca92cba4c2d73c5325c8a85"
integrity sha512-wKgi/w6k1v3R4b6oDc20cRWro2gBzp0wn6CAeYC8ExJMfvXMfiaXzw2tT9ilxdONaVWMCk7B9fMdjos7bF/CWw==
dependencies:
lodash.unescape "4.0.1"
semver "5.5.0"
"@vue/component-compiler-utils@^2.4.0", "@vue/component-compiler-utils@^2.5.1":
version "2.5.2"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.5.2.tgz#a8d57e773354ab10e4742c7d6a8dd86184d4d7be"
@ -5427,6 +5501,11 @@ lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
lodash.unescape@4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@ -7193,6 +7272,11 @@ require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
requireindex@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef"
integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@ -7376,6 +7460,11 @@ selfsigned@^1.9.1:
version "5.5.1"
resolved "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
semver@5.5.0:
version "5.5.0"
resolved "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.5, semver@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
@ -8146,10 +8235,17 @@ ts-jest@^23.10.5:
semver "^5.5"
yargs-parser "10.x"
tslib@^1.9.0:
tslib@^1.8.1, tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
tsutils@^3.7.0:
version "3.9.1"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.9.1.tgz#2a40dc742943c71eca6d5c1994fcf999956be387"
integrity sha512-hrxVtLtPqQr//p8/msPT1X1UYXUjizqSit5d9AQ5k38TcV38NyecL5xODNxa73cLe/5sdiJ+w1FqzDhRBA/anA==
dependencies:
tslib "^1.8.1"
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@ -8185,6 +8281,11 @@ typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
typescript@^3.3.3333:
version "3.3.3333"
resolved "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
uglify-js@^3.1.4:
version "3.4.9"
resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"