From 3651ebd78b83284354bed898988e81562c39557d Mon Sep 17 00:00:00 2001 From: Jyrno Ader Date: Sat, 6 Mar 2021 15:15:41 +0200 Subject: [PATCH] Add i18n-named routes package It is a small wrapper around tg-named-routes which allows one to have language specific routes. Also: - Updated rollup and plugins to resolve issue with React named imports - see https://github.com/rollup/plugins/issues/423#issuecomment-681763091 - Updated react used in development - Updated react-router/react-router-dom --- examples/example-app/package.json | 12 +- examples/example-app/src/server.js | 145 ++++---- examples/example-app/src/views/index.js | 109 +++--- package.json | 30 +- packages/components/package.json | 2 +- packages/errors/package.json | 2 +- packages/i18n-named-routes/README.md | 84 +++++ packages/i18n-named-routes/jest.config.js | 12 + packages/i18n-named-routes/package.json | 47 +++ .../i18n-named-routes/src/I18nNamedLink.tsx | 12 + .../src/I18nNamedRedirect.tsx | 22 ++ .../i18n-named-routes/src/create-config.ts | 105 ++++++ .../src/default-language-redirect.tsx | 24 ++ packages/i18n-named-routes/src/hooks.ts | 40 ++ packages/i18n-named-routes/src/index.ts | 27 ++ packages/i18n-named-routes/src/url-cache.ts | 156 ++++++++ packages/i18n-named-routes/test/i18n-test.ts | 25 ++ packages/i18n-named-routes/test/routes.tsx | 135 +++++++ .../test/translated-routes.test.tsx | 301 +++++++++++++++ .../i18n-named-routes/tsconfig.eslint.json | 9 + packages/i18n-named-routes/tsconfig.json | 10 + packages/named-routes/README.md | 2 +- packages/named-routes/package.json | 4 +- packages/named-routes/src/NamedLink.tsx | 11 +- packages/named-routes/src/NamedRedirect.tsx | 13 +- packages/named-routes/src/routes.ts | 32 +- packages/pending-data/package.json | 4 +- packages/permissions/package.json | 2 +- packages/view-manager/package.json | 4 +- packages/view/package.json | 2 +- rollup.config.js | 71 ++-- yarn.lock | 348 ++++++++++-------- 32 files changed, 1474 insertions(+), 328 deletions(-) create mode 100644 packages/i18n-named-routes/README.md create mode 100644 packages/i18n-named-routes/jest.config.js create mode 100644 packages/i18n-named-routes/package.json create mode 100644 packages/i18n-named-routes/src/I18nNamedLink.tsx create mode 100644 packages/i18n-named-routes/src/I18nNamedRedirect.tsx create mode 100644 packages/i18n-named-routes/src/create-config.ts create mode 100644 packages/i18n-named-routes/src/default-language-redirect.tsx create mode 100644 packages/i18n-named-routes/src/hooks.ts create mode 100644 packages/i18n-named-routes/src/index.ts create mode 100644 packages/i18n-named-routes/src/url-cache.ts create mode 100644 packages/i18n-named-routes/test/i18n-test.ts create mode 100644 packages/i18n-named-routes/test/routes.tsx create mode 100644 packages/i18n-named-routes/test/translated-routes.test.tsx create mode 100644 packages/i18n-named-routes/tsconfig.eslint.json create mode 100644 packages/i18n-named-routes/tsconfig.json diff --git a/examples/example-app/package.json b/examples/example-app/package.json index 169f45ab..6d62a225 100644 --- a/examples/example-app/package.json +++ b/examples/example-app/package.json @@ -29,15 +29,15 @@ "razzle-plugin-long-term-caching": "1.0.0-beta.0", "razzle-plugin-modify-eslint-loader-config": "1.0.0-beta.1", "razzle-plugin-scss": "^3.3.0", - "react": "^16.12.0", - "react-dom": "^16.12.0", + "react": "^16.14.0", + "react-dom": "^16.14.0", "react-helmet": "^5.2.0", "react-redux": "^7.1.0", - "react-router": "^5.1.0", - "react-router-config": "^5.1.0", - "react-router-dom": "^5.1.0", + "react-router": "^5.2.0", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.2.0", "serialize-javascript": "^3.1.0", - "tg-named-routes": "1.0.0-beta.0", + "tg-named-routes": "1.0.0-beta.1", "tg-saga-manager": "1.0.0-beta.0" }, "jest": { diff --git a/examples/example-app/src/server.js b/examples/example-app/src/server.js index b3eca1a3..a9b83584 100644 --- a/examples/example-app/src/server.js +++ b/examples/example-app/src/server.js @@ -1,4 +1,7 @@ -import { createLocationAction, ServerViewManagerWorker } from '@thorgate/spa-view-manager'; +import { + createLocationAction, + ServerViewManagerWorker, +} from '@thorgate/spa-view-manager'; import React from 'react'; import { Provider } from 'react-redux'; import { StaticRouter } from 'react-router-dom'; @@ -11,72 +14,76 @@ import { RenderChildren } from 'tg-named-routes'; import { configureStore } from './store'; import routes from './views'; - const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); const mainEntryPoints = ['runtime', 'vendors', 'client']; -const sortedAssets = Object.entries(assets).sort( - (([firstKey], [secondKey]) => { - const [firstPart] = firstKey.split('.'); - const [secondPart] = secondKey.split('.'); - return mainEntryPoints.indexOf(firstPart) - mainEntryPoints.indexOf(secondPart); - }) -); - -const scripts = sortedAssets.filter( - ([key, asset]) => !!asset.js -).reduce((scripts, [key, asset]) => ( - `${scripts}` -), ''); - -const styles = sortedAssets.filter( - ([key, asset]) => !!asset.css -).reduce((styles, [key, asset]) => ( - `${styles}` -), ''); - - -const asyncWrapper = (fn) => (req, res, next) => ( - Promise.resolve(fn(req, res, next)).catch(next) -); - - -const server = express(); -server - .disable('x-powered-by') - .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) - .get('/*', asyncWrapper(async (req, res) => { - console.log(`GET: ${req.originalUrl}`); - - const { store } = configureStore({}, { +const sortedAssets = Object.entries(assets).sort(([firstKey], [secondKey]) => { + const [firstPart] = firstKey.split('.'); + const [secondPart] = secondKey.split('.'); + return ( + mainEntryPoints.indexOf(firstPart) - mainEntryPoints.indexOf(secondPart) + ); +}); + +const scripts = sortedAssets + .filter(([key, asset]) => !!asset.js) + .reduce( + (scripts, [key, asset]) => + `${scripts}`, + '' + ); + +const styles = sortedAssets + .filter(([key, asset]) => !!asset.css) + .reduce( + (styles, [key, asset]) => + `${styles}`, + '' + ); + +const asyncWrapper = fn => (req, res, next) => + Promise.resolve(fn(req, res, next)).catch(next); + +const routeHandler = asyncWrapper(async (req, res) => { + console.log(`GET: ${req.originalUrl}`); + + const { store } = configureStore( + {}, + { location: req.originalUrl, - }); - - const task = store.runSaga(ServerViewManagerWorker, routes, createLocationAction(store.getState().router), { allowLogger: true }); - - store.close(); - - await task.toPromise(); - - const context = {}; - const markup = renderToString( - - - - - , - ); - - if (context.url) { - console.log(`Redirecting to: ${context.url}`); - res.redirect(context.url); - } else { - const header = Helmet.renderStatic(); - const state = serialize(store.getState()); - - res.status(context.statusCode || 200).send( - ` + } + ); + + const task = store.runSaga( + ServerViewManagerWorker, + routes, + createLocationAction(store.getState().router), + { allowLogger: true } + ); + + store.close(); + + await task.toPromise(); + + const context = {}; + const markup = renderToString( + + + + + + ); + + if (context.url) { + console.log(`Redirecting to: ${context.url}`); + res.redirect(context.url); + } else { + const header = Helmet.renderStatic(); + const state = serialize(store.getState()); + + res.status(context.statusCode || 200).send( + ` ${header.title.toString()} @@ -93,9 +100,15 @@ server window.__initial_state__ = ${state}; -`, - ); - } - })); +` + ); + } +}); + +const server = express(); +server + .disable('x-powered-by') + .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) + .get('*', routeHandler); export default server; diff --git a/examples/example-app/src/views/index.js b/examples/example-app/src/views/index.js index 04b2d25e..1f88fa6c 100644 --- a/examples/example-app/src/views/index.js +++ b/examples/example-app/src/views/index.js @@ -1,6 +1,11 @@ -import { buildUrlCache } from 'tg-named-routes'; +import { buildUrlCache, getUrlMapCache } from 'tg-named-routes'; -import { simulateApiError, simulateLoading, simulateLogin, simulateLogout } from '../sagas/auth'; +import { + simulateApiError, + simulateLoading, + simulateLogin, + simulateLogout, +} from '../sagas/auth'; import simulateWatcher from '../sagas/simulateWatcher'; import App from './App'; @@ -12,56 +17,64 @@ import Restricted from './Restricted'; import RestrictedRedirect from './RestrictedRedirect'; import SimulatedError from './SimulateError'; - const routes = [ { component: App, watcher: simulateWatcher, - routes: [{ - path: '/', - exact: true, - component: Home, - name: 'home', - }, { - path: '/long', - exact: true, - component: HomeLoading, - initial: simulateLoading, - name: 'home-long', - }, { - path: '/home', - exact: true, - component: Restricted, - name: 'restricted', - initial: simulateApiError, - }, { - path: '/home-redirect', - exact: true, - component: RestrictedRedirect, - name: 'restricted-redirect', - initial: simulateApiError, - }, { - path: '/login', - exact: true, - component: RedirectHome, - name: 'login', - initial: simulateLogin, - }, { - path: '/logout', - exact: true, - component: RedirectHome, - name: 'logout', - initial: simulateLogout, - }, { - path: '/error', - exact: true, - component: SimulatedError, - name: 'error-test', - }, { - path: '*', - component: PageNotFound, - name: '404', - }], + routes: [ + { + path: '/', + exact: true, + component: Home, + name: 'home', + }, + { + path: '/long', + exact: true, + component: HomeLoading, + initial: simulateLoading, + name: 'home-long', + }, + { + path: '/home', + exact: true, + component: Restricted, + name: 'restricted', + initial: simulateApiError, + }, + { + path: '/home-redirect', + exact: true, + component: RestrictedRedirect, + name: 'restricted-redirect', + initial: simulateApiError, + }, + { + path: '/login', + exact: true, + component: RedirectHome, + name: 'login', + initial: simulateLogin, + }, + { + path: '/logout', + exact: true, + component: RedirectHome, + name: 'logout', + initial: simulateLogout, + }, + { + path: '/error', + exact: true, + component: SimulatedError, + name: 'error-test', + }, + { + path: '*', + component: PageNotFound, + name: '404', + }, + ], }, ]; diff --git a/package.json b/package.json index 4d0eca81..c63aba41 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "packages/*" ], "devDependencies": { - "@rollup/plugin-commonjs": "^11.0.2", - "@rollup/plugin-json": "^4.0.2", - "@rollup/plugin-node-resolve": "^7.1.1", - "@rollup/plugin-replace": "^2.3.1", + "@rollup/plugin-commonjs": "^17.1.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^11.2.0", + "@rollup/plugin-replace": "^2.4.1", "@testing-library/jest-dom": "^5.1.1", "@testing-library/react": "^9.4.0", "@testing-library/react-hooks": "^3.2.1", @@ -24,15 +24,15 @@ "@types/react": "^16.9.19", "@types/react-dom": "^16.9.5", "@types/react-redux": "^7.1.7", - "@types/react-router": "^5.1.4", - "@types/react-router-config": "^5.0.1", - "@types/react-router-dom": "^5.1.3", + "@types/react-router": "^5.1.12", + "@types/react-router-config": "^5.0.2", + "@types/react-router-dom": "^5.1.7", "@types/uuid": "^3.4.7", "@types/warning": "^3.0.0", "@typescript-eslint/eslint-plugin": "^2.19.0", "@typescript-eslint/parser": "^2.19.0", "check-engines": "^1.5.0", - "connected-react-router": "^6.7.0", + "connected-react-router": "^6.9.1", "coveralls": "*", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", @@ -41,6 +41,7 @@ "eslint-plugin-react-hooks": "^2.3.0", "formik": "^2.1.4", "history": "^4.10.1", + "i18next": "^19.9.1", "jest": "^25.1.0", "jest-cli": "^25.1.0", "lerna": "^3.20.2", @@ -53,18 +54,19 @@ "razzle": "^3.3.0", "razzle-plugin-eslint": "^3.3.0", "razzle-plugin-scss": "^3.3.0", - "react": "^16.12.0", - "react-dom": "^16.12.0", + "react": "^16.14.0", + "react-dom": "^16.14.0", + "react-i18next": "^11.8.8", "react-redux": "^7.1.3", - "react-router": "^5.1.2", + "react-router": "^5.2.0", "react-router-config": "^5.1.1", - "react-router-dom": "^5.1.2", + "react-router-dom": "^5.2.0", "react-test-renderer": "^16.12.0", "redux": "^4.0.5", "redux-saga": "^1.1.3", "rimraf": "^3.0.2", - "rollup": "^1.31.0", - "rollup-plugin-typescript2": "0.25.3", + "rollup": "^2.40.0", + "rollup-plugin-typescript2": "^0.30.0", "tg-resources": "^3.1.3", "ts-jest": "^25.2.0", "tslib": "^1.10.0", diff --git a/packages/components/package.json b/packages/components/package.json index cfe94bdb..e0b6eb88 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -35,7 +35,7 @@ "react-router": "^4.3.0 || ^5.0.0" }, "dependencies": { - "tg-named-routes": "1.0.0-beta.0", + "tg-named-routes": "1.0.0-beta.1", "warning": "^4.0.2" }, "scripts": { diff --git a/packages/errors/package.json b/packages/errors/package.json index f3bc8090..26e28983 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -37,7 +37,7 @@ "dependencies": { "@thorgate/spa-components": "1.0.0-beta.0", "@thorgate/spa-is": "1.0.0-beta.0", - "tg-named-routes": "1.0.0-beta.0", + "tg-named-routes": "1.0.0-beta.1", "typesafe-actions": "^4.4.2", "warning": "^4.0.2" }, diff --git a/packages/i18n-named-routes/README.md b/packages/i18n-named-routes/README.md new file mode 100644 index 00000000..fed78476 --- /dev/null +++ b/packages/i18n-named-routes/README.md @@ -0,0 +1,84 @@ +[![NPM version][npm-image]][npm-url] +[![Build Status][travis-image]][travis-url] +# `tg-i18n-named-routes` + +A drop-in extension of [tg-named-routes](https://www.npmjs.com/package/tg-named-routes) to provide language specific +named routes in collaboration with [react-i18next](https://react.i18next.com/). + +## Usage + + +``` +yarn add tg-i18n-named-routes +``` + +**routes.js:** + +```diff +- import { buildUrlCache } from 'tg-named-routes'; ++ import { buildUrlCache, createLanguageRoutes, storeLanguages, getDefaultLanguageRedirect } from 'tg-i18n-named-routes'; + +- const routes = [ ++ const routes = createLanguageRoutes( ++ 'et', // default language ++ ['et', 'en'], // all languages + { + component: App, + routes: [ + { +- path: '/', ++ path: { ++ et: '/et', ++ en: '/en', ++ }, + name: 'landing', + render: props => null, + }, ++ getDefaultLanguageRedirect('landing', null) + ], + } + ]; + buildUrlCache(routes); + + export default routes; +``` + +Next make sure to use i18n specific versions of NamedRedirect and NamedLink. The easiest way to do that is by changing all your imports to use `tg-i18n-named-routes` instead of `tg-named-routes` as this package re-exports everything from original named routes package: + +```diff +- import { NamedLink } from 'tg-named-routes'; ++ import { NamedLink } from 'tg-i18n-named-routes'; +``` + +Finally replace any usages of resolvePath/stringifyLocation/resolvePattern with hooks based versions: + +- resolvePath -> useI18nResolvePath +- stringifyLocation -> useI18nStringifyLocation +- resolvePattern -> useI18nResolvePattern + +for example: + +```diff + import { Link } from 'react-router-dom'; +- import { resolvePath } from 'tg-named-routes'; ++ import { useI18nResolvePath } from 'tg-i18n-named-routes'; + + const ExampleComponent = () => { ++ const resolvePath = useI18nResolvePath(); ++ + return ( + + ); + } +``` + +## License + +MIT © [Thorgate](http://github.com/thorgate) + + +[npm-url]: https://npmjs.org/package/tg-i18n-named-routes +[npm-image]: https://img.shields.io/npm/v/tg-i18n-named-routes.svg?style=flat-square + +[travis-url]: https://travis-ci.com/thorgate/tg-spa-utils +[travis-image]: https://travis-ci.com/thorgate/tg-spa-utils.svg?branch=master diff --git a/packages/i18n-named-routes/jest.config.js b/packages/i18n-named-routes/jest.config.js new file mode 100644 index 00000000..ad7dbf44 --- /dev/null +++ b/packages/i18n-named-routes/jest.config.js @@ -0,0 +1,12 @@ +const pkg = require('./package.json'); +const defaultConfig = require('../../jest.config.base'); + +module.exports = { + ...defaultConfig, + + name: pkg.name, + displayName: pkg.name, + rootDir: './', + + setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], +}; diff --git a/packages/i18n-named-routes/package.json b/packages/i18n-named-routes/package.json new file mode 100644 index 00000000..ea32f7fd --- /dev/null +++ b/packages/i18n-named-routes/package.json @@ -0,0 +1,47 @@ +{ + "name": "tg-i18n-named-routes", + "version": "1.0.0-beta.1", + "private": false, + "publishConfig": { + "access": "public" + }, + "description": "React Router named routes i18n extension", + "main": "lib/index.js", + "module": "lib/index.esm.js", + "typings": "lib/index.d.ts", + "license": "MIT", + "author": "Thorgate ", + "contributors": [ + "Jörgen Ader (https://github.com/metsavaht)", + "Jürno Ader (https://github.com/jyrno42)" + ], + "homepage": "https://github.com/thorgate/tg-spa-utils/tree/master/packages/i18n-named-routes#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/thorgate/tg-spa-utils.git" + }, + "bugs": { + "url": "https://github.com/thorgate/tg-spa-utils/issues" + }, + "keywords": [ + "react-router", + "named-routes", + "i18n", + "i18n-named-routes" + ], + "files": [ + "lib" + ], + "dependencies": { + "tg-named-routes": ">=1.0.0-beta.1" + }, + "peerDependencies": { + "react-i18next": "^10.0.0 || ^11.0.0" + }, + "scripts": { + "clean": "rimraf ./lib", + "lint": "eslint src -c ../../.eslintrc.js --ext ts --ext tsx", + "build": "rollup -c=../../rollup.config.js", + "prebuild": "yarn clean && yarn lint" + } +} diff --git a/packages/i18n-named-routes/src/I18nNamedLink.tsx b/packages/i18n-named-routes/src/I18nNamedLink.tsx new file mode 100644 index 00000000..92b2cf8f --- /dev/null +++ b/packages/i18n-named-routes/src/I18nNamedLink.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { NamedLink, NamedLinkProps } from 'tg-named-routes'; + +import { useI18nResolvePath } from './hooks'; + +export type I18nNamedLinkProps = Omit; + +export const I18nNamedLink = (props: I18nNamedLinkProps) => { + const resolvePathFn = useI18nResolvePath(); + + return ; +}; diff --git a/packages/i18n-named-routes/src/I18nNamedRedirect.tsx b/packages/i18n-named-routes/src/I18nNamedRedirect.tsx new file mode 100644 index 00000000..ed7848ec --- /dev/null +++ b/packages/i18n-named-routes/src/I18nNamedRedirect.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { NamedRedirect, NamedRedirectProps } from 'tg-named-routes'; + +import { useI18nResolvePath, useI18nResolvePattern } from './hooks'; + +export type I18nNamedRedirectProps = Omit< + NamedRedirectProps, + 'resolvePathFn' | 'resolvePattern' +>; + +export const I18nNamedRedirect = (props: I18nNamedRedirectProps) => { + const resolvePathFn = useI18nResolvePath(); + const resolvePatternFn = useI18nResolvePattern(); + + return ( + + ); +}; diff --git a/packages/i18n-named-routes/src/create-config.ts b/packages/i18n-named-routes/src/create-config.ts new file mode 100644 index 00000000..c2524318 --- /dev/null +++ b/packages/i18n-named-routes/src/create-config.ts @@ -0,0 +1,105 @@ +import { NamedRouteConfig } from 'tg-named-routes'; + +export interface TranslatedNamedRouteConfig + extends Omit { + path?: + | { + [language: string]: string; + } + // note: string should only be used for notfound or default language redirect routes + | '*' + | '/'; + routes?: TranslatedNamedRouteConfig[]; +} + +export const createLanguageRoutes = ( + defaultLanguage: string, + languages: string[], + routes: TranslatedNamedRouteConfig[] +): NamedRouteConfig[] => { + const res: NamedRouteConfig[] = []; + + routes.forEach(route => { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + const children = createLanguageRoute(defaultLanguage, languages, route); + + children.forEach(item => res.push(item)); + }); + + return res; +}; + +const createLanguageRoute = ( + defaultLanguage: string, + languages: string[], + route: TranslatedNamedRouteConfig +): NamedRouteConfig[] => { + const res: NamedRouteConfig[] = []; + + const getChildren = (lang: string | null) => + route.routes + ? createLanguageRoutes( + defaultLanguage, + lang === null ? languages : languages.filter(x => x === lang), + route.routes + ) + : undefined; + + const identifier = route.name || route; + + const getName = (language: string) => { + if (route.name) { + return `${language}:${route.name}`; + } + + return undefined; + }; + + // Route has path, generate all different variants for it + if (route.path && typeof route.path !== 'string') { + const { path } = route; + + languages.forEach(language => { + const thePath = path[language] || path[defaultLanguage]; + + if (!thePath) { + throw new Error(`Missing ${language} for route ${identifier}`); + } + + if (!thePath.startsWith(`/${language}`)) { + throw new Error( + `Route route ${identifier} path does not start with /${language}.` + ); + } + + res.push({ + ...route, + + path: thePath, + name: getName(language), + routes: getChildren(language), + }); + }); + } else if ( + route.name && + route.name !== 'defaultLanguageRedirect' && + route.path !== '*' + ) { + languages.forEach(language => { + res.push({ + ...route, + path: typeof route.path === 'string' ? route.path : undefined, + name: getName(language), + routes: getChildren(language), + }); + }); + } else { + res.push({ + ...route, + path: typeof route.path === 'string' ? route.path : undefined, + routes: getChildren(null), + }); + } + + return res; +}; diff --git a/packages/i18n-named-routes/src/default-language-redirect.tsx b/packages/i18n-named-routes/src/default-language-redirect.tsx new file mode 100644 index 00000000..447fa0fb --- /dev/null +++ b/packages/i18n-named-routes/src/default-language-redirect.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Redirect } from 'react-router'; +import { Kwargs } from 'tg-named-routes/lib/routes'; + +import { TranslatedNamedRouteConfig } from './create-config'; +import { useI18nResolvePath } from './hooks'; + +export const getDefaultLanguageRedirect = ( + routeName: string, + kwargs: Kwargs | null +): TranslatedNamedRouteConfig => { + const Component = () => { + const getPath = useI18nResolvePath(); + + return ; + }; + + return { + path: '/', + name: 'defaultLanguageRedirect', + component: Component, + exact: true, + }; +}; diff --git a/packages/i18n-named-routes/src/hooks.ts b/packages/i18n-named-routes/src/hooks.ts new file mode 100644 index 00000000..dcbbb2d0 --- /dev/null +++ b/packages/i18n-named-routes/src/hooks.ts @@ -0,0 +1,40 @@ +import { Location, LocationState } from 'history'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + resolvePath, + resolvePattern, + stringifyLocation, + Kwargs, + Query, +} from 'tg-named-routes'; + +const useI18nRoutes = (): { + resolvePath: typeof resolvePath; + resolvePattern: typeof resolvePattern; + stringifyLocation: typeof stringifyLocation; +} => { + const { + i18n: { language }, + } = useTranslation(); + + return useMemo( + () => ({ + resolvePath: ( + name: string, + kwargs?: Kwargs, + query?: Query, + state?: LocationState + ): Location => + resolvePath(`${language}:${name}`, kwargs, query, state), + resolvePattern: (name: string): string => + resolvePattern(`${language}:${name}`), + stringifyLocation, + }), + [language] + ); +}; + +export const useI18nResolvePath = () => useI18nRoutes().resolvePath; +export const useI18nResolvePattern = () => useI18nRoutes().resolvePattern; +export const useI18nStringifyLocation = () => useI18nRoutes().stringifyLocation; diff --git a/packages/i18n-named-routes/src/index.ts b/packages/i18n-named-routes/src/index.ts new file mode 100644 index 00000000..aeb0afa4 --- /dev/null +++ b/packages/i18n-named-routes/src/index.ts @@ -0,0 +1,27 @@ +import { I18nNamedRedirect } from './I18nNamedRedirect'; +import { I18nNamedLink } from './I18nNamedLink'; +import { + buildUrlCache, + cleanPathName, + storeLanguages, + getUrlForLanguage, +} from './url-cache'; + +export * from 'tg-named-routes'; + +export * from './create-config'; +export * from './default-language-redirect'; +export * from './hooks'; + +export { + buildUrlCache, + cleanPathName, + I18nNamedRedirect, + I18nNamedLink, + storeLanguages, + getUrlForLanguage, +}; + +// Also make sure to overwrite these from tg-named-routes +export const NamedRedirect = I18nNamedRedirect; +export const NamedLink = I18nNamedLink; diff --git a/packages/i18n-named-routes/src/url-cache.ts b/packages/i18n-named-routes/src/url-cache.ts new file mode 100644 index 00000000..7134290b --- /dev/null +++ b/packages/i18n-named-routes/src/url-cache.ts @@ -0,0 +1,156 @@ +import { + buildUrlCache as baseBuildUrlCache, + getUrlMapCache, + resolvePath, + stringifyLocation, + NamedRouteConfig, +} from 'tg-named-routes'; +import { matchPath } from 'react-router'; + +let allLanguages: string[] | null = null; + +export const storeLanguages = (newLangs: string[]) => { + allLanguages = newLangs.slice(); +}; + +/** + * Clean path name based on namespace and path + * + * For i18n routes this is required to prevent nested language routes from containing + * the language multiple times (e.g. we get `en:landing:home` instead of `en:landing:en:home`). + * + * @param separator Separator pattern for separating namespace and route name. + * @param namespace Parent route name + * @param pathName Route name + * @return Generated route name + */ +export function cleanPathName( + separator: string, + namespace: string | null, + pathName?: string | null +): string { + if (!namespace && !pathName) { + return ''; + } + + let routeName = pathName || ''; + + if (!namespace) { + return routeName; + } + + // start extra logic for i18n + if (namespace && routeName) { + if (allLanguages) { + for (let i = 0; i < allLanguages.length; i += 1) { + const language = allLanguages[i]; + + if (routeName.startsWith(`${language}${separator}`)) { + // This prevents + routeName = routeName.replace( + new RegExp(`^${language}${separator}`), + '' + ); + break; + } + } + } + } + // end extra logic for i18n + + routeName = `${namespace}${separator}${routeName}`; + + return routeName.replace(`${separator}${separator}`, `${separator}`); +} + +let urlNameCache: Record | null = null; + +/** + * Attempt to resolve the url in new language based on current path name + * + * @param currentPathName path name to get the language url for + * @param newLanguage + */ +export function getUrlForLanguage( + currentPathName: string, + newLanguage: string +) { + if (!urlNameCache) { + throw new Error( + 'Missing available urls data, did you call `buildUrlCache`' + ); + } + + // Easy case, no params involved + if (urlNameCache[currentPathName]) { + const newRouteName = urlNameCache[currentPathName].replace( + /^[a-z]+:/, + `${newLanguage}:` + ); + + return stringifyLocation(resolvePath(newRouteName)); + } + + // When params are involved we have to go trough all configured urls and figure out which + // one matches. This is because react-router params are only available in the subtree of + // that particular route (see https://github.com/ReactTraining/react-router/issues/5870). + // If params would be available everywhere this would be easier. + + const keys = Object.keys(urlNameCache); + + for (let i = 0; i < keys.length; i += 1) { + // Ignore catchall routes (usually 404) + if (keys[i] !== '*') { + const match = matchPath(currentPathName, { + path: keys[i], + exact: true, + strict: false, + }); + + if (match && Object.keys(match.params).length > 0) { + const newRouteName = urlNameCache[keys[i]].replace( + /^[a-z]+:/, + `${newLanguage}:` + ); + + return stringifyLocation( + resolvePath(newRouteName, match.params) + ); + } + } + } + + return null; +} + +export function buildUrlCache( + routeData: NamedRouteConfig[], + separator = ':', + namespace: string | null = null, + routeNames: string[] = [] +) { + if (!allLanguages) { + throw new Error( + 'Missing available languages data, did you call `storeLanguages`' + ); + } + + baseBuildUrlCache( + routeData, + separator, + namespace, + routeNames, + cleanPathName + ); + + const urlMapCache = getUrlMapCache(); + + urlNameCache = {}; + if (urlNameCache) { + Object.keys(urlMapCache).forEach((urlName: string) => { + if (urlNameCache) { + urlNameCache[urlMapCache[urlName].pattern] = urlName; + } + }); + } +} diff --git a/packages/i18n-named-routes/test/i18n-test.ts b/packages/i18n-named-routes/test/i18n-test.ts new file mode 100644 index 00000000..4408e6ef --- /dev/null +++ b/packages/i18n-named-routes/test/i18n-test.ts @@ -0,0 +1,25 @@ +/* Client-side translation configuration */ +import i18next from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +const i18nInstance = i18next.createInstance(); + +i18nInstance + .use(initReactI18next as any) // Need to define as any - type not accepted + // tslint:disable-next-line:no-floating-promises + .init({ + lng: 'en', + load: 'languageOnly', + fallbackLng: 'en', + returnEmptyString: false, + interpolation: { + escapeValue: false, // Not needed for React + }, + react: { + useSuspense: false, + nsMode: 'fallback', + }, + initImmediate: false, + }); + +export default i18nInstance; diff --git a/packages/i18n-named-routes/test/routes.tsx b/packages/i18n-named-routes/test/routes.tsx new file mode 100644 index 00000000..9d0bd29b --- /dev/null +++ b/packages/i18n-named-routes/test/routes.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { useLocation } from 'react-router'; +import { + createLanguageRoutes, + getDefaultLanguageRedirect, + RenderChildren, + NamedRouteConfig, + TranslatedNamedRouteConfig, + storeLanguages, + buildUrlCache, +} from '../src'; + +const PageNotFound = () => not found; +const View = () => { + const loc = useLocation(); + + return a view {loc.pathname}; +}; + +const SingleNotFoundRoute: TranslatedNamedRouteConfig = { + path: '*', + name: 'notFound', + component: PageNotFound, +}; + +const NotFoundRoute: TranslatedNamedRouteConfig = { + name: '404', + path: '*', + component: RenderChildren, + + routes: [SingleNotFoundRoute], +}; + +const createBlogRoutes = ( + PageNotFoundRoute: TranslatedNamedRouteConfig +): TranslatedNamedRouteConfig => ({ + path: { + et: '/et/blogi', + en: '/en/blog', + }, + name: 'blog', + component: RenderChildren, + routes: [ + { + path: { + et: '/et/blogi/populaarsed', + en: '/en/blog/popular', + }, + exact: true, + name: 'popular', + component: View, + }, + { + path: { + et: '/et/blogi/artikkel/:slug', + en: '/en/blog/article/:slug', + }, + exact: true, + name: 'article', + component: View, + }, + PageNotFoundRoute, + ], +}); + +const createLandingRoutes = ( + PageNotFoundRoute: TranslatedNamedRouteConfig +): TranslatedNamedRouteConfig => ({ + name: 'landing', + component: RenderChildren, + path: { + en: '/en', + et: '/et', + }, + routes: [ + { + path: { + en: '/en/privacy', + et: '/et/privaatsuspoliitika', + }, + exact: true, + name: 'privacyPolicy', + component: View, + }, + { + path: { + en: '/en/news', + et: '/et/uudised', + }, + exact: true, + name: 'news', + component: View, + }, + { + path: { + en: '/en/news/:slug', + et: '/et/uudis/:slug', + }, + exact: true, + name: 'newsArticle', + component: View, + }, + { + path: { + en: '/en', + et: '/et', + }, + exact: true, + name: 'home', + component: View, + }, + PageNotFoundRoute, + ], +}); + +const routeConfig: NamedRouteConfig[] = createLanguageRoutes( + 'en', + ['en', 'et'], + [ + { + component: RenderChildren, + routes: [ + createBlogRoutes(SingleNotFoundRoute), + createLandingRoutes(SingleNotFoundRoute), + getDefaultLanguageRedirect('landing:home', null), + NotFoundRoute, + ], + }, + ] +); + +storeLanguages(['en', 'et']); +buildUrlCache(routeConfig); + +export const routes = routeConfig; diff --git a/packages/i18n-named-routes/test/translated-routes.test.tsx b/packages/i18n-named-routes/test/translated-routes.test.tsx new file mode 100644 index 00000000..eacf1802 --- /dev/null +++ b/packages/i18n-named-routes/test/translated-routes.test.tsx @@ -0,0 +1,301 @@ +import { act, render } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter, Router } from 'react-router'; +import { I18nextProvider } from 'react-i18next'; +import { + getUrlNames, + NamedRouteConfig, + resolvePath, + stringifyLocation, + useI18nResolvePath, + useI18nStringifyLocation, + Kwargs, + getUrlForLanguage, + RenderChildren, + useI18nResolvePattern, + I18nNamedLink, + I18nNamedRedirect, +} from '../src'; + +import { routes } from './routes'; +import i18nInstance from './i18n-test'; +import { createMemoryHistory } from 'history'; + +const getRouteKey = ( + routeData: NamedRouteConfig | NamedRouteConfig[], + key: string +) => { + const res: string[] = []; + + const items = Array.isArray(routeData) ? routeData : [routeData]; + + items.forEach(route => { + if (route[key]) { + res.push(route[key]); + } + + if (route.routes) { + getRouteKey(route.routes, key).forEach(childPath => { + res.push(childPath); + }); + } + }); + + return res; +}; + +const getPaths = (routeData: NamedRouteConfig | NamedRouteConfig[]) => + getRouteKey(routeData, 'path'); + +const getNames = (routeData: NamedRouteConfig | NamedRouteConfig[]) => + getRouteKey(routeData, 'routeName'); + +const Component = ({ name, kwargs }: { name: string; kwargs?: Kwargs }) => { + const resolve = useI18nResolvePath(); + const stringify = useI18nStringifyLocation(); + const pattern = useI18nResolvePattern(); + + return ( + <> +
{stringify(resolve(name, kwargs))}
+
{pattern(name)}
+ + ); +}; + +Component.defaultProps = { + kwargs: undefined, +}; + +afterEach(() => { + // reset the language + act(() => { + i18nInstance.changeLanguage('en'); + }); +}); + +describe('translated route config', () => { + test('Translated config names are generated correctly', () => { + expect(getNames(routes)).toEqual([ + 'en:blog', + 'en:blog:popular', + 'en:blog:article', + 'en:blog:notFound', + 'et:blog', + 'et:blog:popular', + 'et:blog:article', + 'et:blog:notFound', + 'en:landing', + 'en:landing:privacyPolicy', + 'en:landing:news', + 'en:landing:newsArticle', + 'en:landing:home', + 'en:landing:notFound', + 'et:landing', + 'et:landing:privacyPolicy', + 'et:landing:news', + 'et:landing:newsArticle', + 'et:landing:home', + 'et:landing:notFound', + 'defaultLanguageRedirect', + '404', + '404:notFound', + ]); + }); + + test('getUrlForLanguage works', () => { + expect(getUrlForLanguage('/et/uudised', 'en')).toBe('/en/news'); + expect(getUrlForLanguage('/en/news', 'et')).toBe('/et/uudised'); + + expect(getUrlForLanguage('/et/blogi', 'en')).toBe('/en/blog'); + expect(getUrlForLanguage('/en/blog', 'et')).toBe('/et/blogi'); + + expect(getUrlForLanguage('/et/blogi/populaarsed', 'en')).toBe( + '/en/blog/popular' + ); + expect(getUrlForLanguage('/en/blog/popular', 'et')).toBe( + '/et/blogi/populaarsed' + ); + + expect(getUrlForLanguage('/et', 'en')).toBe('/en'); + expect(getUrlForLanguage('/en', 'et')).toBe('/et'); + + expect(getUrlForLanguage('/', 'et')).toBe('/'); + expect(getUrlForLanguage('/', 'en')).toBe('/'); + + expect(getUrlForLanguage('/et/blogi/artikkel/asdasd', 'en')).toBe( + '/en/blog/article/asdasd' + ); + expect(getUrlForLanguage('/en/blog/article/asdasd', 'et')).toBe( + '/et/blogi/artikkel/asdasd' + ); + + expect(getUrlForLanguage('/asdasdqe', 'en')).toBe(null); + }); + + test('Translated config paths are generated correctly', () => { + expect(getPaths(routes)).toEqual([ + '/en/blog', + '/en/blog/popular', + '/en/blog/article/:slug', + '*', + '/et/blogi', + '/et/blogi/populaarsed', + '/et/blogi/artikkel/:slug', + '*', + '/en', + '/en/privacy', + '/en/news', + '/en/news/:slug', + '/en', + '*', + '/et', + '/et/privaatsuspoliitika', + '/et/uudised', + '/et/uudis/:slug', + '/et', + '*', + '/', + '*', + '*', + ]); + }); + + test('getUrlNames', () => { + expect(getUrlNames()).toEqual([ + '404', + 'en:blog', + 'en:blog:popular', + 'en:blog:article', + 'en:blog:notFound', + 'et:blog', + 'et:blog:popular', + 'et:blog:article', + 'et:blog:notFound', + 'en:landing', + 'en:landing:privacyPolicy', + 'en:landing:news', + 'en:landing:newsArticle', + 'en:landing:home', + 'en:landing:notFound', + 'et:landing', + 'et:landing:privacyPolicy', + 'et:landing:news', + 'et:landing:newsArticle', + 'et:landing:home', + 'et:landing:notFound', + 'defaultLanguageRedirect', + '404:notFound', + ]); + }); + + test('resolving works with standard method by prefixing language namespace', () => { + expect(stringifyLocation(resolvePath('et:landing:news'))).toBe( + '/et/uudised' + ); + expect(stringifyLocation(resolvePath('en:landing:news'))).toBe( + '/en/news' + ); + expect(stringifyLocation(resolvePath('en:blog'))).toBe('/en/blog'); + expect(stringifyLocation(resolvePath('en:blog:popular'))).toBe( + '/en/blog/popular' + ); + expect( + stringifyLocation( + resolvePath('en:blog:article', { slug: 'a-slug' }) + ) + ).toBe('/en/blog/article/a-slug'); + }); + + test('resolving works with hooks', async () => { + const { getByText } = render( + + + + ); + + expect(getByText('/en/news/theSlug')).toBeInTheDocument(); + expect(getByText('/en/news/:slug')).toBeInTheDocument(); + + await act(async () => { + await i18nInstance.changeLanguage('et'); + }); + + expect(getByText('/et/uudis/theSlug')).toBeInTheDocument(); + expect(getByText('/et/uudis/:slug')).toBeInTheDocument(); + }); + + test('default language redirect', () => { + const history = createMemoryHistory({ + initialEntries: ['/'], + }); + + render( + + + + + + ); + + expect(history.location.pathname).toBe('/en'); + }); + + test('NamedLink', async () => { + const { getByText } = render( + + + + Link + + + + ); + + expect(getByText('Link')).toBeInTheDocument(); + expect(getByText('Link').closest('a')).toHaveAttribute( + 'href', + '/en/news/theSlug' + ); + + await act(async () => { + await i18nInstance.changeLanguage('et'); + }); + + expect(getByText('Link').closest('a')).toHaveAttribute( + 'href', + '/et/uudis/theSlug' + ); + }); + + test('NamedRedirect', async () => { + const history = createMemoryHistory({ + initialEntries: ['/'], + }); + + render( + + + + + + ); + + expect(history.location.pathname).toBe('/en/news/theSlug'); + + await act(async () => { + await i18nInstance.changeLanguage('et'); + }); + + expect(history.location.pathname).toBe('/et/uudis/theSlug'); + }); +}); diff --git a/packages/i18n-named-routes/tsconfig.eslint.json b/packages/i18n-named-routes/tsconfig.eslint.json new file mode 100644 index 00000000..f04b48cc --- /dev/null +++ b/packages/i18n-named-routes/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "src/**/*.ts", + "test/**/*.ts", + "typings/**/*.ts", + "src/**/*.js" + ] +} diff --git a/packages/i18n-named-routes/tsconfig.json b/packages/i18n-named-routes/tsconfig.json new file mode 100644 index 00000000..0e4afe3e --- /dev/null +++ b/packages/i18n-named-routes/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "compilerOptions": { + "rootDir": "src", + "declarationDir": "lib", + "outDir": "lib", + "composite": true + } +} diff --git a/packages/named-routes/README.md b/packages/named-routes/README.md index 31e3918a..acf7b65c 100644 --- a/packages/named-routes/README.md +++ b/packages/named-routes/README.md @@ -1,6 +1,6 @@ # `tg-named-routes` -React Router 4 named routes helper library to add support for named routes via [react-router-config](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config). +React Router 4/5 named routes helper library to add support for named routes via [react-router-config](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config). Also provides named versions of [React-Router](https://github.com/ReactTraining/react-router/tree/master/packages/react-router) components. ## Usage diff --git a/packages/named-routes/package.json b/packages/named-routes/package.json index 3c97b475..3bc42e6c 100644 --- a/packages/named-routes/package.json +++ b/packages/named-routes/package.json @@ -1,11 +1,11 @@ { "name": "tg-named-routes", - "version": "1.0.0-beta.0", + "version": "1.0.0-beta.1", "private": false, "publishConfig": { "access": "public" }, - "description": "React Router 4 named routes helper", + "description": "React Router named routes helper", "main": "lib/index.js", "module": "lib/index.esm.js", "typings": "lib/index.d.ts", diff --git a/packages/named-routes/src/NamedLink.tsx b/packages/named-routes/src/NamedLink.tsx index c2f3aa46..e41a5430 100644 --- a/packages/named-routes/src/NamedLink.tsx +++ b/packages/named-routes/src/NamedLink.tsx @@ -5,20 +5,27 @@ import { NamedComponentProps, resolvePath } from './routes'; export interface NamedLinkProps extends NamedComponentProps, - Omit {} + Omit { + resolvePathFn?: typeof resolvePath; +} export const NamedLink = ({ name, kwargs, query, state, + resolvePathFn, ...props }: NamedLinkProps) => ( - + ); NamedLink.defaultProps = { kwargs: {}, query: null, state: null, + resolvePathFn: resolvePath, }; diff --git a/packages/named-routes/src/NamedRedirect.tsx b/packages/named-routes/src/NamedRedirect.tsx index 46e94d23..a84201e7 100644 --- a/packages/named-routes/src/NamedRedirect.tsx +++ b/packages/named-routes/src/NamedRedirect.tsx @@ -10,6 +10,9 @@ export interface NamedRedirectProps extends Omit { toKwargs?: Kwargs; toQuery?: Query; toState?: LocationState; + + resolvePathFn?: typeof resolvePath; + resolvePatternFn?: typeof resolvePattern; } export const NamedRedirect = ({ @@ -18,11 +21,17 @@ export const NamedRedirect = ({ toKwargs, toQuery, toState, + resolvePathFn, + resolvePatternFn, ...props }: NamedRedirectProps) => ( ); diff --git a/packages/named-routes/src/routes.ts b/packages/named-routes/src/routes.ts index e9ae334b..eddbc46e 100644 --- a/packages/named-routes/src/routes.ts +++ b/packages/named-routes/src/routes.ts @@ -89,15 +89,26 @@ export function cleanPathName( * @param [separator=':'] Separator for parent route name and child route name * @param [namespace=null] Parent route namespace * @param [routeNames=[]] For internal use only, used to keep track of generated URLs + * @param [cleanMethod] For internal use only. Can be used to provide a custom method to + * clean path names instead of using cleanPathName. */ export function buildUrlCache( routeData: NamedRouteConfig[], separator = ':', namespace: string | null = null, - routeNames: string[] = [] + routeNames: string[] = [], + cleanMethod?: ( + separator: string, + namespace: string | null, + pathName?: string | null + ) => string ) { routeData.forEach(route => { - route.routeName = cleanPathName(separator, namespace, route.name); + route.routeName = (cleanMethod || cleanPathName)( + separator, + namespace, + route.name + ); // Prevent duplicate route names. if (routeNames.includes(route.routeName)) { @@ -117,7 +128,13 @@ export function buildUrlCache( } if (route.routes) { - buildUrlCache(route.routes, separator, route.routeName, routeNames); + buildUrlCache( + route.routes, + separator, + route.routeName, + routeNames, + cleanMethod + ); } }); } @@ -131,6 +148,15 @@ export function getUrlNames(): string[] { return Object.keys(urlMapCache); } +/** + * Get a copy of the url map cache. + * + * This is useful for debugging/extending named routes. + */ +export function getUrlMapCache(): { [key: string]: URLCache } { + return { ...urlMapCache }; +} + /** * Reset URL cache. * diff --git a/packages/pending-data/package.json b/packages/pending-data/package.json index d281de2e..850e1dc7 100644 --- a/packages/pending-data/package.json +++ b/packages/pending-data/package.json @@ -29,7 +29,7 @@ "lib" ], "peerDependencies": { - "connected-react-router": "^6.2.2", + "connected-react-router": "^6.9.1", "history": "^4.7.2", "react": ">=16.8.4", "react-dom": ">=16.8.4", @@ -40,7 +40,7 @@ "dependencies": { "@thorgate/spa-is": "1.0.0-beta.0", "tg-loading-bar": "1.0.0-beta.0", - "tg-named-routes": "1.0.0-beta.0" + "tg-named-routes": "1.0.0-beta.1" }, "scripts": { "clean": "rimraf ./lib", diff --git a/packages/permissions/package.json b/packages/permissions/package.json index c1966aec..ad90c301 100644 --- a/packages/permissions/package.json +++ b/packages/permissions/package.json @@ -38,7 +38,7 @@ "@thorgate/spa-components": "1.0.0-beta.0", "@thorgate/spa-errors": "1.0.0-beta.0", "@thorgate/spa-pending-data": "1.0.0-beta.0", - "tg-named-routes": "1.0.0-beta.0", + "tg-named-routes": "1.0.0-beta.1", "typesafe-actions": "^4.4.2", "warning": "^4.0.2" }, diff --git a/packages/view-manager/package.json b/packages/view-manager/package.json index 0a0f0a14..b6a5c448 100644 --- a/packages/view-manager/package.json +++ b/packages/view-manager/package.json @@ -26,7 +26,7 @@ "lib" ], "peerDependencies": { - "connected-react-router": "^6.2.2", + "connected-react-router": "^6.9.1", "history": "^4.7.2", "react-router": "^4.3.0 || ^5.0.0", "react-router-config": "^4.3.0 || ^5.0.0", @@ -37,7 +37,7 @@ "@thorgate/spa-errors": "1.0.0-beta.0", "@thorgate/spa-is": "1.0.0-beta.0", "@thorgate/spa-pending-data": "1.0.0-beta.0", - "tg-named-routes": "1.0.0-beta.0", + "tg-named-routes": "1.0.0-beta.1", "typescript-tuple": "^2.1.1", "warning": "^4.0.2" }, diff --git a/packages/view/package.json b/packages/view/package.json index d8814a6b..91054a3d 100644 --- a/packages/view/package.json +++ b/packages/view/package.json @@ -39,7 +39,7 @@ "@thorgate/spa-errors": "1.0.0-beta.0", "@thorgate/spa-is": "1.0.0-beta.0", "@thorgate/spa-permissions": "1.0.0-beta.0", - "tg-named-routes": "1.0.0-beta.0" + "tg-named-routes": "1.0.0-beta.1" }, "scripts": { "clean": "rimraf ./lib", diff --git a/rollup.config.js b/rollup.config.js index 40a40753..3d35bbc7 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -25,6 +25,7 @@ const createConfig = (input, output, tsconfigOverride) => ({ exclude: 'node_modules/**', VERSION: PKG.version, delimiters: ['<@ ', ' @>'], + preventAssignment: true, }), typescript({ cacheRoot: `${require('temp-dir')}/.rpt2_cache`, @@ -39,40 +40,62 @@ const createConfig = (input, output, tsconfigOverride) => ({ console.log(`\nBuilding ${PKG.name}`); - if (PKG.module) { - configs.push(createConfig('./src/index.ts', { - file: PKG.module, - format: 'es', - }, { compilerOptions: { declaration: true } })); + configs.push( + createConfig( + './src/index.ts', + { + file: PKG.module, + format: 'es', + }, + { compilerOptions: { declaration: true } } + ) + ); } if (PKG.main) { - configs.push(createConfig('./src/index.ts', { - file: PKG.main, - format: 'cjs', - }, { compilerOptions: { declaration: true } })); + configs.push( + createConfig( + './src/index.ts', + { + file: PKG.main, + format: 'cjs', + }, + { compilerOptions: { declaration: true } } + ) + ); } - if (PKG.bin && typeof PKG.bin === 'string') { - configs.push(createConfig('', { - file: PKG.bin, - format: 'cjs', - banner: '#!/usr/bin/env node\n', - }, { compilerOptions: { declaration: true, target: 'es5' } })); + configs.push( + createConfig( + '', + { + file: PKG.bin, + format: 'cjs', + banner: '#!/usr/bin/env node\n', + }, + { compilerOptions: { declaration: true, target: 'es5' } } + ) + ); } else if (PKG.bin) { - Object.keys(PKG.bin).map((key) => PKG.bin[key]).forEach((bin) => { - configs.push(createConfig(bin.replace(/\.\/bin\/(.+)\.js/, './src/$1.ts'), { - file: bin, - format: 'cjs', - banner: '#!/usr/bin/env node\n', - }, { compilerOptions: { declaration: true, target: 'es5' } })); - - }); + Object.keys(PKG.bin) + .map(key => PKG.bin[key]) + .forEach(bin => { + configs.push( + createConfig( + bin.replace(/\.\/bin\/(.+)\.js/, './src/$1.ts'), + { + file: bin, + format: 'cjs', + banner: '#!/usr/bin/env node\n', + }, + { compilerOptions: { declaration: true, target: 'es5' } } + ) + ); + }); } - if (!configs.length) { console.error('Missing rollup configs'); process.exit(1); diff --git a/yarn.lock b/yarn.lock index 3be30b16..c60a5f10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1008,14 +1008,14 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.13.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee" integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA== @@ -2349,49 +2349,62 @@ resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204" integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg== -"@rollup/plugin-commonjs@^11.0.2": - version "11.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-11.0.2.tgz#837cc6950752327cb90177b608f0928a4e60b582" - integrity sha512-MPYGZr0qdbV5zZj8/2AuomVpnRVXRU5XKXb3HVniwRoRCreGlf5kOE081isNWeiLIi6IYkwTX9zE0/c7V8g81g== +"@rollup/plugin-commonjs@^17.1.0": + version "17.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d" + integrity sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew== dependencies: - "@rollup/pluginutils" "^3.0.0" - estree-walker "^1.0.1" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" -"@rollup/plugin-json@^4.0.2": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.0.2.tgz#482185ee36ac7dd21c346e2dbcc22ffed0c6f2d6" - integrity sha512-t4zJMc98BdH42mBuzjhQA7dKh0t4vMJlUka6Fz0c+iO5IVnWaEMiYBy1uBj9ruHZzXBW23IPDGL9oCzBkQ9Udg== +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== dependencies: - "@rollup/pluginutils" "^3.0.4" + "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-node-resolve@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.1.tgz#8c6e59c4b28baf9d223028d0e450e06a485bb2b7" - integrity sha512-14ddhD7TnemeHE97a4rLOhobfYvUVcaYuqTnL8Ti7Jxi9V9Jr5LY7Gko4HZ5k4h4vqQM0gBQt6tsp9xXW94WPA== +"@rollup/plugin-node-resolve@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.0.tgz#a5ab88c35bb7622d115f44984dee305112b6f714" + integrity sha512-qHjNIKYt5pCcn+5RUBQxK8krhRvf1HnyVgUCcFFcweDS7fhkOLZeYh0mhHK6Ery8/bb9tvN/ubPzmfF0qjDCTA== dependencies: - "@rollup/pluginutils" "^3.0.6" - "@types/resolve" "0.0.8" + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" builtin-modules "^3.1.0" + deepmerge "^4.2.2" is-module "^1.0.0" - resolve "^1.14.2" + resolve "^1.19.0" -"@rollup/plugin-replace@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.3.1.tgz#16fb0563628f9e6c6ef9e05d48d3608916d466f5" - integrity sha512-qDcXj2VOa5+j0iudjb+LiwZHvBRRgWbHPhRmo1qde2KItTjuxDVQO21rp9/jOlzKR5YO0EsgRQoyox7fnL7y/A== +"@rollup/plugin-replace@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.1.tgz#c411b5ab72809fb1bfc8b487d8d02eef661460d3" + integrity sha512-XwC1oK5rrtRJ0tn1ioLHS6OV5JTluJF7QE1J/q1hN3bquwjnVxjtMyY9iCnoyH9DQbf92CxajB3o98wZbP3oAQ== dependencies: - "@rollup/pluginutils" "^3.0.4" - magic-string "^0.25.5" + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" -"@rollup/pluginutils@^3.0.0", "@rollup/pluginutils@^3.0.4", "@rollup/pluginutils@^3.0.6": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.0.8.tgz#4e94d128d94b90699e517ef045422960d18c8fde" - integrity sha512-rYGeAc4sxcZ+kPG/Tw4/fwJODC3IXHYDH4qusdN/b6aLw5LPUbzpecYbEJh4sVQGPFJxd2dBU4kc1H3oy9/bnw== +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== dependencies: + "@types/estree" "0.0.39" estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" + integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" "@sheerun/mutationobserver-shim@^0.3.2": version "0.3.2" @@ -2787,25 +2800,25 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-router-config@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.1.tgz#54da8418190ee47484dee279975e2b8038fb8b5d" - integrity sha512-D4srFL4XP21GjWWnM7mL8j+Nrrw13pc2TYr57WTHJxU9YTxnrXL7p1iqGtwecgwhyeXJSm4WrGwq0SOgSALVbA== +"@types/react-router-config@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.2.tgz#4d3b52e71ed363a1976a12321e67b09a99ad6d10" + integrity sha512-WOSetDV3YPxbkVJAdv/bqExJjmcdCi/vpCJh3NfQOy1X15vHMSiMioXIcGekXDJJYhqGUMDo9e337mh508foAA== dependencies: "@types/history" "*" "@types/react" "*" "@types/react-router" "*" -"@types/react-router-dom@^5.1.3": - version "5.1.3" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196" - integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA== +"@types/react-router-dom@^5.1.7": + version "5.1.7" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271" + integrity sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg== dependencies: "@types/history" "*" "@types/react" "*" "@types/react-router" "*" -"@types/react-router@*", "@types/react-router@^5.1.4": +"@types/react-router@*": version "5.1.4" resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.4.tgz#7d70bd905543cb6bcbdcc6bd98902332054f31a6" integrity sha512-PZtnBuyfL07sqCJvGg3z+0+kt6fobc/xmle08jBiezLS8FrmGeiGkJnuxL/8Zgy9L83ypUhniV5atZn/L8n9MQ== @@ -2813,6 +2826,14 @@ "@types/history" "*" "@types/react" "*" +"@types/react-router@^5.1.12": + version "5.1.12" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.12.tgz#0f300e09468e7aed86e18241c90238c18c377e51" + integrity sha512-0bhXQwHYfMeJlCh7mGhc0VJTRm0Gk+Z8T00aiP4702mDUuLs9SMhnd2DitpjWFjdOecx2UXtICK14H9iMnziGA== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-test-renderer@*": version "16.9.2" resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz#e1c408831e8183e5ad748fdece02214a7c2ab6c5" @@ -2828,10 +2849,10 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== dependencies: "@types/node" "*" @@ -4783,12 +4804,16 @@ connect-history-api-fallback@^1.6.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== -connected-react-router@^6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.7.0.tgz#1c37a65684f1729533264c1b1903ee91c8ca3a15" - integrity sha512-RDmcmiwSfUWQ3U7J7RVkc9cwNtek26fUn0DWpA8pS7JylC97VNeosrsIxjJ/3CGDrzZPqnc0Hr/kZxjh75JGlw== +connected-react-router@^6.9.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.9.1.tgz#d842eebaa15b9920e2e45fc03d74e41110e94e4c" + integrity sha512-BbtB6t0iqAwGwygDenJl9zmlk7vpKWIRSycULmkAOn2RUaF6+bqETprl0qcIqQmY5CTqSwKanaxkLXYWiffAfQ== dependencies: + lodash.isequalwith "^4.4.0" prop-types "^15.7.2" + optionalDependencies: + immutable "^3.8.1 || ^4.0.0-rc.1" + seamless-immutable "^7.1.3" consola@^2.10.0: version "2.15.3" @@ -5458,7 +5483,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@4.2.2: +deepmerge@4.2.2, 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== @@ -6254,16 +6279,16 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== - estree-walker@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6629,15 +6654,6 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.2.0.tgz#e7fe44c1abc1299f516146e563108fd1006c1874" - integrity sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.0" - pkg-dir "^4.1.0" - find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" @@ -7158,11 +7174,6 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -gud@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" - integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== - gzip-size@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" @@ -7447,6 +7458,13 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-parse-stringify2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-webpack-plugin@^4.0.4: version "4.5.2" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" @@ -7595,6 +7613,13 @@ humanize-number@0.0.2: resolved "https://registry.yarnpkg.com/humanize-number/-/humanize-number-0.0.2.tgz#11c0af6a471643633588588048f1799541489c18" integrity sha1-EcCvakcWQ2M1iFiASPF5lUFInBg= +i18next@^19.9.1: + version "19.9.1" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.9.1.tgz#7a072b75daf677aa51fd4ce55214f21702af3ffd" + integrity sha512-9Azzyb3DvMJUMd7sPhwVEs6PQcogvdHmLQTjMQ+P+h3XwW4O66/8lgZTmYShgkjPOCqTw4Fwl5LOp/VhZgPo5A== + dependencies: + "@babel/runtime" "^7.12.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7658,6 +7683,11 @@ immer@^8.0.1: resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== +"immutable@^3.8.1 || ^4.0.0-rc.1": + version "4.0.0-rc.12" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217" + integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -8171,12 +8201,12 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= -is-reference@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427" - integrity sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw== +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== dependencies: - "@types/estree" "0.0.39" + "@types/estree" "*" is-regex@^1.0.4, is-regex@^1.0.5: version "1.0.5" @@ -9723,6 +9753,11 @@ lodash.isarray@^3.0.0: resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= +lodash.isequalwith@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" + integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -9846,10 +9881,10 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== -magic-string@^0.25.2, magic-string@^0.25.5: - version "0.25.6" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.6.tgz#5586387d1242f919c6d223579cc938bf1420795e" - integrity sha512-3a5LOMSGoCTH5rbqobC2HuDNRtE2glHZ8J7pK+QZYppyWA36yuNpsX994rIY2nCuyP7CZYy7lQq/X2jygiZ89g== +magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== dependencies: sourcemap-codec "^1.4.4" @@ -10127,14 +10162,13 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= -mini-create-react-context@^0.3.0: - version "0.3.2" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" - integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== +mini-create-react-context@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" + integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== dependencies: - "@babel/runtime" "^7.4.0" - gud "^1.0.0" - tiny-warning "^1.0.2" + "@babel/runtime" "^7.12.1" + tiny-warning "^1.0.3" mini-css-extract-plugin@^0.9.0: version "0.9.0" @@ -11403,7 +11437,7 @@ picomatch@^2.0.4, picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== -picomatch@^2.2.1: +picomatch@^2.2.1, picomatch@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -12601,15 +12635,15 @@ react-dev-utils@10.2.0: strip-ansi "6.0.0" text-table "0.2.0" -react-dom@^16.12.0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11" - integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw== +react-dom@^16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.18.0" + scheduler "^0.19.1" react-error-overlay@^6.0.6, react-error-overlay@^6.0.7: version "6.0.9" @@ -12631,6 +12665,14 @@ react-helmet@^5.2.0: react-fast-compare "^2.0.2" react-side-effect "^1.1.0" +react-i18next@^11.8.8: + version "11.8.8" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.8.8.tgz#23d34518c784f2ada7cec41cfe439ac4ae51875c" + integrity sha512-Z8Daifh+FRpcQsCp48mWQViYSlojv0WiL2bf6e9DOzpfVMDaTT6qsYRbHCjLEeDeEioxoaWHMiWu2JPTW3Ni4w== + dependencies: + "@babel/runtime" "^7.13.6" + html-parse-stringify2 "^2.0.1" + react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -12653,36 +12695,36 @@ react-refresh@^0.8.1: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== -react-router-config@^5.1.0, react-router-config@^5.1.1: +react-router-config@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== dependencies: "@babel/runtime" "^7.1.2" -react-router-dom@^5.1.0, react-router-dom@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" - integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== +react-router-dom@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" + integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0" loose-envify "^1.3.1" prop-types "^15.6.2" - react-router "5.1.2" + react-router "5.2.0" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.1.2, react-router@^5.1.0, react-router@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" - integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== +react-router@5.2.0, react-router@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" + integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0" hoist-non-react-statics "^3.1.0" loose-envify "^1.3.1" - mini-create-react-context "^0.3.0" + mini-create-react-context "^0.4.0" path-to-regexp "^1.7.0" prop-types "^15.6.2" react-is "^16.6.0" @@ -12706,10 +12748,10 @@ react-test-renderer@^16.12.0: react-is "^16.8.6" scheduler "^0.18.0" -react@^16.12.0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" - integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== +react@^16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -13167,13 +13209,6 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== - dependencies: - path-parse "^1.0.6" - resolve@1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -13181,14 +13216,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@1.x, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.14.2, resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1: +resolve@1.20.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -13196,6 +13224,13 @@ resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1: is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@1.x, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.3.2: + version "1.15.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + dependencies: + path-parse "^1.0.6" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -13279,32 +13314,23 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-typescript2@0.25.3: - version "0.25.3" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.25.3.tgz#a5fb2f0f85488789334ce540abe6c7011cbdf40f" - integrity sha512-ADkSaidKBovJmf5VBnZBZe+WzaZwofuvYdzGAKTN/J4hN7QJCFYAq7IrH9caxlru6T5qhX41PNFS1S4HqhsGQg== +rollup-plugin-typescript2@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" + integrity sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ== dependencies: - find-cache-dir "^3.0.0" + "@rollup/pluginutils" "^4.1.0" + find-cache-dir "^3.3.1" fs-extra "8.1.0" - resolve "1.12.0" - rollup-pluginutils "2.8.1" - tslib "1.10.0" - -rollup-pluginutils@2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97" - integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg== - dependencies: - estree-walker "^0.6.1" + resolve "1.20.0" + tslib "2.1.0" -rollup@^1.31.0: - version "1.31.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.31.0.tgz#e2a87212e96aa7850f3eb53fdd02cf89f2d2fe9a" - integrity sha512-9C6ovSyNeEwvuRuUUmsTpJcXac1AwSL1a3x+O5lpmQKZqi5mmrjauLeqIjvREC+yNRR8fPdzByojDng+af3nVw== - dependencies: - "@types/estree" "*" - "@types/node" "*" - acorn "^7.1.0" +rollup@^2.40.0: + version "2.40.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.40.0.tgz#efc218eaede7ab590954df50f96195188999c304" + integrity sha512-WiOGAPbXoHu+TOz6hyYUxIksOwsY/21TRWoO593jgYt8mvYafYqQl+axaA8y1z2HFazNUUrsMSjahV2A6/2R9A== + optionalDependencies: + fsevents "~2.3.1" rsvp@^4.8.4: version "4.8.5" @@ -13427,6 +13453,14 @@ scheduler@^0.18.0: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -13453,6 +13487,11 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +seamless-immutable@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8" + integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -14534,7 +14573,7 @@ tiny-invariant@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-warning@^1.0.0, tiny-warning@^1.0.2: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -14688,16 +14727,16 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@1.10.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tslib@^2.0.3: +tslib@2.1.0, tslib@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + tsscmp@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" @@ -15082,6 +15121,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"