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

Fixes #626

Merged
merged 7 commits into from
Nov 20, 2024
Merged

Fixes #626

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
5 changes: 5 additions & 0 deletions .changeset/fifty-pumpkins-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'markdown-to-jsx': patch
---

Adjust type signature for `<Markdown>` component to allow for easier composition.
12 changes: 12 additions & 0 deletions .changeset/shaggy-ants-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'markdown-to-jsx': minor
---

Add support for GFM alert-style blockquotes.

```md
> [!Note]
> This is a note-flavored alert blockquote. The "Note" text is injected as a `<header>` by
> default and the blockquote can be styled via the injected class `markdown-alert-note`
> for example.
```
5 changes: 5 additions & 0 deletions .changeset/strange-ghosts-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'markdown-to-jsx': patch
---

Use newer `React.JSX.*` namespace instead of `JSX.*` for React 19 compatibility.
5 changes: 5 additions & 0 deletions .changeset/tidy-cheetahs-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'markdown-to-jsx': patch
---

Arbitrary HTML no longer punches out pipes when parsing rows. If you absolutely need a pipe character that isn't a table separator, either escape it or enclose it in backticks to trigger inline code handling.
5 changes: 5 additions & 0 deletions .changeset/tricky-cups-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'markdown-to-jsx': patch
---

Drop encountered `ref` attributes when processing inline HTML, React doesn't handle it well.
11 changes: 0 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,3 @@ jobs:
with:
createGithubReleases: true
publish: yarn changeset-publish

- name: Setup site
if: steps.changesets.outputs.published == 'true'
uses: actions/configure-pages@v5

- name: Upload site
if: steps.changesets.outputs.published == 'true'
uses: actions/upload-pages-artifact@v3
with:
# generated during yarn changeset-publish
path: 'docs'
26 changes: 26 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Update website
on: [release]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup node
uses: actions/setup-node@v4
with:
cache: 'yarn'
node-version-file: '.nvmrc'

- run: yarn --immutable
- run: yarn build && yarn release

- name: Setup site
uses: actions/configure-pages@v5

- name: Upload site
uses: actions/upload-pages-artifact@v3
with:
# generated during yarn changeset-publish
path: 'docs'
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ node_modules/
.git/
.DS_Store
coverage/
dist/
*.log
package-lock.json
npm-shrinkwrap.json
Expand All @@ -15,3 +14,8 @@ npm-shrinkwrap.json
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# ephemeral files
dist/
docs/markdown-to-jsx.js
docs/markdown-to-jsx.js.map
Binary file not shown.
893 changes: 0 additions & 893 deletions .yarn/releases/yarn-4.0.2.cjs

This file was deleted.

934 changes: 934 additions & 0 deletions .yarn/releases/yarn-4.5.1.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ enableGlobalCache: false

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.0.2.cjs
yarnPath: .yarn/releases/yarn-4.5.1.cjs
7 changes: 5 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<title>markdown-to-jsx: try it live!</title>
Expand Down Expand Up @@ -45,7 +45,7 @@
<script type="text/plain" id="sample-content">
# This is Markdown

#### You can edit me!
You can edit me!

[Markdown](http://daringfireball.net/projects/markdown/) lets you write content in a really natural way.

Expand All @@ -57,6 +57,9 @@

<small>Sample content borrowed with thanks from [elm-markdown](http://elm-lang.org/examples/markdown) ❤️</small>

> [!Tip]
> Normal and "alert" blockquotes are possible. Use `renderRule` for more control if needed.

Custom handling of code blocks (or any rule!) is possible with the [`renderRule` option](https://github.com/quantizor/markdown-to-jsx#optionsrenderrule). For example, LaTeX support via [`@matejmazur/react-katex`](https://www.npmjs.com/package/@matejmazur/react-katex):

```latex
Expand Down
2 changes: 0 additions & 2 deletions docs/markdown-to-jsx.js

This file was deleted.

1 change: 0 additions & 1 deletion docs/markdown-to-jsx.js.map

This file was deleted.

64 changes: 64 additions & 0 deletions index.compiler.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,21 @@ describe('misc block level elements', () => {
</div>
`)
})

it('should handle alert blockquotes', () => {
render(compiler('> [!NOTE]\n> Something important, perhaps?'))

expect(root.innerHTML).toMatchInlineSnapshot(`
<blockquote class="markdown-alert-note">
<header>
NOTE
</header>
<p>
Something important, perhaps?
</p>
</blockquote>
`)
})
})

describe('headings', () => {
Expand Down Expand Up @@ -2247,6 +2262,55 @@ describe('GFM tables', () => {
`)
})

it('regression #625 - processes self-closing HTML inside of a table row', () => {
render(
compiler(theredoc`
| col1 | col2 | col3 |
|------|-----------------|------------------|
| col1 | <custom-element>col2</custom-element><br> col2 | <custom-element>col3</custom-element><br>col3 |
`)
)

expect(root.innerHTML).toMatchInlineSnapshot(`
<table>
<thead>
<tr>
<th>
col1
</th>
<th>
col2
</th>
<th>
col3
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
col1
</td>
<td>
<custom-element>
col2
</custom-element>
<br>
col2
</td>
<td>
<custom-element>
col3
</custom-element>
<br>
col3
</td>
</tr>
</tbody>
</table>
`)
})

it('processes markdown inside of a table row when a preceeding column contains HTML', () => {
render(
compiler(theredoc`
Expand Down
71 changes: 47 additions & 24 deletions index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ const ATTR_EXTRACTOR_R =

const AUTOLINK_MAILTO_CHECK_R = /mailto:/i
const BLOCK_END_R = /\n{2,}$/
const BLOCKQUOTE_R = /^(\s*>[\s\S]*?)(?=\n{2,})/
const BLOCKQUOTE_R = /^(\s*>[\s\S]*?)(?=\n\n|$)/
const BLOCKQUOTE_TRIM_LEFT_MULTILINE_R = /^ *> ?/gm
const BLOCKQUOTE_ALERT_R = /^(?:\[!([^\]]*)\]\n)?([\s\S]*)/
const BREAK_LINE_R = /^ {2,}\n/
const BREAK_THEMATIC_R = /^(?:( *[-*_])){3,} *(?:\n *)+\n/
const CODE_BLOCK_FENCED_R =
Expand Down Expand Up @@ -624,8 +625,8 @@ function parseTableRow(
state.inTable = true
let tableRow = source
.trim()
// isolate situations where a pipe should be ignored (inline code, HTML)
.split(/( *(?:`[^`]*`|<.*?>.*?<\/.*?>(?!<\/.*?>)|\\\||\|) *)/)
// isolate situations where a pipe should be ignored (inline code, escaped, etc)
.split(/( *(?:`[^`]*`|\\\||\|) *)/)
.reduce((nodes, fragment) => {
if (fragment.trim() === '|')
nodes.push(
Expand Down Expand Up @@ -1230,7 +1231,7 @@ export function compiler(
function attrStringToMap(
tag: MarkdownToJSX.HTMLTags,
str: string
): JSX.IntrinsicAttributes {
): React.JSX.IntrinsicAttributes {
const attributes = str.match(ATTR_EXTRACTOR_R)
if (!attributes) {
return null
Expand All @@ -1244,6 +1245,10 @@ export function compiler(
const value = unquote(raw.slice(delimiterIdx + 1).trim())

const mappedKey = ATTRIBUTE_TO_JSX_PROP_MAP[key] || key

// bail out, not supported
if (mappedKey === 'ref') return map

const normalizedValue = (map[mappedKey] = attributeValueToJSXPropValue(
tag,
key,
Expand Down Expand Up @@ -1302,19 +1307,35 @@ export function compiler(
match: blockRegex(BLOCKQUOTE_R),
order: Priority.HIGH,
parse(capture, parse, state) {
const [, alert, content] = capture[0]
.replace(BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, '')
.match(BLOCKQUOTE_ALERT_R)

return {
children: parse(
capture[0].replace(BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, ''),
state
),
alert,
children: parse(content, state),
}
},
render(node, output, state) {
return (
<blockquote key={state.key}>
{output(node.children, state)}
</blockquote>
)
const props = {
key: state.key,
} as Record<string, unknown>

if (node.alert) {
props.className =
'markdown-alert-' +
options.slugify(node.alert.toLowerCase(), slugify)

node.children.unshift({
attrs: {},
children: [{ type: RuleType.text, text: node.alert }],
noInnerParse: true,
type: RuleType.htmlBlock,
tag: 'header',
})
}

return h('blockquote', props, output(node.children, state))
},
},

Expand Down Expand Up @@ -1967,11 +1988,12 @@ export function compiler(
* A simple HOC for easy React use. Feed the markdown content as a direct child
* and the rest is taken care of automatically.
*/
const Markdown: React.FC<{
[key: string]: any
children: string
options?: MarkdownToJSX.Options
}> = ({ children = '', options, ...props }) => {
const Markdown: React.FC<
Omit<React.HTMLAttributes<Element>, 'children'> & {
children: string
options?: MarkdownToJSX.Options
}
> = ({ children = '', options, ...props }) => {
if (process.env.NODE_ENV !== 'production' && typeof children !== 'string') {
console.error(
'markdown-to-jsx: <Markdown> component only accepts a single string as a child, received:',
Expand All @@ -1981,7 +2003,7 @@ const Markdown: React.FC<{

return React.cloneElement(
compiler(children, options),
props as JSX.IntrinsicAttributes
props as React.JSX.IntrinsicAttributes
)
}

Expand All @@ -1999,7 +2021,7 @@ export namespace MarkdownToJSX {

export type CreateElement = typeof React.createElement

export type HTMLTags = keyof JSX.IntrinsicElements
export type HTMLTags = keyof React.JSX.IntrinsicElements

export type State = {
/** true if the current content is inside anchor link grammar */
Expand All @@ -2019,6 +2041,7 @@ export namespace MarkdownToJSX {
}

export interface BlockQuoteNode {
alert?: string
children: MarkdownToJSX.ParserResult[]
type: typeof RuleType.blockQuote
}
Expand All @@ -2033,7 +2056,7 @@ export namespace MarkdownToJSX {

export interface CodeBlockNode {
type: typeof RuleType.codeBlock
attrs?: JSX.IntrinsicAttributes
attrs?: React.JSX.IntrinsicAttributes
lang?: string
text: string
}
Expand Down Expand Up @@ -2187,7 +2210,7 @@ export namespace MarkdownToJSX {

export interface HTMLNode {
type: typeof RuleType.htmlBlock
attrs: JSX.IntrinsicAttributes
attrs: React.JSX.IntrinsicAttributes
children?: ReturnType<MarkdownToJSX.NestedParser> | undefined
noInnerParse: Boolean
tag: MarkdownToJSX.HTMLTags
Expand All @@ -2196,7 +2219,7 @@ export namespace MarkdownToJSX {

export interface HTMLSelfClosingNode {
type: typeof RuleType.htmlSelfClosing
attrs: JSX.IntrinsicAttributes
attrs: React.JSX.IntrinsicAttributes
tag: string
}

Expand Down Expand Up @@ -2295,7 +2318,7 @@ export namespace MarkdownToJSX {
*/
createElement: (
tag: Parameters<CreateElement>[0],
props: JSX.IntrinsicAttributes,
props: React.JSX.IntrinsicAttributes,
...children: React.ReactChild[]
) => React.ReactChild

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@
"size-limit": [
{
"path": "./dist/index.module.js",
"limit": "6.25 kB"
"limit": "6.5 kB"
},
{
"path": "./dist/index.modern.js",
"limit": "6.2 kB"
"limit": "6.5 kB"
}
],
"jest": {
Expand All @@ -124,5 +124,5 @@
"jest-serializer-html"
]
},
"packageManager": "yarn@4.0.2"
"packageManager": "yarn@4.5.1"
}
Loading
Loading