Socket
Socket
Sign inDemoInstall

mdast-util-to-hast

Package Overview
Dependencies
Maintainers
2
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mdast-util-to-hast - npm Package Compare versions

Comparing version 11.3.0 to 12.0.0

46

lib/footer.d.ts
/**
* @param {H} h
*/
export function footer(h: H): import('hast').Element | null
export function footer(h: H): {
type: string
tagName: string
properties: {
dataFootnotes: boolean
className: string[]
}
children: (
| {
type: string
tagName: string
properties: {
id: string
className: string[]
}
children: {
type: 'text'
value: string
}[]
value?: undefined
}
| {
type: string
value: string
tagName?: undefined
properties?: undefined
children?: undefined
}
| {
type: string
tagName: string
properties: {
id?: undefined
className?: undefined
}
children: import('hast').ElementContent[]
value?: undefined
}
)[]
} | null
export type BlockContent = import('mdast').BlockContent
export type FootnoteDefinition = import('mdast').FootnoteDefinition
export type Link = import('mdast').Link
export type ListItem = import('mdast').ListItem
export type Paragraph = import('mdast').Paragraph
export type Element = import('hast').Element
export type ElementContent = import('hast').ElementContent
export type H = import('./index.js').H

134

lib/footer.js
/**
* @typedef {import('mdast').BlockContent} BlockContent
* @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition
* @typedef {import('mdast').Link} Link
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').Paragraph} Paragraph
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('./index.js').H} H
*/
import {thematicBreak} from './handlers/thematic-break.js'
import {list} from './handlers/list.js'
import {sanitizeUri} from 'micromark-util-sanitize-uri'
import {u} from 'unist-builder'
import {all} from './traverse.js'
import {wrap} from './wrap.js'

@@ -18,10 +18,8 @@

export function footer(h) {
const footnoteById = h.footnoteById
const footnoteOrder = h.footnoteOrder
let index = -1
/** @type {Array.<ListItem>} */
/** @type {ElementContent[]} */
const listItems = []
while (++index < footnoteOrder.length) {
const def = footnoteById[footnoteOrder[index].toUpperCase()]
while (++index < h.footnoteOrder.length) {
const def = h.footnoteById[h.footnoteOrder[index].toUpperCase()]

@@ -32,28 +30,71 @@ if (!def) {

const marker = String(index + 1)
const content = [...def.children]
/** @type {Link} */
const backReference = {
type: 'link',
url: '#fnref' + marker,
data: {hProperties: {className: ['footnote-back'], role: 'doc-backlink'}},
children: [{type: 'text', value: '↩'}]
const content = all(h, def)
const id = String(def.identifier)
const safeId = sanitizeUri(id.toLowerCase())
let referenceIndex = 0
/** @type {ElementContent[]} */
const backReferences = []
while (++referenceIndex <= h.footnoteCounts[id]) {
/** @type {Element} */
const backReference = {
type: 'element',
tagName: 'a',
properties: {
href:
'#' +
h.clobberPrefix +
'fnref-' +
safeId +
(referenceIndex > 1 ? '-' + referenceIndex : ''),
dataFootnoteBackref: true,
className: ['data-footnote-backref'],
ariaLabel: h.footnoteBackLabel
},
children: [{type: 'text', value: '↩'}]
}
if (referenceIndex > 1) {
backReference.children.push({
type: 'element',
tagName: 'sup',
children: [{type: 'text', value: String(referenceIndex)}]
})
}
if (backReferences.length > 0) {
backReferences.push({type: 'text', value: ' '})
}
backReferences.push(backReference)
}
const tail = content[content.length - 1]
if (tail && tail.type === 'paragraph') {
tail.children.push(backReference)
if (tail && tail.type === 'element' && tail.tagName === 'p') {
const tailTail = tail.children[tail.children.length - 1]
if (tailTail && tailTail.type === 'text') {
tailTail.value += ' '
} else {
tail.children.push({type: 'text', value: ' '})
}
tail.children.push(...backReferences)
} else {
// @ts-expect-error Indeed, link directly added in block content.
// Which we do because that way at least the handlers will be called
// for the other HTML we’re generating (as markdown).
content.push(backReference)
content.push(...backReferences)
}
listItems.push({
type: 'listItem',
data: {hProperties: {id: 'fn' + marker, role: 'doc-endnote'}},
children: content,
position: def.position
})
/** @type {Element} */
const listItem = {
type: 'element',
tagName: 'li',
properties: {id: h.clobberPrefix + 'fn-' + safeId},
children: wrap(content, true)
}
if (def.position) {
listItem.position = def.position
}
listItems.push(listItem)
}

@@ -65,14 +106,23 @@

return h(
null,
'section',
{className: ['footnotes'], role: 'doc-endnotes'},
wrap(
[
thematicBreak(h),
list(h, {type: 'list', ordered: true, children: listItems})
],
true
)
)
return {
type: 'element',
tagName: 'section',
properties: {dataFootnotes: true, className: ['footnotes']},
children: [
{
type: 'element',
tagName: 'h2',
properties: {id: 'footnote-label', className: ['sr-only']},
children: [u('text', h.footnoteLabel)]
},
{type: 'text', value: '\n'},
{
type: 'element',
tagName: 'ol',
properties: {},
children: wrap(listItems, true)
},
{type: 'text', value: '\n'}
]
}
}

@@ -6,2 +6,3 @@ /**

import {sanitizeUri} from 'micromark-util-sanitize-uri'
import {u} from 'unist-builder'

@@ -14,20 +15,36 @@

export function footnoteReference(h, node) {
const footnoteOrder = h.footnoteOrder
const identifier = String(node.identifier)
const index = footnoteOrder.indexOf(identifier)
const marker = String(
index === -1 ? footnoteOrder.push(identifier) : index + 1
)
const id = String(node.identifier)
const safeId = sanitizeUri(id.toLowerCase())
const index = h.footnoteOrder.indexOf(id)
/** @type {number} */
let counter
return h(
node,
'a',
{
href: '#fn' + marker,
className: ['footnote-ref'],
id: 'fnref' + marker,
role: 'doc-noteref'
},
[h(node.position, 'sup', [u('text', marker)])]
)
if (index === -1) {
h.footnoteOrder.push(id)
h.footnoteCounts[id] = 1
counter = h.footnoteOrder.length
} else {
h.footnoteCounts[id]++
counter = index + 1
}
const reuseCounter = h.footnoteCounts[id]
return h(node, 'sup', [
h(
node.position,
'a',
{
href: '#' + h.clobberPrefix + 'fn-' + safeId,
id:
h.clobberPrefix +
'fnref-' +
safeId +
(reuseCounter > 1 ? '-' + reuseCounter : ''),
dataFootnoteRef: true,
ariaDescribedBy: 'footnote-label'
},
[u('text', String(counter))]
)
])
}
/**
* @typedef {import('mdast').Footnote} Footnote
* @typedef {import('../index.js').Handler} Handler
*
* @todo
* `footnote` (or “inline note”) are a pandoc footnotes feature (`^[a note]`)
* that does not exist in GFM.
* We still have support for it, so that things remain working with
* `micromark-extension-footnote` and `mdast-util-footnote`, but in the future
* we might be able to remove it?
*/

@@ -14,3 +21,2 @@

const footnoteById = h.footnoteById
const footnoteOrder = h.footnoteOrder
let no = 1

@@ -22,6 +28,2 @@

// No need to check if `identifier` exists in `footnoteOrder`, it’s guaranteed
// to not exist because we just generated it.
footnoteOrder.push(identifier)
footnoteById[identifier] = {

@@ -28,0 +30,0 @@ type: 'footnoteDefinition',

@@ -76,2 +76,14 @@ /**

/**
* Prefix to use to prevent DOM clobbering
*/
clobberPrefix: string
/**
* Label to use to introduce the footnote section
*/
footnoteLabel: string
/**
* Label to use to go back to a footnote call from the footnote section
*/
footnoteBackLabel: string
/**
* Definition cache

@@ -91,2 +103,6 @@ */

/**
* Counts the same footnote was used
*/
footnoteCounts: Record<string, number>
/**
* Applied handlers

@@ -117,2 +133,28 @@ */

/**
* Prefix to use before the `id` attribute to prevent it from *clobbering*.
* attributes.
* DOM clobbering is this:
*
* ```html
* <p id=x></p>
* <script>alert(x)</script>
* ```
*
* Elements by their ID are made available in browsers on the `window` object.
* Using a prefix prevents this from being a problem.
*/
clobberPrefix?: string | undefined
/**
* Label to use for the footnotes section.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
*/
footnoteLabel?: string | undefined
/**
* Label to use from backreferences back to their footnote call.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
*/
footnoteBackLabel?: string | undefined
/**
* Object mapping mdast nodes to functions handling them

@@ -119,0 +161,0 @@ */

@@ -45,5 +45,9 @@ /**

* @property {boolean} dangerous Whether HTML is allowed
* @property {string} clobberPrefix Prefix to use to prevent DOM clobbering
* @property {string} footnoteLabel Label to use to introduce the footnote section
* @property {string} footnoteBackLabel Label to use to go back to a footnote call from the footnote section
* @property {(identifier: string) => Definition|null} definition Definition cache
* @property {Object.<string, FootnoteDefinition>} footnoteById Footnote cache
* @property {Array.<string>} footnoteOrder Order in which footnotes occur
* @property {Record.<string, number>} footnoteCounts Counts the same footnote was used
* @property {Handlers} handlers Applied handlers

@@ -55,9 +59,35 @@ * @property {Handler} unknownHandler Handler for any none not in `passThrough` or otherwise handled

* @typedef Options
* @property {boolean} [allowDangerousHtml=false] Whether to allow `html` nodes and inject them as `raw` HTML
* @property {Handlers} [handlers] Object mapping mdast nodes to functions handling them
* @property {Array.<string>} [passThrough] List of custom mdast node types to pass through (keep) in hast
* @property {Handler} [unknownHandler] Handler for all unknown nodes.
* @property {boolean} [allowDangerousHtml=false]
* Whether to allow `html` nodes and inject them as `raw` HTML
* @property {string} [clobberPrefix='user-content-']
* Prefix to use before the `id` attribute to prevent it from *clobbering*.
* attributes.
* DOM clobbering is this:
*
* @typedef {Record.<string, Handler>} Handlers Map of node types to handlers
* @typedef {HFunctionProps & HFunctionNoProps & HFields} H Handle context
* ```html
* <p id=x></p>
* <script>alert(x)</script>
* ```
*
* Elements by their ID are made available in browsers on the `window` object.
* Using a prefix prevents this from being a problem.
* @property {string} [footnoteLabel='Footnotes']
* Label to use for the footnotes section.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
* @property {string} [footnoteBackLabel='Back to content']
* Label to use from backreferences back to their footnote call.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
* @property {Handlers} [handlers]
* Object mapping mdast nodes to functions handling them
* @property {Array.<string>} [passThrough]
* List of custom mdast node types to pass through (keep) in hast
* @property {Handler} [unknownHandler]
* Handler for all unknown nodes.
*
* @typedef {Record.<string, Handler>} Handlers
* Map of node types to handlers
* @typedef {HFunctionProps & HFunctionNoProps & HFields} H
* Handle context
*/

@@ -89,2 +119,8 @@

h.dangerous = dangerous
h.clobberPrefix =
settings.clobberPrefix === undefined || settings.clobberPrefix === null
? 'user-content-'
: settings.clobberPrefix
h.footnoteLabel = settings.footnoteLabel || 'Footnotes'
h.footnoteBackLabel = settings.footnoteBackLabel || 'Back to content'
h.definition = definitions(tree)

@@ -94,2 +130,4 @@ h.footnoteById = footnoteById

h.footnoteOrder = []
/** @type {Record.<string, number>} */
h.footnoteCounts = {}
h.augment = augment

@@ -96,0 +134,0 @@ h.handlers = {...handlers, ...settings.handlers}

{
"name": "mdast-util-to-hast",
"version": "11.3.0",
"version": "12.0.0",
"description": "mdast utility to transform to hast",

@@ -43,2 +43,3 @@ "license": "MIT",

"mdurl": "^1.0.0",
"micromark-util-sanitize-uri": "^1.0.0",
"unist-builder": "^3.0.0",

@@ -53,7 +54,5 @@ "unist-util-generated": "^2.0.0",

"hast-util-to-html": "^8.0.0",
"mdast-util-footnote": "^1.0.0",
"mdast-util-from-markdown": "^1.0.0",
"mdast-util-gfm": "^1.0.0",
"micromark-extension-footnote": "^1.0.0",
"micromark-extension-gfm": "^1.0.0",
"mdast-util-gfm": "^2.0.0",
"micromark-extension-gfm": "^2.0.0",
"prettier": "^2.0.0",

@@ -66,3 +65,3 @@ "remark-cli": "^10.0.0",

"typescript": "^4.0.0",
"xo": "^0.44.0"
"xo": "^0.45.0"
},

@@ -69,0 +68,0 @@ "scripts": {

@@ -75,2 +75,29 @@ # mdast-util-to-hast

###### `options.clobberPrefix`
Prefix to use before the `id` attribute on footnotes to prevent it from
*clobbering* (`string`, default: `'user-content-'`).
DOM clobbering is this:
```html
<p id=x></p>
<script>alert(x)</script>
```
Elements by their ID are made available in browsers on the `window` object.
Using a prefix this that from being a problem.
###### `options.footnoteLabel`
Label to use for the footnotes section (`string`, default: `'Footnotes'`).
Affects screen reader users.
Change it if you’re authoring in a different language.
###### `options.footnoteBackLabel`
Label to use from backreferences back to their footnote call (`string`, default:
`'Back to content'`).
Affects screen reader users.
Change it if you’re authoring in a different language.
###### `options.handlers`

@@ -233,2 +260,38 @@

## Recommended CSS
The following CSS is needed to make footnotes look a bit like GitHub.
For the complete actual CSS that GitHub uses see
[`sindresorhus/github-markdown-css`](https://github.com/sindresorhus/github-markdown-css).
```css
/* Style the footnotes section. */
.footnotes {
font-size: smaller;
color: #8b949e;
border-top: 1px solid #30363d;
}
/* Hide the section label for visual users. */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
word-wrap: normal;
border: 0;
}
/* Place `[` and `]` around footnote calls. */
[data-footnote-ref]::before {
content: '[';
}
[data-footnote-ref]::after {
content: ']';
}
```
## Security

@@ -235,0 +298,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc