From 1626533ccac91e8635edae9a4c2be69fe5c1485f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 Jan 2021 10:39:38 -0600 Subject: [PATCH] add license dialog --- craco.config.js | 7 ++ package.json | 1 + src/actions/app.ts | 24 +++- src/actions/index.ts | 2 + src/actions/license.ts | 28 +++++ src/components/AboutDialog.tsx | 14 ++- src/components/App.tsx | 2 - src/components/LicenseDialog.tsx | 163 ++++++++++++++++++++++++++++ src/components/SettingsDrawer.tsx | 2 + src/components/about-i18n.en.json | 1 + src/components/about-i18n.ts | 1 + src/components/license-i18n.en.json | 12 ++ src/components/license-i18n.test.ts | 12 ++ src/components/license-i18n.ts | 13 +++ src/components/license.scss | 33 ++++++ src/reducers/app.ts | 14 ++- src/reducers/index.ts | 3 + src/reducers/license.ts | 43 ++++++++ src/sagas/index.ts | 2 + src/sagas/license.ts | 33 ++++++ yarn.lock | 56 +++++++++- 21 files changed, 454 insertions(+), 12 deletions(-) create mode 100644 src/actions/license.ts create mode 100644 src/components/LicenseDialog.tsx create mode 100644 src/components/license-i18n.en.json create mode 100644 src/components/license-i18n.test.ts create mode 100644 src/components/license-i18n.ts create mode 100644 src/components/license.scss create mode 100644 src/reducers/license.ts create mode 100644 src/sagas/license.ts diff --git a/craco.config.js b/craco.config.js index dd1d624a..28a68095 100644 --- a/craco.config.js +++ b/craco.config.js @@ -6,9 +6,16 @@ const { getLoaders, loaderByName, } = require('@craco/craco'); +const LicensePlugin = require('webpack-license-plugin'); module.exports = { webpack: { + plugins: [ + new LicensePlugin({ + outputFilename: 'static/oss-licenses.json', + replenishDefaultLicenseTexts: true, + }), + ], configure: { resolve: { // need 'esnext' first to avoid compile errors diff --git a/package.json b/package.json index d0b560bb..5a518b6a 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "redux-saga": "^1.1.3", "typescript": "~4.1.3", "web-vitals": "^1.0.1", + "webpack-license-plugin": "^4.1.2", "xterm": "^4.9.0", "xterm-addon-fit": "^0.4.0", "zen-push": "^0.2.1" diff --git a/src/actions/app.ts b/src/actions/app.ts index e47531a1..f6bda357 100644 --- a/src/actions/app.ts +++ b/src/actions/app.ts @@ -17,6 +17,10 @@ export enum AppActionType { OpenAboutDialog = 'app.action.openAboutDialog', /** Close about dialog. */ CloseAboutDialog = 'app.action.closeAboutDialog', + /** Open license dialog. */ + OpenLicenseDialog = 'app.action.openLicenseDialog', + /** Close license dialog. */ + CloseLicenseDialog = 'app.action.closeLicenseDialog', } /** Action that indicates the app has just started. */ @@ -59,10 +63,28 @@ export function closeAboutDialog(): AppCloseAboutDialogAction { return { type: AppActionType.CloseAboutDialog }; } +/** Action to open the license dialog. */ +export type AppOpenLicenseDialogAction = Action; + +/** Creates an action to open the license dialog. */ +export function openLicenseDialog(): AppOpenLicenseDialogAction { + return { type: AppActionType.OpenLicenseDialog }; +} + +/** Action to close the license dialog. */ +export type AppCloseLicenseDialogAction = Action; + +/** Creates an action to close the license dialog. */ +export function closeLicenseDialog(): AppCloseLicenseDialogAction { + return { type: AppActionType.CloseLicenseDialog }; +} + /** common type for all app actions. */ export type AppAction = | AppStartupAction | AppOpenSettingsAction | AppCloseSettingsAction | AppOpenAboutDialogAction - | AppCloseAboutDialogAction; + | AppCloseAboutDialogAction + | AppOpenLicenseDialogAction + | AppCloseLicenseDialogAction; diff --git a/src/actions/index.ts b/src/actions/index.ts index 11b549ac..e4ded3ad 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -8,6 +8,7 @@ import { BleUartAction } from './ble-uart'; import { EditorAction } from './editor'; import { FlashFirmwareAction } from './flash-firmware'; import { HubAction, HubMessageAction } from './hub'; +import { LicenseAction } from './license'; import { BootloaderConnectionAction, BootloaderDidRequestAction, @@ -36,6 +37,7 @@ export type Action = | FlashFirmwareAction | HubAction | HubMessageAction + | LicenseAction | MpyAction | NotificationAction | ServiceWorkerAction diff --git a/src/actions/license.ts b/src/actions/license.ts new file mode 100644 index 00000000..3ab1a567 --- /dev/null +++ b/src/actions/license.ts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { Action } from 'redux'; +import { LicenseInfo, LicenseList } from '../reducers/license'; + +export enum LicenseActionType { + DidFetchList = 'license.action.didFetchList', + Select = 'license.action.select', +} + +export type LicenseDidFetchListAction = Action & { + list: LicenseList; +}; + +export function didFetchList(list: LicenseList): LicenseDidFetchListAction { + return { type: LicenseActionType.DidFetchList, list }; +} + +export type LicenseSelectAction = Action & { + info: LicenseInfo; +}; + +export function select(info: LicenseInfo): LicenseSelectAction { + return { type: LicenseActionType.Select, info }; +} + +export type LicenseAction = LicenseDidFetchListAction | LicenseSelectAction; diff --git a/src/components/AboutDialog.tsx b/src/components/AboutDialog.tsx index 162acf9e..f7b506dc 100644 --- a/src/components/AboutDialog.tsx +++ b/src/components/AboutDialog.tsx @@ -3,12 +3,12 @@ // The about dialog -import { AnchorButton, Classes, Dialog } from '@blueprintjs/core'; +import { AnchorButton, Button, Classes, Dialog } from '@blueprintjs/core'; import { WithI18nProps, withI18n } from '@shopify/react-i18n'; import React from 'react'; import { connect } from 'react-redux'; import { Action, Dispatch } from '../actions'; -import { closeAboutDialog } from '../actions/app'; +import { closeAboutDialog, openLicenseDialog } from '../actions/app'; import { RootState } from '../reducers'; import { appName, @@ -17,6 +17,7 @@ import { pybricksWebsiteUrl, } from '../settings/ui'; import ExternalLinkIcon from './ExternalLinkIcon'; +import LicenseDialog from './LicenseDialog'; import { AboutStringId } from './about-i18n'; import en from './about-i18n.en.json'; @@ -24,13 +25,13 @@ import './about.scss'; type StateProps = { showAboutDialog: boolean }; -type DispatchProps = { onClose: () => void }; +type DispatchProps = { onClose: () => void; onLicenseButtonClick: () => void }; type AboutDialogProps = StateProps & DispatchProps & WithI18nProps; class AboutDialog extends React.Component { render(): JSX.Element { - const { i18n, showAboutDialog, onClose } = this.props; + const { i18n, showAboutDialog, onClose, onLicenseButtonClick } = this.props; return ( onClose()}>
@@ -47,12 +48,16 @@ class AboutDialog extends React.Component { {legoDisclaimer}

+ {i18n.translate(AboutStringId.WebsiteButtonLabel)} 
+
); } @@ -64,6 +69,7 @@ const mapStateToProps = (state: RootState): StateProps => ({ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ onClose: (): Action => dispatch(closeAboutDialog()), + onLicenseButtonClick: (): Action => dispatch(openLicenseDialog()), }); export default connect( diff --git a/src/components/App.tsx b/src/components/App.tsx index f42eb9c9..04a15162 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -5,7 +5,6 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import SplitterLayout from 'react-splitter-layout'; import { RootState } from '../reducers'; -import AboutDialog from './AboutDialog'; import Editor from './Editor'; import SettingsDrawer from './SettingsDrawer'; import StatusBar from './StatusBar'; @@ -65,7 +64,6 @@ function App(): JSX.Element { - ); } diff --git a/src/components/LicenseDialog.tsx b/src/components/LicenseDialog.tsx new file mode 100644 index 00000000..50e17fb6 --- /dev/null +++ b/src/components/LicenseDialog.tsx @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +// The license dialog + +import { + Button, + ButtonGroup, + Callout, + Card, + Classes, + Dialog, + NonIdealState, +} from '@blueprintjs/core'; +import { WithI18nProps, withI18n } from '@shopify/react-i18n'; +import React from 'react'; +import { connect } from 'react-redux'; +import { Action, Dispatch } from '../actions'; +import { closeLicenseDialog } from '../actions/app'; +import { select } from '../actions/license'; +import { RootState } from '../reducers'; +import { LicenseInfo, LicenseList } from '../reducers/license'; +import { appName } from '../settings/ui'; +import { LicenseStringId } from './license-i18n'; +import en from './license-i18n.en.json'; + +import './license.scss'; + +type StateProps = { + showLicenseDialog: boolean; + licenseList: LicenseList | null; + licenseInfo: LicenseInfo | null; +}; + +type DispatchProps = { + onClose: () => void; + onSelectPackage: (info: LicenseInfo) => void; +}; + +type LicenseDialogProps = StateProps & DispatchProps & WithI18nProps; + +class LicenseDialog extends React.Component { + render(): JSX.Element { + const infoDiv = React.createRef(); + + const { + i18n, + showLicenseDialog, + onClose, + licenseList, + licenseInfo, + onSelectPackage, + } = this.props; + return ( + onClose()} + className="pb-license-dialog" + > +
+ + {i18n.translate(LicenseStringId.Description, { + name: appName, + })} + + +
+ {licenseList === null ? ( + // TODO: this should be translated and hooked to + // state indicating if download is in progress + // or there was an actual failure. + + Failed to load license data. + + ) : ( + + {licenseList.map((info) => ( + + ))} + + )} +
+
+ {licenseInfo == null ? ( + + {i18n.translate(LicenseStringId.SelectPackageHelp)} + + ) : ( +
+ +

+ + {i18n.translate( + LicenseStringId.PackageLabel, + )} + {' '} + {licenseInfo.name}{' '} + + v{licenseInfo.version} + +

+ {licenseInfo.author && ( +

+ + {' '} + {i18n.translate( + LicenseStringId.AuthorLabel, + )} + {' '} + {licenseInfo.author} +

+ )} +

+ + {' '} + {i18n.translate( + LicenseStringId.LicenseLabel, + )} + {' '} + {licenseInfo.license} +

+
+
+
{licenseInfo.licenseText}
+
+
+ )} +
+
+
+
+ ); + } +} + +const mapStateToProps = (state: RootState): StateProps => ({ + showLicenseDialog: state.app.showLicenseDialog, + licenseList: state.license.list, + licenseInfo: state.license.selected, +}); + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + onClose: (): Action => dispatch(closeLicenseDialog()), + onSelectPackage: (info): Action => dispatch(select(info)), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(withI18n({ id: 'license', fallback: en, translations: { en } })(LicenseDialog)); diff --git a/src/components/SettingsDrawer.tsx b/src/components/SettingsDrawer.tsx index 0b10358c..9e379ec6 100644 --- a/src/components/SettingsDrawer.tsx +++ b/src/components/SettingsDrawer.tsx @@ -27,6 +27,7 @@ import { } from '../settings/ui'; import { SettingId } from '../settings/user'; import { isMacOS } from '../utils/os'; +import AboutDialog from './AboutDialog'; import ExternalLinkIcon from './ExternalLinkIcon'; import { SettingsStringId } from './settings-i18n'; import en from './settings-i18n.en.json'; @@ -192,6 +193,7 @@ class SettingsDrawer extends React.PureComponent { > {i18n.translate(SettingsStringId.HelpAboutLabel)} + diff --git a/src/components/about-i18n.en.json b/src/components/about-i18n.en.json index 820e65fb..5c9a2b5f 100644 --- a/src/components/about-i18n.en.json +++ b/src/components/about-i18n.en.json @@ -1,6 +1,7 @@ { "about": { "description": "A simple web app for programming LEGO® Powered Up smart hubs using Pybricks MicroPython.", + "licenseButton": { "label": "Open Source Licenses" }, "websiteButton": { "label": "Pybricks Website" } } } diff --git a/src/components/about-i18n.ts b/src/components/about-i18n.ts index b73a4ef4..04a9ba9d 100644 --- a/src/components/about-i18n.ts +++ b/src/components/about-i18n.ts @@ -5,5 +5,6 @@ export enum AboutStringId { Description = 'about.description', + LicenseButtonLabel = 'about.licenseButton.label', WebsiteButtonLabel = 'about.websiteButton.label', } diff --git a/src/components/license-i18n.en.json b/src/components/license-i18n.en.json new file mode 100644 index 00000000..e49f0af1 --- /dev/null +++ b/src/components/license-i18n.en.json @@ -0,0 +1,12 @@ +{ + "license": { + "title": "Open Source Licenses", + "description": "{name} is built on open source software. By using {name} you are agreeing to the terms and conditions of all of the included software licenses.", + "packageLabel": "Package:", + "authorLabel": "Author:", + "licenseLabel": "License:", + "help": { + "selectPackage": "Select a package to view the license." + } + } +} diff --git a/src/components/license-i18n.test.ts b/src/components/license-i18n.test.ts new file mode 100644 index 00000000..e4bc2625 --- /dev/null +++ b/src/components/license-i18n.test.ts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { lookup } from '../../test'; +import { LicenseStringId } from './license-i18n'; +import en from './license-i18n.en.json'; + +describe('Ensure .json file has matches for LicenseStringId', () => { + test.each(Object.values(LicenseStringId))('%s', (id) => { + expect(lookup(en, id)).toBeDefined(); + }); +}); diff --git a/src/components/license-i18n.ts b/src/components/license-i18n.ts new file mode 100644 index 00000000..da089f70 --- /dev/null +++ b/src/components/license-i18n.ts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +// License dialog translation keys. + +export enum LicenseStringId { + Title = 'license.title', + Description = 'license.description', + PackageLabel = 'license.packageLabel', + AuthorLabel = 'license.authorLabel', + LicenseLabel = 'license.licenseLabel', + SelectPackageHelp = 'license.help.selectPackage', +} diff --git a/src/components/license.scss b/src/components/license.scss new file mode 100644 index 00000000..0b997eaf --- /dev/null +++ b/src/components/license.scss @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2020-2021 The Pybricks Authors + +// Custom styling for the LicenseDialog control. + +@import '../variables.scss'; + +.pb-license-dialog { + width: 1200px; +} + +.pb-license-browser { + margin-top: 16px; + display: flex; + height: 600px; +} + +.pb-license-list { + width: 25%; + flex-flow: column; + overflow: auto; +} + +.pb-license-info { + width: 75%; + flex-flow: column; + padding: 0px 16px; + overflow: auto; +} + +.pb-license-text { + padding: 0px 16px; +} diff --git a/src/reducers/app.ts b/src/reducers/app.ts index 5339d975..3bcce3ea 100644 --- a/src/reducers/app.ts +++ b/src/reducers/app.ts @@ -10,6 +10,7 @@ import { AppActionType } from '../actions/app'; export interface AppState { readonly showSettings: boolean; readonly showAboutDialog: boolean; + readonly showLicenseDialog: boolean; } const showSettings: Reducer = (state = false, action) => { @@ -34,4 +35,15 @@ const showAboutDialog: Reducer = (state = false, action) => { } }; -export default combineReducers({ showSettings, showAboutDialog }); +const showLicenseDialog: Reducer = (state = false, action) => { + switch (action.type) { + case AppActionType.OpenLicenseDialog: + return true; + case AppActionType.CloseLicenseDialog: + return false; + default: + return state; + } +}; + +export default combineReducers({ showSettings, showAboutDialog, showLicenseDialog }); diff --git a/src/reducers/index.ts b/src/reducers/index.ts index a6ccb937..940b7bf4 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -7,6 +7,7 @@ import ble, { BleState } from './ble'; import bootloader, { BootloaderState } from './bootloader'; import editor, { EditorState } from './editor'; import hub, { HubState } from './hub'; +import license, { LicenseState } from './license'; import notification, { NotificationState } from './notification'; import settings, { SettingsState } from './settings'; import status, { StatusState } from './status'; @@ -21,6 +22,7 @@ export interface RootState { readonly ble: BleState; readonly editor: EditorState; readonly hub: HubState; + readonly license: LicenseState; readonly notification: NotificationState; readonly settings: SettingsState; readonly status: StatusState; @@ -33,6 +35,7 @@ export default combineReducers({ ble, editor, hub, + license, notification, settings, status, diff --git a/src/reducers/license.ts b/src/reducers/license.ts new file mode 100644 index 00000000..78e70fe8 --- /dev/null +++ b/src/reducers/license.ts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { Reducer, combineReducers } from 'redux'; +import { Action } from '../actions'; +import { LicenseActionType } from '../actions/license'; + +export interface LicenseInfo { + readonly name: string; + readonly version: string; + readonly author: string | undefined; + readonly repository: string | null; + readonly source: string; + readonly license: string; + readonly licenseText: string | null; +} + +export type LicenseList = LicenseInfo[]; + +export interface LicenseState { + readonly list: LicenseList | null; + readonly selected: LicenseInfo | null; +} + +const list: Reducer = (state = null, action) => { + switch (action.type) { + case LicenseActionType.DidFetchList: + return action.list; + default: + return state; + } +}; + +const selected: Reducer = (state = null, action) => { + switch (action.type) { + case LicenseActionType.Select: + return action.info; + default: + return state; + } +}; + +export default combineReducers({ list, selected }); diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 24a2cc0c..7682125d 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -8,6 +8,7 @@ import editor from './editor'; import errorLog from './error-log'; import flashFirmware from './flash-firmware'; import hub from './hub'; +import license from './license'; import lwp3BootloaderBle from './lwp3-bootloader-ble'; import lwp3BootloaderProtocol from './lwp3-bootloader-protocol'; import mpy from './mpy'; @@ -24,6 +25,7 @@ export default function* (): Generator { errorLog(), flashFirmware(), hub(), + license(), mpy(), settings(), terminal(), diff --git a/src/sagas/license.ts b/src/sagas/license.ts new file mode 100644 index 00000000..ff574f72 --- /dev/null +++ b/src/sagas/license.ts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { call, put, select, takeEvery } from 'redux-saga/effects'; +import { AppActionType } from '../actions/app'; +import { didFetchList } from '../actions/license'; +import { RootState } from '../reducers'; +import { LicenseList } from '../reducers/license'; + +function* fetchLicenses(): Generator { + const licenses = (yield select( + (s: RootState) => s.license.list, + )) as LicenseList | null; + + // if we already have license list, nothing to do + if (licenses !== null) { + return; + } + + const response = (yield call(() => fetch('static/oss-licenses.json'))) as Response; + if (!response.ok || response.body === null) { + // TODO: dispatch an action to notify user + console.error('failed to fetch oss-licenses.json', response.statusText); + return; + } + + const list = (yield call(() => response.json())) as LicenseList; + yield put(didFetchList(list)); +} + +export default function* (): Generator { + yield takeEvery(AppActionType.OpenLicenseDialog, fetchLicenses); +} diff --git a/yarn.lock b/yarn.lock index aedc6180..8e22a387 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4331,7 +4331,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.1, debug@^3.2.5: +debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -5833,6 +5833,13 @@ get-intrinsic@^1.0.0, get-intrinsic@^1.0.1: has "^1.0.3" has-symbols "^1.0.1" +get-npm-tarball-url@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-npm-tarball-url/-/get-npm-tarball-url-2.0.1.tgz#43c15223c35096e3e4068d8a6c6747bbdfc23462" + integrity sha512-POrVRGyS9X5w+855/H46JGVYBGuVgJXyIkbsTCzW+sv5x2qH+rfQjc7652DzkgOskF+cqLevA2En7V0hu0gZCg== + dependencies: + normalize-registry-url "^1.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -6295,7 +6302,7 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -8197,6 +8204,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.2.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" + integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -8368,6 +8384,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-registry-url@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-registry-url/-/normalize-registry-url-1.0.0.tgz#f75d2c48373da780c76f1f0eeb6382c06e784d13" + integrity sha512-0v6T4851b72ykk5zEtFoN4QX/Fqyk7pouIj9xZyAvAe9jlDhAwT4z6FlwsoQCHjeuK2EGUoAwy/F4y4B1uZq9A== + normalize-url@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -10720,7 +10741,7 @@ sass-loader@8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sax@~1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -11080,7 +11101,7 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" -source-list-map@^2.0.0: +source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== @@ -11165,6 +11186,13 @@ spdx-expression-parse@^3.0.0: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" +spdx-expression-validate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz#25c9408e1c63fad94fff5517bb7101ffcd23350b" + integrity sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids@^3.0.0: version "3.0.7" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" @@ -12393,6 +12421,18 @@ webpack-dev-server@3.11.0: ws "^6.2.1" yargs "^13.3.2" +webpack-license-plugin@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/webpack-license-plugin/-/webpack-license-plugin-4.1.2.tgz#d3eb954babf30ba65c50cc416c0a59d568776215" + integrity sha512-aS/fMx9zGfqs7OSj02iCX7W4hnaiGolihz+sHn6nEwaMTFeTEp0Uu+QIyRMluC731USIB42hFIdI/vje1SKdrQ== + dependencies: + chalk "^4.1.0" + get-npm-tarball-url "^2.0.1" + lodash "^4.17.20" + needle "^2.2.4" + spdx-expression-validate "^2.0.0" + webpack-sources "^2.0.0" + webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -12426,6 +12466,14 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack- source-list-map "^2.0.0" source-map "~0.6.1" +webpack-sources@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + webpack@4.44.2: version "4.44.2" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72"