Skip to content

Commit

Permalink
Add safe helper on state
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Jan 4, 2023
1 parent 19301e7 commit e9f71aa
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 30 deletions.
5 changes: 2 additions & 3 deletions lib/handle/code.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import {longestStreak} from 'longest-streak'
import {formatCodeAsIndented} from '../util/format-code-as-indented.js'
import {checkFence} from '../util/check-fence.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

/**
Expand Down Expand Up @@ -39,7 +38,7 @@ export function code(node, _, state, info) {
if (node.lang) {
const subexit = state.enter(`codeFencedLang${suffix}`)
value += tracker.move(
safe(state, node.lang, {
state.safe(node.lang, {
before: value,
after: ' ',
encode: ['`'],
Expand All @@ -53,7 +52,7 @@ export function code(node, _, state, info) {
const subexit = state.enter(`codeFencedMeta${suffix}`)
value += tracker.move(' ')
value += tracker.move(
safe(state, node.meta, {
state.safe(node.meta, {
before: value,
after: '\n',
encode: ['`'],
Expand Down
9 changes: 4 additions & 5 deletions lib/handle/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import {association} from '../util/association.js'
import {checkQuote} from '../util/check-quote.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

/**
Expand All @@ -25,7 +24,7 @@ export function definition(node, _, state, info) {
const tracker = track(info)
let value = tracker.move('[')
value += tracker.move(
safe(state, association(node), {
state.safe(association(node), {
before: value,
after: ']',
...tracker.current()
Expand All @@ -44,14 +43,14 @@ export function definition(node, _, state, info) {
subexit = state.enter('destinationLiteral')
value += tracker.move('<')
value += tracker.move(
safe(state, node.url, {before: value, after: '>', ...tracker.current()})
state.safe(node.url, {before: value, after: '>', ...tracker.current()})
)
value += tracker.move('>')
} else {
// No whitespace, raw is prettier.
subexit = state.enter('destinationRaw')
value += tracker.move(
safe(state, node.url, {
state.safe(node.url, {
before: value,
after: node.title ? ' ' : '\n',
...tracker.current()
Expand All @@ -65,7 +64,7 @@ export function definition(node, _, state, info) {
subexit = state.enter(`title${suffix}`)
value += tracker.move(' ' + quote)
value += tracker.move(
safe(state, node.title, {
state.safe(node.title, {
before: value,
after: quote,
...tracker.current()
Expand Down
5 changes: 2 additions & 3 deletions lib/handle/image-reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import {association} from '../util/association.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

imageReference.peek = imageReferencePeek
Expand All @@ -24,7 +23,7 @@ export function imageReference(node, _, state, info) {
let subexit = state.enter('label')
const tracker = track(info)
let value = tracker.move('![')
const alt = safe(state, node.alt, {
const alt = state.safe(node.alt, {
before: value,
after: ']',
...tracker.current()
Expand All @@ -40,7 +39,7 @@ export function imageReference(node, _, state, info) {
// up making a `shortcut` reference, because then there is no brace output.
// Practically, in that case, there is no content, so it doesn’t matter that
// we’ve tracked one too many characters.
const reference = safe(state, association(node), {
const reference = state.safe(association(node), {
before: value,
after: ']',
...tracker.current()
Expand Down
9 changes: 4 additions & 5 deletions lib/handle/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import {checkQuote} from '../util/check-quote.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

image.peek = imagePeek
Expand All @@ -26,7 +25,7 @@ export function image(node, _, state, info) {
const tracker = track(info)
let value = tracker.move('![')
value += tracker.move(
safe(state, node.alt, {before: value, after: ']', ...tracker.current()})
state.safe(node.alt, {before: value, after: ']', ...tracker.current()})
)
value += tracker.move('](')

Expand All @@ -41,14 +40,14 @@ export function image(node, _, state, info) {
subexit = state.enter('destinationLiteral')
value += tracker.move('<')
value += tracker.move(
safe(state, node.url, {before: value, after: '>', ...tracker.current()})
state.safe(node.url, {before: value, after: '>', ...tracker.current()})
)
value += tracker.move('>')
} else {
// No whitespace, raw is prettier.
subexit = state.enter('destinationRaw')
value += tracker.move(
safe(state, node.url, {
state.safe(node.url, {
before: value,
after: node.title ? ' ' : ')',
...tracker.current()
Expand All @@ -62,7 +61,7 @@ export function image(node, _, state, info) {
subexit = state.enter(`title${suffix}`)
value += tracker.move(' ' + quote)
value += tracker.move(
safe(state, node.title, {
state.safe(node.title, {
before: value,
after: quote,
...tracker.current()
Expand Down
3 changes: 1 addition & 2 deletions lib/handle/link-reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import {association} from '../util/association.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

linkReference.peek = linkReferencePeek
Expand Down Expand Up @@ -40,7 +39,7 @@ export function linkReference(node, _, state, info) {
// up making a `shortcut` reference, because then there is no brace output.
// Practically, in that case, there is no content, so it doesn’t matter that
// we’ve tracked one too many characters.
const reference = safe(state, association(node), {
const reference = state.safe(association(node), {
before: value,
after: ']',
...tracker.current()
Expand Down
7 changes: 3 additions & 4 deletions lib/handle/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import {checkQuote} from '../util/check-quote.js'
import {formatLinkAsAutolink} from '../util/format-link-as-autolink.js'
import {safe} from '../util/safe.js'
import {track} from '../util/track.js'

link.peek = linkPeek
Expand Down Expand Up @@ -70,14 +69,14 @@ export function link(node, _, state, info) {
subexit = state.enter('destinationLiteral')
value += tracker.move('<')
value += tracker.move(
safe(state, node.url, {before: value, after: '>', ...tracker.current()})
state.safe(node.url, {before: value, after: '>', ...tracker.current()})
)
value += tracker.move('>')
} else {
// No whitespace, raw is prettier.
subexit = state.enter('destinationRaw')
value += tracker.move(
safe(state, node.url, {
state.safe(node.url, {
before: value,
after: node.title ? ' ' : ')',
...tracker.current()
Expand All @@ -91,7 +90,7 @@ export function link(node, _, state, info) {
subexit = state.enter(`title${suffix}`)
value += tracker.move(' ' + quote)
value += tracker.move(
safe(state, node.title, {
state.safe(node.title, {
before: value,
after: quote,
...tracker.current()
Expand Down
4 changes: 1 addition & 3 deletions lib/handle/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* @typedef {import('../types.js').Info} Info
*/

import {safe} from '../util/safe.js'

/**
* @param {Text} node
* @param {Parent | undefined} _
Expand All @@ -15,5 +13,5 @@ import {safe} from '../util/safe.js'
* @returns {string}
*/
export function text(node, _, state, info) {
return safe(state, node.value, info)
return state.safe(node.value, info)
}
32 changes: 32 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @typedef {import('./types.js').Options} Options
* @typedef {import('./types.js').Parent} Parent
* @typedef {import('./types.js').PhrasingContent} PhrasingContent
* @typedef {import('./types.js').SafeConfig} SafeConfig
* @typedef {import('./types.js').State} State
* @typedef {import('./types.js').TrackFields} TrackFields
*/
Expand All @@ -19,6 +20,7 @@ import {unsafe} from './unsafe.js'
import {containerPhrasing} from './util/container-phrasing.js'
import {containerFlow} from './util/container-flow.js'
import {indentLines} from './util/indent-lines.js'
import {safe} from './util/safe.js'

/**
* Turn an mdast syntax tree into markdown.
Expand All @@ -37,6 +39,7 @@ export function toMarkdown(tree, options = {}) {
indentLines,
containerPhrasing: containerPhrasingBound,
containerFlow: containerFlowBound,
safe: safeBound,
stack: [],
unsafe: [],
join: [],
Expand Down Expand Up @@ -150,3 +153,32 @@ function containerPhrasingBound(parent, info) {
function containerFlowBound(parent, info) {
return containerFlow(parent, this, info)
}

/**
* Make a string safe for embedding in markdown constructs.
*
* In markdown, almost all punctuation characters can, in certain cases,
* result in something.
* Whether they do is highly subjective to where they happen and in what
* they happen.
*
* To solve this, `mdast-util-to-markdown` tracks:
*
* * Characters before and after something;
* * What “constructs” we are in.
*
* This information is then used by this function to escape or encode
* special characters.
*
* @this {State}
* Info passed around about the current state.
* @param {string | null | undefined} value
* Raw value to make safe.
* @param {SafeConfig} config
* Configuration.
* @returns {string}
* Serialized markdown safe for embedding.
*/
function safeBound(value, config) {
return safe(this, value, config)
}
35 changes: 35 additions & 0 deletions lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,39 @@
* @returns {string}
* Serialized children, joined by (blank) lines.
*
* @typedef SafeEncodeFields
* Extra configuration for `safe`
* @property {Array<string> | null | undefined} [encode]
* Extra characters that *must* be encoded (as character references) instead
* of escaped (character escapes).
*
* Only ASCII punctuation will use character escapes, so you never need to
* pass non-ASCII-punctuation here.
*
* @typedef {SafeFields & SafeEncodeFields} SafeConfig
*
* @callback Safe
* Make a string safe for embedding in markdown constructs.
*
* In markdown, almost all punctuation characters can, in certain cases,
* result in something.
* Whether they do is highly subjective to where they happen and in what
* they happen.
*
* To solve this, `mdast-util-to-markdown` tracks:
*
* * Characters before and after something;
* * What “constructs” we are in.
*
* This information is then used by this function to escape or encode
* special characters.
* @param {string | null | undefined} input
* Raw value to make safe.
* @param {SafeConfig} config
* Configuration.
* @returns {string}
* Serialized markdown safe for embedding.
*
* @callback Enter
* Enter something.
* @param {ConstructName} name
Expand All @@ -99,6 +132,8 @@
* Serialize the children of a parent that contains phrasing children.
* @property {ContainerFlow} containerFlow
* Serialize the children of a parent that contains flow children.
* @property {Safe} safe
* Serialize the children of a parent that contains flow children.
* @property {Enter} enter
* Enter a construct (returns a corresponding exit function).
* @property {Options} options
Expand Down
23 changes: 21 additions & 2 deletions lib/util/safe.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
/**
* @typedef {import('../types.js').State} State
* @typedef {import('../types.js').SafeFields} SafeFields
* @typedef {import('../types.js').SafeConfig} SafeConfig
*/

import {patternCompile} from './pattern-compile.js'
import {patternInScope} from './pattern-in-scope.js'

/**
* Make a string safe for embedding in markdown constructs.
*
* In markdown, almost all punctuation characters can, in certain cases,
* result in something.
* Whether they do is highly subjective to where they happen and in what
* they happen.
*
* To solve this, `mdast-util-to-markdown` tracks:
*
* * Characters before and after something;
* * What “constructs” we are in.
*
* This information is then used by this function to escape or encode
* special characters.
*
* @param {State} state
* Info passed around about the current state.
* @param {string | null | undefined} input
* @param {SafeFields & {encode?: Array<string>}} config
* Raw value to make safe.
* @param {SafeConfig} config
* Configuration.
* @returns {string}
* Serialized markdown safe for embedding.
*/
export function safe(state, input, config) {
const value = (config.before || '') + (input || '') + (config.after || '')
Expand Down

0 comments on commit e9f71aa

Please sign in to comment.