Update multiple packages (#719)

* Update multiple packages

- @typescript-eslint/eslint-plugin
- @typescript-eslint/parser
- eslint-config-react-app
- eslint-config-standard
- react-scripts

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* fix type

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* deduplicate code

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Disable test because it doesn't work

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* repair service worker

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Lazy load mermaid

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* use show error

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* fix tsconfig in cypress

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* fix import integration test

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
mrdrogdrog 2020-10-25 21:17:59 +01:00 committed by GitHub
parent 0c222fae64
commit 460badb97b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 4468 additions and 5286 deletions

View file

@ -2,7 +2,7 @@
"parserOptions": { "parserOptions": {
"tsconfigRootDir": "", "tsconfigRootDir": "",
"project": [ "project": [
"./cypress/tsconfig.json" "./tsconfig.json"
] ]
}, },
"plugins": [ "plugins": [

View file

@ -14,7 +14,7 @@ describe('Import markdown file', () => {
cy.get('.import-md-file') cy.get('.import-md-file')
.click() .click()
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]') cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
.attachFile('import.md') .attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file') .should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
@ -29,11 +29,11 @@ describe('Import markdown file', () => {
cy.get('.import-md-file') cy.get('.import-md-file')
.click() .click()
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]') cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
.attachFile('import.md') .attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', 'test') .should('have.text', 'test')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
.should('have.text', 'abc') .should('have.text', 'abc')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file') .should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span') cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span')

View file

@ -32,8 +32,8 @@
"@types/react-router-bootstrap": "0.24.5", "@types/react-router-bootstrap": "0.24.5",
"@types/react-router-dom": "5.1.6", "@types/react-router-dom": "5.1.6",
"@types/uuid": "8.3.0", "@types/uuid": "8.3.0",
"@typescript-eslint/eslint-plugin": "3.10.1", "@typescript-eslint/eslint-plugin": "4.5.0",
"@typescript-eslint/parser": "3.10.1", "@typescript-eslint/parser": "4.5.0",
"abcjs": "5.12.0", "abcjs": "5.12.0",
"bootstrap": "4.5.3", "bootstrap": "4.5.3",
"codemirror": "5.58.2", "codemirror": "5.58.2",
@ -42,8 +42,8 @@
"diff": "4.0.2", "diff": "4.0.2",
"emoji-picker-element": "1.2.1", "emoji-picker-element": "1.2.1",
"emojibase-data": "5.1.1", "emojibase-data": "5.1.1",
"eslint-config-react-app": "5.2.1", "eslint-config-react-app": "6.0.0",
"eslint-config-standard": "14.1.1", "eslint-config-standard": "15.0.0",
"eslint-plugin-flowtype": "5.2.0", "eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1", "eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.3.1", "eslint-plugin-jsx-a11y": "6.3.1",
@ -93,7 +93,7 @@
"react-router": "5.2.0", "react-router": "5.2.0",
"react-router-bootstrap": "0.25.0", "react-router-bootstrap": "0.25.0",
"react-router-dom": "5.2.0", "react-router-dom": "5.2.0",
"react-scripts": "3.4.4", "react-scripts": "4.0.0",
"react-use": "15.3.4", "react-use": "15.3.4",
"redux": "4.0.5", "redux": "4.0.5",
"ts-mockery": "1.2.0", "ts-mockery": "1.2.0",
@ -125,6 +125,9 @@
"./tsconfig.json" "./tsconfig.json"
] ]
}, },
"rules": {
"no-use-before-define": "off"
},
"plugins": [ "plugins": [
"@typescript-eslint" "@typescript-eslint"
], ],

View file

@ -15,15 +15,15 @@ export const Branding: React.FC<BrandingProps> = ({ inline = false }) => {
return ( return (
<ShowIf condition={showBranding}> <ShowIf condition={showBranding}>
<strong className={`mx-1 ${inline ? 'inline-size' : 'regular-size'}`} >@</strong> <strong className={`mx-1 ${inline ? 'inline-size' : 'regular-size'}`}>@</strong>
{ {
branding.logo branding.logo
? <img ? <img
src={branding.logo} src={branding.logo}
alt={branding.name} alt={branding.name}
title={branding.name} title={branding.name}
className={inline ? 'inline-size' : 'regular-size'} className={inline ? 'inline-size' : 'regular-size'}
/> />
: branding.name : branding.name
} }
</ShowIf> </ShowIf>

View file

@ -2,11 +2,11 @@ import React from 'react'
import { Trans } from 'react-i18next' import { Trans } from 'react-i18next'
import { IconButton, IconButtonProps } from './icon-button' import { IconButton, IconButtonProps } from './icon-button'
export interface TranslatedIconButton extends IconButtonProps { export interface TranslatedIconButtonProps extends IconButtonProps {
i18nKey: string i18nKey: string
} }
export const TranslatedIconButton: React.FC<TranslatedIconButton> = ({ i18nKey, ...props }) => { export const TranslatedIconButton: React.FC<TranslatedIconButtonProps> = ({ i18nKey, ...props }) => {
return ( return (
<IconButton {...props}> <IconButton {...props}>
<Trans i18nKey={i18nKey}/> <Trans i18nKey={i18nKey}/>

View file

@ -1,5 +1,5 @@
import React from 'react'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import React from 'react'
import { ListGroup } from 'react-bootstrap' import { ListGroup } from 'react-bootstrap'
import { Trans } from 'react-i18next' import { Trans } from 'react-i18next'
import { RevisionListEntry } from '../../../../api/revisions/types' import { RevisionListEntry } from '../../../../api/revisions/types'
@ -32,11 +32,11 @@ export const RevisionModalListEntry: React.FC<RevisionModalListEntryProps> = ({
<span className={'d-flex flex-row my-1 align-items-center'}> <span className={'d-flex flex-row my-1 align-items-center'}>
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/> <ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/>
{ {
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => { revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
return ( return (
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/> <UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
) )
}) })
} }
</span> </span>
</ListGroup.Item> </ListGroup.Item>

View file

@ -61,32 +61,34 @@ const tab = (editor: Editor) => {
} }
} }
export const defaultKeyMap: KeyMap = !isMac ? { export const defaultKeyMap: KeyMap = !isMac
F10: f10, ? {
Esc: esc, F10: f10,
'Ctrl-S': suppressSave, Esc: esc,
Enter: 'newlineAndIndentContinueMarkdownList', 'Ctrl-S': suppressSave,
Tab: tab, Enter: 'newlineAndIndentContinueMarkdownList',
Home: 'goLineLeftSmart', Tab: tab,
End: 'goLineRight', Home: 'goLineLeftSmart',
'Ctrl-I': makeSelectionItalic, End: 'goLineRight',
'Ctrl-B': makeSelectionBold, 'Ctrl-I': makeSelectionItalic,
'Ctrl-U': underlineSelection, 'Ctrl-B': makeSelectionBold,
'Ctrl-D': strikeThroughSelection, 'Ctrl-U': underlineSelection,
'Ctrl-M': markSelection 'Ctrl-D': strikeThroughSelection,
} : { 'Ctrl-M': markSelection
F10: f10, }
Esc: esc, : {
'Cmd-S': suppressSave, F10: f10,
Enter: 'newlineAndIndentContinueMarkdownList', Esc: esc,
Tab: tab, 'Cmd-S': suppressSave,
'Cmd-Left': 'goLineLeftSmart', Enter: 'newlineAndIndentContinueMarkdownList',
'Cmd-Right': 'goLineRight', Tab: tab,
Home: 'goLineLeftSmart', 'Cmd-Left': 'goLineLeftSmart',
End: 'goLineRight', 'Cmd-Right': 'goLineRight',
'Cmd-I': makeSelectionItalic, Home: 'goLineLeftSmart',
'Cmd-B': makeSelectionBold, End: 'goLineRight',
'Cmd-U': underlineSelection, 'Cmd-I': makeSelectionItalic,
'Cmd-D': strikeThroughSelection, 'Cmd-B': makeSelectionBold,
'Cmd-M': markSelection 'Cmd-U': underlineSelection,
} 'Cmd-D': strikeThroughSelection,
'Cmd-M': markSelection
}

View file

@ -14,7 +14,7 @@ export interface RawYAMLMetadata {
disqus: string | undefined disqus: string | undefined
type: string | undefined type: string | undefined
slideOptions: unknown slideOptions: unknown
opengraph: { [key: string]:string } opengraph: { [key: string]:string } | null
} }
export class YAMLMetaData { export class YAMLMetaData {

View file

@ -50,16 +50,16 @@ export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entri
const mapViewStateToComponent = (viewState: ViewStateEnum) => { const mapViewStateToComponent = (viewState: ViewStateEnum) => {
switch (viewState) { switch (viewState) {
default: case ViewStateEnum.TABLE:
case ViewStateEnum.CARD: return <HistoryTable entries={entries}
return <HistoryCardList entries={entries}
onPinClick={onPinClick} onPinClick={onPinClick}
onRemoveClick={onRemoveClick} onRemoveClick={onRemoveClick}
onDeleteClick={onDeleteClick} onDeleteClick={onDeleteClick}
pageIndex={pageIndex} pageIndex={pageIndex}
onLastPageIndexChange={setLastPageIndex}/> onLastPageIndexChange={setLastPageIndex}/>
case ViewStateEnum.TABLE: case ViewStateEnum.CARD:
return <HistoryTable entries={entries} default:
return <HistoryCardList entries={entries}
onPinClick={onPinClick} onPinClick={onPinClick}
onRemoveClick={onRemoveClick} onRemoveClick={onRemoveClick}
onDeleteClick={onDeleteClick} onDeleteClick={onDeleteClick}

View file

@ -11,13 +11,14 @@ export enum SortModeEnum {
const getIcon = (direction: SortModeEnum): IconName => { const getIcon = (direction: SortModeEnum): IconName => {
switch (direction) { switch (direction) {
default:
case SortModeEnum.no: case SortModeEnum.no:
return 'sort' return 'sort'
case SortModeEnum.up: case SortModeEnum.up:
return 'sort-asc' return 'sort-asc'
case SortModeEnum.down: case SortModeEnum.down:
return 'sort-desc' return 'sort-desc'
default:
return 'sort'
} }
} }
@ -32,8 +33,8 @@ const toggleDirection = (direction: SortModeEnum) => {
return SortModeEnum.up return SortModeEnum.up
case SortModeEnum.up: case SortModeEnum.up:
return SortModeEnum.down return SortModeEnum.down
default:
case SortModeEnum.down: case SortModeEnum.down:
default:
return SortModeEnum.no return SortModeEnum.no
} }
} }

View file

@ -27,17 +27,17 @@ const HeaderBar: React.FC = () => {
<div className="d-inline-flex"> <div className="d-inline-flex">
{!userExists {!userExists
? <Fragment> ? <Fragment>
<span className={'mx-1 d-flex'}> <span className={'mx-1 d-flex'}>
<NewGuestNoteButton/> <NewGuestNoteButton/>
</span> </span>
<SignInButton size="sm"/> <SignInButton size="sm"/>
</Fragment> </Fragment>
: <Fragment> : <Fragment>
<span className={'mx-1 d-flex'}> <span className={'mx-1 d-flex'}>
<NewUserNoteButton/> <NewUserNoteButton/>
</span> </span>
<UserDropdown/> <UserDropdown/>
</Fragment> </Fragment>
} }
</div> </div>
</Navbar> </Navbar>

View file

@ -39,10 +39,12 @@ export class FullMarkdownItConfigurator extends BasicMarkdownItConfigurator {
tasksLists, tasksLists,
(markdownIt) => { (markdownIt) => {
frontmatterExtract(markdownIt, frontmatterExtract(markdownIt,
!this.useFrontmatter ? undefined : { !this.useFrontmatter
onYamlError: (error: boolean) => this.onYamlError(error), ? undefined
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta) : {
}) onYamlError: (error: boolean) => this.onYamlError(error),
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta)
})
}, },
headlineAnchors, headlineAnchors,
KatexReplacer.markdownItPlugin, KatexReplacer.markdownItPlugin,

View file

@ -1,4 +1,3 @@
import mermaid from 'mermaid'
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Alert } from 'react-bootstrap' import { Alert } from 'react-bootstrap'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -22,30 +21,37 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
useEffect(() => { useEffect(() => {
if (!mermaidInitialized) { if (!mermaidInitialized) {
mermaid.initialize({ startOnLoad: false }) import('mermaid').then((mermaid) => {
mermaidInitialized = true mermaid.default.initialize({ startOnLoad: false })
mermaidInitialized = true
}).catch(() => { console.error('error while loading mermaid') })
} }
}, []) }, [])
const showError = useCallback((error: string) => { const showError = useCallback((error: string) => {
setError(error)
console.error(error)
if (!diagramContainer.current) { if (!diagramContainer.current) {
return return
} }
setError(error)
console.error(error)
diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove()) diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove())
}, []) }, [setError])
useEffect(() => { useEffect(() => {
if (!diagramContainer.current) { if (!diagramContainer.current) {
return return
} }
try { try {
mermaid.parse(code) import('mermaid').then((mermaid) => {
delete diagramContainer.current.dataset.processed if (!diagramContainer.current) {
diagramContainer.current.textContent = code return
mermaid.init(diagramContainer.current) }
setError(undefined) mermaid.default.parse(code)
delete diagramContainer.current.dataset.processed
diagramContainer.current.textContent = code
mermaid.default.init(diagramContainer.current)
setError(undefined)
}).catch(() => showError('Error while loading mermaid'))
} catch (error) { } catch (error) {
const message = (error as MermaidParseError).str const message = (error as MermaidParseError).str
showError(message || t('renderer.mermaid.unknownError')) showError(message || t('renderer.mermaid.unknownError'))

View file

@ -1,6 +1,5 @@
declare module 'markdown-it-mathjax' { declare module 'markdown-it-mathjax' {
import MarkdownIt from 'markdown-it/lib' import MarkdownIt from 'markdown-it/lib'
import { MathJaxOptions } from './interface'
const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple
export = markdownItMathJax export = markdownItMathJax
} }

View file

@ -14,7 +14,7 @@ import { PadViewOnly } from './components/pad-view-only/pad-view-only'
import { ProfilePage } from './components/profile-page/profile-page' import { ProfilePage } from './components/profile-page/profile-page'
import { RegisterPage } from './components/register-page/register-page' import { RegisterPage } from './components/register-page/register-page'
import { store } from './redux' import { store } from './redux'
import * as serviceWorker from './service-worker' import * as serviceWorkerRegistration from './service-worker-registration'
import './style/dark.scss' import './style/dark.scss'
import './style/index.scss' import './style/index.scss'
@ -77,4 +77,4 @@ ReactDOM.render(
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. // unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA // Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister() serviceWorkerRegistration.unregister()

View file

@ -0,0 +1,144 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
const localhostRegex = /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
localhostRegex.exec(window.location.hostname)
)
type Config = {
onSuccess?: (registration: ServiceWorkerRegistration) => void;
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register (config?: Config): void {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config)
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://cra.link/PWA'
)
}).catch((error: Error) => console.error(error))
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config)
}
})
}
}
function registerValidSW (swUrl: string, config?: Config) {
navigator.serviceWorker
.register(swUrl)
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing
if (installingWorker == null) {
return
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://cra.link/PWA.'
)
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration)
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.')
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration)
}
}
}
}
}
})
.catch((error) => {
console.error('Error during service worker registration:', error)
})
}
function checkValidServiceWorker (swUrl: string, config?: Config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type')
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload()
}).catch((error: Error) => console.error(error))
}).catch((error: Error) => console.error(error))
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config)
}
})
.catch(() => {
console.log('No internet connection found. App is running in offline mode.')
})
}
export function unregister (): void {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then((registration) => {
registration.unregister().catch((error: Error) => console.error(error))
})
.catch((error:Error) => {
console.error(error.message)
})
}
}

View file

@ -1,138 +1,81 @@
// This optional code is used to register a service worker. /// <reference lib="webworker" />
// register() is not called by default. /* eslint-disable no-restricted-globals */
// This lets the app load faster on subsequent visits in production, and gives // This service worker can be customized!
// it offline capabilities. However, it also means that developers (and users) // See https://developers.google.com/web/tools/workbox/modules
// will only see deployed updates on subsequent visits to a page, after all the // for the list of available Workbox modules, or add any other
// existing tabs open on the page have been closed, since previously cached // code you'd like.
// resources are updated in the background. // You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
// To learn more about the benefits of this model and instructions on how to import { clientsClaim } from 'workbox-core'
// opt-in, read https://bit.ly/CRA-PWA import { ExpirationPlugin } from 'workbox-expiration'
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'
import { StaleWhileRevalidate } from 'workbox-strategies'
const isLocalhost = Boolean( declare const self: ServiceWorkerGlobalScope
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
new RegExp(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/).exec(window.location.hostname)
)
type Config = { clientsClaim()
onSuccess?: (registration: ServiceWorkerRegistration) => void;
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register (config?: Config):void { // Precache all of the assets generated by your build process.
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // Their URLs are injected into the manifest variable below.
// The URL constructor is available in all browsers that support SW. // This variable must be present somewhere in your service worker file,
const publicUrl = new URL( // even if you decide not to use precaching. See https://cra.link/PWA
process.env.PUBLIC_URL, precacheAndRoute(self.__WB_MANIFEST)
window.location.href
) // Set up App Shell-style routing, so that all navigation requests
if (publicUrl.origin !== window.location.origin) { // are fulfilled with your index.html shell. Learn more at
// Our service worker won't work if PUBLIC_URL is on a different origin // https://developers.google.com/web/fundamentals/architecture/app-shell
// from what our page is served on. This might happen if a CDN is used to const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$')
// serve assets; see https://github.com/facebook/create-react-app/issues/2374 registerRoute(
return // Return false to exempt requests from being fulfilled by index.html.
({ request, url }: { request: Request; url: URL }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false
} }
window.addEventListener('load', () => { // If this is a URL that starts with /_, skip.
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` if (url.pathname.startsWith('/_')) {
return false
}
if (isLocalhost) { // If this looks like a URL for a resource, because it contains
// This is running on localhost. Let's check if a service worker still exists or not. // a file extension, skip.
checkValidServiceWorker(swUrl, config) if (fileExtensionRegexp.exec(url.pathname)) {
} else { return false
// Is not localhost. Just register service worker }
registerValidSW(swUrl, config)
}
})
}
}
function registerValidSW (swUrl: string, config?: Config) { // Return true to signal that we want to use the handler.
navigator.serviceWorker return true
.register(swUrl) },
.then(registration => { createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
registration.onupdatefound = () => { )
const installingWorker = registration.installing
if (installingWorker == null) {
return
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
)
// Execute callback // An example runtime caching route for requests that aren't handled by the
if (config && config.onUpdate) { // precache, in this case same-origin .png requests like those from in public/
config.onUpdate(registration) registerRoute(
} // Add in any other file extensions or routing criteria as needed.
} else { ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
// At this point, everything has been precached. // Customize this strategy as needed, e.g., by changing to CacheFirst.
// It's the perfect time to display a new StaleWhileRevalidate({
// "Content is cached for offline use." message. cacheName: 'images',
console.log('Content is cached for offline use.') plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// Execute callback // least-recently used images are removed.
if (config && config.onSuccess) { new ExpirationPlugin({ maxEntries: 50 })
config.onSuccess(registration) ]
}
}
}
}
}
})
.catch(error => {
console.error('Error during service worker registration:', error)
})
}
function checkValidServiceWorker (swUrl: string, config?: Config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
}) })
.then(response => { )
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type')
if (
response.status === 404 ||
(contentType != null && !contentType.includes('javascript'))
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
return registration.unregister().then(() => {
window.location.reload()
})
}).catch(() => console.log('Service worker not ready'))
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config)
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
)
})
}
export function unregister ():void { // This allows the web app to trigger skipWaiting via
if ('serviceWorker' in navigator) { // registration.waiting.postMessage({type: 'SKIP_WAITING'})
navigator.serviceWorker.ready self.addEventListener('message', (event) => {
.then(registration => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return registration.unregister() if (event.data && event.data.type === 'SKIP_WAITING') {
}) self.skipWaiting().catch((e) => console.error(e))
.catch((error:Error) => {
console.error(error.message)
})
} }
} })
// Any other custom service worker logic can go here.

View file

@ -1,25 +1,26 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": [
"dom", "dom",
"dom.iterable", "dom.iterable",
"esnext" "esnext"
], ],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"module": "esnext", "noFallthroughCasesInSwitch": true,
"moduleResolution": "node", "module": "esnext",
"resolveJsonModule": true, "moduleResolution": "node",
"isolatedModules": true, "resolveJsonModule": true,
"noEmit": true, "isolatedModules": true,
"jsx": "react" "noEmit": true,
}, "jsx": "react"
"include": [ },
"src" "include": [
] "src"
]
} }

6053
yarn.lock

File diff suppressed because it is too large Load diff