refactor(common): extract frontmatter code into commons

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2023-03-26 12:30:16 +02:00 committed by Tilman Vatteroth
parent a78ac5f097
commit 8bd7fd1be8
26 changed files with 433 additions and 121 deletions

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -7,10 +7,10 @@ import type { Config } from '../api/config/types'
import type { HistoryEntryWithOrigin } from '../api/history/types'
import type { DarkModeConfig } from './dark-mode/types'
import type { EditorConfig } from './editor/types'
import type { NoteDetails } from './note-details/types/note-details'
import type { RealtimeStatus } from './realtime/types'
import type { RendererStatus } from './renderer-status/types'
import type { OptionalUserState } from './user/types'
import type { NoteDetails } from '@hedgedoc/commons'
export interface ApplicationState {
user: OptionalUserState

View file

@ -1,15 +1,14 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { calculateLineStartIndexes } from './calculate-line-start-indexes'
import { extractFrontmatter } from './frontmatter-extractor/extractor'
import type { PresentFrontmatterExtractionResult } from './frontmatter-extractor/types'
import { generateNoteTitle } from './generate-note-title'
import { initialState } from './initial-state'
import { createNoteFrontmatterFromYaml } from './raw-note-frontmatter-parser/parser'
import type { NoteDetails } from './types/note-details'
import { extractFrontmatter, generateNoteTitle } from '@hedgedoc/commons'
import type { PresentFrontmatterExtractionResult } from '@hedgedoc/commons'
/**
* Copies a {@link NoteDetails} but with another markdown content.

View file

@ -1,93 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { extractFrontmatter } from './extractor'
import type { PresentFrontmatterExtractionResult } from './types'
describe('frontmatter extraction', () => {
describe('isPresent property', () => {
it('is false when note does not contain three dashes at all', () => {
const testNote = ['abcdef', 'more text']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is false when note does not start with three dashes', () => {
const testNote = ['', '---', 'this is not frontmatter']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is false when note start with less than three dashes', () => {
const testNote = ['--', 'this is not frontmatter']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is false when note starts with three dashes but contains other characters in the same line', () => {
const testNote = ['--- a', 'this is not frontmatter']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is false when note has no ending marker for frontmatter', () => {
const testNote = ['---', 'this is not frontmatter', 'because', 'there is no', 'end marker']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is false when note end marker is present but with not the same amount of dashes as start marker', () => {
const testNote = ['---', 'this is not frontmatter', '----', 'content']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(false)
})
it('is true when note end marker is present with the same amount of dashes as start marker', () => {
const testNote = ['---', 'this is frontmatter', '---', 'content']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(true)
})
it('is true when note end marker is present with the same amount of dashes as start marker but without content', () => {
const testNote = ['---', 'this is frontmatter', '---']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(true)
})
it('is true when note end marker is present with the same amount of dots as start marker', () => {
const testNote = ['---', 'this is frontmatter', '...', 'content']
const extraction = extractFrontmatter(testNote)
expect(extraction.isPresent).toBe(true)
})
})
describe('lineOffset property', () => {
it('is correct for single line frontmatter without content', () => {
const testNote = ['---', 'single line frontmatter', '...']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.lineOffset).toEqual(3)
})
it('is correct for single line frontmatter with content', () => {
const testNote = ['---', 'single line frontmatter', '...', 'content']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.lineOffset).toEqual(3)
})
it('is correct for multi-line frontmatter without content', () => {
const testNote = ['---', 'abc', '123', 'def', '...']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.lineOffset).toEqual(5)
})
it('is correct for multi-line frontmatter with content', () => {
const testNote = ['---', 'abc', '123', 'def', '...', 'content']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.lineOffset).toEqual(5)
})
})
describe('rawText property', () => {
it('contains single-line frontmatter text', () => {
const testNote = ['---', 'single-line', '...', 'content']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.rawText).toEqual('single-line')
})
it('contains multi-line frontmatter text', () => {
const testNote = ['---', 'multi', 'line', '...', 'content']
const extraction = extractFrontmatter(testNote) as PresentFrontmatterExtractionResult
expect(extraction.rawText).toEqual('multi\nline')
})
})
})

View file

@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { FrontmatterExtractionResult } from './types'
const FRONTMATTER_BEGIN_REGEX = /^-{3,}$/
const FRONTMATTER_END_REGEX = /^(?:-{3,}|\.{3,})$/
/**
* Extracts a frontmatter block from a given multiline string.
* A valid frontmatter block requires the content to start with a line containing at least three dashes.
* The block is terminated by a line containing the same amount of dashes or dots as the first line.
* @param lines The lines from which the frontmatter should be extracted.
* @return { isPresent } false if no frontmatter block could be found, true if a block was found.
* { rawFrontmatterText } if a block was found, this property contains the extracted text without the fencing.
* { frontmatterLines } if a block was found, this property contains the number of lines to skip from the
* given multiline string for retrieving the non-frontmatter content.
*/
export const extractFrontmatter = (lines: string[]): FrontmatterExtractionResult => {
if (lines.length < 2 || !FRONTMATTER_BEGIN_REGEX.test(lines[0])) {
return {
isPresent: false
}
}
for (let i = 1; i < lines.length; i++) {
if (lines[i].length === lines[0].length && FRONTMATTER_END_REGEX.test(lines[i])) {
return {
isPresent: true,
rawText: lines.slice(1, i).join('\n'),
lineOffset: i + 1
}
}
}
return {
isPresent: false
}
}

View file

@ -1,17 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export type FrontmatterExtractionResult = PresentFrontmatterExtractionResult | NonPresentFrontmatterExtractionResult
export interface PresentFrontmatterExtractionResult {
isPresent: true
rawText: string
lineOffset: number
}
interface NonPresentFrontmatterExtractionResult {
isPresent: false
}

View file

@ -1,30 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { generateNoteTitle } from './generate-note-title'
import { initialState } from './initial-state'
describe('generate note title', () => {
it('will choose the frontmatter title first', () => {
const actual = generateNoteTitle(
{ ...initialState.frontmatter, title: 'frontmatter', opengraph: { title: 'opengraph' } },
'first-heading'
)
expect(actual).toEqual('frontmatter')
})
it('will choose the opengraph title second', () => {
const actual = generateNoteTitle(
{ ...initialState.frontmatter, opengraph: { title: 'opengraph' } },
'first-heading'
)
expect(actual).toEqual('opengraph')
})
it('will choose the first heading third', () => {
const actual = generateNoteTitle({ ...initialState.frontmatter }, 'first-heading')
expect(actual).toEqual('first-heading')
})
})

View file

@ -1,27 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NoteFrontmatter } from './types/note-details'
/**
* Generates the note title from the given frontmatter or the first heading in the markdown content.
*
* @param frontmatter The frontmatter of the note
* @param firstHeading The first heading in the markdown content
* @return The title from the frontmatter or, if no title is present in the frontmatter, the first heading.
*/
export const generateNoteTitle = (frontmatter: NoteFrontmatter, firstHeading?: string): string => {
if (frontmatter?.title && frontmatter?.title !== '') {
return frontmatter.title.trim()
} else if (
frontmatter?.opengraph &&
frontmatter?.opengraph.title !== undefined &&
frontmatter?.opengraph.title !== ''
) {
return (frontmatter?.opengraph.title ?? firstHeading ?? '').trim()
} else {
return (firstHeading ?? '').trim()
}
}

View file

@ -1,11 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NoteDetails } from './types/note-details'
import { NoteTextDirection, NoteType } from './types/note-details'
import type { SlideOptions } from './types/slide-show-options'
import { NoteTextDirection, NoteType } from '@hedgedoc/commons'
import type { SlideOptions } from '@hedgedoc/commons'
export const initialSlideOptions: SlideOptions = {
transition: 'zoom',

View file

@ -1,14 +1,12 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { initialSlideOptions, initialState } from '../initial-state'
import { ISO6391 } from '../types/iso6391'
import type { Iso6391Language, NoteFrontmatter, OpenGraph } from '../types/note-details'
import { NoteTextDirection, NoteType } from '../types/note-details'
import type { SlideOptions } from '../types/slide-show-options'
import type { RawNoteFrontmatter } from './types'
import type { Iso6391Language, NoteFrontmatter, OpenGraph, SlideOptions } from '@hedgedoc/commons'
import { ISO6391, NoteTextDirection, NoteType } from '@hedgedoc/commons'
import { load } from 'js-yaml'
/**

View file

@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { generateNoteTitle } from '../generate-note-title'
import type { NoteDetails } from '../types/note-details'
import { generateNoteTitle } from '@hedgedoc/commons'
/**
* Builds a {@link NoteDetails} redux state with an updated note title from frontmatter data and the first heading.

View file

@ -1,210 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const ISO6391 = [
'aa',
'ab',
'af',
'am',
'ar',
'ar-ae',
'ar-bh',
'ar-dz',
'ar-eg',
'ar-iq',
'ar-jo',
'ar-kw',
'ar-lb',
'ar-ly',
'ar-ma',
'ar-om',
'ar-qa',
'ar-sa',
'ar-sy',
'ar-tn',
'ar-ye',
'as',
'ay',
'de-at',
'de-ch',
'de-li',
'de-lu',
'div',
'dz',
'el',
'en',
'en-au',
'en-bz',
'en-ca',
'en-gb',
'en-ie',
'en-jm',
'en-nz',
'en-ph',
'en-tt',
'en-us',
'en-za',
'en-zw',
'eo',
'es',
'es-ar',
'es-bo',
'es-cl',
'es-co',
'es-cr',
'es-do',
'es-ec',
'es-es',
'es-gt',
'es-hn',
'es-mx',
'es-ni',
'es-pa',
'es-pe',
'es-pr',
'es-py',
'es-sv',
'es-us',
'es-uy',
'es-ve',
'et',
'eu',
'fa',
'fi',
'fj',
'fo',
'fr',
'fr-be',
'fr-ca',
'fr-ch',
'fr-lu',
'fr-mc',
'fy',
'ga',
'gd',
'gl',
'gn',
'gu',
'ha',
'he',
'hi',
'hr',
'hu',
'hy',
'ia',
'id',
'ie',
'ik',
'in',
'is',
'it',
'it-ch',
'iw',
'ja',
'ji',
'jw',
'ka',
'kk',
'kl',
'km',
'kn',
'ko',
'kok',
'ks',
'ku',
'ky',
'kz',
'la',
'ln',
'lo',
'ls',
'lt',
'lv',
'mg',
'mi',
'mk',
'ml',
'mn',
'mo',
'mr',
'ms',
'mt',
'my',
'na',
'nb-no',
'ne',
'nl',
'nl-be',
'nn-no',
'no',
'oc',
'om',
'or',
'pa',
'pl',
'ps',
'pt',
'pt-br',
'qu',
'rm',
'rn',
'ro',
'ro-md',
'ru',
'ru-md',
'rw',
'sa',
'sb',
'sd',
'sg',
'sh',
'si',
'sk',
'sl',
'sm',
'sn',
'so',
'sq',
'sr',
'ss',
'st',
'su',
'sv',
'sv-fi',
'sw',
'sx',
'syr',
'ta',
'te',
'tg',
'th',
'ti',
'tk',
'tl',
'tn',
'to',
'tr',
'ts',
'tt',
'tw',
'uk',
'ur',
'us',
'uz',
'vi',
'vo',
'wo',
'xh',
'yi',
'yo',
'zh',
'zh-cn',
'zh-hk',
'zh-mo',
'zh-sg',
'zh-tw',
'zu'
] as const

View file

@ -1,12 +1,12 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NoteMetadata } from '../../../api/notes/types'
import type { CursorSelection } from '../../../components/editor-page/editor-pane/tool-bar/formatters/types/cursor-selection'
import type { ISO6391 } from './iso6391'
import type { SlideOptions } from './slide-show-options'
import type { SlideOptions } from '@hedgedoc/commons'
import type { NoteFrontmatter } from '@hedgedoc/commons'
type UnnecessaryNoteAttributes = 'updatedAt' | 'createdAt' | 'tags' | 'description'
@ -28,36 +28,6 @@ export interface NoteDetails extends Omit<NoteMetadata, UnnecessaryNoteAttribute
frontmatterRendererInfo: RendererFrontmatterInfo
}
export type Iso6391Language = (typeof ISO6391)[number]
export type OpenGraph = Record<string, string>
export interface NoteFrontmatter {
title: string
description: string
tags: string[]
robots: string
lang: Iso6391Language
dir: NoteTextDirection
newlinesAreBreaks: boolean
GA: string
disqus: string
license: string
type: NoteType
opengraph: OpenGraph
slideOptions: SlideOptions
}
export enum NoteTextDirection {
LTR = 'ltr',
RTL = 'rtl'
}
export enum NoteType {
DOCUMENT = '',
SLIDE = 'slide'
}
export interface RendererFrontmatterInfo {
lineOffset: number
frontmatterInvalid: boolean

View file

@ -1,10 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { RevealOptions } from 'reveal.js'
type WantedRevealOptions = 'autoSlide' | 'autoSlideStoppable' | 'transition' | 'backgroundTransition' | 'slideNumber'
export type SlideOptions = Required<Pick<RevealOptions, WantedRevealOptions>>