From 470b164f6af71f3646bd505c6991d1b7ae71b7f4 Mon Sep 17 00:00:00 2001
From: Mateusz Falkowski <mateusz@appidea.pl>
Date: Thu, 8 Feb 2024 01:31:51 +0100
Subject: [PATCH] chore: adds position data to JSX attributes, updates the unit
 tests

---
 lib/index.js |  41 +++++++--
 test.js      | 246 ++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 268 insertions(+), 19 deletions(-)

diff --git a/lib/index.js b/lib/index.js
index 3c6e342..577f468 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -16,6 +16,8 @@
  * @typedef {import('../index.js').MdxJsxExpressionAttribute} MdxJsxExpressionAttribute
  * @typedef {import('../index.js').MdxJsxFlowElement} MdxJsxFlowElement
  * @typedef {import('../index.js').MdxJsxTextElement} MdxJsxTextElement
+ *
+ * @typedef {import('unist').Point} Point
  */
 
 /**
@@ -136,6 +138,18 @@ export function mdxJsxFromMarkdown() {
     this.buffer()
   }
 
+  /**
+   * Copy a point-like value.
+   *
+   * @param {Point} d
+   *   Point-like value.
+   * @returns {Point}
+   *   unist point.
+   */
+  function point(d) {
+    return {line: d.line, column: d.column, offset: d.offset}
+  }
+
   /**
    * @this {CompileContext}
    * @type {FromMarkdownHandle}
@@ -263,7 +277,16 @@ export function mdxJsxFromMarkdown() {
     const tag = this.data.mdxJsxTag
     assert(tag, 'expected `mdxJsxTag`')
     enterMdxJsxTagAnyAttribute.call(this, token)
-    tag.attributes.push({type: 'mdxJsxAttribute', name: '', value: null})
+    tag.attributes.push({
+      type: 'mdxJsxAttribute',
+      name: '',
+      value: null,
+      position: {
+        start: point(token.start),
+        // @ts-expect-error: `end` will be patched later.
+        end: undefined
+      }
+    })
   }
 
   /**
@@ -306,6 +329,8 @@ export function mdxJsxFromMarkdown() {
     const node = tag.attributes[tag.attributes.length - 1]
     assert(node.type === 'mdxJsxAttribute')
     node.name = this.sliceSerialize(token)
+    assert(node.position !== undefined)
+    node.position.end = point(token.end)
   }
 
   /**
@@ -318,19 +343,21 @@ export function mdxJsxFromMarkdown() {
     const node = tag.attributes[tag.attributes.length - 1]
     assert(node.type === 'mdxJsxAttribute')
     node.name += ':' + this.sliceSerialize(token)
+    assert(node.position !== undefined)
+    node.position.end = point(token.end)
   }
 
   /**
    * @this {CompileContext}
    * @type {FromMarkdownHandle}
    */
-  function exitMdxJsxTagAttributeValueLiteral() {
+  function exitMdxJsxTagAttributeValueLiteral(token) {
     const tag = this.data.mdxJsxTag
     assert(tag, 'expected `mdxJsxTag`')
-    tag.attributes[tag.attributes.length - 1].value = parseEntities(
-      this.resume(),
-      {nonTerminated: false}
-    )
+    const node = tag.attributes[tag.attributes.length - 1]
+    node.value = parseEntities(this.resume(), {nonTerminated: false})
+    assert(node.position !== undefined)
+    node.position.end = point(token.end)
   }
 
   /**
@@ -351,6 +378,8 @@ export function mdxJsxFromMarkdown() {
     }
 
     tail.value = node
+    assert(tail.position !== undefined)
+    tail.position.end = point(token.end)
   }
 
   /**
diff --git a/test.js b/test.js
index c2fb9bb..1938798 100644
--- a/test.js
+++ b/test.js
@@ -303,6 +303,18 @@ test('mdxJsxFromMarkdown', async function (t) {
                   {
                     type: 'mdxJsxAttribute',
                     name: 'c',
+                    position: {
+                      end: {
+                        column: 15,
+                        line: 1,
+                        offset: 14
+                      },
+                      start: {
+                        column: 6,
+                        line: 1,
+                        offset: 5
+                      }
+                    },
                     value: {
                       type: 'mdxJsxAttributeValueExpression',
                       value: '1 + 1'
@@ -493,6 +505,18 @@ test('mdxJsxFromMarkdown', async function (t) {
               {
                 type: 'mdxJsxAttribute',
                 name: 'b',
+                position: {
+                  end: {
+                    column: 9,
+                    line: 1,
+                    offset: 8
+                  },
+                  start: {
+                    column: 4,
+                    line: 1,
+                    offset: 3
+                  }
+                },
                 value: {
                   type: 'mdxJsxAttributeValueExpression',
                   value: '1',
@@ -715,18 +739,77 @@ test('mdxJsxFromMarkdown', async function (t) {
         {
           type: 'paragraph',
           children: [
-            {type: 'text', value: 'a '},
+            {
+              type: 'text',
+              value: 'a '
+            },
             {
               type: 'mdxJsxTextElement',
               name: 'b',
               attributes: [
-                {type: 'mdxJsxAttribute', name: 'c', value: null},
-                {type: 'mdxJsxAttribute', name: 'd', value: 'd'},
-                {type: 'mdxJsxAttribute', name: 'efg', value: 'h'}
+                {
+                  type: 'mdxJsxAttribute',
+                  name: 'c',
+                  value: null,
+                  position: {
+                    start: {
+                      line: 1,
+                      column: 6,
+                      offset: 5
+                    },
+                    end: {
+                      line: 1,
+                      column: 7,
+                      offset: 6
+                    }
+                  }
+                },
+                {
+                  type: 'mdxJsxAttribute',
+                  name: 'd',
+                  value: 'd',
+                  position: {
+                    start: {
+                      line: 1,
+                      column: 12,
+                      offset: 11
+                    },
+                    end: {
+                      line: 1,
+                      column: 17,
+                      offset: 16
+                    }
+                  }
+                },
+                {
+                  type: 'mdxJsxAttribute',
+                  name: 'efg',
+                  value: 'h',
+                  position: {
+                    start: {
+                      line: 1,
+                      column: 19,
+                      offset: 18
+                    },
+                    end: {
+                      line: 1,
+                      column: 26,
+                      offset: 25
+                    }
+                  }
+                }
               ],
-              children: [{type: 'text', value: 'i'}]
+              children: [
+                {
+                  type: 'text',
+                  value: 'i'
+                }
+              ]
             },
-            {type: 'text', value: '.'}
+            {
+              type: 'text',
+              value: '.'
+            }
           ]
         }
       ]
@@ -748,8 +831,40 @@ test('mdxJsxFromMarkdown', async function (t) {
           type: 'mdxJsxFlowElement',
           name: 'a',
           attributes: [
-            {type: 'mdxJsxAttribute', name: 'xml:lang', value: 'de-CH'},
-            {type: 'mdxJsxAttribute', name: 'foo:bar', value: null}
+            {
+              type: 'mdxJsxAttribute',
+              name: 'xml:lang',
+              position: {
+                end: {
+                  column: 10,
+                  line: 2,
+                  offset: 23
+                },
+                start: {
+                  column: 4,
+                  line: 1,
+                  offset: 3
+                }
+              },
+              value: 'de-CH'
+            },
+            {
+              type: 'mdxJsxAttribute',
+              name: 'foo:bar',
+              position: {
+                end: {
+                  column: 18,
+                  line: 2,
+                  offset: 31
+                },
+                start: {
+                  column: 11,
+                  line: 2,
+                  offset: 24
+                }
+              },
+              value: null
+            }
           ],
           children: []
         }
@@ -767,6 +882,7 @@ test('mdxJsxFromMarkdown', async function (t) {
 
       removePosition(tree, {force: true})
 
+      // @todo check it, includes spaces at end of tags
       assert.deepEqual(tree, {
         type: 'root',
         children: [
@@ -774,10 +890,74 @@ test('mdxJsxFromMarkdown', async function (t) {
             type: 'mdxJsxFlowElement',
             name: 'b',
             attributes: [
-              {type: 'mdxJsxAttribute', name: 'a', value: null},
-              {type: 'mdxJsxAttribute', name: 'b:c', value: null},
-              {type: 'mdxJsxAttribute', name: 'd:e', value: 'f'},
-              {type: 'mdxJsxAttribute', name: 'g', value: null}
+              {
+                type: 'mdxJsxAttribute',
+                name: 'a',
+                value: null,
+                position: {
+                  start: {
+                    line: 1,
+                    column: 4,
+                    offset: 3
+                  },
+                  end: {
+                    line: 1,
+                    column: 5,
+                    offset: 4
+                  }
+                }
+              },
+              {
+                type: 'mdxJsxAttribute',
+                name: 'b:c',
+                value: null,
+                position: {
+                  start: {
+                    line: 1,
+                    column: 6,
+                    offset: 5
+                  },
+                  end: {
+                    line: 1,
+                    column: 11,
+                    offset: 10
+                  }
+                }
+              },
+              {
+                type: 'mdxJsxAttribute',
+                name: 'd:e',
+                value: 'f',
+                position: {
+                  start: {
+                    line: 1,
+                    column: 12,
+                    offset: 11
+                  },
+                  end: {
+                    line: 1,
+                    column: 23,
+                    offset: 22
+                  }
+                }
+              },
+              {
+                type: 'mdxJsxAttribute',
+                name: 'g',
+                value: null,
+                position: {
+                  start: {
+                    line: 1,
+                    column: 24,
+                    offset: 23
+                  },
+                  end: {
+                    line: 1,
+                    column: 25,
+                    offset: 24
+                  }
+                }
+              }
             ],
             children: []
           }
@@ -1050,6 +1230,18 @@ test('mdxJsxFromMarkdown', async function (t) {
               {
                 type: 'mdxJsxAttribute',
                 name: 'y',
+                position: {
+                  end: {
+                    column: 158,
+                    line: 1,
+                    offset: 157
+                  },
+                  start: {
+                    column: 4,
+                    line: 1,
+                    offset: 3
+                  }
+                },
                 value:
                   'Character references can be used: ", \', <, >, {, and }, they can be named, decimal, or hexadecimal: © ≠ 𝌆'
               }
@@ -1238,7 +1430,23 @@ test('mdxJsxFromMarkdown', async function (t) {
                     type: 'mdxJsxTextElement',
                     name: 'b',
                     attributes: [
-                      {type: 'mdxJsxAttribute', name: 'c', value: 'd\ne'}
+                      {
+                        type: 'mdxJsxAttribute',
+                        name: 'c',
+                        position: {
+                          end: {
+                            column: 5,
+                            line: 2,
+                            offset: 16
+                          },
+                          start: {
+                            column: 8,
+                            line: 1,
+                            offset: 7
+                          }
+                        },
+                        value: 'd\ne'
+                      }
                     ],
                     children: []
                   },
@@ -1279,6 +1487,18 @@ test('mdxJsxFromMarkdown', async function (t) {
                       {
                         type: 'mdxJsxAttribute',
                         name: 'c',
+                        position: {
+                          end: {
+                            column: 5,
+                            line: 2,
+                            offset: 16
+                          },
+                          start: {
+                            column: 8,
+                            line: 1,
+                            offset: 7
+                          }
+                        },
                         value: {
                           type: 'mdxJsxAttributeValueExpression',
                           value: 'd\ne'