Merge remote-tracking branch 'origin/develop' into dbkr/tests_lint
commit
6aa0a4428e
|
@ -54,6 +54,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/commonmark": "^0.27.4",
|
||||||
"await-lock": "^2.1.0",
|
"await-lock": "^2.1.0",
|
||||||
"blurhash": "^1.1.3",
|
"blurhash": "^1.1.3",
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
"browser-encrypt-attachment": "^0.3.0",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,16 +16,24 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as commonmark from 'commonmark';
|
import * as commonmark from 'commonmark';
|
||||||
import {escape} from "lodash";
|
import { escape } from "lodash";
|
||||||
|
|
||||||
const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u'];
|
const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u'];
|
||||||
|
|
||||||
// These types of node are definitely text
|
// These types of node are definitely text
|
||||||
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];
|
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];
|
||||||
|
|
||||||
function is_allowed_html_tag(node) {
|
// As far as @types/commonmark is concerned, these are not public, so add them
|
||||||
|
interface CommonmarkHtmlRendererInternal extends commonmark.HtmlRenderer {
|
||||||
|
paragraph: (node: commonmark.Node, entering: boolean) => void;
|
||||||
|
link: (node: commonmark.Node, entering: boolean) => void;
|
||||||
|
html_inline: (node: commonmark.Node) => void; // eslint-disable-line camelcase
|
||||||
|
html_block: (node: commonmark.Node) => void; // eslint-disable-line camelcase
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAllowedHtmlTag(node: commonmark.Node): boolean {
|
||||||
if (node.literal != null &&
|
if (node.literal != null &&
|
||||||
node.literal.match('^<((div|span) data-mx-maths="[^"]*"|\/(div|span))>$') != null) {
|
node.literal.match('^<((div|span) data-mx-maths="[^"]*"|/(div|span))>$') != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,21 +48,12 @@ function is_allowed_html_tag(node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function html_if_tag_allowed(node) {
|
|
||||||
if (is_allowed_html_tag(node)) {
|
|
||||||
this.lit(node.literal);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.lit(escape(node.literal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if the parse output containing the node
|
* Returns true if the parse output containing the node
|
||||||
* comprises multiple block level elements (ie. lines),
|
* comprises multiple block level elements (ie. lines),
|
||||||
* or false if it is only a single line.
|
* or false if it is only a single line.
|
||||||
*/
|
*/
|
||||||
function is_multi_line(node) {
|
function isMultiLine(node: commonmark.Node): boolean {
|
||||||
let par = node;
|
let par = node;
|
||||||
while (par.parent) {
|
while (par.parent) {
|
||||||
par = par.parent;
|
par = par.parent;
|
||||||
|
@ -67,6 +67,9 @@ function is_multi_line(node) {
|
||||||
* it's plain text.
|
* it's plain text.
|
||||||
*/
|
*/
|
||||||
export default class Markdown {
|
export default class Markdown {
|
||||||
|
private input: string;
|
||||||
|
private parsed: commonmark.Node;
|
||||||
|
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ export default class Markdown {
|
||||||
this.parsed = parser.parse(this.input);
|
this.parsed = parser.parse(this.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlainText() {
|
isPlainText(): boolean {
|
||||||
const walker = this.parsed.walker();
|
const walker = this.parsed.walker();
|
||||||
|
|
||||||
let ev;
|
let ev;
|
||||||
|
@ -87,7 +90,7 @@ export default class Markdown {
|
||||||
// if it's an allowed html tag, we need to render it and therefore
|
// if it's an allowed html tag, we need to render it and therefore
|
||||||
// we will need to use HTML. If it's not allowed, it's not HTML since
|
// we will need to use HTML. If it's not allowed, it's not HTML since
|
||||||
// we'll just be treating it as text.
|
// we'll just be treating it as text.
|
||||||
if (is_allowed_html_tag(node)) {
|
if (isAllowedHtmlTag(node)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,7 +100,7 @@ export default class Markdown {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
toHTML({ externalLinks = false } = {}) {
|
toHTML({ externalLinks = false } = {}): string {
|
||||||
const renderer = new commonmark.HtmlRenderer({
|
const renderer = new commonmark.HtmlRenderer({
|
||||||
safe: false,
|
safe: false,
|
||||||
|
|
||||||
|
@ -107,7 +110,7 @@ export default class Markdown {
|
||||||
// block quote ends up all on one line
|
// block quote ends up all on one line
|
||||||
// (https://github.com/vector-im/element-web/issues/3154)
|
// (https://github.com/vector-im/element-web/issues/3154)
|
||||||
softbreak: '<br />',
|
softbreak: '<br />',
|
||||||
});
|
}) as CommonmarkHtmlRendererInternal;
|
||||||
|
|
||||||
// Trying to strip out the wrapping <p/> causes a lot more complication
|
// Trying to strip out the wrapping <p/> causes a lot more complication
|
||||||
// than it's worth, i think. For instance, this code will go and strip
|
// than it's worth, i think. For instance, this code will go and strip
|
||||||
|
@ -118,16 +121,16 @@ export default class Markdown {
|
||||||
//
|
//
|
||||||
// Let's try sending with <p/>s anyway for now, though.
|
// Let's try sending with <p/>s anyway for now, though.
|
||||||
|
|
||||||
const real_paragraph = renderer.paragraph;
|
const realParagraph = renderer.paragraph;
|
||||||
|
|
||||||
renderer.paragraph = function(node, entering) {
|
renderer.paragraph = function(node: commonmark.Node, entering: boolean) {
|
||||||
// If there is only one top level node, just return the
|
// If there is only one top level node, just return the
|
||||||
// bare text: it's a single line of text and so should be
|
// bare text: it's a single line of text and so should be
|
||||||
// 'inline', rather than unnecessarily wrapped in its own
|
// 'inline', rather than unnecessarily wrapped in its own
|
||||||
// p tag. If, however, we have multiple nodes, each gets
|
// p tag. If, however, we have multiple nodes, each gets
|
||||||
// its own p tag to keep them as separate paragraphs.
|
// its own p tag to keep them as separate paragraphs.
|
||||||
if (is_multi_line(node)) {
|
if (isMultiLine(node)) {
|
||||||
real_paragraph.call(this, node, entering);
|
realParagraph.call(this, node, entering);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,19 +153,26 @@ export default class Markdown {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderer.html_inline = html_if_tag_allowed;
|
renderer.html_inline = function(node: commonmark.Node) {
|
||||||
|
if (isAllowedHtmlTag(node)) {
|
||||||
|
this.lit(node.literal);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.lit(escape(node.literal));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
renderer.html_block = function(node) {
|
renderer.html_block = function(node: commonmark.Node) {
|
||||||
/*
|
/*
|
||||||
// as with `paragraph`, we only insert line breaks
|
// as with `paragraph`, we only insert line breaks
|
||||||
// if there are multiple lines in the markdown.
|
// if there are multiple lines in the markdown.
|
||||||
const isMultiLine = is_multi_line(node);
|
const isMultiLine = is_multi_line(node);
|
||||||
if (isMultiLine) this.cr();
|
if (isMultiLine) this.cr();
|
||||||
*/
|
*/
|
||||||
html_if_tag_allowed.call(this, node);
|
renderer.html_inline(node);
|
||||||
/*
|
/*
|
||||||
if (isMultiLine) this.cr();
|
if (isMultiLine) this.cr();
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
return renderer.render(this.parsed);
|
return renderer.render(this.parsed);
|
||||||
|
@ -177,23 +187,22 @@ export default class Markdown {
|
||||||
* N.B. this does **NOT** render arbitrary MD to plain text - only MD
|
* N.B. this does **NOT** render arbitrary MD to plain text - only MD
|
||||||
* which has no formatting. Otherwise it emits HTML(!).
|
* which has no formatting. Otherwise it emits HTML(!).
|
||||||
*/
|
*/
|
||||||
toPlaintext() {
|
toPlaintext(): string {
|
||||||
const renderer = new commonmark.HtmlRenderer({safe: false});
|
const renderer = new commonmark.HtmlRenderer({ safe: false }) as CommonmarkHtmlRendererInternal;
|
||||||
const real_paragraph = renderer.paragraph;
|
|
||||||
|
|
||||||
renderer.paragraph = function(node, entering) {
|
renderer.paragraph = function(node: commonmark.Node, entering: boolean) {
|
||||||
// as with toHTML, only append lines to paragraphs if there are
|
// as with toHTML, only append lines to paragraphs if there are
|
||||||
// multiple paragraphs
|
// multiple paragraphs
|
||||||
if (is_multi_line(node)) {
|
if (isMultiLine(node)) {
|
||||||
if (!entering && node.next) {
|
if (!entering && node.next) {
|
||||||
this.lit('\n\n');
|
this.lit('\n\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderer.html_block = function(node) {
|
renderer.html_block = function(node: commonmark.Node) {
|
||||||
this.lit(node.literal);
|
this.lit(node.literal);
|
||||||
if (is_multi_line(node) && node.next) this.lit('\n\n');
|
if (isMultiLine(node) && node.next) this.lit('\n\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
return renderer.render(this.parsed);
|
return renderer.render(this.parsed);
|
|
@ -1478,6 +1478,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf"
|
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf"
|
||||||
integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==
|
integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==
|
||||||
|
|
||||||
|
"@types/commonmark@^0.27.4":
|
||||||
|
version "0.27.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/commonmark/-/commonmark-0.27.4.tgz#8f42990e5cf3b6b95bd99eaa452e157aab679b82"
|
||||||
|
integrity sha512-7koSjp08QxKoS1/+3T15+kD7+vqOUvZRHvM8PutF3Xsk5aAEkdlIGRsHJ3/XsC3izoqTwBdRW/vH7rzCKkIicA==
|
||||||
|
|
||||||
"@types/counterpart@^0.18.1":
|
"@types/counterpart@^0.18.1":
|
||||||
version "0.18.1"
|
version "0.18.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/counterpart/-/counterpart-0.18.1.tgz#b1b784d9e54d9879f0a8cb12f2caedab65430fe8"
|
resolved "https://registry.yarnpkg.com/@types/counterpart/-/counterpart-0.18.1.tgz#b1b784d9e54d9879f0a8cb12f2caedab65430fe8"
|
||||||
|
|
Loading…
Reference in New Issue