Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/markdown/src/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { marked } from 'marked'

import MarkdownManager from './MarkdownManager.js'
import type { ContentType } from './types.js'
import { assumeContentType } from './utils.js'
import { assumeContentType } from './utils/assumeContentType.js'

declare module '@tiptap/core' {
interface Editor {
Expand Down
18 changes: 8 additions & 10 deletions packages/markdown/src/MarkdownManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,15 @@ import {
} from '@tiptap/core'
import { type Lexer, type Token, type TokenizerExtension, type TokenizerThis, marked } from 'marked'

import {
closeMarksBeforeNode,
extractAbsorbedBlankLines,
findMarksToClose,
findMarksToCloseAtEnd,
findMarksToOpen,
isTaskItem,
reopenMarksAfterNode,
wrapInMarkdownBlock,
} from './utils.js'
import { htmlContainsUnrecognizedTag } from './utils/htmlTagDetection.js'
import { closeMarksBeforeNode } from './utils/closeMarksBeforeNode.js'
import { extractAbsorbedBlankLines } from './utils/extractAbsorbedBlankLines.js'
import { findMarksToClose } from './utils/findMarksToClose.js'
import { findMarksToCloseAtEnd } from './utils/findMarksToCloseAtEnd.js'
import { findMarksToOpen } from './utils/findMarksToOpen.js'
import { isTaskItem } from './utils/isTaskItem.js'
import { reopenMarksAfterNode } from './utils/reopenMarksAfterNode.js'
import { wrapInMarkdownBlock } from './utils/wrapInMarkdownBlock.js'

export class MarkdownManager {
private markedInstance: typeof marked
Expand Down
10 changes: 9 additions & 1 deletion packages/markdown/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export * from './Extension.js'
export * from './MarkdownManager.js'
export * from './utils.js'
export * from './utils/assumeContentType.js'
export * from './utils/closeMarksBeforeNode.js'
export * from './utils/extractAbsorbedBlankLines.js'
export * from './utils/findMarksToClose.js'
export * from './utils/findMarksToCloseAtEnd.js'
export * from './utils/findMarksToOpen.js'
export * from './utils/isTaskItem.js'
export * from './utils/reopenMarksAfterNode.js'
export * from './utils/wrapInMarkdownBlock.js'
236 changes: 0 additions & 236 deletions packages/markdown/src/utils.ts

This file was deleted.

22 changes: 22 additions & 0 deletions packages/markdown/src/utils/assumeContentType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Content } from '@tiptap/core'
import type { Fragment, Node } from '@tiptap/pm/model'

import type { ContentType } from '../types.js'

/**
* Assumes the content type based off the content.
* @param content The content to assume the type for.
* @param contentType The content type that should be prioritized.
*/
Comment thread
bdbch marked this conversation as resolved.
export function assumeContentType(
content: (Content | Fragment | Node) | string,
contentType: ContentType,
): ContentType {
// if not a string, we assume it will be a json content object
if (typeof content !== 'string') {
return 'json'
}

// otherwise we let the content type be what it is
return contentType
}
21 changes: 21 additions & 0 deletions packages/markdown/src/utils/closeMarksBeforeNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Closes active marks before rendering a non-text node.
* Returns the closing markdown syntax and clears the active marks.
*/
export function closeMarksBeforeNode(
activeMarks: Map<string, any>,
getMarkClosing: (markType: string, mark: any) => string,
): string {
let beforeMarkdown = ''
Array.from(activeMarks.keys())
.reverse()
.forEach(markType => {
const mark = activeMarks.get(markType)
const closeMarkdown = getMarkClosing(markType, mark)
if (closeMarkdown) {
beforeMarkdown = closeMarkdown + beforeMarkdown
}
})
activeMarks.clear()
return beforeMarkdown
}
44 changes: 44 additions & 0 deletions packages/markdown/src/utils/extractAbsorbedBlankLines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { MarkdownToken } from '@tiptap/core'

/**
* Matches a run of two or more consecutive line breaks (a blank line) at the
* end of a string, allowing horizontal whitespace between the breaks.
*
* @example
* TRAILING_BLANK_LINES.test('paragraph\n \n')
* // => true
*/
const TRAILING_BLANK_LINES = /\n[^\S\n]*(?:\n[^\S\n]*)+$/

/**
* Extracts blank lines absorbed into marked token `raw` back into explicit
* `space` tokens so they're handled uniformly by the reconstruction path.
* @param tokens The marked token stream to normalize.
* @returns A new token array with absorbed blank lines as `space` tokens.
*/
export function extractAbsorbedBlankLines(tokens: MarkdownToken[]): MarkdownToken[] {
return tokens.flatMap((token, index) =>
extractAbsorbedBlankLinesFromToken(token, tokens[index + 1]),
)
}

function extractAbsorbedBlankLinesFromToken(
token: MarkdownToken,
nextToken: MarkdownToken | undefined,
): MarkdownToken[] {
// A following `space` token already carries the blank lines for this gap.
if (token.type === 'space' || nextToken?.type === 'space') {
return [token]
}

const trailingBlankLines = (token.raw || '').match(TRAILING_BLANK_LINES)

if (!trailingBlankLines) {
return [token]
}

return [
{ ...token, raw: (token.raw || '').slice(0, -trailingBlankLines[0].length) },
{ type: 'space', raw: trailingBlankLines[0] } as MarkdownToken,
]
}
Loading
Loading