Skip to content

Commit 8ed9bd2

Browse files
authored
fix(attributes): treat non-bounded attribute like html attributes (#91)
1 parent 80673e0 commit 8ed9bd2

File tree

5 files changed

+81
-14
lines changed

5 files changed

+81
-14
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ Object
210210
::
211211
```
212212

213+
> **Escape Character Behavior:** In MDC, the escape character (backslash `\`) can be used to escape special characters within attribute values. However, this behavior only applies to bound attributes. For example, in a bound attribute like `:list="[\"item 1\"]"`, the escape character will allow you to include quotes within the value. In contrast, for non-bound attributes such as `class="my-class"`, the escape character will not have any effect, and the value will be rendered as is. Therefore, it's important to use binding when you need to include special characters in attribute values.
214+
213215
### `---` Yaml Props
214216

215217
The YAML method uses the `---` identifier to declare one prop per line, which can be useful for readability.

Diff for: src/micromark-extension/factory-attributes.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function createAttributes(
3030
) {
3131
let type: keyof TokenTypeMap
3232
let marker: number | undefined
33+
let isBindAttribute: boolean = false
3334

3435
return start
3536

@@ -57,6 +58,7 @@ export default function createAttributes(
5758
effects.enter(attributeType)
5859
effects.enter(attributeNameType)
5960
effects.consume(code)
61+
isBindAttribute = code === Codes.colon
6062
return (code === Codes.colon ? bindAttributeName : name) as State
6163
}
6264

@@ -296,7 +298,7 @@ export default function createAttributes(
296298
}
297299

298300
function valueQuoted(code: number) {
299-
if (code === Codes.backSlash) {
301+
if (isBindAttribute && code === Codes.backSlash) {
300302
effects.exit(attributeValueData)
301303
effects.exit(attributeValueType)
302304
effects.enter('escapeCharacter')

Diff for: test/__snapshots__/attributes.test.ts.snap

+55-5
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ exports[`Attributes > id-suger 1`] = `
613613
}
614614
`;
615615

616-
exports[`Attributes > ignoreEscapeAttrInNormalAttribute 1`] = `
616+
exports[`Attributes > ignoreEscapeCharacterInNormalAttribute 1`] = `
617617
{
618618
"children": [
619619
{
@@ -631,9 +631,9 @@ exports[`Attributes > ignoreEscapeAttrInNormalAttribute 1`] = `
631631
"name": "copy",
632632
"position": {
633633
"end": {
634-
"column": 29,
634+
"column": 27,
635635
"line": 1,
636-
"offset": 28,
636+
"offset": 26,
637637
},
638638
"start": {
639639
"column": 1,
@@ -646,9 +646,59 @@ exports[`Attributes > ignoreEscapeAttrInNormalAttribute 1`] = `
646646
],
647647
"position": {
648648
"end": {
649-
"column": 29,
649+
"column": 27,
650+
"line": 1,
651+
"offset": 26,
652+
},
653+
"start": {
654+
"column": 1,
650655
"line": 1,
651-
"offset": 28,
656+
"offset": 0,
657+
},
658+
},
659+
"type": "root",
660+
}
661+
`;
662+
663+
exports[`Attributes > ignoreEscapeCharacterInNormalAttributeYaml 1`] = `
664+
{
665+
"children": [
666+
{
667+
"attributes": {},
668+
"children": [],
669+
"data": {
670+
"hName": "copy",
671+
"hProperties": {
672+
"code": "D:\\Software\\",
673+
},
674+
},
675+
"fmAttributes": {
676+
"code": "D:\\Software\\",
677+
},
678+
"name": "copy",
679+
"position": {
680+
"end": {
681+
"column": 3,
682+
"line": 5,
683+
"offset": 36,
684+
},
685+
"start": {
686+
"column": 1,
687+
"line": 1,
688+
"offset": 0,
689+
},
690+
},
691+
"rawData": "
692+
code: D:\\Software\\
693+
---",
694+
"type": "containerComponent",
695+
},
696+
],
697+
"position": {
698+
"end": {
699+
"column": 3,
700+
"line": 5,
701+
"offset": 36,
652702
},
653703
"start": {
654704
"column": 1,

Diff for: test/attributes.test.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,6 @@ describe('Attributes', () => {
6161
'emphasis': {
6262
markdown: '_emphasis_{#id .class}',
6363
},
64-
'ignoreEscapeAttrInNormalAttribute': {
65-
markdown: ':copy{code="D:\\\\Software\\\\"}',
66-
expected: ':copy{code="D:\\Software\\"}',
67-
extra: (_md, ast) => {
68-
expect(ast.children[0].type).toBe('textComponent')
69-
expect(ast.children[0].attributes.code).toBe('D:\\Software\\')
70-
},
71-
},
7264
'nested-in-table': {
7365
markdown: [
7466
'| Col1 | Col2 |',
@@ -81,5 +73,21 @@ describe('Attributes', () => {
8173
'| aa | [a](/a){a="a"} |',
8274
].join('\n'),
8375
},
76+
'ignoreEscapeCharacterInNormalAttribute': {
77+
markdown: ':copy{code="D:\\Software\\"}',
78+
expected: ':copy{code="D:\\Software\\"}',
79+
extra: (_md, ast) => {
80+
expect(ast.children[0].type).toBe('textComponent')
81+
expect(ast.children[0].attributes.code).toBe('D:\\Software\\')
82+
},
83+
},
84+
'ignoreEscapeCharacterInNormalAttributeYaml': {
85+
markdown: '::copy\n---\ncode: D:\\Software\\\n---\n::',
86+
expected: '::copy\n---\ncode: D:\\Software\\\n---\n::',
87+
extra: (_md, ast) => {
88+
expect(ast.children[0].type).toBe('containerComponent')
89+
expect(ast.children[0].fmAttributes.code).toBe('D:\\Software\\')
90+
},
91+
},
8492
})
8593
})

Diff for: test/utils/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export function runMarkdownTests(tests: Record<string, MarkdownTest>) {
2626
if (extra) {
2727
extra(markdown, ast, expected || markdown)
2828
}
29+
30+
// We should be able regenerate same markdown starting from the `regeneratedMarkdown`
31+
const ast2 = await markdownToAST(regeneratedMarkdown, plugins, mdcOptions)
32+
const regeneratedMarkdown2 = await astToMarkdown(ast2, plugins, mdcOptions)
33+
expect(regeneratedMarkdown2).toEqual(regeneratedMarkdown)
2934
})
3035
}
3136
}

0 commit comments

Comments
 (0)