import { marked } from 'marked';
import kebabCase from 'lodash/kebabCase';
import { extractStars } from '../helper/extract-stars.function.js';
import IsBot from '../app/modules/web/util/is-bot.js';
import hljs from 'highlight.js/lib/core';
import nginx from 'highlight.js/lib/languages/nginx';
import xml from 'highlight.js/lib/languages/xml';
import css from 'highlight.js/lib/languages/css';
import scss from 'highlight.js/lib/languages/scss';
import yaml from 'highlight.js/lib/languages/yaml';
import powershell from 'highlight.js/lib/languages/powershell';
import javascript from 'highlight.js/lib/languages/javascript';
import json from 'highlight.js/lib/languages/json';
import shell from 'highlight.js/lib/languages/shell';
import typescript from 'highlight.js/lib/languages/typescript';
import ini from 'highlight.js/lib/languages/ini';
import htmlLang from 'highlight.js/lib/languages/xml';
import markdown from 'highlight.js/lib/languages/markdown';
import python from 'highlight.js/lib/languages/python';
import sql from 'highlight.js/lib/languages/sql';
import dockerfile from 'highlight.js/lib/languages/dockerfile';
import diff from 'highlight.js/lib/languages/diff';
import plaintext from 'highlight.js/lib/languages/plaintext';
import java from 'highlight.js/lib/languages/java';
import c from 'highlight.js/lib/languages/c';
import cpp from 'highlight.js/lib/languages/cpp';
import go from 'highlight.js/lib/languages/go';
import rust from 'highlight.js/lib/languages/rust';
import php from 'highlight.js/lib/languages/php';
import ruby from 'highlight.js/lib/languages/ruby';
import swift from 'highlight.js/lib/languages/swift';
import kotlin from 'highlight.js/lib/languages/kotlin';
import r from 'highlight.js/lib/languages/r';
import perl from 'highlight.js/lib/languages/perl';
import lua from 'highlight.js/lib/languages/lua';
import makefile from 'highlight.js/lib/languages/makefile';
import properties from 'highlight.js/lib/languages/properties';
import graphql from 'highlight.js/lib/languages/graphql';
hljs.registerLanguage('conf', nginx);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('html', htmlLang);
hljs.registerLanguage('css', css);
hljs.registerLanguage('scss', scss);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('yml', yaml);
hljs.registerLanguage('powershell', powershell);
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('js', javascript);
hljs.registerLanguage('json', json);
hljs.registerLanguage('bash', shell);
hljs.registerLanguage('sh', shell);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('ts', typescript);
hljs.registerLanguage('ini', ini);
hljs.registerLanguage('markdown', markdown);
hljs.registerLanguage('md', markdown);
hljs.registerLanguage('python', python);
hljs.registerLanguage('py', python);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('dockerfile', dockerfile);
hljs.registerLanguage('docker', dockerfile);
hljs.registerLanguage('diff', diff);
hljs.registerLanguage('plaintext', plaintext);
hljs.registerLanguage('java', java);
hljs.registerLanguage('c', c);
hljs.registerLanguage('cpp', cpp);
hljs.registerLanguage('go', go);
hljs.registerLanguage('rust', rust);
hljs.registerLanguage('rs', rust);
hljs.registerLanguage('php', php);
hljs.registerLanguage('ruby', ruby);
hljs.registerLanguage('rb', ruby);
hljs.registerLanguage('swift', swift);
hljs.registerLanguage('kotlin', kotlin);
hljs.registerLanguage('kt', kotlin);
hljs.registerLanguage('r', r);
hljs.registerLanguage('perl', perl);
hljs.registerLanguage('lua', lua);
hljs.registerLanguage('makefile', makefile);
hljs.registerLanguage('properties', properties);
hljs.registerLanguage('graphql', graphql);
function htmlStrip(html: string): string {
return html.replace(/<\/?[^>]+(>|$)/g, "");
}
const sanitize = (str: any): string => {
if (typeof str !== 'string') return '';
return str.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(//g, '>');
};
const RegexpEscape = (s: string): string => {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
const extract = (template: any, area: any): string => {
const start: string = `[//]: #@${area}`;
const end: string = `[//]: #@${area}:end`;
const startIndex: number = template.indexOf(start);
const endIndex: number = template.indexOf(end);
if (startIndex === -1 || endIndex === -1) {
return template;
}
let result: string = template.substring(0, startIndex + start.length);
result += template.substring(endIndex + end.length);
return result;
};
interface RenderState {
currentRepo: string;
settings: any;
currentRepoPath: string;
locationOrigin: string;
locationPathname: string;
locationHref: string;
locationHostname: string;
codeIndex: number;
}
const renderImages = (tokens: any[]): string => {
return tokens.map((childToken: any) => {
if (childToken.type === 'image') {
const imgTitle = childToken.title ? ` title="${sanitize(childToken.title)}"` : '';
const altText = sanitize(childToken.text);
if (childToken.href === 'https://snapcraft.io/static/images/badges/en/snap-store-black.svg') {
childToken.href = 'https://cdn.corifeus.com/assets/svg/snap-store-black.svg'
}
return ``;
}
return '';
}).join('');
};
function createRenderer(state: RenderState) {
const renderer = new marked.Renderer();
renderer.heading = (token: any): string => {
const text: string = token.text;
const level: number = token.depth;
const raw: string = token.raw;
const ref: string = kebabCase(htmlStrip(raw)).replace(/[^\x00-\xFF]/g, "");
const id: string = `${ref}-parent`;
let navClick: string = '';
if (!IsBot()) {
navClick = `onclick="return window.coryAppWebPagesNavigateHash('${sanitize(id)}');"`;
}
return `
`;
}
return `
${sanitize(title)}
${sanitize(text)}
`;
};
renderer.link = (token: any): string => {
const title: any = token.title;
let href: any = token.href;
const text: any = token.text;
let a: string;
let tooltip: string = '';
if (title !== null && title !== undefined) {
tooltip = `tooltip="${sanitize(title)}"`;
}
let fixed: boolean = false;
let path: any;
const testHref: any = href.toLowerCase();
const fixedUrl = (): void => {
try {
const url = new URL(href);
href = url.pathname;
path = `${href}`;
fixed = true;
} catch (error: any) {
console.error('Invalid URL:', href, error);
}
};
if (typeof testHref === 'string' && (testHref.startsWith('https://') || testHref.startsWith('http://'))) {
try {
const testUrl = new URL(testHref);
for (let defaultDomain of state.settings.pages.defaultDomain) {
if (testUrl.hostname === defaultDomain) {
fixedUrl();
break;
}
}
} catch (error: any) {
console.error('Invalid testHref URL:', testHref, error);
}
} else if (testHref.includes('localhost:8080')) {
fixedUrl();
}
const hasImage: boolean = token.tokens && token.tokens.some((childToken: any) => childToken.type === 'image');
const isExternal: boolean = (
!href.startsWith(state.locationOrigin) &&
(href.startsWith('https://') || href.startsWith('http://') || href.startsWith('mailto:'))
);
if (isExternal) {
if (href.endsWith('#cory-non-external')) {
if (hasImage) {
a = `${renderImages(token.tokens)}`;
} else {
a = `${marked.parseInline(text)}`;
}
} else {
if (hasImage) {
a = `${renderImages(token.tokens)}`;
} else {
a = `${marked.parseInline(text)} `;
}
}
} else {
if (!fixed) {
if (href.endsWith('.md')) {
href = href.substr(0, href.length - 3) + '.html';
}
if (href.startsWith(state.locationOrigin)) {
path = `/${href.substring(state.locationOrigin.length + 1)}`;
} else if (href.startsWith('./')) {
let base = state.locationHref;
if (!base.includes('.')) {
base = state.locationHref + '/';
}
try {
path = `${new URL(href, base).pathname}`;
} catch (error: any) {
console.error('Invalid relative URL:', href, error);
path = href;
}
} else {
path = `/${sanitize(state.currentRepo)}/${sanitize(href)}`;
}
}
const navClick: string = !IsBot() ? `onclick="window.coryAppWebPagesNavigate('${sanitize(path)}'); return false;"` : '';
if (hasImage) {
a = `${renderImages(token.tokens)}`;
} else {
a = `${marked.parseInline(text)}`;
}
}
return a;
};
renderer.code = (token: any): string => {
const code: string = token.text;
let language: any = token.lang;
if (!language) {
language = 'text';
}
language = language.toLowerCase();
if (hljs.getLanguage(language) === undefined && language !== 'text' && language !== 'txt') {
console.warn(`Missing highlight.js language: ${language} | Repo: ${state.currentRepo} | Path: ${state.currentRepoPath}`);
}
const validLang: boolean = !!(language && hljs.getLanguage(language));
const highlighted: string = validLang ? hljs.highlight(code, {
language: language as string
}).value : sanitize(code);
state.codeIndex++;
return `
${highlighted}${sanitize(code)}`;
};
renderer.table = function (token: any): string {
const header = token.header.map((cell: any) => this.tablecell(cell)).join('');
const rows = token.rows
.map((row: any[]) => this.tablerow({ text: row.map((cell: any) => this.tablecell(cell)).join('') }))
.join('');
return `