snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,353 @@
import hljs from 'highlight.js/lib/core';
import clean from 'highlight.js/lib/languages/clean';
import javascript from 'highlight.js/lib/languages/javascript';
import markdown from 'highlight.js/lib/languages/markdown';
hljs.registerLanguage('markdown', markdown);
hljs.registerLanguage('clean', clean);
hljs.registerLanguage('javascript', javascript);
export const register = async (lang) => {
switch (lang) {
case 'onec':
return hljs.registerLanguage('onec', (await import('highlight.js/lib/languages/1c')).default);
case 'abnf':
return hljs.registerLanguage('abnf', (await import('highlight.js/lib/languages/abnf')).default);
case 'accesslog':
return hljs.registerLanguage('accesslog', (await import('highlight.js/lib/languages/accesslog')).default);
case 'actionscript':
return hljs.registerLanguage('actionscript', (await import('highlight.js/lib/languages/actionscript')).default);
case 'ada':
return hljs.registerLanguage('ada', (await import('highlight.js/lib/languages/ada')).default);
case 'apache':
return hljs.registerLanguage('apache', (await import('highlight.js/lib/languages/apache')).default);
case 'applescript':
return hljs.registerLanguage('applescript', (await import('highlight.js/lib/languages/applescript')).default);
case 'arduino':
return hljs.registerLanguage('arduino', (await import('highlight.js/lib/languages/arduino')).default);
case 'armasm':
return hljs.registerLanguage('armasm', (await import('highlight.js/lib/languages/armasm')).default);
case 'asciidoc':
return hljs.registerLanguage('asciidoc', (await import('highlight.js/lib/languages/asciidoc')).default);
case 'aspectj':
return hljs.registerLanguage('aspectj', (await import('highlight.js/lib/languages/aspectj')).default);
case 'autohotkey':
return hljs.registerLanguage('autohotkey', (await import('highlight.js/lib/languages/autohotkey')).default);
case 'autoit':
return hljs.registerLanguage('autoit', (await import('highlight.js/lib/languages/autoit')).default);
case 'avrasm':
return hljs.registerLanguage('avrasm', (await import('highlight.js/lib/languages/avrasm')).default);
case 'awk':
return hljs.registerLanguage('awk', (await import('highlight.js/lib/languages/awk')).default);
case 'axapta':
return hljs.registerLanguage('axapta', (await import('highlight.js/lib/languages/axapta')).default);
case 'bash':
return hljs.registerLanguage('bash', (await import('highlight.js/lib/languages/bash')).default);
case 'basic':
return hljs.registerLanguage('basic', (await import('highlight.js/lib/languages/basic')).default);
case 'bnf':
return hljs.registerLanguage('bnf', (await import('highlight.js/lib/languages/bnf')).default);
case 'brainfuck':
return hljs.registerLanguage('brainfuck', (await import('highlight.js/lib/languages/brainfuck')).default);
case 'cal':
return hljs.registerLanguage('cal', (await import('highlight.js/lib/languages/cal')).default);
case 'capnproto':
return hljs.registerLanguage('capnproto', (await import('highlight.js/lib/languages/capnproto')).default);
case 'ceylon':
return hljs.registerLanguage('ceylon', (await import('highlight.js/lib/languages/ceylon')).default);
case 'clean':
return hljs.registerLanguage('clean', (await import('highlight.js/lib/languages/clean')).default);
case 'clojure':
return hljs.registerLanguage('clojure', (await import('highlight.js/lib/languages/clojure')).default);
case 'clojure-repl':
return hljs.registerLanguage('clojure-repl', (await import('highlight.js/lib/languages/clojure-repl')).default);
case 'cmake':
return hljs.registerLanguage('cmake', (await import('highlight.js/lib/languages/cmake')).default);
case 'coffeescript':
return hljs.registerLanguage('coffeescript', (await import('highlight.js/lib/languages/coffeescript')).default);
case 'coq':
return hljs.registerLanguage('coq', (await import('highlight.js/lib/languages/coq')).default);
case 'cos':
return hljs.registerLanguage('cos', (await import('highlight.js/lib/languages/cos')).default);
case 'cpp':
return hljs.registerLanguage('cpp', (await import('highlight.js/lib/languages/cpp')).default);
case 'crmsh':
return hljs.registerLanguage('crmsh', (await import('highlight.js/lib/languages/crmsh')).default);
case 'crystal':
return hljs.registerLanguage('crystal', (await import('highlight.js/lib/languages/crystal')).default);
case 'csp':
return hljs.registerLanguage('csp', (await import('highlight.js/lib/languages/csp')).default);
case 'css':
return hljs.registerLanguage('css', (await import('highlight.js/lib/languages/css')).default);
case 'd':
return hljs.registerLanguage('d', (await import('highlight.js/lib/languages/d')).default);
case 'dart':
return hljs.registerLanguage('dart', (await import('highlight.js/lib/languages/dart')).default);
case 'delphi':
return hljs.registerLanguage('delphi', (await import('highlight.js/lib/languages/delphi')).default);
case 'diff':
return hljs.registerLanguage('diff', (await import('highlight.js/lib/languages/diff')).default);
case 'django':
return hljs.registerLanguage('django', (await import('highlight.js/lib/languages/django')).default);
case 'dns':
return hljs.registerLanguage('dns', (await import('highlight.js/lib/languages/dns')).default);
case 'dockerfile':
return hljs.registerLanguage('dockerfile', (await import('highlight.js/lib/languages/dockerfile')).default);
case 'dos':
return hljs.registerLanguage('dos', (await import('highlight.js/lib/languages/dos')).default);
case 'dsconfig':
return hljs.registerLanguage('dsconfig', (await import('highlight.js/lib/languages/dsconfig')).default);
case 'dts':
return hljs.registerLanguage('dts', (await import('highlight.js/lib/languages/dts')).default);
case 'dust':
return hljs.registerLanguage('dust', (await import('highlight.js/lib/languages/dust')).default);
case 'ebnf':
return hljs.registerLanguage('ebnf', (await import('highlight.js/lib/languages/ebnf')).default);
case 'elixir':
return hljs.registerLanguage('elixir', (await import('highlight.js/lib/languages/elixir')).default);
case 'elm':
return hljs.registerLanguage('elm', (await import('highlight.js/lib/languages/elm')).default);
case 'erb':
return hljs.registerLanguage('erb', (await import('highlight.js/lib/languages/erb')).default);
case 'erlang':
return hljs.registerLanguage('erlang', (await import('highlight.js/lib/languages/erlang')).default);
case 'excel':
return hljs.registerLanguage('excel', (await import('highlight.js/lib/languages/excel')).default);
case 'fix':
return hljs.registerLanguage('fix', (await import('highlight.js/lib/languages/fix')).default);
case 'flix':
return hljs.registerLanguage('flix', (await import('highlight.js/lib/languages/flix')).default);
case 'fortran':
return hljs.registerLanguage('fortran', (await import('highlight.js/lib/languages/fortran')).default);
case 'fsharp':
return hljs.registerLanguage('fsharp', (await import('highlight.js/lib/languages/fsharp')).default);
case 'gams':
return hljs.registerLanguage('gams', (await import('highlight.js/lib/languages/gams')).default);
case 'gauss':
return hljs.registerLanguage('gauss', (await import('highlight.js/lib/languages/gauss')).default);
case 'gcode':
return hljs.registerLanguage('gcode', (await import('highlight.js/lib/languages/gcode')).default);
case 'gherkin':
return hljs.registerLanguage('gherkin', (await import('highlight.js/lib/languages/gherkin')).default);
case 'glsl':
return hljs.registerLanguage('glsl', (await import('highlight.js/lib/languages/glsl')).default);
case 'go':
return hljs.registerLanguage('go', (await import('highlight.js/lib/languages/go')).default);
case 'golo':
return hljs.registerLanguage('golo', (await import('highlight.js/lib/languages/golo')).default);
case 'gradle':
return hljs.registerLanguage('gradle', (await import('highlight.js/lib/languages/gradle')).default);
case 'groovy':
return hljs.registerLanguage('groovy', (await import('highlight.js/lib/languages/groovy')).default);
case 'haml':
return hljs.registerLanguage('haml', (await import('highlight.js/lib/languages/haml')).default);
case 'handlebars':
return hljs.registerLanguage('handlebars', (await import('highlight.js/lib/languages/handlebars')).default);
case 'haskell':
return hljs.registerLanguage('haskell', (await import('highlight.js/lib/languages/haskell')).default);
case 'haxe':
return hljs.registerLanguage('haxe', (await import('highlight.js/lib/languages/haxe')).default);
case 'hsp':
return hljs.registerLanguage('hsp', (await import('highlight.js/lib/languages/hsp')).default);
case 'http':
return hljs.registerLanguage('http', (await import('highlight.js/lib/languages/http')).default);
case 'hy':
return hljs.registerLanguage('hy', (await import('highlight.js/lib/languages/hy')).default);
case 'inform7':
return hljs.registerLanguage('inform7', (await import('highlight.js/lib/languages/inform7')).default);
case 'ini':
return hljs.registerLanguage('ini', (await import('highlight.js/lib/languages/ini')).default);
case 'irpf90':
return hljs.registerLanguage('irpf90', (await import('highlight.js/lib/languages/irpf90')).default);
case 'java':
return hljs.registerLanguage('java', (await import('highlight.js/lib/languages/java')).default);
case 'javascript':
return hljs.registerLanguage('javascript', (await import('highlight.js/lib/languages/javascript')).default);
case 'jboss-cli':
return hljs.registerLanguage('jboss-cli', (await import('highlight.js/lib/languages/jboss-cli')).default);
case 'json':
return hljs.registerLanguage('json', (await import('highlight.js/lib/languages/json')).default);
case 'julia':
return hljs.registerLanguage('julia', (await import('highlight.js/lib/languages/julia')).default);
case 'julia-repl':
return hljs.registerLanguage('julia-repl', (await import('highlight.js/lib/languages/julia-repl')).default);
case 'kotlin':
return hljs.registerLanguage('kotlin', (await import('highlight.js/lib/languages/kotlin')).default);
case 'lasso':
return hljs.registerLanguage('lasso', (await import('highlight.js/lib/languages/lasso')).default);
case 'ldif':
return hljs.registerLanguage('ldif', (await import('highlight.js/lib/languages/ldif')).default);
case 'leaf':
return hljs.registerLanguage('leaf', (await import('highlight.js/lib/languages/leaf')).default);
case 'less':
return hljs.registerLanguage('less', (await import('highlight.js/lib/languages/less')).default);
case 'lisp':
return hljs.registerLanguage('lisp', (await import('highlight.js/lib/languages/lisp')).default);
case 'livecodeserver':
return hljs.registerLanguage('livecodeserver', (await import('highlight.js/lib/languages/livecodeserver')).default);
case 'livescript':
return hljs.registerLanguage('livescript', (await import('highlight.js/lib/languages/livescript')).default);
case 'llvm':
return hljs.registerLanguage('llvm', (await import('highlight.js/lib/languages/llvm')).default);
case 'lsl':
return hljs.registerLanguage('lsl', (await import('highlight.js/lib/languages/lsl')).default);
case 'lua':
return hljs.registerLanguage('lua', (await import('highlight.js/lib/languages/lua')).default);
case 'makefile':
return hljs.registerLanguage('makefile', (await import('highlight.js/lib/languages/makefile')).default);
case 'markdown':
return hljs.registerLanguage('markdown', (await import('highlight.js/lib/languages/markdown')).default);
case 'mathematica':
return hljs.registerLanguage('mathematica', (await import('highlight.js/lib/languages/mathematica')).default);
case 'matlab':
return hljs.registerLanguage('matlab', (await import('highlight.js/lib/languages/matlab')).default);
case 'maxima':
return hljs.registerLanguage('maxima', (await import('highlight.js/lib/languages/maxima')).default);
case 'mel':
return hljs.registerLanguage('mel', (await import('highlight.js/lib/languages/mel')).default);
case 'mercury':
return hljs.registerLanguage('mercury', (await import('highlight.js/lib/languages/mercury')).default);
case 'mipsasm':
return hljs.registerLanguage('mipsasm', (await import('highlight.js/lib/languages/mipsasm')).default);
case 'mizar':
return hljs.registerLanguage('mizar', (await import('highlight.js/lib/languages/mizar')).default);
case 'perl':
return hljs.registerLanguage('perl', (await import('highlight.js/lib/languages/perl')).default);
case 'mojolicious':
return hljs.registerLanguage('mojolicious', (await import('highlight.js/lib/languages/mojolicious')).default);
case 'monkey':
return hljs.registerLanguage('monkey', (await import('highlight.js/lib/languages/monkey')).default);
case 'moonscript':
return hljs.registerLanguage('moonscript', (await import('highlight.js/lib/languages/moonscript')).default);
case 'n1ql':
return hljs.registerLanguage('n1ql', (await import('highlight.js/lib/languages/n1ql')).default);
case 'nginx':
return hljs.registerLanguage('nginx', (await import('highlight.js/lib/languages/nginx')).default);
case 'nix':
return hljs.registerLanguage('nix', (await import('highlight.js/lib/languages/nix')).default);
case 'nsis':
return hljs.registerLanguage('nsis', (await import('highlight.js/lib/languages/nsis')).default);
case 'objectivec':
return hljs.registerLanguage('objectivec', (await import('highlight.js/lib/languages/objectivec')).default);
case 'ocaml':
return hljs.registerLanguage('ocaml', (await import('highlight.js/lib/languages/ocaml')).default);
case 'openscad':
return hljs.registerLanguage('openscad', (await import('highlight.js/lib/languages/openscad')).default);
case 'oxygene':
return hljs.registerLanguage('oxygene', (await import('highlight.js/lib/languages/oxygene')).default);
case 'parser3':
return hljs.registerLanguage('parser3', (await import('highlight.js/lib/languages/parser3')).default);
case 'pf':
return hljs.registerLanguage('pf', (await import('highlight.js/lib/languages/pf')).default);
case 'php':
return hljs.registerLanguage('php', (await import('highlight.js/lib/languages/php')).default);
case 'pony':
return hljs.registerLanguage('pony', (await import('highlight.js/lib/languages/pony')).default);
case 'powershell':
return hljs.registerLanguage('powershell', (await import('highlight.js/lib/languages/powershell')).default);
case 'processing':
return hljs.registerLanguage('processing', (await import('highlight.js/lib/languages/processing')).default);
case 'profile':
return hljs.registerLanguage('profile', (await import('highlight.js/lib/languages/profile')).default);
case 'prolog':
return hljs.registerLanguage('prolog', (await import('highlight.js/lib/languages/prolog')).default);
case 'protobuf':
return hljs.registerLanguage('protobuf', (await import('highlight.js/lib/languages/protobuf')).default);
case 'puppet':
return hljs.registerLanguage('puppet', (await import('highlight.js/lib/languages/puppet')).default);
case 'purebasic':
return hljs.registerLanguage('purebasic', (await import('highlight.js/lib/languages/purebasic')).default);
case 'python':
return hljs.registerLanguage('python', (await import('highlight.js/lib/languages/python')).default);
case 'q':
return hljs.registerLanguage('q', (await import('highlight.js/lib/languages/q')).default);
case 'qml':
return hljs.registerLanguage('qml', (await import('highlight.js/lib/languages/qml')).default);
case 'r':
return hljs.registerLanguage('r', (await import('highlight.js/lib/languages/r')).default);
case 'rib':
return hljs.registerLanguage('rib', (await import('highlight.js/lib/languages/rib')).default);
case 'roboconf':
return hljs.registerLanguage('roboconf', (await import('highlight.js/lib/languages/roboconf')).default);
case 'rsl':
return hljs.registerLanguage('rsl', (await import('highlight.js/lib/languages/rsl')).default);
case 'ruleslanguage':
return hljs.registerLanguage('ruleslanguage', (await import('highlight.js/lib/languages/ruleslanguage')).default);
case 'rust':
return hljs.registerLanguage('rust', (await import('highlight.js/lib/languages/rust')).default);
case 'scala':
return hljs.registerLanguage('scala', (await import('highlight.js/lib/languages/scala')).default);
case 'scheme':
return hljs.registerLanguage('scheme', (await import('highlight.js/lib/languages/scheme')).default);
case 'scilab':
return hljs.registerLanguage('scilab', (await import('highlight.js/lib/languages/scilab')).default);
case 'scss':
return hljs.registerLanguage('scss', (await import('highlight.js/lib/languages/scss')).default);
case 'shell':
return hljs.registerLanguage('shell', (await import('highlight.js/lib/languages/shell')).default);
case 'smali':
return hljs.registerLanguage('smali', (await import('highlight.js/lib/languages/smali')).default);
case 'smalltalk':
return hljs.registerLanguage('smalltalk', (await import('highlight.js/lib/languages/smalltalk')).default);
case 'sml':
return hljs.registerLanguage('sml', (await import('highlight.js/lib/languages/sml')).default);
case 'sqf':
return hljs.registerLanguage('sqf', (await import('highlight.js/lib/languages/sqf')).default);
case 'sql':
return hljs.registerLanguage('sql', (await import('highlight.js/lib/languages/sql')).default);
case 'stan':
return hljs.registerLanguage('stan', (await import('highlight.js/lib/languages/stan')).default);
case 'stata':
return hljs.registerLanguage('stata', (await import('highlight.js/lib/languages/stata')).default);
case 'step21':
return hljs.registerLanguage('step21', (await import('highlight.js/lib/languages/step21')).default);
case 'stylus':
return hljs.registerLanguage('stylus', (await import('highlight.js/lib/languages/stylus')).default);
case 'subunit':
return hljs.registerLanguage('subunit', (await import('highlight.js/lib/languages/subunit')).default);
case 'swift':
return hljs.registerLanguage('swift', (await import('highlight.js/lib/languages/swift')).default);
case 'taggerscript':
return hljs.registerLanguage('taggerscript', (await import('highlight.js/lib/languages/taggerscript')).default);
case 'yaml':
return hljs.registerLanguage('yaml', (await import('highlight.js/lib/languages/yaml')).default);
case 'tap':
return hljs.registerLanguage('tap', (await import('highlight.js/lib/languages/tap')).default);
case 'tcl':
return hljs.registerLanguage('tcl', (await import('highlight.js/lib/languages/tcl')).default);
case 'thrift':
return hljs.registerLanguage('thrift', (await import('highlight.js/lib/languages/thrift')).default);
case 'tp':
return hljs.registerLanguage('tp', (await import('highlight.js/lib/languages/tp')).default);
case 'twig':
return hljs.registerLanguage('twig', (await import('highlight.js/lib/languages/twig')).default);
case 'typescript':
return hljs.registerLanguage('typescript', (await import('highlight.js/lib/languages/typescript')).default);
case 'vala':
return hljs.registerLanguage('vala', (await import('highlight.js/lib/languages/vala')).default);
case 'vbnet':
return hljs.registerLanguage('vbnet', (await import('highlight.js/lib/languages/vbnet')).default);
case 'vbscript':
return hljs.registerLanguage('vbscript', (await import('highlight.js/lib/languages/vbscript')).default);
case 'vbscript-html':
return hljs.registerLanguage('vbscript-html(', (await import('highlight.js/lib/languages/vbscript-html')).default);
case 'verilog':
return hljs.registerLanguage('verilog', (await import('highlight.js/lib/languages/verilog')).default);
case 'vhdl':
return hljs.registerLanguage('vhdl', (await import('highlight.js/lib/languages/vhdl')).default);
case 'vim':
return hljs.registerLanguage('vim', (await import('highlight.js/lib/languages/vim')).default);
case 'x86asm':
return hljs.registerLanguage('x86asm', (await import('highlight.js/lib/languages/x86asm')).default);
case 'xl':
return hljs.registerLanguage('xl', (await import('highlight.js/lib/languages/xl')).default);
case 'xquery':
return hljs.registerLanguage('xquery', (await import('highlight.js/lib/languages/xquery')).default);
case 'zephir':
return hljs.registerLanguage('zephir', (await import('highlight.js/lib/languages/zephir')).default);
default:
return hljs.registerLanguage('plaintext', (await import('highlight.js/lib/languages/plaintext')).default);
}
};
export default hljs;

View File

@@ -0,0 +1,94 @@
/*
* Markdown is a named function that will parse markdown syntax
* @param {Object} message - The message object
*/
import { escapeHTML } from '@rocket.chat/tools';
import { Meteor } from 'meteor/meteor';
import { filtered } from './parser/filtered/filtered';
import { code } from './parser/original/code';
import { original } from './parser/original/original';
const parsers = {
original,
filtered,
};
class MarkdownClass {
parse(text) {
const message = {
html: escapeHTML(text),
};
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseNotEscaped(text) {
const message = {
html: text,
};
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseMessageNotEscaped(message) {
const options = {
rootUrl: Meteor.absoluteUrl(),
};
return parsers.original(message, options);
}
mountTokensBackRecursively(message, tokenList, useHtml = true) {
const missingTokens = [];
if (tokenList.length > 0) {
for (const { token, text, noHtml } of tokenList) {
if (message.html.indexOf(token) >= 0) {
message.html = message.html.replace(token, () => (useHtml ? text : noHtml)); // Uses lambda so doesn't need to escape $
} else {
missingTokens.push({ token, text, noHtml });
}
}
}
// If there are tokens that were missing from the string, but the last iteration replaced at least one token, then go again
// this is done because one of the tokens may have been hidden by another one
if (missingTokens.length > 0 && missingTokens.length < tokenList.length) {
this.mountTokensBackRecursively(message, missingTokens, useHtml);
}
}
mountTokensBack(message, useHtml = true) {
if (message.tokens) {
this.mountTokensBackRecursively(message, message.tokens, useHtml);
}
return message;
}
code(...args) {
return code(...args);
}
/** @param {string} message */
filterMarkdownFromMessage(message) {
return parsers.filtered(message);
}
}
export const Markdown = new MarkdownClass();
/** @param {string} message */
export const filterMarkdown = (message) => Markdown.filterMarkdownFromMessage(message);
export const createMarkdownMessageRenderer = ({ ...options }) => {
const markedParser = parsers.marked;
return (message, useMarkedParser = false) => {
if (!message?.html?.trim()) {
return message;
}
return useMarkedParser ? markedParser(message, options) : parsers.original(message, options);
};
};
export const createMarkdownNotificationRenderer = () => (message) => parsers.filtered(message);

View File

@@ -0,0 +1,53 @@
import { getFilteredLinkRegexes } from '../linkRegexes';
const inlineCodeRegex = /`([^`\r\n]+)\`/gm;
/**
* Filter markdown tags in message
* Use case: notifications
* @param {string} message
*/
export const filtered = (
message,
options = {
supportSchemesForLink: 'http,https',
},
) => {
const schemes = (options.supportSchemesForLink || 'http,https').split(',').join('|');
const linkRegexes = getFilteredLinkRegexes(schemes);
// Remove block code backticks
message = message.replace(/```/g, '');
// Remove inline code backticks
message = message.replace(inlineCodeRegex, (match) => match.substr(1, match.length - 2));
// Filter [text](url), ![alt_text](image_url)
message = message.replace(linkRegexes.link, (match, title) => title);
// Filter <http://link|Text>
message = message.replace(linkRegexes.pipedLink, (match, title) => title);
// Filter headings
message = message.replace(
/(^#{1,4}) (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm,
'$2',
);
// Filter bold
message = message.replace(/(^|>|[ >_~`])\*{1,2}([^\*\r\n]+)\*{1,2}([<_~`]|\B|\b|$)/gm, '$1$2$3');
// Filter italics
message = message.replace(/(^|>|[ >*~`])\_{1,2}([^\_\r\n]+)\_{1,2}([<*~`]|\B|\b|$)/gm, '$1$2$3');
// Filter strike-through text
message = message.replace(/(^|>|[ >_*`])\~{1,2}([^~\r\n]+)\~{1,2}([<_*`]|\B|\b|$)/gm, '$1$2$3');
// Filter block quotes
message = message.replace(/(?:>){3}\n+([\s\S]*?)\n+(?:<){3}/g, '$1');
// Filter > quote
message = message.replace(/^>(.*)$/gm, '$1');
return message;
};

View File

@@ -0,0 +1,20 @@
import mem from 'mem';
// The link regexes only vary with the configured URL schemes, which come from
// a setting that rarely changes; cache the compiled regexes per schemes value
// instead of recompiling them for every message.
//
// The two parsers intentionally use different patterns: `filtered` only strips
// markup (no URL capture, URLs may contain `)` escapes) while `original`
// renders it (captures the URL, splits image from link).
export const getMarkdownLinkRegexes = mem((schemes) => ({
image: new RegExp(`!\\[([^\\]]+)\\]\\(((?:${schemes}):\\/\\/[^\\s]+)\\)`, 'gm'),
link: new RegExp(`\\[([^\\]]+)\\]\\(((?:${schemes}):\\/\\/[^\\s]+)\\)`, 'gm'),
pipedLink: new RegExp(`(?:<|&lt;)((?:${schemes}):\\/\\/[^\\|]+)\\|(.+?)(?=>|&gt;)(?:>|&gt;)`, 'gm'),
}));
export const getFilteredLinkRegexes = mem((schemes) => ({
link: new RegExp(`!?\\[([^\\]]+)\\]\\((?:${schemes}):\\/\\/[^\\)]+\\)`, 'gm'),
pipedLink: new RegExp(`(?:<|&lt;)(?:${schemes}):\\/\\/[^\\|]+\\|(.+?)(?=>|&gt;)(?:>|&gt;)`, 'gm'),
}));

View File

@@ -0,0 +1,87 @@
/*
* code() is a named function that will parse `inline code` and ```codeblock``` syntaxes
* @param {Object} message - The message object
*/
import { unescapeHTML } from '@rocket.chat/tools';
import { addAsToken } from './token';
import hljs, { register } from '../../hljs';
const inlinecode = (message) => {
// Support `text`
message.html = message.html.replace(/\`([^`\r\n]+)\`([<_*~]|\B|\b|$)/gm, (match, p1, p2) =>
addAsToken(
message,
`<span class=\"copyonly\">\`</span><span><code class=\"code-colors inline\">${p1}</code></span><span class=\"copyonly\">\`</span>${p2}`,
'inlinecode',
{ noHtml: match },
),
);
};
const codeblocks = (message) => {
// Count occurencies of ```
const count = (message.html.match(/```/gm) || []).length;
if (count) {
// Check if we need to add a final ```
if (count % 2 > 0) {
message.html = `${message.html}\n\`\`\``;
message.msg = `${message.msg}\n\`\`\``;
}
// Separate text in code blocks and non code blocks
const msgParts = message.html.split(/(^.*)(```(?:[a-zA-Z]+)?(?:(?:.|\r|\n)*?)```)(.*\n?)$/gm);
for (let index = 0; index < msgParts.length; index++) {
// Verify if this part is code
const part = msgParts[index];
const codeMatch = part.match(/^```[\r\n]*(.*[\r\n\ ]?)[\r\n]*([\s\S]*?)```+?$/);
if (codeMatch) {
// Process highlight if this part is code
const singleLine = codeMatch[0].indexOf('\n') === -1;
const lang = !singleLine && Array.from(hljs.listLanguages()).includes(codeMatch[1].trim()) ? codeMatch[1].trim() : '';
const emptyLanguage = lang === '' ? unescapeHTML(codeMatch[1] + codeMatch[2]) : unescapeHTML(codeMatch[2]);
const code = singleLine ? unescapeHTML(codeMatch[1]) : emptyLanguage;
const result = (() => {
if (lang) {
try {
register(lang);
return hljs.highlight(lang, code);
} catch (error) {
console.error(error);
}
}
return hljs.highlightAuto(lang + code);
})();
const token = addAsToken(
message,
`<pre><code class='code-colors hljs ${result.language}'><span class='copyonly'>\`\`\`<br></span>${result.value}<span class='copyonly'><br>\`\`\`</span></code></pre>`,
'code',
{
noHtml: codeMatch[0],
highlight: true,
},
);
msgParts[index] = token;
} else {
msgParts[index] = part;
}
}
// Re-mount message
message.html = msgParts.join('');
}
};
export const code = (message) => {
if (message.html?.trim()) {
codeblocks(message);
inlinecode(message);
}
return message;
};

View File

@@ -0,0 +1,194 @@
import { addAsToken, isToken, validateAllowedTokens } from './token';
import { getMarkdownLinkRegexes } from '../linkRegexes';
const validateUrl = (url, message) => {
// Don't render markdown inside links
if (message?.tokens?.some((token) => url.includes(token.token))) {
return false;
}
// Valid urls don't contain whitespaces
if (/\s/.test(url.trim())) {
return false;
}
try {
new URL(url);
return true;
} catch (e) {
return false;
}
};
const endsWithWhitespace = (text) => text.substring(text.length - 1).match(/\s/);
const getParseableMarkersCount = (start, end) => {
const usableMarkers = start.length > 1 ? 2 : 1;
return end.length - usableMarkers >= 0 ? usableMarkers : 1;
};
const getTextWrapper = (marker, tagName) => (textPrepend, wrappedText, textAppend) =>
`${textPrepend}<span class="copyonly">${marker}</span><${tagName}>${wrappedText}</${tagName}><span class="copyonly">${marker}</span>${textAppend}`;
const getRegexReplacer = (replaceFunction, getRegex) => (marker, tagName) => {
const wrapper = getTextWrapper(marker, tagName);
// The regex only depends on the marker, so build it once when the parser is
// created instead of on every message. Safe with /g because
// String.prototype.replace resets lastIndex on each call.
const regex = getRegex(marker);
return (msg) => msg.replace(regex, (...args) => replaceFunction(wrapper, ...args));
};
const getParserWithCustomMarker = getRegexReplacer(
(wrapper, match, p1, p2, p3) => {
if (endsWithWhitespace(p2)) {
return match;
}
const finalMarkerCount = getParseableMarkersCount(p1, p3);
return wrapper(p1.substring(finalMarkerCount), p2, p3.substring(finalMarkerCount));
},
(marker) => new RegExp(`(\\${marker}+(?!\\s))([^\\${marker}\\r\\n]+)(\\${marker}+)`, 'gm'),
);
const parseBold = getParserWithCustomMarker('*', 'strong');
const parseStrike = getParserWithCustomMarker('~', 'strike');
const parseItalic = getRegexReplacer(
(wrapper, match, p1, p2, p3, p4, p5) => {
if (p1 || p5 || endsWithWhitespace(p3)) {
return match;
}
const finalMarkerCount = getParseableMarkersCount(p2, p4);
return wrapper(p2.substring(finalMarkerCount), p3, p4.substring(finalMarkerCount));
},
() => new RegExp('([^\\r\\n\\s~*_]){0,1}(\\_+(?!\\s))([^\\_\\r\\n]+)(\\_+)([^\\r\\n\\s]){0,1}', 'gm'),
)('_', 'em');
const parseNotEscaped = (message, { supportSchemesForLink, headers, rootUrl }) => {
let msg = message.html;
if (!message.tokens) {
message.tokens = [];
}
const schemes = (supportSchemesForLink || '').split(',').join('|');
const linkRegexes = getMarkdownLinkRegexes(schemes);
if (headers) {
// Support # Text for h1
msg = msg.replace(
/^# (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm,
'<h1>$1</h1>',
);
// Support # Text for h2
msg = msg.replace(
/^## (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm,
'<h2>$1</h2>',
);
// Support # Text for h3
msg = msg.replace(
/^### (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm,
'<h3>$1</h3>',
);
// Support # Text for h4
msg = msg.replace(
/^#### (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm,
'<h4>$1</h4>',
);
}
// Support *text* to make bold
msg = parseBold(msg);
// Support _text_ to make italics
msg = parseItalic(msg);
// // Support ~text~ to strike through text
msg = parseStrike(msg);
// Support for block quote
// >>>
// Text
// <<<
msg = msg.replace(
/(?:&gt;){3}\n+([\s\S]*?)\n+(?:&lt;){3}/g,
'<blockquote class="background-transparent-darker-before"><span class="copyonly">&gt;&gt;&gt;</span>$1<span class="copyonly">&lt;&lt;&lt;</span></blockquote>',
);
// Support >Text for quote
msg = msg.replace(
/^&gt;(.*)$/gm,
'<blockquote class="background-transparent-darker-before"><span class="copyonly">&gt;</span>$1</blockquote>',
);
// Remove white-space around blockquote (prevent <br>). Because blockquote is block element.
msg = msg.replace(
/\s*<blockquote class="background-transparent-darker-before">/gm,
'<blockquote class="background-transparent-darker-before">',
);
msg = msg.replace(/<\/blockquote>\s*/gm, '</blockquote>');
// Remove new-line between blockquotes.
msg = msg.replace(/<\/blockquote>\n<blockquote/gm, '</blockquote><blockquote');
// Support ![alt text](http://image url)
msg = msg.replace(linkRegexes.image, (match, title, url) => {
if (!validateUrl(url, message)) {
return match;
}
if (isToken(title) && !validateAllowedTokens(message, title, ['bold', 'italic', 'strike'])) {
return match;
}
url = encodeURI(url);
const target = url.indexOf(rootUrl) === 0 ? '' : '_blank';
return addAsToken(
message,
`<a data-title="${url}" href="${url}" title="${title}" target="${target}" rel="noopener noreferrer"><div class="inline-image" style="background-image: url(${url});"></div></a>`,
'link',
);
});
// Support [Text](http://link)
msg = msg.replace(linkRegexes.link, (match, title, url) => {
if (!validateUrl(url, message)) {
return match;
}
if (isToken(title) && !validateAllowedTokens(message, title, ['bold', 'italic', 'strike'])) {
return match;
}
const target = url.indexOf(rootUrl) === 0 ? '' : '_blank';
title = title.replace(/&amp;/g, '&');
const escapedUrl = encodeURI(url);
return addAsToken(
message,
`<a data-title="${escapedUrl}" href="${escapedUrl}" target="${target}" rel="noopener noreferrer">${title}</a>`,
'link',
);
});
// Support <http://link|Text>
msg = msg.replace(linkRegexes.pipedLink, (match, url, title) => {
if (!validateUrl(url, message)) {
return match;
}
if (isToken(title) && !validateAllowedTokens(message, title, ['bold', 'italic', 'strike'])) {
return match;
}
url = encodeURI(url);
const target = url.indexOf(rootUrl) === 0 ? '' : '_blank';
return addAsToken(message, `<a data-title="${url}" href="${url}" target="${target}" rel="noopener noreferrer">${title}</a>`, 'link');
});
return msg;
};
export const markdown = (message, options) => {
message.html = parseNotEscaped(message, options);
return message;
};

View File

@@ -0,0 +1,25 @@
/*
* Markdown is a named function that will parse markdown syntax
* @param {Object} message - The message object
*/
import { code } from './code.js';
import { markdown } from './markdown.js';
export const original = (
message,
options = {
supportSchemesForLink: 'http,https',
headers: true,
},
) => {
// Parse markdown code
message = code(message);
// Parse markdown
message = markdown(message, options);
// Replace linebreak to br
message.html = message.html.replace(/\n/gm, '<br>');
return message;
};

View File

@@ -0,0 +1,38 @@
/*
* Markdown is a named function that will parse markdown syntax
* @param {String} msg - The message html
*/
import type { TokenType, TokenExtra } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
type MessageTokens = {
tokens?: {
token: string;
type: TokenType;
text: string;
extra?: TokenExtra;
}[];
};
export const addAsToken = (message: MessageTokens, html: string, type: TokenType, extra?: TokenExtra): string => {
if (!message.tokens) {
message.tokens = [];
}
const token = `=!=${Random.id()}=!=`;
message.tokens.push({
token,
type,
text: html,
...(extra && { ...extra }),
});
return token;
};
export const isToken = (msg: string): boolean => /=!=[.a-z0-9]{17}=!=/gim.test(msg.trim());
export const validateAllowedTokens = (message: MessageTokens, id: string, desiredTokens: TokenType[]): boolean => {
const tokens: string[] = id.match(/=!=[.a-z0-9]{17}=!=/gim) || [];
const tokensFound = message.tokens?.filter(({ token }) => tokens.includes(token)) || [];
return tokensFound.length === 0 || tokensFound.every((token) => token.type && desiredTokens.includes(token.type));
};