import * as fs from 'fs-extra';
import { pathExistsSync } from 'fs-extra';
const Entities = require('html-entities').AllHtmlEntities;
const htmlparser2 = require('@joplin/fork-htmlparser2');
const Datauri = require('datauri/sync');
import { CssTypes, parse as cssParse, stringify as cssStringify } from '@adobe/css-tools';
const selfClosingElements = [
'area',
'base',
'basefont',
'br',
'col',
'command',
'embed',
'frame',
'hr',
'img',
'input',
'isindex',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
];
const htmlentities = (s: string): string => {
const output = (new Entities()).encode(s);
return output.replace(/	/ig, '\t');
};
const dataUriEncode = (filePath: string): string => {
try {
const result = Datauri(filePath);
return result.content;
} catch (error) {
// If the file path is invalid, the Datauri will throw an exception.
// Instead, since we can just ignore that particular file.
// Fixes https://github.com/laurent22/joplin/issues/8305
return '';
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const attributesHtml = (attr: any) => {
const output = [];
for (const n in attr) {
if (!attr.hasOwnProperty(n)) continue;
output.push(`${n}="${htmlentities(attr[n])}"`);
}
return output.join(' ');
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const attrValue = (attrs: any, name: string): string => {
if (!attrs[name]) return '';
return attrs[name];
};
const isSelfClosingTag = (tagName: string) => {
return selfClosingElements.includes(tagName.toLowerCase());
};
const processCssContent = (cssBaseDir: string, content: string): string => {
const o = cssParse(content, {
silent: false,
});
for (const rule of o.stylesheet.rules) {
if (rule.type === 'font-face') {
for (const declaration of rule.declarations) {
if (declaration.type === CssTypes.comment) {
continue;
}
if (declaration.property === 'src') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
declaration.value = declaration.value.replace(/url\((.*?)\)/g, (_v: any, url: string) => {
const cssFilePath = `${cssBaseDir}/${url}`;
if (fs.existsSync(cssFilePath)) {
return `url(${dataUriEncode(cssFilePath)})`;
} else {
return `url(${url})`;
}
});
}
}
}
}
return cssStringify(o);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const processLinkTag = (baseDir: string, _name: string, attrs: any): string => {
const href = attrValue(attrs, 'href');
if (!href) return null;
const filePath = `${baseDir}/${href}`;
if (!pathExistsSync(filePath)) return null;
const content = fs.readFileSync(filePath, 'utf8');
return ``;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const processScriptTag = (baseDir: string, _name: string, attrs: any): string => {
const src = attrValue(attrs, 'src');
if (!src) return null;
const scriptFilePath = `${baseDir}/${src}`;
let content = fs.readFileSync(scriptFilePath, 'utf8');
// There's no simple way to insert arbitrary content in ` or `