-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for inline image Markdown - attribute fix #661
Changes from all commits
6a95384
6838521
76b8141
7e45bc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,12 +113,13 @@ | |
* Converts markdown style images to img tags e.g.  | ||
* We need to convert before linking rules since they will not try to create a link from an existing img | ||
* tag. | ||
* Additional sanitization is done to the alt attribute to prevent parsing it further to html by later rules. | ||
*/ | ||
{ | ||
name: 'image', | ||
regex: MARKDOWN_IMAGE_REGEX, | ||
replacement: (match, g1, g2) => `<img src="${Str.sanitizeURL(g2)}" alt="${g1}" />`, | ||
rawInputReplacement: (match, g1, g2) => `<img src="${Str.sanitizeURL(g2)}" alt="${g1}" data-raw-href="${g2}" data-link-variant="labeled" />` | ||
replacement: (match, g1, g2) => `<img src="${Str.sanitizeURL(g2)}" alt="${this.escapeMarkdownEntities(g1)}" />`, | ||
rawInputReplacement: (match, g1, g2) => `<img src="${Str.sanitizeURL(g2)}" alt="${this.escapeMarkdownEntities(g1)}" data-raw-href="${g2}" data-link-variant="labeled" />` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escaping is good for the "markdown entities" - but why display them at all? The alt tag will show a bunch of junk if the image fails to load or they hover over it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered keeping the text plain initially, but realized that it wouldn't allow us to revert to the original Markdown message from the HTML. This way, the original format is retained. Regarding hover text, the If an image fails to load, the fallback is something like: But NewDot and react-native-web don't display this fallback since they render a hidden There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah hmm. Quite weird. Perhaps we can filter it before rendering i.e. not actually modify the underlying |
||
}, | ||
|
||
/** | ||
|
@@ -152,7 +153,7 @@ | |
*/ | ||
{ | ||
name: 'hereMentions', | ||
regex: /([a-zA-Z0-9.!$%&+/=?^`{|}_-]?)(@here)([.!$%&+/=?^`{|}_-]?)(?=\b)(?!([\w'#%+-]*@(?:[a-z\d-]+\.)+[a-z]{2,}(?:\s|$|@here))|((?:(?!<a).)+)?<\/a>|[^<]*(<\/pre>|<\/code>))/gm, | ||
replacement: (match, g1, g2, g3) => { | ||
if (!Str.isValidMention(match)) { | ||
return match; | ||
|
@@ -197,7 +198,7 @@ | |
|
||
process: (textToProcess, replacement) => { | ||
const regex = new RegExp( | ||
`(?![^<]*>|[^<>]*<\\/(?!h1>))([_*~]*?)${MARKDOWN_URL_REGEX}\\1(?!((?:(?!<a).)+)?<\\/a>|[^<]*(<\\/pre>|<\\/code>))`, | ||
`(?![^<]*>|[^<>]*<\\/(?!h1>))([_*~]*?)${MARKDOWN_URL_REGEX}\\1(?!((?:(?!<a).)+)?<\\/a>|[^<]*(<\\/pre>|<\\/code>|.+\\/>))`, | ||
'gi', | ||
); | ||
return this.modifyTextForUrlLinks(regex, textToProcess, replacement); | ||
|
@@ -945,4 +946,27 @@ | |
const linksInNew = this.extractLinksInMarkdownComment(newComment); | ||
return linksInOld === undefined || linksInNew === undefined ? [] : _.difference(linksInOld, linksInNew); | ||
} | ||
|
||
/** | ||
* Replace MD characters with their HTML entity equivalent | ||
* @param {String} text | ||
* @return {String} | ||
*/ | ||
escapeMarkdownEntities(text) { | ||
// A regex pattern matching special MD characters we'd like to escape | ||
const pattern = /([*_{}[\]~])/g; | ||
|
||
// A map of MD characters to their HTML entity equivalent | ||
const entities = { | ||
'*': '*', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A backtick is missing here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've intentionally left out inline code and code blocks. These rules are applied first, converting anything that matches into HTML. Therefore, for input like:  The result is: <img src="<a href="https://example.com/image.png" target="_blank" rel="noreferrer noopener">https://example.com/image.png</a>" alt="# fake-heading *bold* _italic_ ~strike~ <code>code</code>" /> This approach reveals a limitation in our rule application method, but I'm unable to cover it easily in the current PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The aforementioned bug has been resolved by modifying the |
||
_: '_', | ||
'{': '{', | ||
'}': '}', | ||
'[': '[', | ||
']': ']', | ||
'~': '~', | ||
}; | ||
|
||
return text.replace(pattern, char => entities[char] || char); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kidroca Shouldn't it be
alt="<code>code</code>"
instead ofalt="<code>code</code>"
?This breaks Live Markdown library 😢 Not sure if
<
and>
characters are supported in HTML attributes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, the text inside the
alt
attribute should bealt="`code`"
or use escaped entities. However, achieving this proved challenging without significant changes. Thecode
rules execute first, generating a string like[<code>code</code>](...)
, and subsequent rules then move the text into thealt
attribute.I introduced this test to address a scenario where using the syntax

incorrectly generated an<img>
tag with asrc
attribute like<img src="<a href="..." />" />"
. This format, obviously, failed to load the image.How does this impact Live Markdown functionality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, Live Markdown uses pretty basic algorithm for parsing HTML that alternates between finding
<
and then>
.The output
<img src="..." alt="<code>...</code>" ... />
breaks the parser as the>
from<code>
gets matched instead of the (self-)closing>
fromimg
tag.According to the HTML standard,
>
is not a valid character in HTML attribute:Is there a chance we could somehow escape the
alt
attribute?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi,
The text you're referring to in your quote is regarding the attribute name - an attribute name cannot contain the specified characters
Attribute values on the other hand, are less restricted as long as the value is wrapped in matching quotes. There's a specific text in the shared spec clearly stating that
<
and>
are supported as long as the attribute value is wrapped in quotes.In any case I'll look into it a bit further to see if there's anything I can do to mitigate the issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kidroca oops, you're right, sorry for mistake!
I believe we could use a more complex HTML parser in Live Markdown.
cc @robertKozik who adapted ExpensiMark to Live Markdown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But does reverting back when editing work? When I've tested it yesterday I think typingnwm I was wrong 😃```test```
as img name, and trying to edit it leads totest
string. I may be wrong tho, as I'm not remember exactlyYeah your alternative solution will indeed solve our issues as we wouldn't have an
<
or>
inside attributes. What is ETA for merging it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We seem to be in a bit of a dead lock situation here. To advance my
expensify-common
PR, I need to demonstrate its effectiveness within themain
branch of NewDot. Would the solution I proposed here be acceptable to you: Expensify/App#38952 (comment)?Feel free to utilize this snapshot of
expensify-common
in your projects:This snapshot reflects the changes in my PR for
expensify-common
.expensify-common
PRexpensify-common
to the Expensify's repoI'll be ready to cover any follow-up work related to the MD image feature.
What are your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit tricky because we're incorporating the
expensify-common
version insidereact-native-live-markdown
as well. So we would need to point to your branch within the new library version, which isn't my preferred approach. If possible, merging yourexpensify-common
PR as it is would be great. If not, we can go with a patch containing your changes.Here's how I envision the process:
expensify-common
PR (if possible).react-native-live-markdown
with both our and your fixes.react-native-live-markdown
andexpensify-common
in the E/App repo.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me just test your changes in live-markdown beforehand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kidroca Apart from the one problem I've mentioned here everything looks great. I think we can proceed with the patch inside the
react-native-live-markdown
so it will unblock you - does it sound good for you?