payload: {"action":"created","issue":{"url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/361","repository_url":"https://api.github.com/repos/jonschlinkert/remarkable","labels_url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/361/labels{/name}","comments_url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/361/comments","events_url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/361/events","html_url":"https://github.com/jonschlinkert/remarkable/issues/361","id":479750683,"node_id":"MDU6SXNzdWU0Nzk3NTA2ODM=","number":361,"title":"JSX","user":{"login":"sdegutis","id":4399718,"node_id":"MDQ6VXNlcjQzOTk3MTg=","avatar_url":"https://avatars0.githubusercontent.com/u/4399718?v=4","gravatar_id":"","url":"https://api.github.com/users/sdegutis","html_url":"https://github.com/sdegutis","followers_url":"https://api.github.com/users/sdegutis/followers","following_url":"https://api.github.com/users/sdegutis/following{/other_user}","gists_url":"https://api.github.com/users/sdegutis/gists{/gist_id}","starred_url":"https://api.github.com/users/sdegutis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sdegutis/subscriptions","organizations_url":"https://api.github.com/users/sdegutis/orgs","repos_url":"https://api.github.com/users/sdegutis/repos","events_url":"https://api.github.com/users/sdegutis/events{/privacy}","received_events_url":"https://api.github.com/users/sdegutis/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":9,"created_at":"2019-08-12T16:58:46Z","updated_at":"2019-08-13T15:43:52Z","closed_at":null,"author_association":"NONE","body":"Hi. I've been evaluating different JS-based Markdown parsers for use in a client project I have which would benefit from a Markdown parser that understands JSX code, including the ability to contain arbitrary JavaScript within attributes (such as arrow-functions within onClick handlers). The goal is to have Markdown that can be compiled into raw React-friendly JSX code.\r\n\r\nFor example, this would be allowed:\r\n\r\n```markdown\r\n# This is a <span onClick={(e) => doStuff(e.target.innerText.toUpperCase())}>header</span>\r\n\r\n<div className='something'>\r\n\r\nThis *should* be within a paragraph tag.\r\n\r\n- And this is a list:\r\n  - <Foo/>\r\n  - <Bar/>\r\n  - <Qux/>\r\n\r\n</div>\r\n```\r\n\r\n(I'm not sure on the specifics of proper indentation within the div, that's one of my weakest points of understanding about the intricacies of Markdown syntax, so please ignore that aspect of my example.)\r\n\r\nThe library you maintain ticked every box for a JS-based Markdown compiler for my use-case, but especially the ease and flexibility of extending the language's syntax. (I should also add that it's some of the best code I've ever seen in my career, kudos to @puzrin and @rlidwka et al.)\r\n\r\nAfter reading your plugin guides, I think I have an idea of how this can be implemented as a `remarkable` extension. This issue is partly to get feedback and general guidance on my implementation idea, and partly to suggest that this feature might be universally useful enough to merit becoming a core part of the language, implemented as built-in rules, and enabled by a configuration option.\r\n\r\nThe goal is to be able to transform these:\r\n\r\n```jsx\r\n// simple tranformation:\r\n<div>*foo*</div>\r\n<div><em>foo</em></div>\r\n\r\n// more complex transformation:\r\n<List render={items.map(item => <>*foo {item}*</>)}/>\r\n<List render={items.map(item => <><em>foo {item}</em></>)}/>\r\n```\r\n\r\nThe current `htmltag` rule's default parsing implementation, as far as I can tell, starts parsing when it hits `<`, parses it as either an opening or closing tag, and returns control back to the parser when the tag is done. Because both opening and closing tags start with `<`, it can use the same parse logic when hitting both, so it doesn't need to do anything special for the inner text. In other words, each opening and closing tag are self-sufficient and considered independent from one another.\r\n\r\nBut this strategy won't work with JSX. In the list-render section above, consider that it could be split into these semantically distinct parts:\r\n\r\n- `<List render={items.map(item => <>` (the first JSX portion, should be emitted as-is)\r\n- `*foo ` (the first plain text portion, should be rendered as Markdown, starting the process of an emph token)\r\n- `{item}` (the second JSX portion, should also be emitted as-is)\r\n- `*` (the second plain text portion, should close and emit the emph token)\r\n- `</>)}/>` (the final JSX portion, should be emitted as-is)\r\n\r\nI found a way to use `require('@babel/parse').parseExpression(...)` to get the end of the current JSX expression and all text nodes within it, in a way that can be represented like this:\r\n\r\n```typescript\r\ninterface ParseResult {\r\n  endPos: number;\r\n  innerTextNodes: Array<{\r\n    startPos: number;\r\n    endPos: number;\r\n    text: string;\r\n  }>;\r\n}\r\n```\r\n\r\nAt first I was thinking of just calling the top-level `md.renderInline(textNode)` on each inner text node (called JSXText in Babel's parser terms) using the visitor pattern via a Babel plugin to get to them. But in this example, it wouldn't know that the fourth node containing `*` is the closing emphasis-tag for the one started in the second node containing `*foo `.\r\n\r\nThen I figured out that inline parsers have the ability to call `state.parser.tokenize(state)` to continue parsing with the current context. Using this function, the main issue to be solved now is that the parser doesn't have the same \"idempotent\" way of knowing that parsing our JSX needs to be resumed. For instance, the third node in the above example starts with `{`, not `<`.\r\n\r\nSo my implementation idea involves two phases, represented by two different rules.\r\n\r\nThe first replaces `htmltag` via `md.inline.rules.at`, and is responsible for recognizing the JSX expression at `<`, storing the `ParseResult` information internally, consuming and emitting the JSX literally as a token, and giving control back to the parser via `state.parser.tokenize(state)`.\r\n\r\nThe second tag would be responsible for knowing when the state enters back into the JSX expression parsing phase. It would do this by looking at the current parser's *position*, and comparing it to the `ParseResult` expression stored from the first rule, and would return true IFF the parser's position is at the exact end of any inner text nodes.\r\n\r\nThe reason for having two rules is because the `htmltag` rule has lower precedence and may not be able to catch all characters that could possibly trigger the end of a JSXText node, thus the second rule should have higher precedence, possibly even above 'text'.\r\n\r\nAlternatively, maybe the `htmltag` rule could just be moved higher in precedence and contain both responsibilities, but then it would have to make sure it's not inside of an illogical context, e.g. inside of the URL portion of a link where JSX isn't allowed but where `<` (theoretically) might be. This example may not be perfect but it illustrates the reason I'm thinking precedence might matter.\r\n\r\nAlso, it *seems* that the only two things that can end a text node is `{` and `<`, and that the only character that can start a new overall JSX expression is `<`, but I haven't thought of enough examples that might disprove these two assumptions. If they can be (at least reasonably) proven to be true, it would make figuring out this precedence issue a lot easier.\r\n\r\nThis is my plan, and all my notes and thoughts on its implementation. If anyone is interested in solving the same problem with me, I'd love to hear your feedback and thoughts on it. In the meantime, I'm going to work on the implementation and continue to post any relevant notes in here related to my progress on this.\r\n"},"comment":{"url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/comments/520890590","html_url":"https://github.com/jonschlinkert/remarkable/issues/361#issuecomment-520890590","issue_url":"https://api.github.com/repos/jonschlinkert/remarkable/issues/361","id":520890590,"node_id":"MDEyOklzc3VlQ29tbWVudDUyMDg5MDU5MA==","user":{"login":"sdegutis","id":4399718,"node_id":"MDQ6VXNlcjQzOTk3MTg=","avatar_url":"https://avatars0.githubusercontent.com/u/4399718?v=4","gravatar_id":"","url":"https://api.github.com/users/sdegutis","html_url":"https://github.com/sdegutis","followers_url":"https://api.github.com/users/sdegutis/followers","following_url":"https://api.github.com/users/sdegutis/following{/other_user}","gists_url":"https://api.github.com/users/sdegutis/gists{/gist_id}","starred_url":"https://api.github.com/users/sdegutis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sdegutis/subscriptions","organizations_url":"https://api.github.com/users/sdegutis/orgs","repos_url":"https://api.github.com/users/sdegutis/repos","events_url":"https://api.github.com/users/sdegutis/events{/privacy}","received_events_url":"https://api.github.com/users/sdegutis/received_events","type":"User","site_admin":false},"created_at":"2019-08-13T15:43:52Z","updated_at":"2019-08-13T15:43:52Z","author_association":"NONE","body":"The regex isn't necessary, we can just use:\r\n\r\n```js\r\nfirstTextNode.startsWith('\\n\\n') &&\r\n lastTextNode.endsWith  ('\\n\\n')\r\n```"}}
other: {"actor":{"display_login":"sdegutis"}}