Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps)!: update unified ecosystem dependencies #395

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"automerge": false
},
{
"matchPackagePatterns": ["gfm-autolink-literal"],
"matchPackagePatterns": ["gfm-autolink-literal$"],
"groupName": "gfm autolink literal packages"
},
{
"matchPackagePatterns": ["gfm-strikethrough"],
"matchPackagePatterns": ["gfm-strikethrough$"],
"groupName": "gfm strikethrough packages"
}
]
Expand Down
8 changes: 8 additions & 0 deletions .storybook/preview.style.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ body,
#storybook-docs .sbdocs.sbdocs-wrapper {
padding: 3rem;
}

/*
* This is required becase `github-markdown-css` is a dev dependency (not necessarily used by
* consumers) which hides what the `rehypeCodeBlock` extension does.
*/
.markdown-body code br {
display: unset !important;
}
1,706 changes: 1,045 additions & 661 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 21 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@
"@testing-library/dom": "9.3.4",
"@testing-library/jest-dom": "6.3.0",
"@testing-library/react": "14.1.2",
"@types/hast": "3.0.3",
"@types/lodash-es": "4.17.12",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"@types/react-syntax-highlighter": "15.5.11",
"@types/turndown": "5.0.4",
"@types/unist": "3.0.2",
"@vitejs/plugin-react": "4.2.1",
"boring-avatars": "1.10.1",
"classnames": "2.5.1",
Expand All @@ -127,8 +129,10 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "5.0.1",
"react-markdown": "8.0.7",
"react-markdown": "9.0.1",
"react-syntax-highlighter": "15.5.0",
"rehype-raw": "7.0.0",
"remark-gfm": "4.0.0",
"rimraf": "5.0.5",
"semantic-release": "23.0.0",
"tippy.js": "6.3.7",
Expand All @@ -145,24 +149,25 @@
"hast-util-is-element": "^2.1.0",
"linkifyjs": "^4.1.1",
"lodash-es": "^4.17.21",
"mdast-util-gfm-autolink-literal": "^1.0.0",
"mdast-util-gfm-strikethrough": "^1.0.0",
"micromark-extension-gfm-autolink-literal": "^1.0.0",
"micromark-extension-gfm-strikethrough": "^1.0.0",
"mdast-util-gfm-autolink-literal": "^2.0.0",
"mdast-util-gfm-strikethrough": "^2.0.0",
"micromark-extension-gfm-autolink-literal": "^2.0.0",
"micromark-extension-gfm-strikethrough": "^2.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0",
"rehype": "^12.0.0",
"rehype-minify-whitespace": "^5.0.0",
"rehype-raw": "^6.1.0",
"rehype-stringify": "^9.0.0",
"remark": "^14.0.0",
"remark-breaks": "^3.0.0",
"remark-gfm": "^3.0.0",
"remark-rehype": "^10.1.0",
"rehype": "^13.0.0",
"rehype-minify-whitespace": "^6.0.0",
"rehype-raw": "^7.0.0",
"rehype-stringify": "^10.0.0",
"remark": "^15.0.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"turndown": "^7.1.0",
"unified": "^10.1.0",
"unist-util-is": "^5.2.0",
"unified": "^11.0.0",
"unist-util-is": "^6.0.0",
"unist-util-remove": "^4.0.0",
"unist-util-visit": "^4.1.0"
"unist-util-visit": "^5.0.0"
}
}
28 changes: 24 additions & 4 deletions src/helpers/unified.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
import { isTextNode } from './unified'
import { isHastElement, isHastTextNode } from './unified'

describe('Helper: Unified', () => {
describe('#isTextNode', () => {
describe('#isHastElement', () => {
test('returns `true` when the given node is an element with the specified tag name', () => {
expect(
// @ts-expect-error Simplified node for testing purposes
isHastElement({ type: 'element', tagName: 'div' }, 'div'),
).toBe(true)
})

test('returns `false` when the given node is NOT an element with the specified tag name', () => {
expect(
// @ts-expect-error Simplified node for testing purposes
isHastElement({ type: 'element', tagName: 'span' }, 'div'),
).toBe(false)
})

test('returns `false` when the given node is NOT an element', () => {
expect(isHastElement({ type: 'text' }, 'div')).toBe(false)
})
})

describe('#isHastTextNode', () => {
test('returns `true` when the given node is a hast text node', () => {
expect(isTextNode({ type: 'text' })).toBe(true)
expect(isHastTextNode({ type: 'text' })).toBe(true)
})

test('returns `false` when the given node is NOT a hast text node', () => {
expect(isTextNode({ type: 'element' })).toBe(false)
expect(isHastTextNode({ type: 'element' })).toBe(false)
})
})
})
23 changes: 18 additions & 5 deletions src/helpers/unified.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { is } from 'unist-util-is'

import type { Node, Text } from 'hast'
import type { Element, Text } from 'hast'
import type { Node } from 'unist'

/**
* Check if a given node is a unist text node.
* Determines whether a given node is an hast element with a specific tag name.
*
* @param node The node to check.
* @param tagName The tag name to check for.
*
* @returns `true` if the node is a unist text node, `false` otherwise.
* @returns `true` if the node is an hast element with the specified tag name, `false` otherwise.
*/
function isTextNode(node: Node): node is Text {
function isHastElement(node: Node, tagName: Element['tagName']): node is Element {
return is(node, { type: 'element', tagName })
}

/**
* Determines whether a given node is hast a text node.
*
* @param node The node to check.
*
* @returns `true` if the node is a hast text node, `false` otherwise.
*/
function isHastTextNode(node: Node): node is Text {
return is(node, { type: 'text' })
}

export { isTextNode }
export { isHastElement, isHastTextNode }
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export type {
export { createSuggestionExtension } from './factories/create-suggestion-extension'
export { isMultilineDocument, isPlainTextDocument } from './helpers/schema'
export { createHTMLSerializer, getHTMLSerializerInstance } from './serializers/html/html'
export { remarkAutolinkLiteral } from './serializers/html/plugins/remark-autolink-literal'
export { remarkStrikethrough } from './serializers/html/plugins/remark-strikethrough'
export {
createMarkdownSerializer,
getMarkdownSerializerInstance,
Expand Down
2 changes: 1 addition & 1 deletion src/serializers/html/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function createHTMLSerializer(schema: Schema): HTMLSerializerReturnType {
// Configure the unified processor with an official plugin that defines how to take a syntax
// tree as input and turn it into serialized HTML
unifiedProcessor.use(rehypeStringify, {
entities: {
characterReferences: {
// Compatibility with the previous implementation in Marked
useNamedReferences: true,
},
Expand Down
11 changes: 5 additions & 6 deletions src/serializers/html/plugins/rehype-code-block.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { isElement } from 'hast-util-is-element'
import { visit } from 'unist-util-visit'

import { isTextNode } from '../../../helpers/unified'
import { isHastElement, isHastTextNode } from '../../../helpers/unified'

import type { Node } from 'hast'
import type { Transformer } from 'unified'
import type { Node } from 'unist'

/**
* A rehype plugin to remove the trailing newline from code blocks (i.e. the newline between the
Expand All @@ -16,9 +15,9 @@ function rehypeCodeBlock(): Transformer {
return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {
visit(tree, 'element', (node: Node) => {
if (
isElement(node, 'pre') &&
isElement(node.children[0], 'code') &&
isTextNode(node.children[0].children[0])
isHastElement(node, 'pre') &&
isHastElement(node.children[0], 'code') &&
isHastTextNode(node.children[0].children[0])
) {
node.children[0].children[0].value = node.children[0].children[0].value.replace(
/\n$/,
Expand Down
11 changes: 6 additions & 5 deletions src/serializers/html/plugins/rehype-image.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { isElement } from 'hast-util-is-element'
import { remove } from 'unist-util-remove'
import { visit } from 'unist-util-visit'

import { isHastElement } from '../../../helpers/unified'

import type { Schema } from '@tiptap/pm/model'
import type { Transformer } from 'unified'
import type { Node, Parent } from 'unist'
Expand All @@ -17,21 +18,21 @@ function rehypeImage(schema: Schema): Transformer {

// Return the tree as-is if the editor does not support inline images
if (allowInlineImages) {
return (tree: Node) => tree
return (tree) => tree
}

return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {
visit(tree, 'element', (node: Node, index: number, parent: Parent) => {
if (isElement(node, 'p')) {
const areAllChildrenImages = node.children.every((c) => isElement(c, 'img'))
if (isHastElement(node, 'p')) {
const areAllChildrenImages = node.children.every((c) => isHastElement(c, 'img'))

// Replace the paragraph with the image children if all children are images, or
// remove all images from the paragraph if it contains non-image children since the
// editor does not support inline images
if (areAllChildrenImages) {
parent.children.splice(index, 1, ...node.children)
} else {
remove(node, (n) => isElement(n, 'img'))
remove(node, (n) => isHastElement(n, 'img'))
}
}
})
Expand Down
12 changes: 7 additions & 5 deletions src/serializers/html/plugins/rehype-suggestions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { isElement } from 'hast-util-is-element'
import { visit } from 'unist-util-visit'

import { buildSuggestionSchemaPartialRegex } from '../../../helpers/serializer'
import { isTextNode } from '../../../helpers/unified'
import { isHastElement, isHastTextNode } from '../../../helpers/unified'

import type { Schema } from '@tiptap/pm/model'
import type { Node } from 'hast'
import type { Transformer } from 'unified'
import type { Node } from 'unist'

/**
* A rehype plugin to add support for suggestions nodes (e.g., `@username` or `#channel).
Expand All @@ -25,12 +24,15 @@ function rehypeSuggestions(schema: Schema): Transformer {
const suggestionSchemaRegex = new RegExp(`^${suggestionSchemaPartialRegex}`)

visit(tree, 'element', (node: Node) => {
if (isElement(node, 'a') && suggestionSchemaRegex.test(String(node.properties?.href))) {
if (
isHastElement(node, 'a') &&
suggestionSchemaRegex.test(String(node.properties?.href))
) {
const [, schema, id] =
/^([a-z-]+):\/\/(\S+)$/i.exec(String(node.properties?.href)) || []

// Replace the link element with a span containing the suggestion attributes
if (schema && id && isTextNode(node.children[0])) {
if (schema && id && isHastTextNode(node.children[0])) {
node.tagName = 'span'
node.properties = {
[`data-${schema}`]: '',
Expand Down
13 changes: 6 additions & 7 deletions src/serializers/html/plugins/rehype-task-list.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { isElement } from 'hast-util-is-element'
import { visit } from 'unist-util-visit'

import { isTextNode } from '../../../helpers/unified'
import { isHastElement, isHastTextNode } from '../../../helpers/unified'

import type { Node } from 'hast'
import type { Transformer } from 'unified'
import type { Node } from 'unist'

/**
* A rehype plugin to add support for Tiptap task lists (i.e., `* [ ] Task`).
*/
function rehypeTaskList(): Transformer {
return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {
visit(tree, 'element', (node: Node) => {
if (isElement(node, 'ul')) {
if (isHastElement(node, 'ul')) {
const areAllChildrenTaskItems = node.children.every(
(c) =>
isElement(c, 'li') &&
isTextNode(c.children[0]) &&
isHastElement(c, 'li') &&
isHastTextNode(c.children[0]) &&
/^\[[ x]\] /i.test(c.children[0].value),
)

Expand All @@ -29,7 +28,7 @@ function rehypeTaskList(): Transformer {
}

node.children.forEach((c) => {
if (isElement(c, 'li') && isTextNode(c.children[0])) {
if (isHastElement(c, 'li') && isHastTextNode(c.children[0])) {
c.properties = {
...c.properties,
'data-type': 'taskItem',
Expand Down
14 changes: 6 additions & 8 deletions src/serializers/html/plugins/remark-autolink-literal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ import type { Processor } from 'unified'
function remarkAutolinkLiteral(this: Processor) {
const data = this.data()

function add(field: string, value: unknown) {
const list = (data[field] ? data[field] : (data[field] = [])) as unknown[]
const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])
const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])
const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])

list.push(value)
}

add('micromarkExtensions', gfmAutolinkLiteral)
add('fromMarkdownExtensions', gfmAutolinkLiteralFromMarkdown)
add('toMarkdownExtensions', gfmAutolinkLiteralToMarkdown)
micromarkExtensions.push(gfmAutolinkLiteral())
fromMarkdownExtensions.push(gfmAutolinkLiteralFromMarkdown())
toMarkdownExtensions.push(gfmAutolinkLiteralToMarkdown())
}

export { remarkAutolinkLiteral }
10 changes: 3 additions & 7 deletions src/serializers/html/plugins/remark-disable-constructs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ import type { Processor } from 'unified'
function remarkDisableConstructs(this: Processor, schema: Schema) {
const data = this.data()

function add(field: string, value: unknown) {
const list = (data[field] ? data[field] : (data[field] = [])) as unknown[]

list.push(value)
}

const disabledConstructs: string[] = []

if (!schema.nodes.blockquote) {
Expand Down Expand Up @@ -54,8 +48,10 @@ function remarkDisableConstructs(this: Processor, schema: Schema) {
disabledConstructs.push('labelStartLink')
}

const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])

// https://github.com/micromark/micromark#case-turn-off-constructs
add('micromarkExtensions', {
micromarkExtensions.push({
disable: {
null: disabledConstructs,
},
Expand Down
14 changes: 6 additions & 8 deletions src/serializers/html/plugins/remark-strikethrough.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ import type { Processor } from 'unified'
function remarkStrikethrough(this: Processor, options: Options = {}) {
const data = this.data()

function add(field: string, value: unknown) {
const list = (data[field] ? data[field] : (data[field] = [])) as unknown[]
const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])
const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])
const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])

list.push(value)
}

add('micromarkExtensions', gfmStrikethrough(options))
add('fromMarkdownExtensions', gfmStrikethroughFromMarkdown)
add('toMarkdownExtensions', gfmStrikethroughToMarkdown)
micromarkExtensions.push(gfmStrikethrough(options))
fromMarkdownExtensions.push(gfmStrikethroughFromMarkdown())
toMarkdownExtensions.push(gfmStrikethroughToMarkdown())
}

export { remarkStrikethrough }
8 changes: 8 additions & 0 deletions src/types/unified.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Options as Extension } from 'mdast-util-to-markdown'

// https://github.com/remarkjs/remark/blob/5017a27db024db6feec85a3e1e19f8d78a485680/packages/remark-stringify/index.d.ts#L26-L41
declare module 'unified' {
interface Data {
toMarkdownExtensions?: Extension[]
}
}
Loading
Loading