mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-24 18:56:32 -05:00
perf: add performance marker to monitor the needed time for rendering
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
a303bb261f
commit
354700e973
4 changed files with 83 additions and 4 deletions
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { measurePerformance } from '../../../utils/measure-performance'
|
||||||
import type { ParserOptions } from '@hedgedoc/html-to-react'
|
import type { ParserOptions } from '@hedgedoc/html-to-react'
|
||||||
import { convertHtmlToReact } from '@hedgedoc/html-to-react'
|
import { convertHtmlToReact } from '@hedgedoc/html-to-react'
|
||||||
import type DOMPurify from 'dompurify'
|
import type DOMPurify from 'dompurify'
|
||||||
|
@ -24,8 +25,12 @@ export interface HtmlToReactProps {
|
||||||
*/
|
*/
|
||||||
export const HtmlToReact: React.FC<HtmlToReactProps> = ({ htmlCode, domPurifyConfig, parserOptions }) => {
|
export const HtmlToReact: React.FC<HtmlToReactProps> = ({ htmlCode, domPurifyConfig, parserOptions }) => {
|
||||||
const elements = useMemo(() => {
|
const elements = useMemo(() => {
|
||||||
const sanitizedHtmlCode = sanitize(htmlCode, { ...domPurifyConfig, RETURN_DOM_FRAGMENT: false, RETURN_DOM: false })
|
const sanitizedHtmlCode = measurePerformance('html-to-react: sanitize', () => {
|
||||||
return convertHtmlToReact(sanitizedHtmlCode, parserOptions)
|
return sanitize(htmlCode, { ...domPurifyConfig, RETURN_DOM_FRAGMENT: false, RETURN_DOM: false })
|
||||||
|
})
|
||||||
|
return measurePerformance('html-to-react: convertHtmlToReact', () => {
|
||||||
|
return convertHtmlToReact(sanitizedHtmlCode, parserOptions)
|
||||||
|
})
|
||||||
}, [domPurifyConfig, htmlCode, parserOptions])
|
}, [domPurifyConfig, htmlCode, parserOptions])
|
||||||
|
|
||||||
return <Fragment>{elements}</Fragment>
|
return <Fragment>{elements}</Fragment>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { measurePerformance } from '../../../utils/measure-performance'
|
||||||
import { HtmlToReact } from '../../common/html-to-react/html-to-react'
|
import { HtmlToReact } from '../../common/html-to-react/html-to-react'
|
||||||
import type { MarkdownRendererExtension } from '../extensions/_base-classes/markdown-renderer-extension'
|
import type { MarkdownRendererExtension } from '../extensions/_base-classes/markdown-renderer-extension'
|
||||||
import { useCombinedNodePreprocessor } from './hooks/use-combined-node-preprocessor'
|
import { useCombinedNodePreprocessor } from './hooks/use-combined-node-preprocessor'
|
||||||
|
@ -43,7 +44,9 @@ export const MarkdownToReact: React.FC<MarkdownToReactProps> = ({
|
||||||
}, [nodeToReactTransformer, markdownRenderExtensions])
|
}, [nodeToReactTransformer, markdownRenderExtensions])
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
nodeToReactTransformer.setLineIds(lineNumberMapper.updateLineMapping(markdownContentLines))
|
measurePerformance('markdown-to-react: update-line-mapping', () => {
|
||||||
|
nodeToReactTransformer.setLineIds(lineNumberMapper.updateLineMapping(markdownContentLines))
|
||||||
|
})
|
||||||
}, [nodeToReactTransformer, lineNumberMapper, markdownContentLines])
|
}, [nodeToReactTransformer, lineNumberMapper, markdownContentLines])
|
||||||
|
|
||||||
const nodePreProcessor = useCombinedNodePreprocessor(markdownRenderExtensions)
|
const nodePreProcessor = useCombinedNodePreprocessor(markdownRenderExtensions)
|
||||||
|
@ -60,7 +63,11 @@ export const MarkdownToReact: React.FC<MarkdownToReactProps> = ({
|
||||||
[nodeToReactTransformer, nodePreProcessor]
|
[nodeToReactTransformer, nodePreProcessor]
|
||||||
)
|
)
|
||||||
|
|
||||||
const html = useMemo(() => markdownIt.render(markdownContentLines.join('\n')), [markdownContentLines, markdownIt])
|
const html = useMemo(() => {
|
||||||
|
return measurePerformance('markdown-to-react: markdown-it', () =>
|
||||||
|
markdownIt.render(markdownContentLines.join('\n'))
|
||||||
|
)
|
||||||
|
}, [markdownContentLines, markdownIt])
|
||||||
const domPurifyConfig: DOMPurify.Config = useMemo(
|
const domPurifyConfig: DOMPurify.Config = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
ADD_TAGS: markdownRenderExtensions.flatMap((extension) => extension.buildTagNameAllowList())
|
ADD_TAGS: markdownRenderExtensions.flatMap((extension) => extension.buildTagNameAllowList())
|
||||||
|
|
42
frontend/src/utils/measure-performance.spec.ts
Normal file
42
frontend/src/utils/measure-performance.spec.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { measurePerformance } from './measure-performance'
|
||||||
|
import { Mock } from 'ts-mockery'
|
||||||
|
|
||||||
|
describe('measure performance', () => {
|
||||||
|
it('uses the global performance functions', () => {
|
||||||
|
const measurementName = 'marker-name'
|
||||||
|
const markMock = jest.fn()
|
||||||
|
const measureMock = jest.fn()
|
||||||
|
const startMaker = `${measurementName} - start`
|
||||||
|
const endMarker = `${measurementName} - end`
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'performance', {
|
||||||
|
get: () =>
|
||||||
|
Mock.of<Performance>({
|
||||||
|
mark: markMock,
|
||||||
|
measure: measureMock
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const result = measurePerformance(measurementName, () => {
|
||||||
|
return 'value'
|
||||||
|
})
|
||||||
|
expect(result).toBe('value')
|
||||||
|
expect(markMock).toHaveBeenNthCalledWith(1, startMaker)
|
||||||
|
expect(markMock).toHaveBeenNthCalledWith(2, endMarker)
|
||||||
|
expect(measureMock).toBeCalledWith(measurementName, startMaker, endMarker)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('runs the task without global performance functions', () => {
|
||||||
|
Object.defineProperty(window, 'performance', {
|
||||||
|
get: () => undefined
|
||||||
|
})
|
||||||
|
const result = measurePerformance('measurementName', () => {
|
||||||
|
return 'value'
|
||||||
|
})
|
||||||
|
expect(result).toBe('value')
|
||||||
|
})
|
||||||
|
})
|
25
frontend/src/utils/measure-performance.ts
Normal file
25
frontend/src/utils/measure-performance.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the browser's performance API to measure how long a given task is running.
|
||||||
|
*
|
||||||
|
* @param measurementName The name of the measurement. Is also used to derive the name of the markers
|
||||||
|
* @param task The task to run
|
||||||
|
* @return the result of the task
|
||||||
|
*/
|
||||||
|
export const measurePerformance = <T = void>(measurementName: string, task: () => T): T => {
|
||||||
|
if (!window.performance || !window.performance.mark || !window.performance.measure) {
|
||||||
|
return task()
|
||||||
|
}
|
||||||
|
const startMarkName = `${measurementName} - start`
|
||||||
|
const endMarkName = `${measurementName} - end`
|
||||||
|
window.performance.mark(startMarkName)
|
||||||
|
const result = task()
|
||||||
|
window.performance.mark(endMarkName)
|
||||||
|
window.performance.measure(measurementName, startMarkName, endMarkName)
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in a new issue