mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-28 22:15:12 -04:00
refactor: move frontmatter parser into commons package
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
4d0a2cb79e
commit
db43e1db3f
26 changed files with 462 additions and 321 deletions
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { extractFrontmatter } from './extractor.js'
|
||||
import { describe, expect, it } from '@jest/globals'
|
||||
|
||||
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).toBeUndefined()
|
||||
})
|
||||
it('is false when note does not start with three dashes', () => {
|
||||
const testNote = ['', '---', 'this is not frontmatter']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction).toBeUndefined()
|
||||
})
|
||||
it('is false when note start with less than three dashes', () => {
|
||||
const testNote = ['--', 'this is not frontmatter']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction).toBeUndefined()
|
||||
})
|
||||
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).toBeUndefined()
|
||||
})
|
||||
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).toBeUndefined()
|
||||
})
|
||||
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).toBeUndefined()
|
||||
})
|
||||
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).toBeDefined()
|
||||
})
|
||||
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).toBeDefined()
|
||||
})
|
||||
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).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('lineOffset property', () => {
|
||||
it('is correct for single line frontmatter without content', () => {
|
||||
const testNote = ['---', 'single line frontmatter', '...']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.lineOffset).toEqual(3)
|
||||
})
|
||||
it('is correct for single line frontmatter with content', () => {
|
||||
const testNote = ['---', 'single line frontmatter', '...', 'content']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.lineOffset).toEqual(3)
|
||||
})
|
||||
it('is correct for multi-line frontmatter without content', () => {
|
||||
const testNote = ['---', 'abc', '123', 'def', '...']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.lineOffset).toEqual(5)
|
||||
})
|
||||
it('is correct for multi-line frontmatter with content', () => {
|
||||
const testNote = ['---', 'abc', '123', 'def', '...', 'content']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.lineOffset).toEqual(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('rawText property', () => {
|
||||
it('contains single-line frontmatter text', () => {
|
||||
const testNote = ['---', 'single-line', '...', 'content']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.rawText).toEqual('single-line')
|
||||
})
|
||||
it('contains multi-line frontmatter text', () => {
|
||||
const testNote = ['---', 'multi', 'line', '...', 'content']
|
||||
const extraction = extractFrontmatter(testNote)
|
||||
expect(extraction?.rawText).toEqual('multi\nline')
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { FrontmatterExtractionResult } from './types.js'
|
||||
|
||||
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 | undefined => {
|
||||
if (lines.length < 2 || !FRONTMATTER_BEGIN_REGEX.test(lines[0])) {
|
||||
return undefined
|
||||
}
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (
|
||||
lines[i].length === lines[0].length &&
|
||||
FRONTMATTER_END_REGEX.test(lines[i])
|
||||
) {
|
||||
return {
|
||||
rawText: lines.slice(1, i).join('\n'),
|
||||
lineOffset: i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export interface FrontmatterExtractionResult {
|
||||
rawText: string
|
||||
lineOffset: number
|
||||
}
|
|
@ -3,9 +3,12 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
NoteFrontmatter,
|
||||
NoteTextDirection,
|
||||
NoteType
|
||||
} from '../note-frontmatter/frontmatter.js'
|
||||
import { generateNoteTitle } from './generate-note-title.js'
|
||||
import { NoteFrontmatter, NoteTextDirection } from './types/frontmatter.js'
|
||||
import { NoteType } from './types/frontmatter.js'
|
||||
import { describe, expect, it } from '@jest/globals'
|
||||
|
||||
const testFrontmatter: NoteFrontmatter = {
|
||||
|
@ -36,7 +39,7 @@ describe('generate note title', () => {
|
|||
title: 'frontmatter',
|
||||
opengraph: { title: 'opengraph' }
|
||||
},
|
||||
'first-heading'
|
||||
() => 'first-heading'
|
||||
)
|
||||
expect(actual).toEqual('frontmatter')
|
||||
})
|
||||
|
@ -44,13 +47,16 @@ describe('generate note title', () => {
|
|||
it('will choose the opengraph title second', () => {
|
||||
const actual = generateNoteTitle(
|
||||
{ ...testFrontmatter, opengraph: { title: 'opengraph' } },
|
||||
'first-heading'
|
||||
() => 'first-heading'
|
||||
)
|
||||
expect(actual).toEqual('opengraph')
|
||||
})
|
||||
|
||||
it('will choose the first heading third', () => {
|
||||
const actual = generateNoteTitle({ ...testFrontmatter }, 'first-heading')
|
||||
const actual = generateNoteTitle(
|
||||
{ ...testFrontmatter },
|
||||
() => 'first-heading'
|
||||
)
|
||||
expect(actual).toEqual('first-heading')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,28 +3,24 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { NoteFrontmatter } from './types/frontmatter.js'
|
||||
import type { NoteFrontmatter } from '../note-frontmatter/frontmatter.js'
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param firstHeadingProvider A function that provides the first heading of 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
|
||||
frontmatter: NoteFrontmatter | undefined,
|
||||
firstHeadingProvider: () => string | undefined
|
||||
): string => {
|
||||
if (frontmatter?.title && frontmatter?.title !== '') {
|
||||
if (frontmatter?.title) {
|
||||
return frontmatter.title.trim()
|
||||
} else if (
|
||||
frontmatter?.opengraph &&
|
||||
frontmatter?.opengraph.title !== undefined &&
|
||||
frontmatter?.opengraph.title !== ''
|
||||
) {
|
||||
return (frontmatter?.opengraph.title ?? firstHeading ?? '').trim()
|
||||
} else if (frontmatter?.opengraph.title) {
|
||||
return (frontmatter?.opengraph.title ?? firstHeadingProvider() ?? '').trim()
|
||||
} else {
|
||||
return (firstHeading ?? '').trim()
|
||||
return (firstHeadingProvider() ?? '').trim()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ISO6391 } from './iso6391.js'
|
||||
import { SlideOptions } from './slide-show-options.js'
|
||||
|
||||
export type Iso6391Language = (typeof ISO6391)[number]
|
||||
|
||||
export type OpenGraph = Record<string, string>
|
||||
|
||||
export enum NoteTextDirection {
|
||||
LTR = 'ltr',
|
||||
RTL = 'rtl'
|
||||
}
|
||||
|
||||
export enum NoteType {
|
||||
DOCUMENT = '',
|
||||
SLIDE = 'slide'
|
||||
}
|
||||
export interface NoteFrontmatter {
|
||||
title: string
|
||||
description: string
|
||||
tags: string[]
|
||||
robots: string
|
||||
lang: Iso6391Language
|
||||
dir: NoteTextDirection
|
||||
newlinesAreBreaks: boolean
|
||||
license: string
|
||||
type: NoteType
|
||||
opengraph: OpenGraph
|
||||
slideOptions: SlideOptions
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 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
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 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>>
|
Loading…
Add table
Add a link
Reference in a new issue