ts-markdown-parser
Version:
TypeScript library that converts markdown to HTML (with code support).
112 lines • 6.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.highlightJavaScript = void 0;
const keywords_1 = require("./keywords");
const markdown_parser_1 = require("../../utils/markdown-parser");
const highlightJavaScript = (code) => {
// Check if there's an opening comment but no closing comment
const hasOpenComment = /\/\*/g.test(code);
const hasCloseComment = /\*\//g.test(code);
// Return early if there's an opening or closing comment without its counterpart
if ((hasOpenComment && !hasCloseComment) || (!hasOpenComment && hasCloseComment)) {
return code; // Return early if the code does not have a complete multi-line comment
}
// Ensure TypeScript generics (<T extends ...> and similar constructs) are properly escaped
code = code.replace(/</g, "<").replace(/>/g, ">");
// Highlight comments
const commentRegex = /(\/\/.*|\/\*[\s\S]*?\*\/)/g;
let highlighted = code.replace(commentRegex, '<span class="md-comment">$1</span>');
// Highlight strings that are not inside comments
highlighted = highlighted.replace(/<span class="md-comment">.*?<\/span>|(["'`])(.*?)(\1)/g, (match, p1, p2, p3) => {
// If it's a comment, return it unchanged
if (match.startsWith('<span class="md-comment">'))
return match;
// Otherwise, highlight the string
const stringLine = `<span class="md-string">${p1}${p2}${p3}</span>`;
// console.dir({ stringLine });
return stringLine;
});
//? Hacky way to fix JS lines with comments on end
// TODO: Maybe come up with a better way later
const mdCommentSpan = `<span class="md-comment">`;
let commentPart = "";
if (highlighted.includes(mdCommentSpan)) {
const splitLine = highlighted.split(mdCommentSpan);
if (splitLine.length > 1) {
if (splitLine[0].includes(mdCommentSpan)) {
return highlighted;
}
else {
highlighted = splitLine[0];
commentPart = mdCommentSpan + splitLine[1];
}
}
else {
return highlighted;
}
}
// Highlight specific RegExp patterns
const regexEqualsPattern = /=([\s+]?\/.*\/[gimuy]*;)/g;
highlighted = highlighted.replace(regexEqualsPattern, '=<span class="md-regex">$1</span>');
const regexTestPattern = /([\s+]?\/.*\/[gimuy]*).test/g;
highlighted = highlighted.replace(regexTestPattern, '<span class="md-regex">$1</span>.test');
const blockCommentRegex = /\/\*(.*)\*\//g;
highlighted = highlighted.replace(blockCommentRegex, `<span class="md-comment">/*$1*/</span>`);
// Highlight decorators
const decoratorRegex = /(^|\s)@[\w]+/gm;
highlighted = highlighted.replace(decoratorRegex, '<span class="md-decorator">$&</span>');
// Arrow functions
const arrowFuncRegex = /(\w+)\s*=\s*\(([^)]+)\)/gi;
highlighted = highlighted.replace(arrowFuncRegex, '<span class="md-special">$1</span> = ($2)');
// if() statements and other function/method calls and declarations (with space between keywords and parenth)
const declareFuncRegexWithSpace = /(\w+\s+[if]) \s*\(([^)]*)\)/gi;
highlighted = highlighted.replace(declareFuncRegexWithSpace, '<span class="md-special">$1</span> ($2)');
// else if() statements and other function/method calls and declarations (with space between keywords and parenth)
const elseIfRegex = /(if+|else\sif+)\s*\(([^)]*)\)/g;
highlighted = highlighted.replace(elseIfRegex, '<span class="md-special">$1</span> ($2)');
// Function/method calls and declarations (without space)
const declareFuncRegex = /(\w+\s+[if])\s*\(([^)]*)\)/gi;
highlighted = highlighted.replace(declareFuncRegex, '<span class="md-special">$1</span>($2)');
// Highlight code like `function funcName(`
const functionDeclareRe = /function (\w+)(<[^>]+>)?\(/g;
highlighted = highlighted.replace(functionDeclareRe, (match, funcName, generics) => {
return `function <span class="md-special">${funcName}</span>${generics || ""}(`;
});
// Highlight code like `function funcName(`
const constFuncDeclareRe = /const (\w+)(<[^>]+>)? =/g;
highlighted = highlighted.replace(constFuncDeclareRe, (match, funcName, generics) => {
return `const <span class="md-special">${funcName}</span>${generics || ""} =`;
});
// Class declaration statements
const classDeclareRegex = /^(?:export\s+)?class\s+([A-Z][a-zA-Z0-9_]*)/gi;
highlighted = highlighted.replace(classDeclareRegex, (match, className) => {
return match.replace(className, `<span class="md-class">${className}</span>`);
});
// Highlight method calls like `.myMethod(` or `.myFunc(`
const methodCallRegEx = /\.(\w+)\(/g;
highlighted = highlighted.replace(methodCallRegEx, '.<span class="md-call-method">$1</span>(');
// Highlight reserved keywords
const replaceKeywords = (text) => {
return text.replace(/(<span[^>]*>.*?<\/span>)|(\b\w+\b)/g, (match, span, word) => {
if (span)
return span; // Skip spans
if (word && keywords_1.reservedKeywords.includes(word)) {
return `<span class="md-keyword">${(0, markdown_parser_1.escapeHtml)(word)}</span>`;
}
return word;
});
};
highlighted = replaceKeywords(highlighted);
// Highlight TS types
// tsTypes.forEach((tsType) => {
// tsType = tsType.replace(": ", "");
// tsType = tsType.replace(":", "");
// const regexKeyword = new RegExp(`\\b(${tsType})\\b`, "g");
// highlighted = highlighted.replace(regexKeyword, '<span class="md-decorator">$1</span>');
// });
// TODO: Hacky way to fix broken `https://` strings--maybe come up with a better way later
highlighted = highlighted.replace(`:<span class="</span>md-comment<span class="md-string">">//`, `://`);
return highlighted + commentPart;
};
exports.highlightJavaScript = highlightJavaScript;
//# sourceMappingURL=highlight.js.map