chore: Use markdown-it instead of marked
This commit is contained in:
parent
26ada8b342
commit
17167aa550
7 changed files with 170 additions and 64 deletions
|
@ -422,11 +422,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
|
||||||
background: var(--color-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
p:last-child {
|
p:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
pre {
|
||||||
|
background: var(--b-50);
|
||||||
|
border-radius: var(--border-radius-normal);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
display: block;
|
||||||
|
margin: var(--space-small) 0;
|
||||||
|
padding: var(--space-slab);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: var(--b-50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,57 +1,49 @@
|
||||||
import { marked } from 'marked';
|
import mila from 'markdown-it-link-attributes';
|
||||||
import DOMPurify from 'dompurify';
|
import mentionPlugin from './markdownIt/link';
|
||||||
import { escapeHtml, afterSanitizeAttributes } from './HTMLSanitizer';
|
const md = require('markdown-it')({
|
||||||
|
html: false,
|
||||||
|
xhtmlOut: true,
|
||||||
|
breaks: true,
|
||||||
|
langPrefix: 'language-',
|
||||||
|
linkify: true,
|
||||||
|
typographer: true,
|
||||||
|
quotes: '\u201c\u201d\u2018\u2019',
|
||||||
|
maxNesting: 20,
|
||||||
|
})
|
||||||
|
.use(mentionPlugin)
|
||||||
|
.use(mila, {
|
||||||
|
attrs: {
|
||||||
|
class: 'link',
|
||||||
|
rel: 'noreferrer noopener nofollow',
|
||||||
|
target: '_blank',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const TWITTER_USERNAME_REGEX = /(^|[^@\w])@(\w{1,15})\b/g;
|
const TWITTER_USERNAME_REGEX = /(^|[^@\w])@(\w{1,15})\b/g;
|
||||||
const TWITTER_USERNAME_REPLACEMENT =
|
const TWITTER_USERNAME_REPLACEMENT = '$1[@$2](http://twitter.com/$2)';
|
||||||
'$1<a href="http://twitter.com/$2" target="_blank" rel="noreferrer nofollow noopener">@$2</a>';
|
|
||||||
|
|
||||||
const TWITTER_HASH_REGEX = /(^|\s)#(\w+)/g;
|
const TWITTER_HASH_REGEX = /(^|\s)#(\w+)/g;
|
||||||
const TWITTER_HASH_REPLACEMENT =
|
const TWITTER_HASH_REPLACEMENT = '$1[#$2](https://twitter.com/hashtag/$2)';
|
||||||
'$1<a href="https://twitter.com/hashtag/$2" target="_blank" rel="noreferrer nofollow noopener">#$2</a>';
|
|
||||||
|
|
||||||
const USER_MENTIONS_REGEX = /mention:\/\/(user|team)\/(\d+)\/(.+)/gm;
|
|
||||||
|
|
||||||
class MessageFormatter {
|
class MessageFormatter {
|
||||||
constructor(message, isATweet = false, isAPrivateNote = false) {
|
constructor(message, isATweet = false, isAPrivateNote = false) {
|
||||||
this.message = DOMPurify.sanitize(escapeHtml(message || ''));
|
this.message = message || '';
|
||||||
this.isAPrivateNote = isAPrivateNote;
|
this.isAPrivateNote = isAPrivateNote;
|
||||||
this.isATweet = isATweet;
|
this.isATweet = isATweet;
|
||||||
this.marked = marked;
|
|
||||||
|
|
||||||
const renderer = {
|
|
||||||
heading(text) {
|
|
||||||
return `<strong>${text}</strong>`;
|
|
||||||
},
|
|
||||||
link(url, title, text) {
|
|
||||||
const mentionRegex = new RegExp(USER_MENTIONS_REGEX);
|
|
||||||
if (url.match(mentionRegex)) {
|
|
||||||
return `<span class="prosemirror-mention-node">${text}</span>`;
|
|
||||||
}
|
|
||||||
return `<a rel="noreferrer noopener nofollow" href="${url}" class="link" title="${title ||
|
|
||||||
''}" target="_blank">${text}</a>`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
this.marked.use({ renderer });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
formatMessage() {
|
formatMessage() {
|
||||||
|
let updatedMessage = this.message;
|
||||||
if (this.isATweet && !this.isAPrivateNote) {
|
if (this.isATweet && !this.isAPrivateNote) {
|
||||||
const withUserName = this.message.replace(
|
updatedMessage = updatedMessage.replace(
|
||||||
TWITTER_USERNAME_REGEX,
|
TWITTER_USERNAME_REGEX,
|
||||||
TWITTER_USERNAME_REPLACEMENT
|
TWITTER_USERNAME_REPLACEMENT
|
||||||
);
|
);
|
||||||
const withHash = withUserName.replace(
|
updatedMessage = updatedMessage.replace(
|
||||||
TWITTER_HASH_REGEX,
|
TWITTER_HASH_REGEX,
|
||||||
TWITTER_HASH_REPLACEMENT
|
TWITTER_HASH_REPLACEMENT
|
||||||
);
|
);
|
||||||
const markedDownOutput = marked(withHash);
|
|
||||||
return markedDownOutput;
|
|
||||||
}
|
}
|
||||||
DOMPurify.addHook('afterSanitizeAttributes', afterSanitizeAttributes);
|
return md.render(updatedMessage);
|
||||||
return DOMPurify.sanitize(
|
|
||||||
marked(this.message, { breaks: true, gfm: true })
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get formattedMessage() {
|
get formattedMessage() {
|
||||||
|
|
69
app/javascript/shared/helpers/markdownIt/link.js
Normal file
69
app/javascript/shared/helpers/markdownIt/link.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Process [@mention](mention://user/1/Pranav)
|
||||||
|
const USER_MENTIONS_REGEX = /mention:\/\/(user|team)\/(\d+)\/(.+)/gm;
|
||||||
|
|
||||||
|
const buildMentionTokens = () => (state, silent) => {
|
||||||
|
var label;
|
||||||
|
var labelEnd;
|
||||||
|
var labelStart;
|
||||||
|
var pos;
|
||||||
|
var res;
|
||||||
|
var token;
|
||||||
|
var href = '';
|
||||||
|
var max = state.posMax;
|
||||||
|
|
||||||
|
if (state.src.charCodeAt(state.pos) !== 0x5b /* [ */) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
labelStart = state.pos + 1;
|
||||||
|
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);
|
||||||
|
|
||||||
|
// parser failed to find ']', so it's not a valid link
|
||||||
|
if (labelEnd < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = state.src.slice(labelStart, labelEnd);
|
||||||
|
pos = labelEnd + 1;
|
||||||
|
|
||||||
|
if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) {
|
||||||
|
pos += 1;
|
||||||
|
res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);
|
||||||
|
if (res.ok) {
|
||||||
|
href = state.md.normalizeLink(res.str);
|
||||||
|
if (state.md.validateLink(href)) {
|
||||||
|
pos = res.pos;
|
||||||
|
} else {
|
||||||
|
href = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!href.match(new RegExp(USER_MENTIONS_REGEX))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
state.pos = labelStart;
|
||||||
|
state.posMax = labelEnd;
|
||||||
|
|
||||||
|
token = state.push('mention', '');
|
||||||
|
token.href = href;
|
||||||
|
token.content = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.pos = pos;
|
||||||
|
state.posMax = max;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderMentions = () => (tokens, idx) => {
|
||||||
|
return `<span class="prosemirror-mention-node">${tokens[idx].content}</span>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function mentionPlugin(md) {
|
||||||
|
md.renderer.rules.mention = renderMentions(md);
|
||||||
|
md.inline.ruler.before('link', 'mention', buildMentionTokens(md));
|
||||||
|
}
|
|
@ -6,14 +6,14 @@ describe('#MessageFormatter', () => {
|
||||||
const message =
|
const message =
|
||||||
'Chatwoot is an opensource tool. [Chatwoot](https://www.chatwoot.com)';
|
'Chatwoot is an opensource tool. [Chatwoot](https://www.chatwoot.com)';
|
||||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||||
'<p>Chatwoot is an opensource tool. <a title="" class="link" href="https://www.chatwoot.com" rel="noreferrer noopener nofollow" target="_blank">Chatwoot</a></p>'
|
'<p>Chatwoot is an opensource tool. <a href="https://www.chatwoot.com" class="link" rel="noreferrer noopener nofollow" target="_blank">Chatwoot</a></p>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should format correctly', () => {
|
it('should format correctly', () => {
|
||||||
const message =
|
const message =
|
||||||
'Chatwoot is an opensource tool. https://www.chatwoot.com';
|
'Chatwoot is an opensource tool. https://www.chatwoot.com';
|
||||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||||
'<p>Chatwoot is an opensource tool. <a title="" class="link" href="https://www.chatwoot.com" rel="noreferrer noopener nofollow" target="_blank">https://www.chatwoot.com</a></p>'
|
'<p>Chatwoot is an opensource tool. <a href="https://www.chatwoot.com" class="link" rel="noreferrer noopener nofollow" target="_blank">https://www.chatwoot.com</a></p>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,8 @@ describe('#MessageFormatter', () => {
|
||||||
it('should format correctly', () => {
|
it('should format correctly', () => {
|
||||||
const message = '### opensource \n ## tool';
|
const message = '### opensource \n ## tool';
|
||||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||||
'<strong>opensource</strong><strong>tool</strong>'
|
`<h3>opensource</h3>
|
||||||
|
<h2>tool</h2>`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -39,7 +40,7 @@ describe('#MessageFormatter', () => {
|
||||||
expect(
|
expect(
|
||||||
new MessageFormatter(message, true, false).formattedMessage
|
new MessageFormatter(message, true, false).formattedMessage
|
||||||
).toMatch(
|
).toMatch(
|
||||||
'<p><a href="http://twitter.com/chatwootapp" target="_blank" rel="noreferrer nofollow noopener">@chatwootapp</a> is an opensource tool thanks @longnonexistenttwitterusername</p>'
|
'<p><a href="http://twitter.com/chatwootapp" class="link" rel="noreferrer noopener nofollow" target="_blank">@chatwootapp</a> is an opensource tool thanks @longnonexistenttwitterusername</p>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ describe('#MessageFormatter', () => {
|
||||||
expect(
|
expect(
|
||||||
new MessageFormatter(message, true, false).formattedMessage
|
new MessageFormatter(message, true, false).formattedMessage
|
||||||
).toMatch(
|
).toMatch(
|
||||||
'<p><a href="https://twitter.com/hashtag/chatwootapp" target="_blank" rel="noreferrer nofollow noopener">#chatwootapp</a> is an opensource tool</p>'
|
'<p><a href="https://twitter.com/hashtag/chatwootapp" class="link" rel="noreferrer noopener nofollow" target="_blank">#chatwootapp</a> is an opensource tool</p>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -90,7 +91,8 @@ describe('#MessageFormatter', () => {
|
||||||
const message =
|
const message =
|
||||||
'[xssLink](javascript:alert(document.cookie))\n[normalLink](https://google.com)**I am a bold text paragraph**';
|
'[xssLink](javascript:alert(document.cookie))\n[normalLink](https://google.com)**I am a bold text paragraph**';
|
||||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||||
'<p><a title="" class="link" rel="noreferrer noopener nofollow" target="_blank">xssLink</a><br><a title="" class="link" href="https://google.com" rel="noreferrer noopener nofollow" target="_blank">normalLink</a><strong>I am a bold text paragraph</strong></p>'
|
`<p>[xssLink](javascript:alert(document.cookie))<br />
|
||||||
|
<a href="https://google.com" class="link" rel="noreferrer noopener nofollow" target="_blank">normalLink</a><strong>I am a bold text paragraph</strong></p>`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -117,7 +117,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.user.has-attachment {
|
.user.has-attachment {
|
||||||
.icon-wrap {
|
.icon-wrap {
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
|
@ -147,7 +146,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.unread-messages {
|
.unread-messages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -206,7 +204,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.chat-bubble {
|
.chat-bubble {
|
||||||
@include light-shadow;
|
@include light-shadow;
|
||||||
border-radius: $space-two;
|
border-radius: $space-two;
|
||||||
|
@ -217,6 +214,7 @@
|
||||||
padding: $space-slab $space-normal;
|
padding: $space-slab $space-normal;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
:not([audio]) {
|
:not([audio]) {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
"highlight.js": "~10.4.1",
|
"highlight.js": "~10.4.1",
|
||||||
"ionicons": "~2.0.1",
|
"ionicons": "~2.0.1",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"marked": "4.0.10",
|
"markdown-it": "^13.0.1",
|
||||||
|
"markdown-it-link-attributes": "^4.0.1",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"ninja-keys": "^1.1.9",
|
"ninja-keys": "^1.1.9",
|
||||||
"opus-recorder": "^8.0.5",
|
"opus-recorder": "^8.0.5",
|
||||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -4819,6 +4819,11 @@ argparse@^1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
argparse@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||||
|
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||||
|
|
||||||
aria-query@^4.2.2:
|
aria-query@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
|
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
|
||||||
|
@ -7482,6 +7487,11 @@ entities@~2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
||||||
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
||||||
|
|
||||||
|
entities@~3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
|
||||||
|
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
|
||||||
|
|
||||||
errno@^0.1.3, errno@~0.1.7:
|
errno@^0.1.3, errno@~0.1.7:
|
||||||
version "0.1.8"
|
version "0.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||||
|
@ -10914,6 +10924,13 @@ linkify-it@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
uc.micro "^1.0.1"
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
|
linkify-it@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec"
|
||||||
|
integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==
|
||||||
|
dependencies:
|
||||||
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
lint-staged@10.5.4:
|
lint-staged@10.5.4:
|
||||||
version "10.5.4"
|
version "10.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665"
|
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665"
|
||||||
|
@ -11314,6 +11331,11 @@ markdown-escapes@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
|
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
|
||||||
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
|
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
|
||||||
|
|
||||||
|
markdown-it-link-attributes@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz#25751f2cf74fd91f0a35ba7b3247fa45f2056d88"
|
||||||
|
integrity sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==
|
||||||
|
|
||||||
markdown-it@^10.0.0:
|
markdown-it@^10.0.0:
|
||||||
version "10.0.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
|
||||||
|
@ -11325,10 +11347,16 @@ markdown-it@^10.0.0:
|
||||||
mdurl "^1.0.1"
|
mdurl "^1.0.1"
|
||||||
uc.micro "^1.0.5"
|
uc.micro "^1.0.5"
|
||||||
|
|
||||||
marked@4.0.10:
|
markdown-it@^13.0.1:
|
||||||
version "4.0.10"
|
version "13.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.10.tgz#423e295385cc0c3a70fa495e0df68b007b879423"
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430"
|
||||||
integrity sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==
|
integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==
|
||||||
|
dependencies:
|
||||||
|
argparse "^2.0.1"
|
||||||
|
entities "~3.0.1"
|
||||||
|
linkify-it "^4.0.1"
|
||||||
|
mdurl "^1.0.1"
|
||||||
|
uc.micro "^1.0.5"
|
||||||
|
|
||||||
material-colors@^1.0.0:
|
material-colors@^1.0.0:
|
||||||
version "1.2.6"
|
version "1.2.6"
|
||||||
|
|
Loading…
Reference in a new issue