refactor: Move directive processing before parsing

Directives and fronmatter will be preprocessed
and removed from the text before parsing.
This commit is contained in:
Sidharth Vinod
2023-08-25 12:54:44 +05:30
parent 2dd1415849
commit 276fd7ad84
17 changed files with 384 additions and 329 deletions

View File

@@ -25,7 +25,7 @@ import {
select,
} from 'd3';
import common from './diagrams/common/common.js';
import { configKeys } from './defaultConfig.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
import { log } from './logger.js';
import { detectType } from './diagram-api/detectType.js';
import assignWithDepth from './assignWithDepth.js';
@@ -62,7 +62,6 @@ const d3CurveTypes = {
const directiveWithoutOpen =
/\s*(?:(\w+)(?=:):|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
/**
* Detects the init config object from the text
*
@@ -197,6 +196,10 @@ export const detectDirective = function (
}
};
export const removeDirectives = function (text: string): string {
return text.replace(directiveRegex, '');
};
/**
* Detects whether a substring in present in a given array
*
@@ -842,88 +845,6 @@ export const entityDecode = function (html: string): string {
return unescape(decoder.textContent);
};
/**
* Sanitizes directive objects
*
* @param args - Directive's JSON
*/
export const sanitizeDirective = (args: unknown): void => {
log.debug('sanitizeDirective called with', args);
// Return if not an object
if (typeof args !== 'object' || args == null) {
return;
}
// Sanitize each element if an array
if (Array.isArray(args)) {
args.forEach((arg) => sanitizeDirective(arg));
return;
}
// Sanitize each key if an object
for (const key of Object.keys(args)) {
log.debug('Checking key', key);
if (
key.startsWith('__') ||
key.includes('proto') ||
key.includes('constr') ||
!configKeys.has(key) ||
args[key] == null
) {
log.debug('sanitize deleting key: ', key);
delete args[key];
continue;
}
// Recurse if an object
if (typeof args[key] === 'object') {
log.debug('sanitizing object', key);
sanitizeDirective(args[key]);
continue;
}
const cssMatchers = ['themeCSS', 'fontFamily', 'altFontFamily'];
for (const cssKey of cssMatchers) {
if (key.includes(cssKey)) {
log.debug('sanitizing css option', key);
args[key] = sanitizeCss(args[key]);
}
}
}
if (args.themeVariables) {
for (const k of Object.keys(args.themeVariables)) {
const val = args.themeVariables[k];
if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
args.themeVariables[k] = '';
}
}
}
log.debug('After sanitization', args);
};
export const sanitizeCss = (str: string): string => {
let startCnt = 0;
let endCnt = 0;
for (const element of str) {
if (startCnt < endCnt) {
return '{ /* ERROR: Unbalanced CSS */ }';
}
if (element === '{') {
startCnt++;
} else if (element === '}') {
endCnt++;
}
}
if (startCnt !== endCnt) {
return '{ /* ERROR: Unbalanced CSS */ }';
}
// Todo add more checks here
return str;
};
export interface DetailedError {
str: string;
hash: any;
@@ -1021,8 +942,6 @@ export default {
runFunc,
entityDecode,
initIdGenerator,
sanitizeDirective,
sanitizeCss,
insertTitle,
parseFontSize,
};