diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-14 09:09:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-14 09:09:22 +0300 |
commit | 067b3d04573d1473dbc6c81ef775d70c6636ff3f (patch) | |
tree | e05bfa986e49c1527fa93f03b2592f0c1a3da735 /app/assets/javascripts/content_editor | |
parent | ef9eff8e7e1b38f48a354f90ecaeeb67da35b08c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r-- | app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/content_editor/services/serialization_helpers.js | 97 |
2 files changed, 76 insertions, 23 deletions
diff --git a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js index f38e4514393..2c462cdde91 100644 --- a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js +++ b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js @@ -63,7 +63,7 @@ function maybeMerge(a, b) { function createSourceMapAttributes(hastNode, source) { const { position } = hastNode; - return position.end + return position && position.end ? { sourceMapKey: `${position.start.offset}:${position.end.offset}`, sourceMarkdown: source.substring(position.start.offset, position.end.offset), diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js index 055a32420b2..88f5192af77 100644 --- a/app/assets/javascripts/content_editor/services/serialization_helpers.js +++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js @@ -12,22 +12,6 @@ const ignoreAttrs = { const tableMap = new WeakMap(); -// Source taken from -// prosemirror-markdown/src/to_markdown.js -export function isPlainURL(link, parent, index, side) { - if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) return false; - const content = parent.child(index + (side < 0 ? -1 : 0)); - if ( - !content.isText || - content.text !== link.attrs.href || - content.marks[content.marks.length - 1] !== link - ) - return false; - if (index === (side < 0 ? 1 : parent.childCount - 1)) return true; - const next = parent.child(index + (side < 0 ? -2 : 1)); - return !link.isInSet(next.marks); -} - function containsOnlyText(node) { if (node.childCount === 1) { const child = node.child(0); @@ -498,10 +482,79 @@ const linkType = (sourceMarkdown) => { return LINK_HTML; }; +const removeUrlProtocol = (url) => url.replace(/^\w+:\/?\/?/, ''); + +const normalizeUrl = (url) => decodeURIComponent(removeUrlProtocol(url)); + +/** + * Validates that the provided URL is well-formed + * + * @param {String} url + * @returns Returns true when the browser’s URL constructor + * can successfully parse the URL string + */ +const isValidUrl = (url) => { + try { + return new URL(url) && true; + } catch { + return false; + } +}; + +const findChildWithMark = (mark, parent) => { + let child; + let offset; + let index; + + parent.forEach((_child, _offset, _index) => { + if (mark.isInSet(_child.marks)) { + child = _child; + offset = _offset; + index = _index; + } + }); + + return child ? { child, offset, index } : null; +}; + +/** + * This function detects whether a link should be serialized + * as an autolink. + * + * See https://github.github.com/gfm/#autolinks-extension- + * to understand the parsing rules of autolinks. + * */ +const isAutoLink = (linkMark, parent) => { + const { title, href } = linkMark.attrs; + + if (title || !/^\w+:/.test(href)) { + return false; + } + + const { child } = findChildWithMark(linkMark, parent); + + if ( + !child || + !child.isText || + !isValidUrl(href) || + normalizeUrl(child.text) !== normalizeUrl(href) + ) { + return false; + } + + return true; +}; + +/** + * Returns true if the user used brackets to the define + * the autolink in the original markdown source + */ +const isBracketAutoLink = (sourceMarkdown) => /^<.+?>$/.test(sourceMarkdown); + export const link = { - open(state, mark, parent, index) { - if (isPlainURL(mark, parent, index, 1)) { - return '<'; + open(state, mark, parent) { + if (isAutoLink(mark, parent)) { + return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '<' : ''; } const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; @@ -518,9 +571,9 @@ export const link = { return openTag('a', attrs); }, - close(state, mark, parent, index) { - if (isPlainURL(mark, parent, index, -1)) { - return '>'; + close(state, mark, parent) { + if (isAutoLink(mark, parent)) { + return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '>' : ''; } const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; |