From 51bece08563fae0069691ed7c1ae58313e931cfa Mon Sep 17 00:00:00 2001 From: Juan Picado Date: Sat, 13 Mar 2021 23:05:14 +0100 Subject: [PATCH 01/16] refactor: api provider and config provider --- .eslintrc | 1 + jest/jest.config.js | 1 + package.json | 6 +- partials/storage/jquery/package.json | 4 +- src/App/App.test.tsx | 2 +- src/App/App.tsx | 2 +- src/App/AppContext.ts | 2 +- src/App/AppRoute.tsx | 4 +- src/App/Header/Header.tsx | 2 +- src/App/Header/HeaderRight.tsx | 2 +- .../Header/LoginDialog/LoginDialog.test.tsx | 2 +- src/App/Header/LoginDialog/LoginDialog.tsx | 36 +++++- src/App/Header/Search/Search.test.tsx | 18 ++- src/App/Header/Search/Search.tsx | 5 +- src/components/ActionBar/ActionBarAction.tsx | 16 ++- src/components/ActionBar/download-tarball.ts | 18 --- src/components/ActionBar/index.ts | 1 - src/index.tsx | 10 +- .../ActionBar/download-tarball.ts | 18 --- src/pages/Version/VersionContextProvider.tsx | 41 +++--- src/pages/home/Home.tsx | 13 +- .../home/PackageList/Package/Package.tsx | 35 ++++-- src/pages/home/PackageList/Package/styles.ts | 10 +- src/providers/API/APIProvider.tsx | 118 ++++++++++++++++++ src/{utils => providers/API}/api.test.ts | 21 +--- src/{utils => providers/API}/api.ts | 17 +-- src/providers/API/index.ts | 1 + src/providers/API/storage.ts | 12 ++ .../config/AppConfigurationProvider.tsx | 41 ++++++ src/providers/config/index.ts | 1 + src/template/index.html | 3 +- src/utils/__setPublicPath__.ts | 2 +- src/utils/calls.ts | 25 ---- src/utils/login.test.ts | 50 +------- src/utils/login.ts | 40 ------ tools/webpack.config.js | 1 + tools/webpack.dev.config.babel.js | 3 +- tsconfig.json | 3 +- yarn.lock | 25 ++-- 39 files changed, 351 insertions(+), 261 deletions(-) delete mode 100644 src/components/ActionBar/download-tarball.ts delete mode 100644 src/pages/Version/DetailSidebar/ActionBar/download-tarball.ts create mode 100644 src/providers/API/APIProvider.tsx rename src/{utils => providers/API}/api.test.ts (85%) rename src/{utils => providers/API}/api.ts (78%) create mode 100644 src/providers/API/index.ts create mode 100644 src/providers/API/storage.ts create mode 100644 src/providers/config/AppConfigurationProvider.tsx create mode 100644 src/providers/config/index.ts delete mode 100644 src/utils/calls.ts diff --git a/.eslintrc b/.eslintrc index 1d1b87842..c007b0994 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,7 @@ { "extends": [ "@verdaccio", + "prettier", "plugin:react/recommended", "plugin:jest/recommended", "plugin:prettier/recommended", diff --git a/jest/jest.config.js b/jest/jest.config.js index 6eba0620c..0c0d9814f 100644 --- a/jest/jest.config.js +++ b/jest/jest.config.js @@ -28,6 +28,7 @@ module.exports = { // note: this section has to be on sync with webpack configuration 'verdaccio-ui/components/(.*)': '/src/components/$1', 'verdaccio-ui/utils/(.*)': '/src/utils/$1', + 'verdaccio-ui/providers/(.*)': '/src/providers/$1', 'verdaccio-ui/design-tokens/(.*)': '/src/design-tokens/$1', }, }; diff --git a/package.json b/package.json index bd3fc4b6f..a0c49e14b 100644 --- a/package.json +++ b/package.json @@ -90,11 +90,11 @@ "emotion": "10.0.27", "emotion-theming": "10.0.27", "eslint": "7.15.0", + "eslint-config-prettier": "7.2.0", "eslint-loader": "^4.0.2", "eslint-plugin-codeceptjs": "1.3.0", "eslint-plugin-import": "2.22.1", "eslint-plugin-jsx-a11y": "6.4.1", - "eslint-plugin-prettier": "3.2.0", "eslint-plugin-react": "7.21.5", "eslint-plugin-react-hooks": "4.2.0", "eslint-plugin-verdaccio": "9.6.1", @@ -212,11 +212,9 @@ }, "lint-staged": { "*.{js,tsx,ts}": [ - "eslint . --ext .js,.ts,.tsx", - "prettier --write" + "eslint . --ext .js,.ts,.tsx" ], "*": [ - "detect-secrets-launcher --baseline .secrets-baseline --word-list", "git add" ] }, diff --git a/partials/storage/jquery/package.json b/partials/storage/jquery/package.json index 408c4caec..f0a9c361c 100644 --- a/partials/storage/jquery/package.json +++ b/partials/storage/jquery/package.json @@ -4981,7 +4981,7 @@ "shasum": "958ce29e81c9790f31be7792df5d4d95fc57fbca" } }, - "_rev": "61-e6be890a78963127", + "_rev": "62-040142a7508cf597", "readme": "# jQuery\n\n> jQuery is a fast, small, and feature-rich JavaScript library.\n\nFor information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/).\nFor source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).\n\nIf upgrading, please see the [blog post for 3.3.1](https://blog.jquery.com/2017/03/20/jquery-3.3.1-now-available/). This includes notable differences from the previous version and a more readable changelog.\n\n## Including jQuery\n\nBelow are some of the most common ways to include jQuery.\n\n### Browser\n\n#### Script tag\n\n```html\n\n```\n\n#### Babel\n\n[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.\n\n```js\nimport $ from \"jquery\";\n```\n\n#### Browserify/Webpack\n\nThere are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this...\n\n```js\nvar $ = require(\"jquery\");\n```\n\n#### AMD (Asynchronous Module Definition)\n\nAMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html).\n\n```js\ndefine([\"jquery\"], function($) {\n\n});\n```\n\n### Node\n\nTo include jQuery in [Node](nodejs.org), first install with npm.\n\n```sh\nnpm install jquery\n```\n\nFor jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.\n\n```js\nrequire(\"jsdom\").env(\"\", function(err, window) {\n\tif (err) {\n\t\tconsole.error(err);\n\t\treturn;\n\t}\n\n\tvar $ = require(\"jquery\")(window);\n});\n```", "_id": "jquery" -} +} \ No newline at end of file diff --git a/src/App/App.test.tsx b/src/App/App.test.tsx index 1f03b6bbd..dd4cea4bc 100644 --- a/src/App/App.test.tsx +++ b/src/App/App.test.tsx @@ -30,7 +30,7 @@ jest.mock('verdaccio-ui/utils/storage', () => { return new LocalStorageMock(); }); -jest.mock('verdaccio-ui/utils/api', () => ({ +jest.mock('verdaccio-ui/providers/API/api', () => ({ // eslint-disable-next-line jest/no-mocks-import request: require('../../jest/unit/components/__mocks__/api').default.request, })); diff --git a/src/App/App.tsx b/src/App/App.tsx index c375f5c49..ea23e9e13 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -35,7 +35,7 @@ const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({ /* eslint-disable react/jsx-no-bind */ /* eslint-disable react-hooks/exhaustive-deps */ const App: React.FC = () => { - const [user, setUser] = useState(); + const [user, setUser] = useState(); /** * Logout user * Required by:
diff --git a/src/App/AppContext.ts b/src/App/AppContext.ts index a8837ab52..006c1c09b 100644 --- a/src/App/AppContext.ts +++ b/src/App/AppContext.ts @@ -6,7 +6,7 @@ export interface AppProps { } export interface User { - username: string; + username: string | null; } export interface AppContextProps extends AppProps { diff --git a/src/App/AppRoute.tsx b/src/App/AppRoute.tsx index 99244214f..df6265781 100644 --- a/src/App/AppRoute.tsx +++ b/src/App/AppRoute.tsx @@ -23,7 +23,7 @@ enum Route { export const history = createBrowserHistory({ // @ts-ignore - basename: window.__VERDACCIO_BASENAME_UI_OPTIONS && window.__VERDACCIO_BASENAME_UI_OPTIONS.url_prefix, + basename: window?.__VERDACCIO_BASENAME_UI_OPTIONS?.url_prefix, }); const AppRoute: React.FC = () => { @@ -36,7 +36,7 @@ const AppRoute: React.FC = () => { const { user } = appContext; - const isUserLoggedIn = user && user.username; + const isUserLoggedIn = user?.username; return ( diff --git a/src/App/Header/Header.tsx b/src/App/Header/Header.tsx index 8e6664b2e..f7c8b5d56 100644 --- a/src/App/Header/Header.tsx +++ b/src/App/Header/Header.tsx @@ -52,7 +52,7 @@ const Header: React.FC = ({ withoutSearch }) => { onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)} onToggleLogin={() => setShowLoginModal(!showLoginModal)} onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)} - username={user && user.username} + username={user?.username} withoutSearch={withoutSearch} /> diff --git a/src/App/Header/HeaderRight.tsx b/src/App/Header/HeaderRight.tsx index 3265b344a..4977ad80b 100644 --- a/src/App/Header/HeaderRight.tsx +++ b/src/App/Header/HeaderRight.tsx @@ -11,7 +11,7 @@ import { RightSide } from './styles'; interface Props { withoutSearch?: boolean; - username?: string; + username?: string | null; onToggleLogin: () => void; onOpenRegistryInfoDialog: () => void; onToggleMobileNav: () => void; diff --git a/src/App/Header/LoginDialog/LoginDialog.test.tsx b/src/App/Header/LoginDialog/LoginDialog.test.tsx index b8eb18b96..2c36f6b33 100644 --- a/src/App/Header/LoginDialog/LoginDialog.test.tsx +++ b/src/App/Header/LoginDialog/LoginDialog.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import api from 'verdaccio-ui/utils/api'; +import api from 'verdaccio-ui/providers/API/api'; import { render, waitFor, fireEvent } from 'verdaccio-ui/utils/test-react-testing-library'; import AppContext, { AppContextProps } from '../../AppContext'; diff --git a/src/App/Header/LoginDialog/LoginDialog.tsx b/src/App/Header/LoginDialog/LoginDialog.tsx index 4b78ee060..b59a8ad1b 100644 --- a/src/App/Header/LoginDialog/LoginDialog.tsx +++ b/src/App/Header/LoginDialog/LoginDialog.tsx @@ -1,9 +1,12 @@ +import i18next from 'i18next'; +import isEmpty from 'lodash/isEmpty'; import React, { useState, useContext, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import Dialog from 'verdaccio-ui/components/Dialog'; import DialogContent from 'verdaccio-ui/components/DialogContent'; -import { makeLogin, LoginError } from 'verdaccio-ui/utils/login'; +import { useAPI, LoginBody } from 'verdaccio-ui/providers/API/APIProvider'; +import { LoginError } from 'verdaccio-ui/utils/login'; import storage from 'verdaccio-ui/utils/storage'; import AppContext from '../../../App/AppContext'; @@ -20,6 +23,35 @@ interface Props { const LoginDialog: React.FC = ({ onClose, open = false }) => { const { t } = useTranslation(); const appContext = useContext(AppContext); + const { doLogin } = useAPI(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const makeLogin = useCallback( + async (username?: string, password?: string): Promise => { + // checks isEmpty + if (isEmpty(username) || isEmpty(password)) { + const error = { + type: 'error', + description: i18next.t('form-validation.username-or-password-cant-be-empty'), + }; + return { error }; + } + + try { + const response: LoginBody = await doLogin(username as string, password as string); + + return response; + } catch (e) { + console.error('login error', e.message); + const error = { + type: 'error', + description: i18next.t('form-validation.unable-to-sign-in'), + }; + return { error }; + } + }, + [doLogin] + ); if (!appContext) { throw Error(t('app-context-not-correct-used')); @@ -42,7 +74,7 @@ const LoginDialog: React.FC = ({ onClose, open = false }) => { onClose(); } }, - [appContext, onClose] + [appContext, onClose, makeLogin] ); return ( diff --git a/src/App/Header/Search/Search.test.tsx b/src/App/Header/Search/Search.test.tsx index cabb7eae9..8337db5ec 100644 --- a/src/App/Header/Search/Search.test.tsx +++ b/src/App/Header/Search/Search.test.tsx @@ -2,15 +2,29 @@ import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import '@testing-library/jest-dom/extend-expect'; -import api from 'verdaccio-ui/utils/api'; + +import api from 'verdaccio-ui/providers/API/api'; +import APIProvider from 'verdaccio-ui/providers/API/APIProvider'; +import AppConfigurationProvider from 'verdaccio-ui/providers/config'; import { render, fireEvent, waitFor } from 'verdaccio-ui/utils/test-react-testing-library'; +jest.mock('lodash/debounce', () => + jest.fn(fn => { + fn.cancel = jest.fn(); + return fn; + }) +); + import Search from './Search'; /* eslint-disable verdaccio/jsx-spread */ const ComponentToBeRendered: React.FC = () => ( - + + + + + ); diff --git a/src/App/Header/Search/Search.tsx b/src/App/Header/Search/Search.tsx index 4daff5c5b..7059ee450 100644 --- a/src/App/Header/Search/Search.tsx +++ b/src/App/Header/Search/Search.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { RouteComponentProps, withRouter } from 'react-router'; import AutoComplete from 'verdaccio-ui/components/AutoComplete'; -import { callSearch } from 'verdaccio-ui/utils/calls'; +import { useAPI } from 'verdaccio-ui/providers/API/APIProvider'; import SearchAdornment from './SearchAdornment'; @@ -23,6 +23,7 @@ const Search: React.FC = ({ history }) => { const [loading, setLoading] = useState(false); const mountedRef = useRef(true); const [requestList, setRequestList] = useState void }>>([]); + const { callSearch } = useAPI(); /** * Cancel all the requests which are in pending state. @@ -138,7 +139,7 @@ const Search: React.FC = ({ history }) => { } } }, - [requestList, setRequestList, setSuggestions, setLoaded, setError, setLoading] + [requestList, setRequestList, setSuggestions, setLoaded, setError, setLoading, callSearch] ); useEffect(() => { diff --git a/src/components/ActionBar/ActionBarAction.tsx b/src/components/ActionBar/ActionBarAction.tsx index 19ade7677..198782536 100644 --- a/src/components/ActionBar/ActionBarAction.tsx +++ b/src/components/ActionBar/ActionBarAction.tsx @@ -2,17 +2,17 @@ import styled from '@emotion/styled'; import BugReportIcon from '@material-ui/icons/BugReport'; import DownloadIcon from '@material-ui/icons/CloudDownload'; import HomeIcon from '@material-ui/icons/Home'; -import React from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Theme } from 'verdaccio-ui/design-tokens/theme'; +import { useAPI } from 'verdaccio-ui/providers/API/APIProvider'; +import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url'; import FloatingActionButton from '../FloatingActionButton'; import Link from '../Link'; import Tooltip from '../Tooltip'; -import downloadTarball from './download-tarball'; - export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(({ theme }) => ({ backgroundColor: theme?.palette.type === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue, color: theme?.palette.white, @@ -33,6 +33,14 @@ export interface ActionBarActionProps { /* eslint-disable react/jsx-no-bind */ const ActionBarAction: React.FC = ({ type, link }) => { const { t } = useTranslation(); + const { getResource } = useAPI(); + + const handleDownload = useCallback(async () => { + const fileStream = await getResource(link); + const fileName = extractFileName(link); + downloadFile(fileStream, fileName); + }, [getResource, link]); + switch (type) { case 'VISIT_HOMEPAGE': return ( @@ -57,7 +65,7 @@ const ActionBarAction: React.FC = ({ type, link }) => { case 'DOWNLOAD_TARBALL': return ( - + diff --git a/src/components/ActionBar/download-tarball.ts b/src/components/ActionBar/download-tarball.ts deleted file mode 100644 index 0a4eb3958..000000000 --- a/src/components/ActionBar/download-tarball.ts +++ /dev/null @@ -1,18 +0,0 @@ -import api from 'verdaccio-ui/utils/api'; -import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url'; - -function downloadTarball(link: string) { - return async function downloadHandler(): Promise { - const fileStream: Blob = await api.request(link, 'GET', { - headers: { - ['accept']: - 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', - }, - credentials: 'include', - }); - const fileName = extractFileName(link); - downloadFile(fileStream, fileName); - }; -} - -export default downloadTarball; diff --git a/src/components/ActionBar/index.ts b/src/components/ActionBar/index.ts index 518dc2495..d08aed069 100644 --- a/src/components/ActionBar/index.ts +++ b/src/components/ActionBar/index.ts @@ -1,2 +1 @@ export { default } from './ActionBar'; -export { default as downloadTarball } from './download-tarball'; diff --git a/src/index.tsx b/src/index.tsx index 56f977fb2..df0cda3e2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,18 +4,24 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { AppContainer } from 'react-hot-loader'; +import APIProvider from 'verdaccio-ui/providers/API/APIProvider'; +import AppConfigurationContext from 'verdaccio-ui/providers/config'; + import App from './App'; import StyleBaseline from './design-tokens/StyleBaseline'; import ThemeProvider from './design-tokens/ThemeProvider'; const rootNode = document.getElementById('root'); - const renderApp = (Component: React.ElementType): void => { ReactDOM.render( - + + + + + , rootNode diff --git a/src/pages/Version/DetailSidebar/ActionBar/download-tarball.ts b/src/pages/Version/DetailSidebar/ActionBar/download-tarball.ts deleted file mode 100644 index 0a4eb3958..000000000 --- a/src/pages/Version/DetailSidebar/ActionBar/download-tarball.ts +++ /dev/null @@ -1,18 +0,0 @@ -import api from 'verdaccio-ui/utils/api'; -import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url'; - -function downloadTarball(link: string) { - return async function downloadHandler(): Promise { - const fileStream: Blob = await api.request(link, 'GET', { - headers: { - ['accept']: - 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', - }, - credentials: 'include', - }); - const fileName = extractFileName(link); - downloadFile(fileStream, fileName); - }; -} - -export default downloadTarball; diff --git a/src/pages/Version/VersionContextProvider.tsx b/src/pages/Version/VersionContextProvider.tsx index 755ef2f75..1eab5e0fd 100644 --- a/src/pages/Version/VersionContextProvider.tsx +++ b/src/pages/Version/VersionContextProvider.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { useParams } from 'react-router-dom'; import { PackageMetaInterface } from 'types/packageMeta'; -import { callDetailPage, callReadme } from 'verdaccio-ui/utils/calls'; +import { useAPI } from 'verdaccio-ui/providers/API/APIProvider'; import { DetailContext } from './context'; import getRouterPackageName from './get-route-package-name'; @@ -22,6 +22,7 @@ const VersionContextProvider: React.FC = ({ children }) => { const [readMe, setReadme] = useState(); const [isLoading, setIsLoading] = useState(true); const [hasNotBeenFound, setHasNotBeenFound] = useState(); + const { callDetailPage, callReadme } = useAPI(); useEffect(() => { const updatedPackageName = getRouterPackageName(pkgName, scope); @@ -32,25 +33,27 @@ const VersionContextProvider: React.FC = ({ children }) => { setPackageVersion(version); }, [version]); - useEffect(() => { - (async () => { - try { - const packageMeta = await callDetailPage(packageName, packageVersion); - const readMe = await callReadme(packageName, packageVersion); - if (isPackageVersionValid(packageMeta, packageVersion)) { - setReadme(readMe); - setPackageMeta(packageMeta); - setIsLoading(false); - } else { - setIsLoading(false); - setHasNotBeenFound(true); - } - } catch (error) { - setHasNotBeenFound(true); + const doCalls = useCallback(async () => { + try { + const packageMeta = await callDetailPage(packageName, packageVersion); + const readMe = await callReadme(packageName, packageVersion); + if (isPackageVersionValid(packageMeta, packageVersion)) { + setReadme(readMe); + setPackageMeta(packageMeta); + setIsLoading(false); + } else { setIsLoading(false); + setHasNotBeenFound(true); } - })(); - }, [packageName, packageVersion]); + } catch (error) { + setHasNotBeenFound(true); + setIsLoading(false); + } + }, [packageName, packageVersion, callDetailPage, callReadme]); + + useEffect(() => { + doCalls(); + }, [doCalls]); return ( = ({ isUserLoggedIn }) => { const [packages, setPackages] = useState([]); + const { getPackages } = useAPI(); const [isLoading, setIsLoading] = useState(true); - const loadPackages = async () => { + const loadPackages = useCallback(async () => { try { - const packages = await API.request('packages', 'GET'); + const packages = await getPackages(); // FIXME add correct type for package setPackages(packages as never[]); } catch (error) { @@ -25,10 +26,10 @@ const Home: React.FC = ({ isUserLoggedIn }) => { }); } setIsLoading(false); - }; + }, [getPackages]); useEffect(() => { loadPackages().then(); - }, [isUserLoggedIn]); + }, [isUserLoggedIn, loadPackages]); return (
diff --git a/src/pages/home/PackageList/Package/Package.tsx b/src/pages/home/PackageList/Package/Package.tsx index a38c840c8..816adcb19 100644 --- a/src/pages/home/PackageList/Package/Package.tsx +++ b/src/pages/home/PackageList/Package/Package.tsx @@ -3,18 +3,20 @@ import styled from '@emotion/styled'; import BugReport from '@material-ui/icons/BugReport'; import DownloadIcon from '@material-ui/icons/CloudDownload'; import HomeIcon from '@material-ui/icons/Home'; -import React from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { downloadTarball } from 'verdaccio-ui/components/ActionBar'; import Grid from 'verdaccio-ui/components/Grid'; import { Version, FileBinary, Time, Law } from 'verdaccio-ui/components/Icons'; import Link from 'verdaccio-ui/components/Link'; import ListItem from 'verdaccio-ui/components/ListItem'; import Tooltip from 'verdaccio-ui/components/Tooltip'; import { Theme } from 'verdaccio-ui/design-tokens/theme'; +import { useAPI } from 'verdaccio-ui/providers/API/APIProvider'; +import { useConfig } from 'verdaccio-ui/providers/config'; import fileSizeSI from 'verdaccio-ui/utils/file-size'; import { formatDate, formatDateDistance, getAuthorName } from 'verdaccio-ui/utils/package'; +import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url'; import { isURL } from 'verdaccio-ui/utils/url'; import { PackageMetaInterface, Author as PackageAuthor } from '../../../../../types/packageMeta'; @@ -71,6 +73,19 @@ const Package: React.FC = ({ version, }) => { const { t } = useTranslation(); + const { getResource } = useAPI(); + const { configOptions } = useConfig(); + + const handleDownload = useCallback( + async (tarballDist: string) => { + // FIXME: check, the dist might be different thant npmjs + const link = tarballDist.replace(`https://registry.npmjs.org/`, configOptions.base as string); + const fileStream = await getResource(link); + const fileName = extractFileName(link); + downloadFile(fileStream, fileName); + }, + [getResource, configOptions] + ); const renderVersionInfo = (): React.ReactNode => version && ( @@ -93,8 +108,7 @@ const Package: React.FC = ({ }; const renderFileSize = (): React.ReactNode => - dist && - dist.unpackedSize && ( + dist?.unpackedSize && ( {fileSizeSI(dist.unpackedSize)} @@ -131,8 +145,7 @@ const Package: React.FC = ({ ); const renderBugsLink = (): React.ReactNode => - bugs && - bugs.url && + bugs?.url && isURL(bugs.url) && ( @@ -144,11 +157,15 @@ const Package: React.FC = ({ ); const renderDownloadLink = (): React.ReactNode => - dist && - dist.tarball && + dist?.tarball && isURL(dist.tarball) && ( // eslint-disable-next-line - + { + handleDownload(dist.tarball); + }} + to="#"> diff --git a/src/pages/home/PackageList/Package/styles.ts b/src/pages/home/PackageList/Package/styles.ts index 6c4d1e9d9..7eea62093 100644 --- a/src/pages/home/PackageList/Package/styles.ts +++ b/src/pages/home/PackageList/Package/styles.ts @@ -16,12 +16,12 @@ export const OverviewItem = styled('span')<{ theme?: Theme }>(({ theme }) => ({ margin: '0 0 0 16px', color: theme?.palette.type === 'light' ? theme?.palette.greyLight2 : theme?.palette.white, fontSize: 12, - [`@media (max-width: ${theme && theme.breakPoints.medium}px)`]: { + [`@media (max-width: ${theme?.breakPoints.medium}px)`]: { ':nth-of-type(3)': { display: 'none', }, }, - [`@media (max-width: ${theme && theme.breakPoints.small}px)`]: { + [`@media (max-width: ${theme?.breakPoints.small}px)`]: { ':nth-of-type(4)': { display: 'none', }, @@ -61,13 +61,13 @@ export const WrapperLink = styled(Link)({ }); export const PackageTitle = styled('span')<{ theme?: Theme }>(({ theme }) => ({ - fontWeight: theme && theme.fontWeight.bold, + fontWeight: theme?.fontWeight.bold, fontSize: 20, display: 'block', marginBottom: 12, color: theme?.palette.type == 'dark' ? theme?.palette.dodgerBlue : theme?.palette.eclipse, cursor: 'pointer', - [`@media (max-width: ${theme && theme.breakPoints.small}px)`]: { + [`@media (max-width: ${theme?.breakPoints.small}px)`]: { fontSize: 14, marginBottom: 8, }, @@ -95,7 +95,7 @@ export const TagContainer = styled('span')<{ theme?: Theme }>(({ theme }) => ({ marginBottom: 12, display: 'flex', flexWrap: 'wrap', - [`@media (max-width: ${theme && theme.breakPoints.medium}px)`]: { + [`@media (max-width: ${theme?.breakPoints.medium}px)`]: { display: 'none', }, })); diff --git a/src/providers/API/APIProvider.tsx b/src/providers/API/APIProvider.tsx new file mode 100644 index 000000000..fd11e2d30 --- /dev/null +++ b/src/providers/API/APIProvider.tsx @@ -0,0 +1,118 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { createContext, FunctionComponent, useContext, useMemo } from 'react'; + +import { useConfig } from 'verdaccio-ui/providers/config'; + +import { HEADERS } from '../../../lib/constants'; +import { PackageMetaInterface } from '../../../types/packageMeta'; + +import API from './api'; + +type ConfigProviderProps = { + callReadme: (packageName: string, packageVersion?: string) => Promise; + callDetailPage: (packageName: string, packageVersion?: string) => Promise; + callSearch: (value: string, signal: AbortSignal) => Promise; + getPackages: () => Promise; + doLogin: (username: string, password: string) => Promise; + getResource: (link: string) => Promise; +}; + +export interface LoginError { + type: string; + description: string; +} + +export interface LoginBody { + username?: string; + token?: string; + error?: LoginError; +} + +// eslint-disable-next-line @typescript-eslint/ban-ts-ignore +// @ts-ignore +const AppAPIContext = createContext({}); + +const APIProvider: FunctionComponent = ({ children }) => { + const { configOptions } = useConfig(); + + const buildURL = (basePath: string) => { + const url = `${configOptions?.base}-/verdaccio/${basePath}`; + + return url; + }; + + const callReadme = async (packageName: string, packageVersion?: string): Promise => { + return await API.request( + buildURL(`package/readme/${packageName}${packageVersion ? `?v=${packageVersion}` : ''}`), + 'GET' + ); + }; + + const callDetailPage = async (packageName: string, packageVersion?: string): Promise => { + const packageMeta = await API.request( + buildURL(`sidebar/${packageName}${packageVersion ? `?v=${packageVersion}` : ''}`), + 'GET' + ); + + return packageMeta; + }; + + const callSearch = async (value: string, signal: AbortSignal): Promise => { + // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#Browser_compatibility + // FUTURE: signal is not well supported for IE and Samsung Browser + return API.request(buildURL(`search/${encodeURIComponent(value)}`), 'GET', { signal, headers: {} }); + }; + + const getPackages = async (): Promise => { + const packages = await API.request(buildURL('packages'), 'GET'); + + return packages; + }; + + const doLogin = async (username: string, password: string): Promise => { + const response: LoginBody = await API.request(buildURL('login'), 'POST', { + body: JSON.stringify({ username, password }), + headers: { + Accept: HEADERS.JSON, + 'Content-Type': HEADERS.JSON, + }, + }); + const result: LoginBody = { + username: response.username, + token: response.token, + }; + return result; + }; + + const getResource = async (link: string): Promise => { + const fileStream: Blob = await API.request(link, 'GET', { + headers: { + ['accept']: + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', + }, + credentials: 'include', + }); + + return fileStream; + }; + + const value = useMemo( + () => ({ + callReadme, + callDetailPage, + callSearch, + getPackages, + doLogin, + getResource, + }), + [callReadme, getResource, callDetailPage, callSearch, doLogin] + ); + + return {children}; +}; + +export default APIProvider; + +const useAPI = () => useContext(AppAPIContext); + +export { useAPI }; diff --git a/src/utils/api.test.ts b/src/providers/API/api.test.ts similarity index 85% rename from src/utils/api.test.ts rename to src/providers/API/api.test.ts index 1c9ba8703..175a9cea9 100644 --- a/src/utils/api.test.ts +++ b/src/providers/API/api.test.ts @@ -1,4 +1,4 @@ -import api, { handleResponseType } from '../../src/utils/api'; +import api, { handleResponseType } from './api'; describe('api', () => { describe('handleResponseType', () => { @@ -43,19 +43,6 @@ describe('api', () => { fetchSpy.mockRestore(); }); - test('when there is no VERDACCIO_URL is defined', () => { - const { VERDACCIO_API_URL } = window; - delete window.VERDACCIO_API_URL; - // @ts-ignore - window.VERDACCIO_API_URL = undefined; - - expect(() => { - api.request('https://verdaccio.tld'); - }).toThrow(new Error('VERDACCIO_API_URL is not defined!')); - - window.VERDACCIO_API_URL = VERDACCIO_API_URL; - }); - test('when url is a resource url', async () => { fetchSpy.mockImplementation(() => Promise.resolve({ @@ -67,7 +54,7 @@ describe('api', () => { }) ); - const response = await api.request('/resource'); + const response = await api.request('https://verdaccio.tld/resource'); expect(fetchSpy).toHaveBeenCalledWith('https://verdaccio.tld/resource', { credentials: 'same-origin', @@ -91,8 +78,8 @@ describe('api', () => { }) ); - const api = require('../../src/utils/api').default; - const response = await api.request('/resource', 'GET'); + const api = require('./api').default; + const response = await api.request('https://verdaccio.tld/resource', 'GET'); expect(fetchSpy).toHaveBeenCalledWith('https://verdaccio.tld/resource', { credentials: 'same-origin', diff --git a/src/utils/api.ts b/src/providers/API/api.ts similarity index 78% rename from src/utils/api.ts rename to src/providers/API/api.ts index 21b83d57d..a70f57355 100644 --- a/src/utils/api.ts +++ b/src/providers/API/api.ts @@ -1,5 +1,5 @@ import storage from './storage'; -import '../../types'; +import '../../../types'; /** * Handles response according to content type @@ -9,14 +9,14 @@ import '../../types'; export function handleResponseType(response: Response): Promise<[boolean, any]> { if (response.headers) { const contentType = response.headers.get('Content-Type'); - if (contentType && contentType.includes('application/pdf')) { + if (contentType?.includes('application/pdf')) { return Promise.all([response.ok, response.blob()]); } - if (contentType && contentType.includes('application/json')) { + if (contentType?.includes('application/json')) { return Promise.all([response.ok, response.json()]); } // it includes all text types - if (contentType && contentType.includes('text/')) { + if (contentType?.includes('text/')) { return Promise.all([response.ok, response.text()]); } @@ -31,10 +31,6 @@ export function handleResponseType(response: Response): Promise<[boolean, any]> class API { public request(url: string, method = 'GET', options: RequestInit = { headers: {} }): Promise { - if (!window.VERDACCIO_API_URL) { - throw new Error('VERDACCIO_API_URL is not defined!'); - } - const token = storage.getItem('token'); const headers = new Headers(options.headers); @@ -43,10 +39,6 @@ class API { options.headers = headers; } - if (!['http://', 'https://', '//'].some(prefix => url.startsWith(prefix))) { - url = window.VERDACCIO_API_URL + url; - } - return new Promise((resolve, reject) => { fetch(url, { method, @@ -58,6 +50,7 @@ class API { if (response[0]) { resolve(response[1]); } else { + console.error(response); reject(new Error('something went wrong')); } }) diff --git a/src/providers/API/index.ts b/src/providers/API/index.ts new file mode 100644 index 000000000..437e8bae6 --- /dev/null +++ b/src/providers/API/index.ts @@ -0,0 +1 @@ +export { default, useAPI } from './APIProvider'; diff --git a/src/providers/API/storage.ts b/src/providers/API/storage.ts new file mode 100644 index 000000000..390270376 --- /dev/null +++ b/src/providers/API/storage.ts @@ -0,0 +1,12 @@ +import memoryStorage from 'localstorage-memory'; + +let storage: Storage; +try { + localStorage.setItem('__TEST__', ''); + localStorage.removeItem('__TEST__'); + storage = localStorage; +} catch (err) { + storage = memoryStorage; +} + +export default storage; diff --git a/src/providers/config/AppConfigurationProvider.tsx b/src/providers/config/AppConfigurationProvider.tsx new file mode 100644 index 000000000..a1057a615 --- /dev/null +++ b/src/providers/config/AppConfigurationProvider.tsx @@ -0,0 +1,41 @@ +import React, { createContext, FunctionComponent, useContext, useMemo, useState } from 'react'; + +export type VerdaccioOptions = { + url_prefix?: string; + base?: string; + language?: string; + darkMode?: boolean; +}; + +type ConfigProviderProps = { + configOptions: VerdaccioOptions; + setConfigOptions: any; +}; + +const defaultValues: ConfigProviderProps = { + configOptions: {}, + // eslint-disable-next-line @typescript-eslint/no-empty-function + setConfigOptions: () => {}, +}; + +const AppConfigurationContext = createContext(defaultValues); + +const AppConfigurationProvider: FunctionComponent = ({ children }) => { + const [configOptions, setConfigOptions] = useState(window?.__VERDACCIO_BASENAME_UI_OPTIONS ?? {}); + + const value = useMemo( + () => ({ + configOptions, + setConfigOptions, + }), + [configOptions] + ); + + return {children}; +}; + +export default AppConfigurationProvider; + +const useConfig = () => useContext(AppConfigurationContext); + +export { useConfig }; diff --git a/src/providers/config/index.ts b/src/providers/config/index.ts new file mode 100644 index 000000000..e7d94eb07 --- /dev/null +++ b/src/providers/config/index.ts @@ -0,0 +1 @@ +export { default, useConfig } from './AppConfigurationProvider'; diff --git a/src/template/index.html b/src/template/index.html index 6dd5c354b..b7d8d72fa 100644 --- a/src/template/index.html +++ b/src/template/index.html @@ -6,7 +6,8 @@ <%= htmlWebpackPlugin.options.title %> - + + diff --git a/types/index.ts b/types/index.ts index 2b24b1372..0ae02a793 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,8 +1,15 @@ // FIXME: this should comes from @verdaccio/types export interface VerdaccioOptions { - url_prefix: string; - base: string; + url_prefix?: string; + uri?: string; + base?: string; language?: string; + version?: string; + protocol?: string; + host?: string; + scope?: string; + title?: string; + primaryColor?: string; darkMode?: boolean; } @@ -12,6 +19,5 @@ declare global { VERDACCIO_PRIMARY_COLOR: string; VERDACCIO_LOGO: string; VERDACCIO_SCOPE: string; - VERDACCIO_VERSION: string; } } diff --git a/yarn.lock b/yarn.lock index 030b3368b..88844349e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3722,29 +3722,29 @@ __metadata: languageName: node linkType: hard -"@verdaccio/local-storage@npm:9.7.2": - version: 9.7.2 - resolution: "@verdaccio/local-storage@npm:9.7.2" +"@verdaccio/local-storage@npm:9.7.5": + version: 9.7.5 + resolution: "@verdaccio/local-storage@npm:9.7.5" dependencies: "@verdaccio/commons-api": ^9.7.1 "@verdaccio/file-locking": ^9.7.2 "@verdaccio/streams": ^9.7.2 async: 3.2.0 level: 5.0.1 - lodash: 4.17.19 + lodash: 4.17.21 mkdirp: 0.5.5 - checksum: 178a202ae8c80aab9c35a41cb9f9e7267633876a810f6eb44919ae11a2980ca87fe747398503462dcd84adbcd694bebaf76992dcbbe29bff2035cab00330deb6 + checksum: 98989ecf635a68dcac2debbcd5bbede41f25a82dd090426031c74bb99eee783a2dcc25e81055ab040da8f52e07131b2a3cd1b0db6e78d290353ea9b78dde728a languageName: node linkType: hard -"@verdaccio/readme@npm:9.7.3": - version: 9.7.3 - resolution: "@verdaccio/readme@npm:9.7.3" +"@verdaccio/readme@npm:9.7.5": + version: 9.7.5 + resolution: "@verdaccio/readme@npm:9.7.5" dependencies: - dompurify: 2.0.8 + dompurify: ^2.2.6 jsdom: 15.2.1 - marked: 1.1.1 - checksum: 72eb013673bcb3804da79b13a56a23527eef4a1017e06d89e683bff5a9b64556b6c46d659999190d54de2a8280b449fd914e07bf233d4add5da294244f6a4c67 + marked: ^2.0.1 + checksum: 177bb41a3ae339df067abac744b1c4fea7edb632bcb9b05dbe9ed6db5116f49c6daf52aeb06615340af6b8fc13da2e51cb54f7b01772eb5419c4a7bd1d2849a5 languageName: node linkType: hard @@ -3762,10 +3762,10 @@ __metadata: languageName: node linkType: hard -"@verdaccio/ui-theme@npm:1.12.1": - version: 1.12.1 - resolution: "@verdaccio/ui-theme@npm:1.12.1" - checksum: baa5d3d486bbdfb60d7c159a61a97a98219c0f662ee58b83a98468a5e1827f732d2a692a0dc392e21d50ecfd70700c91d56cae0de7aeadc182925d50ea0e94df +"@verdaccio/ui-theme@npm:1.15.1": + version: 1.15.1 + resolution: "@verdaccio/ui-theme@npm:1.15.1" + checksum: e6dfeaae7043daa639042345f62ba4d872a7457984b751ae39694f504c33b09d875c42d72b58df9968bed80cf8e49c6554cc6062a0884dd0c1869a2ae40722aa languageName: node linkType: hard @@ -3913,7 +3913,7 @@ __metadata: typescript: 3.9.6 url-loader: ^4.1.1 validator: 13.1.1 - verdaccio: 4.8.1 + verdaccio: 4.11.3 verdaccio-auth-memory: 9.7.2 verdaccio-memory: 9.7.2 wait-on: 5.2.0 @@ -5544,9 +5544,9 @@ __metadata: languageName: node linkType: hard -"bunyan@npm:1.8.14": - version: 1.8.14 - resolution: "bunyan@npm:1.8.14" +"bunyan@npm:1.8.15": + version: 1.8.15 + resolution: "bunyan@npm:1.8.15" dependencies: dtrace-provider: ~0.8 moment: ^2.19.3 @@ -5563,7 +5563,7 @@ __metadata: optional: true bin: bunyan: bin/bunyan - checksum: f2105894ef1f429e45af710778972e4ee7c3f76401477e287562ffed214061684c491727d4fc8b3400911aab61270791d9263bcdce288c5d255f0ca84e8e605a + checksum: 73c30ffb8c1a6b8dfe9dc3bca325bba85cb706442aa78cc44fdc93cf16b929b0b7c1884b92110fac9f287342a94f2ae8ebe8c83664b09c7f6743ffe1621c204c languageName: node linkType: hard @@ -7396,10 +7396,10 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:1.8.28": - version: 1.8.28 - resolution: "dayjs@npm:1.8.28" - checksum: 1d8a48d3b4888f3b6c0443b69db71105fa47a6ca45b34f02516c6c2367bd2fded163cfcdf292262b85171c91ef1a445f674d0c2f74e869741078d9044136700f +"dayjs@npm:1.10.4": + version: 1.10.4 + resolution: "dayjs@npm:1.10.4" + checksum: 3b7bb2232fff808209870bc72d4b2000941a1aa45f0226e2907f9dd1dda306d4b3a1eb9058450fd2c324b01a007a37809ac6f9b525806a86902c55e2934cd5d5 languageName: node linkType: hard @@ -7915,10 +7915,10 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:2.0.8": - version: 2.0.8 - resolution: "dompurify@npm:2.0.8" - checksum: 3089aeaf76d0889a7233102355ca8a394208a69a5060d777d0db61d9ffd96a40af48d0462107ffe19e81cd2d3389baf77203d4cb557bc9145185e58c7551412a +"dompurify@npm:^2.2.6": + version: 2.2.7 + resolution: "dompurify@npm:2.2.7" + checksum: ddf23c494b0417c764384817716a19daebd7c3c528ce26360390952d6661679d9058ccc643ea8930223201a41a89affb07c3f01d1c6cbe057d01c6607e01b6c4 languageName: node linkType: hard @@ -8213,12 +8213,12 @@ __metadata: languageName: node linkType: hard -"envinfo@npm:7.5.1": - version: 7.5.1 - resolution: "envinfo@npm:7.5.1" +"envinfo@npm:7.7.4": + version: 7.7.4 + resolution: "envinfo@npm:7.7.4" bin: envinfo: dist/cli.js - checksum: 0c3a8c1deb2c855298d56d21bc1f2b6065b34f2bc6e85a2b5aa85148d1b215bd0544ed91683f2e2f6543ee2df2c717713d67b4f2a66742ba4f382c789e372810 + checksum: 0a3ffb4ad515a9c7068824d57da6d146205478da71b54d3129d364eacd429fea2e8fb7921a66acd6773af0d066a849f517ab4a694a91eba6ef508d9a9771214a languageName: node linkType: hard @@ -10211,7 +10211,25 @@ fsevents@^1.2.7: languageName: node linkType: hard -"handlebars@npm:4.7.6, handlebars@npm:^4.7.6": +"handlebars@npm:4.7.7": + version: 4.7.7 + resolution: "handlebars@npm:4.7.7" + dependencies: + minimist: ^1.2.5 + neo-async: ^2.6.0 + source-map: ^0.6.1 + uglify-js: ^3.1.4 + wordwrap: ^1.0.0 + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 2df9a6b422e2ccc0b7ca53f7a1f9915b47d19bf3fd372824a87e2a28b7952fa2cb3348cbe33a87ef49ee04f42d10359aab44819ca8d680ee3a5b53d48bd062a1 + languageName: node + linkType: hard + +"handlebars@npm:^4.7.6": version: 4.7.6 resolution: "handlebars@npm:4.7.6" dependencies: @@ -12294,6 +12312,18 @@ fsevents@^1.2.7: languageName: node linkType: hard +"js-yaml@npm:3.14.1": + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" + dependencies: + argparse: ^1.0.7 + esprima: ^4.0.0 + bin: + js-yaml: bin/js-yaml.js + checksum: 46b61f889796a20d16b0b64580a01b6a02b2e45c1a2744906346da54d07e14cde764e887ab6d1512d8b2541c63711bd4b75859c28eb99605baf59fa173fc38c2 + languageName: node + linkType: hard + "js2xmlparser@npm:^3.0.0": version: 3.0.0 resolution: "js2xmlparser@npm:3.0.0" @@ -12712,10 +12742,10 @@ fsevents@^1.2.7: languageName: node linkType: hard -"kleur@npm:4.0.2": - version: 4.0.2 - resolution: "kleur@npm:4.0.2" - checksum: 6be2b659cfb17dafc0e4335d3b7536bb2304180f51c8fdcebb748a26e4fac8958c25f8ad5f965b532ca1946f2201f63847d9a1eb2f3f3086a3e04824bf02485b +"kleur@npm:4.1.4": + version: 4.1.4 + resolution: "kleur@npm:4.1.4" + checksum: 18e52c52a0138448dea0137d09ace853e096f1bbc32b99d8781254258b892214718f5f7c2bc25f25d5fb84b4086bca465a4c0b7562e27ae3a25116f77ea6436f languageName: node linkType: hard @@ -13328,10 +13358,10 @@ fsevents@^1.2.7: languageName: node linkType: hard -"lodash@npm:4.17.19": - version: 4.17.19 - resolution: "lodash@npm:4.17.19" - checksum: ff2b7a95f0129dba9101e346d44e0eda0f159d76bbbf23721eec1969b87a32bde3de0cfef0733218c64620e9be08040a973278d46a686540233b356115f3527c +"lodash@npm:4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 4983720b9abca930a4a46f18db163d7dad8dd00dbed6db0cc7b499b33b717cce69f80928b27bbb1ff2cbd3b19d251ee90669a8b5ea466072ca81c2ebe91e7468 languageName: node linkType: hard @@ -13470,6 +13500,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: ^4.0.0 + checksum: b8b78353d2391c0f135cdc245c4744ad41c2efb1a6d98f31bc57a2cf48ebf02de96e4876657c3026673576bf1f1f61fc3fdd77ab00ad1ead737537bf17d8019d + languageName: node + linkType: hard + "ltgt@npm:^2.1.2": version: 2.2.1 resolution: "ltgt@npm:2.2.1" @@ -13590,12 +13629,12 @@ fsevents@^1.2.7: languageName: node linkType: hard -"marked@npm:1.1.1": - version: 1.1.1 - resolution: "marked@npm:1.1.1" +"marked@npm:2.0.1, marked@npm:^2.0.1": + version: 2.0.1 + resolution: "marked@npm:2.0.1" bin: marked: bin/marked - checksum: 4fb077a99ca6a6a4f389bbe2f27620509a332da6fac019560fc25e0e9636226cc86420ddbcfc6e8956920cbca68d64cd11d03954d22123a95ffc63c2e6c4e0ae + checksum: dd6f468a96ddfd6efbfdaa2f0fa61899b2ce1f8dac0a93c13d847d0fca6872d1f32362d7fe097932aeabc1dfbd83e33bb009a159860f60551d4c6adbdc5a0faf languageName: node linkType: hard @@ -13805,7 +13844,16 @@ fsevents@^1.2.7: languageName: node linkType: hard -"mime@npm:2.4.6, mime@npm:^2.3.1, mime@npm:^2.4.4": +"mime@npm:2.5.2": + version: 2.5.2 + resolution: "mime@npm:2.5.2" + bin: + mime: cli.js + checksum: 3e5377f0a1891350247699c5fff0469752a35d5c0baeb7cbee86907c143215ee8621d17c17401f10ffe020a0b327aa503b98cb7340039fce69bc465aed414fb7 + languageName: node + linkType: hard + +"mime@npm:^2.3.1, mime@npm:^2.4.4": version: 2.4.6 resolution: "mime@npm:2.4.6" bin: @@ -17601,15 +17649,6 @@ resolve@~1.7.1: languageName: node linkType: hard -"semver@npm:6.3.0, semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0": - version: 6.3.0 - resolution: "semver@npm:6.3.0" - bin: - semver: ./bin/semver.js - checksum: f0d155c06a67cc7e500c92d929339f1c6efd4ce9fe398aee6acc00a2333489cca0f5b4e76ee7292beba237fcca4b5a3d4a6153471f105f56299801bdab37289f - languageName: node - linkType: hard - "semver@npm:7.0.0": version: 7.0.0 resolution: "semver@npm:7.0.0" @@ -17628,6 +17667,26 @@ resolve@~1.7.1: languageName: node linkType: hard +"semver@npm:7.3.4": + version: 7.3.4 + resolution: "semver@npm:7.3.4" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: f2c7f9aeb976d1484b2f39aa7afc8332a1d21fd32ca4a6fbf650e1423455ebf3e7029f6e2e7ba0cd71935b85942521f1ec25b6cc2c031b953c8ca4ff2d7a823d + languageName: node + linkType: hard + +"semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0": + version: 6.3.0 + resolution: "semver@npm:6.3.0" + bin: + semver: ./bin/semver.js + checksum: f0d155c06a67cc7e500c92d929339f1c6efd4ce9fe398aee6acc00a2333489cca0f5b4e76ee7292beba237fcca4b5a3d4a6153471f105f56299801bdab37289f + languageName: node + linkType: hard + "send@npm:0.17.1": version: 0.17.1 resolution: "send@npm:0.17.1" @@ -19934,46 +19993,46 @@ typescript@3.9.6: languageName: node linkType: hard -"verdaccio@npm:4.8.1": - version: 4.8.1 - resolution: "verdaccio@npm:4.8.1" +"verdaccio@npm:4.11.3": + version: 4.11.3 + resolution: "verdaccio@npm:4.11.3" dependencies: "@verdaccio/commons-api": 9.7.1 - "@verdaccio/local-storage": 9.7.2 - "@verdaccio/readme": 9.7.3 + "@verdaccio/local-storage": 9.7.5 + "@verdaccio/readme": 9.7.5 "@verdaccio/streams": 9.7.2 - "@verdaccio/ui-theme": 1.12.1 + "@verdaccio/ui-theme": 1.15.1 JSONStream: 1.3.5 async: 3.2.0 body-parser: 1.19.0 - bunyan: 1.8.14 + bunyan: 1.8.15 commander: 3.0.2 compression: 1.7.4 cookies: 0.8.0 cors: 2.8.5 - dayjs: 1.8.28 - envinfo: 7.5.1 + dayjs: 1.10.4 + envinfo: 7.7.4 express: 4.17.1 - handlebars: 4.7.6 + handlebars: 4.7.7 http-errors: 1.8.0 - js-yaml: 3.14.0 + js-yaml: 3.14.1 jsonwebtoken: 8.5.1 - kleur: 4.0.2 - lodash: 4.17.19 + kleur: 4.1.4 + lodash: 4.17.21 lunr-mutable-indexes: 2.3.2 - marked: 1.1.1 - mime: 2.4.6 + marked: 2.0.1 + mime: 2.5.2 minimatch: 3.0.4 mkdirp: 0.5.5 mv: 2.1.1 pkginfo: 0.4.1 request: 2.88.0 - semver: 6.3.0 + semver: 7.3.4 verdaccio-audit: 9.7.3 verdaccio-htpasswd: 9.7.2 bin: verdaccio: bin/verdaccio - checksum: c613b7b1ebe120502508d6a79fb9498742251075f0b29310aa932f9b766bf61cb642ee66d4c93c8c3508bc329fb1e620ebfffe963786cd4aba8a02ed0c0be3b4 + checksum: 97e5a578bd94bed063ed1ab3320f9356dea14c336aee52426f5aeae0fa7953abc68a9a883e8dec13123fca167394c33e7c2d9e2b8330b41bfee927e9553dfc8c languageName: node linkType: hard From e54a2059177f9239287acc222da676143672c129 Mon Sep 17 00:00:00 2001 From: Juan Picado Date: Sun, 14 Mar 2021 10:09:19 +0100 Subject: [PATCH 05/16] fix: lint --- src/App/Footer/Footer.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App/Footer/Footer.test.tsx b/src/App/Footer/Footer.test.tsx index 1883c76a0..6f2cace01 100644 --- a/src/App/Footer/Footer.test.tsx +++ b/src/App/Footer/Footer.test.tsx @@ -12,7 +12,7 @@ describe('