diff --git a/frontend/package.json b/frontend/package.json index eb2beb892c3..99992563b79 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,8 @@ "license": "Apache-2.0", "private": true, "workspaces": [ - "packages/*" + "packages/*", + "public" ], "scripts": { "dev": "rm -rf ./public/dist && ts-node -O '{\"module\":\"commonjs\"}' ./node_modules/.bin/webpack --mode development --watch --progress", @@ -36,7 +37,7 @@ ".(ts|tsx|js|jsx)": "./node_modules/ts-jest/preprocessor.js" }, "transformIgnorePatterns": [ - "/node_modules/(?!lodash-es/.*)" + "/node_modules/(?!lodash-es|@console)" ], "testRegex": "/__tests__/.*\\.(ts|tsx|js|jsx)$", "setupFiles": [ @@ -73,6 +74,7 @@ "js-base64": "^2.4.5", "js-yaml": "3.x", "lodash-es": "4.x", + "memoize-one": "5.x", "murmurhash-js": "1.0.x", "openshift-logos-icon": "1.7.1", "patternfly": "^3.59.0", @@ -108,6 +110,7 @@ }, "devDependencies": { "@types/enzyme": "3.x", + "@types/glob": "7.x", "@types/immutable": "3.x", "@types/jasmine": "2.8.x", "@types/jasminewd2": "2.0.x", @@ -121,7 +124,7 @@ "@types/react-router-dom": "4.2.7", "@types/react-transition-group": "2.x", "@types/react-virtualized": "9.x", - "@types/webpack": "^4.1.1", + "@types/webpack": "4.x", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "bootstrap-sass": "^3.3.7", @@ -137,6 +140,7 @@ "eslint-plugin-react-hooks": "^1.5.1", "file-loader": "1.x", "fork-ts-checker-webpack-plugin": "0.x", + "glob": "7.x", "glslify-loader": "1.x", "html-webpack-plugin": "3.x", "jasmine-console-reporter": "2.x", @@ -149,6 +153,7 @@ "protractor": "5.4.x", "protractor-fail-fast": "3.x", "protractor-jasmine2-screenshot-reporter": "0.5.x", + "read-pkg": "5.x", "resolve-url-loader": "2.x", "sass-loader": "6.x", "thread-loader": "1.x", @@ -158,7 +163,8 @@ "typescript": "3.4.4", "webpack": "4.29.6", "webpack-bundle-analyzer": "2.x", - "webpack-cli": "^2.0.12" + "webpack-cli": "^2.0.12", + "webpack-virtual-modules": "^0.1.10" }, "engines": { "node": ">=8.x" diff --git a/frontend/packages/console-app/package.json b/frontend/packages/console-app/package.json index c8ba85630ab..c8c069a4d8c 100644 --- a/frontend/packages/console-app/package.json +++ b/frontend/packages/console-app/package.json @@ -5,6 +5,8 @@ "private": true, "main": "src/index.ts", "dependencies": { + "@console/internal": "0.0.0-fixed", + "@console/plugin-sdk": "0.0.0-fixed", "@console/shared": "0.0.0-fixed" } } diff --git a/frontend/packages/console-app/src/index.ts b/frontend/packages/console-app/src/index.ts index f4be21b2b22..7e6d2c59bc3 100644 --- a/frontend/packages/console-app/src/index.ts +++ b/frontend/packages/console-app/src/index.ts @@ -1 +1 @@ -import '../../../public/components/app'; +import '@console/internal/components/app'; diff --git a/frontend/packages/console-demo-plugin/package.json b/frontend/packages/console-demo-plugin/package.json new file mode 100644 index 00000000000..98603f3ad9c --- /dev/null +++ b/frontend/packages/console-demo-plugin/package.json @@ -0,0 +1,13 @@ +{ + "name": "@console/demo-plugin", + "version": "0.0.0-fixed", + "description": "Demo plugin for Console web application", + "private": true, + "dependencies": { + "@console/plugin-sdk": "0.0.0-fixed", + "@console/shared": "0.0.0-fixed" + }, + "consolePlugin": { + "entry": "src/plugin.ts" + } +} diff --git a/frontend/packages/console-demo-plugin/src/plugin.ts b/frontend/packages/console-demo-plugin/src/plugin.ts new file mode 100644 index 00000000000..af64ab9eddb --- /dev/null +++ b/frontend/packages/console-demo-plugin/src/plugin.ts @@ -0,0 +1,55 @@ +import { + Plugin, + HrefNavItem, + ResourceNSNavItem, + ResourceListPage, + ResourceDetailPage, +} from '@console/plugin-sdk'; + +// TODO(vojtech): internal code needed by plugins should be moved to console-shared package +import { PodModel } from '@console/internal/models'; + +type ConsumedExtensions = + | HrefNavItem + | ResourceNSNavItem + | ResourceListPage + | ResourceDetailPage; + +const plugin: Plugin = [ + { + type: 'NavItem/Href', + properties: { + section: 'Home', + componentProps: { + name: 'Test Href Link', + href: '/test', + }, + }, + }, + { + type: 'NavItem/ResourceNS', + properties: { + section: 'Workloads', + componentProps: { + name: 'Test ResourceNS Link', + resource: 'pods', + }, + }, + }, + { + type: 'ResourcePage/List', + properties: { + model: PodModel, + loader: () => import('@console/internal/components/pod' /* webpackChunkName: "pod" */).then(m => m.PodsPage), + }, + }, + { + type: 'ResourcePage/Detail', + properties: { + model: PodModel, + loader: () => import('@console/internal/components/pod' /* webpackChunkName: "pod" */).then(m => m.PodsDetailsPage), + }, + }, +]; + +export default plugin; diff --git a/frontend/packages/console-plugin-sdk/package.json b/frontend/packages/console-plugin-sdk/package.json new file mode 100644 index 00000000000..0e92ab006e7 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/package.json @@ -0,0 +1,13 @@ +{ + "name": "@console/plugin-sdk", + "version": "0.0.0-fixed", + "description": "Console static plugin SDK", + "private": true, + "main": "src/index.ts", + "scripts": { + "test": "yarn --cwd ../.. run test packages/console-plugin-sdk" + }, + "dependencies": { + "@console/shared": "0.0.0-fixed" + } +} diff --git a/frontend/packages/console-plugin-sdk/src/codegen/index.ts b/frontend/packages/console-plugin-sdk/src/codegen/index.ts new file mode 100644 index 00000000000..c16615c0d65 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/codegen/index.ts @@ -0,0 +1,61 @@ +/* eslint-env node */ + +import * as path from 'path'; +import * as readPkg from 'read-pkg'; + +type Package = readPkg.NormalizedPackageJson; + +interface PluginPackage extends Package { + consolePlugin: { + entry: string; + } +} + +function isValidPluginPackage(pkg: Package): pkg is PluginPackage { + return (pkg as PluginPackage).consolePlugin && typeof (pkg as PluginPackage).consolePlugin.entry === 'string'; +} + +function readPackages(packageFiles: string[]) { + const pkgList: Package[] = packageFiles.map(file => readPkg.sync({ cwd: path.dirname(file), normalize: true })); + + return { + appPackage: pkgList.find(pkg => pkg.name === '@console/app'), + pluginPackages: pkgList.filter(isValidPluginPackage), + }; +} + +/** + * Generate the "active plugins" module source. + * + * @param packageFiles Paths to `package.json` files (all the monorepo packages). + */ +export function getActivePluginsModule(packageFiles: string[]): string { + const { appPackage, pluginPackages } = readPackages(packageFiles); + let output = ` + const activePlugins = []; + `; + + if (appPackage) { + for (const depName of Object.keys(appPackage.dependencies)) { + const depVersion = appPackage.dependencies[depName]; + const foundPluginPackage = pluginPackages.find(pkg => pkg.name === depName && pkg.version === depVersion); + + if (foundPluginPackage) { + const importName = `plugin_${pluginPackages.indexOf(foundPluginPackage)}`; + const importPath = `${foundPluginPackage.name}/${foundPluginPackage.consolePlugin.entry}`; + output = ` + ${output} + import ${importName} from '${importPath}'; + activePlugins.push(${importName}); + `; + } + } + } + + output = ` + ${output} + export default activePlugins; + `; + + return output.replace(/^\s+/gm, ''); +} diff --git a/frontend/packages/console-plugin-sdk/src/index.ts b/frontend/packages/console-plugin-sdk/src/index.ts new file mode 100644 index 00000000000..9b0a0181d87 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/index.ts @@ -0,0 +1,2 @@ +export * from './typings'; +export * from './registry'; diff --git a/frontend/packages/console-plugin-sdk/src/registry.ts b/frontend/packages/console-plugin-sdk/src/registry.ts new file mode 100644 index 00000000000..1e312044ba8 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/registry.ts @@ -0,0 +1,23 @@ +import * as _ from 'lodash-es'; +import { Extension, PluginList, isNavItem, isResourcePage } from './typings'; + +/** + * Registry used to query for Console extensions. + */ +export class ExtensionRegistry { + + private readonly extensions: Extension[]; + + public constructor(plugins: PluginList) { + this.extensions = _.flatMap(plugins); + } + + public getNavItems(section: string) { + return this.extensions.filter(isNavItem).filter(e => e.properties.section === section); + } + + public getResourcePages() { + return this.extensions.filter(isResourcePage); + } + +} diff --git a/frontend/packages/console-plugin-sdk/src/typings/index.ts b/frontend/packages/console-plugin-sdk/src/typings/index.ts new file mode 100644 index 00000000000..87dc142a710 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/typings/index.ts @@ -0,0 +1,66 @@ +/** + * An extension of the Console web application. + * + * Each extension is a realization (instance) of an extension `type` using the + * parameters provided via the `properties` object. + * + * Core extension types should follow `Category` or `Category/Specialization` + * format, e.g. `NavItem/Href`. + * + * @todo(vojtech) write ESLint rule to guard against extension type duplicity + */ +export interface Extension

{ + type: string; + properties: P; +} + +/** + * A plugin is simply a list of extensions. + * + * Plugin metadata is stored in the `package.json` file of the corresponding + * monorepo package. The `consolePlugin.entry` path should point to a module + * that exports the plugin object. + * + * ```json + * { + * "name": "@console/demo-plugin", + * "version": "0.0.0-fixed", + * // scripts, dependencies, etc. + * "consolePlugin": { + * "entry": "src/plugin.ts" + * } + * } + * ``` + * + * For better type checking and code completion, use a type parameter that + * represents the union of all the extension types consumed by the plugin: + * + * ```ts + * // packages/console-demo-plugin/src/plugin.ts + * import { Plugin } from '@console/plugin-sdk'; + * + * const plugin: Plugin = [ + * { + * type: 'Foo', + * properties: {} // Foo extension specific properties + * }, + * { + * type: 'Bar', + * properties: {} // Bar extension specific properties + * } + * ]; + * + * export default plugin; + * ``` + */ +export type Plugin> = E[]; + +/** + * A list of arbitrary plugins. + */ +export type PluginList = Plugin>[]; + +// TODO(vojtech): internal code needed by plugin SDK should be moved to console-shared package + +export * from './nav'; +export * from './pages'; diff --git a/frontend/packages/console-plugin-sdk/src/typings/nav.ts b/frontend/packages/console-plugin-sdk/src/typings/nav.ts new file mode 100644 index 00000000000..ef794333533 --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/typings/nav.ts @@ -0,0 +1,50 @@ +import { Extension } from '.'; +import { K8sKind } from '@console/internal/module/k8s'; + +export interface NavItemProperties { + // TODO(vojtech): link to existing nav sections by value + section: 'Home' | 'Workloads'; + componentProps: { + name: string; + required?: string; + disallowed?: string; + startsWith?: string[]; + } +} + +export interface HrefProperties extends NavItemProperties { + componentProps: NavItemProperties['componentProps'] & { + href: string; + activePath?: string; + } +} + +export interface ResourceNSProperties extends NavItemProperties { + componentProps: NavItemProperties['componentProps'] & { + resource: string; + model?: K8sKind; + } +} + +export interface HrefNavItem extends Extension { + type: 'NavItem/Href'; +} + +export interface ResourceNSNavItem extends Extension { + type: 'NavItem/ResourceNS'; +} + +// TODO(vojtech): add ResourceClusterNavItem +export type NavItem = HrefNavItem | ResourceNSNavItem; + +export function isHrefNavItem(e: Extension): e is HrefNavItem { + return e.type === 'NavItem/Href'; +} + +export function isResourceNSNavItem(e: Extension): e is ResourceNSNavItem { + return e.type === 'NavItem/ResourceNS'; +} + +export function isNavItem(e: Extension): e is NavItem { + return isHrefNavItem(e) || isResourceNSNavItem(e); +} diff --git a/frontend/packages/console-plugin-sdk/src/typings/pages.ts b/frontend/packages/console-plugin-sdk/src/typings/pages.ts new file mode 100644 index 00000000000..05f5945aa3e --- /dev/null +++ b/frontend/packages/console-plugin-sdk/src/typings/pages.ts @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { Extension } from '.'; +import { K8sKind } from '@console/internal/module/k8s'; + +export interface ResourcePageProperties { + model: K8sKind; + loader: () => Promise>; +} + +export interface ResourceListPage extends Extension { + type: 'ResourcePage/List'; +} + +export interface ResourceDetailPage extends Extension { + type: 'ResourcePage/Detail'; +} + +export type ResourcePage = ResourceListPage | ResourceDetailPage; + +export function isResourceListPage(e: Extension): e is ResourceListPage { + return e.type === 'ResourcePage/List'; +} + +export function isResourceDetailPage(e: Extension): e is ResourceDetailPage { + return e.type === 'ResourcePage/Detail'; +} + +export function isResourcePage(e: Extension): e is ResourcePage { + return isResourceListPage(e) || isResourceDetailPage(e); +} diff --git a/frontend/public/components/nav.jsx b/frontend/public/components/nav.jsx index 1985e173734..230b7222cb1 100644 --- a/frontend/public/components/nav.jsx +++ b/frontend/public/components/nav.jsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import * as _ from 'lodash-es'; import * as PropTypes from 'prop-types'; +import memoize from 'memoize-one'; import { FLAGS, featureReducerName, flagPending } from '../features'; import { monitoringReducerName, MonitoringRoutes } from '../monitoring'; @@ -26,6 +27,7 @@ import { import { referenceForModel } from '../module/k8s'; import { stripBasePath } from './utils'; +import * as plugins from '../plugins'; export const matchesPath = (resourcePath, prefix) => resourcePath === prefix || _.startsWith(resourcePath, `${prefix}/`); export const matchesModel = (resourcePath, model) => model && matchesPath(resourcePath, referenceForModel(model)); @@ -227,6 +229,10 @@ const NavSection = connect(navSectionStateToProps)( this.setState({isOpen: expandState}); } + getPluginNavItems = memoize( + (section) => plugins.registry.getNavItems(section) + ); + render() { if (!this.props.canRender) { return null; @@ -236,7 +242,7 @@ const NavSection = connect(navSectionStateToProps)( const { isOpen, activeChild } = this.state; const isActive = !!activeChild; - const Children = React.Children.map(children, c => { + const mapChild = (c) => { if (!c) { return null; } @@ -252,11 +258,23 @@ const NavSection = connect(navSectionStateToProps)( return null; } return React.cloneElement(c, {key: name, isActive: name === this.state.activeChild, activeNamespace, flags}); - }); + }; + + const Children = React.Children.map(children, mapChild); + + const PluginChildren = _.compact(this.getPluginNavItems(title).map((item, index) => { + if (plugins.isHrefNavItem(item)) { + return ; + } + if (plugins.isResourceNSNavItem(item)) { + return ; + } + })).map(mapChild); - return Children ? ( + return (Children || PluginChildren.length > 0) ? ( {Children} + {PluginChildren} ) : null; } diff --git a/frontend/public/components/resource-pages.ts b/frontend/public/components/resource-pages.ts index bba9c5701de..a15d250c1d2 100644 --- a/frontend/public/components/resource-pages.ts +++ b/frontend/public/components/resource-pages.ts @@ -63,6 +63,8 @@ import { TemplateInstanceModel, } from '../models'; +import * as plugins from '../plugins'; + export const resourceDetailPages = ImmutableMap Promise>>() .set(referenceForModel(ClusterServiceClassModel), () => import('./cluster-service-class' /* webpackChunkName: "cluster-service-class" */).then(m => m.ClusterServiceClassDetailsPage)) .set(referenceForModel(ClusterServiceBrokerModel), () => import('./cluster-service-broker' /* webpackChunkName: "cluster-service-broker" */).then(m => m.ClusterServiceBrokerDetailsPage)) @@ -117,7 +119,12 @@ export const resourceDetailPages = ImmutableMap .set(referenceForModel(InstallPlanModel), () => import('./operator-lifecycle-manager/install-plan' /* webpackChunkName: "install-plan" */).then(m => m.InstallPlanDetailsPage)) .set(referenceForModel(ClusterOperatorModel), () => import('./cluster-settings/cluster-operator' /* webpackChunkName: "cluster-operator" */).then(m => m.ClusterOperatorDetailsPage)) .set(referenceForModel(ClusterVersionModel), () => import('./cluster-settings/cluster-version' /* webpackChunkName: "cluster-version" */).then(m => m.ClusterVersionDetailsPage)) - .set(referenceForModel(OAuthModel), () => import('./cluster-settings/oauth' /* webpackChunkName: "oauth" */).then(m => m.OAuthDetailsPage)); + .set(referenceForModel(OAuthModel), () => import('./cluster-settings/oauth' /* webpackChunkName: "oauth" */).then(m => m.OAuthDetailsPage)) + .withMutations(map => { + plugins.registry.getResourcePages().filter(plugins.isResourceDetailPage).forEach(page => { + map.set(referenceForModel(page.properties.model), page.properties.loader); + }); + }); export const resourceListPages = ImmutableMap Promise>>() .set(referenceForModel(ClusterServiceClassModel), () => import('./cluster-service-class' /* webpackChunkName: "cluster-service-class" */).then(m => m.ClusterServiceClassPage)) @@ -171,4 +178,9 @@ export const resourceListPages = ImmutableMap P .set(referenceForModel(PackageManifestModel), () => import('./operator-lifecycle-manager/package-manifest' /* webpackChunkName: "package-manifest" */).then(m => m.PackageManifestsPage)) .set(referenceForModel(SubscriptionModel), () => import('./operator-lifecycle-manager/subscription' /* webpackChunkName: "subscription" */).then(m => m.SubscriptionsPage)) .set(referenceForModel(InstallPlanModel), () => import('./operator-lifecycle-manager/install-plan' /* webpackChunkName: "install-plan" */).then(m => m.InstallPlansPage)) - .set(referenceForModel(ClusterOperatorModel), () => import('./cluster-settings/cluster-operator' /* webpackChunkName: "cluster-operator" */).then(m => m.ClusterOperatorPage)); + .set(referenceForModel(ClusterOperatorModel), () => import('./cluster-settings/cluster-operator' /* webpackChunkName: "cluster-operator" */).then(m => m.ClusterOperatorPage)) + .withMutations(map => { + plugins.registry.getResourcePages().filter(plugins.isResourceListPage).forEach(page => { + map.set(referenceForModel(page.properties.model), page.properties.loader); + }); + }); diff --git a/frontend/public/package.json b/frontend/public/package.json new file mode 100644 index 00000000000..ba68d5cafbc --- /dev/null +++ b/frontend/public/package.json @@ -0,0 +1,6 @@ +{ + "name": "@console/internal", + "version": "0.0.0-fixed", + "description": "Console code to be moved into appropriate packages", + "private": true +} diff --git a/frontend/public/plugins.ts b/frontend/public/plugins.ts new file mode 100644 index 00000000000..a2e71bb689e --- /dev/null +++ b/frontend/public/plugins.ts @@ -0,0 +1,14 @@ +/* eslint-disable no-undef */ + +import { PluginList, ExtensionRegistry } from '@console/plugin-sdk'; +export * from '@console/plugin-sdk'; + +// the '@console/active-plugins' module is generated during webpack build +const activePlugins = (process.env.NODE_ENV !== 'test') + ? require('@console/active-plugins').default as PluginList + : []; + +export const registry = new ExtensionRegistry(activePlugins); + +// eslint-disable-next-line no-console +console.info(`${activePlugins.length} plugins active`); diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts index ae6efa3fc72..3821565e257 100644 --- a/frontend/webpack.config.ts +++ b/frontend/webpack.config.ts @@ -3,20 +3,21 @@ import * as webpack from 'webpack'; import * as path from 'path'; +import * as glob from 'glob'; import * as HtmlWebpackPlugin from 'html-webpack-plugin'; import * as ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import * as MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import * as VirtualModulesPlugin from 'webpack-virtual-modules'; +import { getActivePluginsModule } from '@console/plugin-sdk/src/codegen'; const NODE_ENV = process.env.NODE_ENV; /* Helpers */ const extractCSS = new MiniCssExtractPlugin({filename: 'app-bundle.css'}); +const packageFiles = glob.sync('packages/*/package.json', { absolute: true }); const config: webpack.Configuration = { - entry: [ - './polyfills.js', - '@console/app', - ], + entry: ['./polyfills.js', '@console/app'], output: { path: path.resolve(__dirname, 'public/dist'), publicPath: 'static/', @@ -116,6 +117,10 @@ const config: webpack.Configuration = { chunksSortMode: 'none', }), extractCSS, + // Generate '@console/active-plugins' module + new VirtualModulesPlugin({ + 'node_modules/@console/active-plugins.js': getActivePluginsModule(packageFiles), + }), ], devtool: 'cheap-module-source-map', stats: 'minimal', diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0f7a6f5bea4..0de790b3c57 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -196,6 +196,11 @@ prop-types "^15.6.2" tippy.js "^3.2.0" +"@types/anymatch@*": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" + integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== + "@types/c3@^0.6.0": version "0.6.2" resolved "https://registry.yarnpkg.com/@types/c3/-/c3-0.6.2.tgz#b4d5fc5dbe05b900a531544786f8cfedf5b8e416" @@ -389,10 +394,24 @@ "@types/cheerio" "*" "@types/react" "*" +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + "@types/geojson@*": version "7946.0.4" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.4.tgz#4e049756383c3f055dd8f3d24e63fb543e98eb07" +"@types/glob@7.x": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + "@types/history@*": version "4.6.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0" @@ -427,6 +446,11 @@ version "4.14.106" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd" +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + "@types/node@*": version "9.6.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" @@ -435,6 +459,11 @@ version "6.0.102" resolved "http://registry.npmjs.org/@types/node/-/node-6.0.102.tgz#a6cf3b9843286b63eb362a8522bc382d96fe68d1" +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + "@types/prop-types@*", "@types/prop-types@15.5.6": version "15.5.6" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" @@ -520,10 +549,12 @@ dependencies: source-map "^0.6.1" -"@types/webpack@^4.1.1": - version "4.1.2" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.1.2.tgz#96b45333201dd1526b85fbe18f1972f828f7e996" +"@types/webpack@4.x": + version "4.4.27" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.27.tgz#8bb9429185977a6b3b9e6e6132f561066aa7e7c2" + integrity sha512-xSll/4UXnLQ0xjdAoTRIFxA6NPC2abJ8nHxRH6SqTymHrfGCc8er7qH0npwCP8q3VFoJh2Hjz1wH8oTjwx9/jQ== dependencies: + "@types/anymatch" "*" "@types/node" "*" "@types/tapable" "*" "@types/uglify-js" "*" @@ -3717,6 +3748,13 @@ debug@4.1.0: dependencies: ms "^2.1.1" +debug@^3.0.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -5736,6 +5774,17 @@ glob2base@^0.0.12: dependencies: find-index "^0.1.1" +glob@7.x, glob@^7.0.3, glob@^7.0.5, glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" @@ -5757,17 +5806,6 @@ glob@^7.0.0, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1, glob@~7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.5, glob@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" @@ -8256,6 +8294,11 @@ mem@^1.1.0: dependencies: mimic-fn "^1.0.0" +memoize-one@5.x: + version "5.0.4" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc" + integrity sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA== + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -8872,6 +8915,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -9440,6 +9493,11 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -10748,6 +10806,16 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" +read-pkg@5.x: + version "5.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.1.0.tgz#62b924384c4525a1a7a96e2d456b80df142b4390" + integrity sha512-NjNkqf8hlMuSxh+p8h8x8sCIfEv/MoRs/nYDmSRSAk879F9C94ADq+kHUJ4LTy5Nn0PYSmMYsy1mD4+lcXiBKg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^4.0.0" + type-fest "^0.4.1" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -11381,6 +11449,13 @@ resolve@^1.0.0, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.5.0, dependencies: path-parse "^1.0.5" +resolve@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" + integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== + dependencies: + path-parse "^1.0.6" + resolve@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" @@ -13009,6 +13084,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== + type-is@~1.6.16: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" @@ -13627,6 +13707,13 @@ webpack-sources@^1.3.0: source-list-map "^2.0.0" source-map "~0.6.1" +webpack-virtual-modules@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.1.10.tgz#2039529cbf1007e19f6e897c8d35721cc2c41f68" + integrity sha1-IDlSnL8QB+Gfbol8jTVyHMLEH2g= + dependencies: + debug "^3.0.0" + webpack@4.29.6: version "4.29.6" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.6.tgz#66bf0ec8beee4d469f8b598d3988ff9d8d90e955"