Skip to content

Commit 1779d91

Browse files
committed
Add options.quoteSmart for attribute values
Related to: GH-3.
1 parent ba751fe commit 1779d91

File tree

4 files changed

+90
-12
lines changed

4 files changed

+90
-12
lines changed

lib/index.js

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@
1919
*
2020
* @typedef ToMarkdownOptions
2121
* @property {'"'|"'"} [quote='"']
22+
* Preferred quote to use around attribute values.
23+
* @property {boolean} [quoteSmart=false]
24+
* Use the other quote if that results in less bytes.
2225
*/
2326

27+
import {ccount} from 'ccount'
2428
import {parseEntities} from 'parse-entities'
2529
import {stringifyPosition} from 'unist-util-stringify-position'
2630
import {VFileMessage} from 'vfile-message'
@@ -359,7 +363,8 @@ export function mdxJsxFromMarkdown() {
359363
* @returns {ToMarkdownExtension}
360364
*/
361365
export function mdxJsxToMarkdown(options = {}) {
362-
const {quote = '"'} = options
366+
const {quote = '"', quoteSmart} = options
367+
const alternative = quote === '"' ? "'" : '"'
363368

364369
if (quote !== '"' && quote !== "'") {
365370
throw new Error(
@@ -419,16 +424,28 @@ export function mdxJsxToMarkdown(options = {}) {
419424
throw new Error('Cannot serialize attribute w/o name')
420425
}
421426

422-
result =
423-
attribute.name +
424-
(attribute.value === undefined || attribute.value === null
425-
? ''
426-
: '=' +
427-
(typeof attribute.value === 'object'
428-
? '{' + (attribute.value.value || '') + '}'
429-
: quote +
430-
stringifyEntitiesLight(attribute.value, {subset: [quote]}) +
431-
quote))
427+
const value = attribute.value
428+
let initializer = ''
429+
430+
if (value === undefined || value === null) {
431+
// Empty.
432+
} else if (typeof value === 'object') {
433+
initializer = '={' + (value.value || '') + '}'
434+
} else {
435+
// If the alternative is less common than `quote`, switch.
436+
const appliedQuote =
437+
quoteSmart && ccount(value, quote) > ccount(value, alternative)
438+
? alternative
439+
: quote
440+
441+
initializer +=
442+
'=' +
443+
appliedQuote +
444+
stringifyEntitiesLight(value, {subset: [appliedQuote]}) +
445+
appliedQuote
446+
}
447+
448+
result = attribute.name + initializer
432449
}
433450

434451
attributes.push((isMultiFlow ? '\n ' : ' ') + result)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"dependencies": {
3939
"@types/estree-jsx": "^0.0.1",
4040
"@types/mdast": "^3.0.0",
41+
"ccount": "^2.0.0",
4142
"mdast-util-to-markdown": "^1.0.0",
4243
"parse-entities": "^4.0.0",
4344
"stringify-entities": "^4.0.0",

readme.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ Configuration (optional).
236236

237237
###### `options.quote`
238238

239-
Quote to use around attribute values (`'"'` or `"'"`, default: `'"'`).
239+
Preferred quote to use around attribute values (`'"'` or `"'"`, default: `'"'`).
240+
241+
###### `options.quoteSmart`
242+
243+
Use the other quote if that results in less bytes (`boolean`, default: `false`).
240244

241245
## Syntax tree
242246

test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,5 +1867,61 @@ test('mdast -> markdown', (t) => {
18671867
'should crash on an unclosed text jsx (agnostic)'
18681868
)
18691869

1870+
t.deepEqual(
1871+
toMarkdown(
1872+
{
1873+
type: 'mdxJsxFlowElement',
1874+
name: 'x',
1875+
attributes: [{type: 'mdxJsxAttribute', name: 'y', value: 'z'}],
1876+
children: []
1877+
},
1878+
{extensions: [mdxJsxToMarkdown({quoteSmart: true})]}
1879+
),
1880+
'<x y="z"/>\n',
1881+
'should support `options.quoteSmart`: prefer `quote` w/o quotes'
1882+
)
1883+
1884+
t.deepEqual(
1885+
toMarkdown(
1886+
{
1887+
type: 'mdxJsxFlowElement',
1888+
name: 'x',
1889+
attributes: [{type: 'mdxJsxAttribute', name: 'y', value: 'z"a\'b'}],
1890+
children: []
1891+
},
1892+
{extensions: [mdxJsxToMarkdown({quoteSmart: true})]}
1893+
),
1894+
'<x y="z&#x22;a\'b"/>\n',
1895+
'should support `options.quoteSmart`: prefer `quote` w/ equal quotes'
1896+
)
1897+
1898+
t.deepEqual(
1899+
toMarkdown(
1900+
{
1901+
type: 'mdxJsxFlowElement',
1902+
name: 'x',
1903+
attributes: [{type: 'mdxJsxAttribute', name: 'y', value: 'z"a\'b"c'}],
1904+
children: []
1905+
},
1906+
{extensions: [mdxJsxToMarkdown({quoteSmart: true})]}
1907+
),
1908+
'<x y=\'z"a&#x27;b"c\'/>\n',
1909+
'should support `options.quoteSmart`: use alternative w/ more preferred quotes'
1910+
)
1911+
1912+
t.deepEqual(
1913+
toMarkdown(
1914+
{
1915+
type: 'mdxJsxFlowElement',
1916+
name: 'x',
1917+
attributes: [{type: 'mdxJsxAttribute', name: 'y', value: "z\"a'b'c"}],
1918+
children: []
1919+
},
1920+
{extensions: [mdxJsxToMarkdown({quoteSmart: true})]}
1921+
),
1922+
'<x y="z&#x22;a\'b\'c"/>\n',
1923+
'should support `options.quoteSmart`: use quote w/ more alternative quotes'
1924+
)
1925+
18701926
t.end()
18711927
})

0 commit comments

Comments
 (0)