chore: Cleanup potentially

problematic code.
This commit is contained in:
Sidharth Vinod
2022-11-15 18:48:16 +05:30
parent 60c352a849
commit 85ad89b915
16 changed files with 104 additions and 73 deletions
+5 -1
View File
@@ -5,6 +5,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier'
],
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
@@ -19,7 +20,10 @@ module.exports = {
'package.json',
'tsconfig.json'
],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
overrides: [
{ files: ['*.svelte'], processor: 'svelte3/svelte3' }
// { files: ['*.ts'], extends: ['plugin:@typescript-eslint/recommended-requiring-type-checking'] }
],
settings: {
'svelte3/typescript': () => require('typescript')
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"editor.formatOnSave": true,
"cSpell.words": ["asyncable", "mindmap", "pako", "Serde", "serdes"],
"cSpell.words": ["asyncable", "mindmap", "pako", "Serde", "serdes", "tailwindcss"],
"vitest.commandLine": "yarn test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
+2 -1
View File
@@ -9,7 +9,8 @@ declare global {
namespace jest {
interface Matchers<R = void>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
// eslint-disable-next-line no-undef
extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}
}
+15 -9
View File
@@ -15,8 +15,8 @@
`mermaid-diagram-${moment().format('YYYY-MM-DD-HHmmss')}.${ext}`;
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
svg?.setAttribute('height', `${height}px`);
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
height && svg?.setAttribute('height', `${height}px`);
width && svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
if (!svg) {
svg = getSvgEl();
}
@@ -28,7 +28,10 @@
const exportImage = (event: Event, exporter: Exporter) => {
const canvas: HTMLCanvasElement = document.createElement('canvas');
const svg: HTMLElement = document.querySelector('#container svg');
const svg: HTMLElement | null = document.querySelector('#container svg');
if (!svg) {
throw new Error('svg not found');
}
const box: DOMRect = svg.getBoundingClientRect();
canvas.width = box.width;
canvas.height = box.height;
@@ -43,6 +46,9 @@
}
const context = canvas.getContext('2d');
if (!context) {
throw new Error('context not found');
}
context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
@@ -56,12 +62,12 @@
const getSvgEl = () => {
const svgEl: HTMLElement = document
.querySelector('#container svg')
.querySelector('#container svg')!
.cloneNode(true) as HTMLElement;
svgEl.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
.map((l) => l.href)
.find((h) => h && h.includes('font-awesome'));
.find((h) => h.includes('font-awesome'));
if (fontAwesomeCdnUrl == null) {
return svgEl;
}
@@ -99,10 +105,10 @@
context.drawImage(image, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
try {
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
if (!blob) {
throw new Error('blob is empty');
}
void navigator.clipboard.write([
/* eslint-disable no-undef */
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
new ClipboardItem({
[blob.type]: blob
})
@@ -142,7 +148,7 @@
let gistURL = '';
stateStore.subscribe(({ loader }) => {
if (loader?.type === 'gist') {
// @ts-ignore Gist will have url
// @ts-expect-error Gist will have url
gistURL = loader.config.url;
}
});
+27 -14
View File
@@ -8,9 +8,9 @@
import initEditor from 'monaco-mermaid';
import { logEvent } from '$lib/util/stats';
let divEl: HTMLDivElement = null;
let editor: monaco.editor.IStandaloneCodeEditor;
let Monaco: typeof monaco;
let divEl: HTMLDivElement | undefined = undefined;
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
let Monaco: typeof monaco | undefined;
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
minimap: {
enabled: false
@@ -22,7 +22,7 @@
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
console.log('editor store subscription', { code, mermaid });
if (!editor) {
if (!editor || !Monaco) {
return;
}
@@ -36,12 +36,17 @@
// Update editor mode if it's different
const language = editorMode === 'code' ? 'mermaid' : 'json';
if (editor.getModel().getLanguageId() !== language) {
Monaco?.editor.setModelLanguage(editor.getModel(), language);
const model = editor.getModel();
if (!model) {
console.error("editor model doesn't exist");
return;
}
if (model.getLanguageId() !== language) {
Monaco.editor.setModelLanguage(model, language);
}
// Display/clear errors
Monaco?.editor.setModelMarkers(editor.getModel(), 'mermaid', errorMarkers);
Monaco.editor.setModelMarkers(model, 'mermaid', errorMarkers);
});
themeStore.subscribe(({ isDark }) => {
@@ -62,7 +67,7 @@
// errorDebug();
let i = 0;
while (i++ < 500) {
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
// @ts-expect-error : This is a hack to handle a svelte-kit error when importing monaco.
Monaco = window.monaco;
if (Monaco !== undefined) {
return;
@@ -74,14 +79,20 @@
onMount(async () => {
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
if (!Monaco) {
throw new Error('Monaco failed to load');
}
if (!divEl) {
throw new Error('divEl is undefined');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
initEditor(Monaco);
errorDebug(100);
editor = Monaco.editor.create(divEl, editorOptions);
editor.onDidChangeModelContent(({ isFlush, changes }) => {
const newText = editor.getValue();
const newText = editor?.getValue();
console.log('editor onDidChangeModelContent', { text, newText, isFlush, changes });
if (text === newText || isFlush) {
if (!newText || text === newText || isFlush) {
return;
}
text = newText;
@@ -98,19 +109,21 @@
});
}
});
Monaco?.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
Monaco.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor.layout({
editor!.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
resizeObserver.observe(divEl.parentElement);
if (divEl.parentElement) {
resizeObserver.observe(divEl.parentElement);
}
console.log(`editor mounted`);
return () => {
console.log(`editor disposed`);
editor.dispose();
editor?.dispose();
};
});
</script>
+2 -2
View File
@@ -114,8 +114,8 @@
};
// Adding in this array will add an icon to the preset menu
const newDiagrams: Array<SampleTypes> = ['Mindmap'];
const diagramOrder: Array<SampleTypes> = [
const newDiagrams: SampleTypes[] = ['Mindmap'];
const diagramOrder: SampleTypes[] = [
'Sequence',
'Flow',
'Class',
+1 -1
View File
@@ -25,7 +25,7 @@
'🧛‍♂️ dracula'
];
function checkTheme(theme: string) {
function checkTheme(theme: string): boolean {
return theme.includes($themeStore.theme);
}
</script>
+12 -3
View File
@@ -17,9 +17,12 @@
let hide = false;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let pzoom: typeof panzoom;
let pzoom: typeof panzoom | undefined;
const handlePanZoomChange = () => {
if (!pzoom) {
return;
}
const pan = pzoom.getPan();
const zoom = pzoom.getZoom();
updateCodeStore({ pan, zoom });
@@ -35,6 +38,9 @@
pzoom = undefined;
void Promise.resolve().then(() => {
const graphDiv = document.getElementById('graph-div');
if (!graphDiv) {
return;
}
pzoom = panzoom(graphDiv, {
onPan: handlePanZoomChange,
onZoom: handlePanZoomChange,
@@ -71,7 +77,7 @@
code = state.code;
config = state.mermaid;
panZoomEnabled = state.panZoom;
const scroll = view.parentElement.scrollTop;
const scroll = view.parentElement!.scrollTop;
delete container.dataset.processed;
await renderDiagram(
Object.assign({}, JSON.parse(state.mermaid)) as MermaidConfig,
@@ -83,6 +89,9 @@
container.innerHTML = svgCode;
// console.log(container.innerHTML);
const graphDiv = document.getElementById('graph-div');
if (!graphDiv) {
throw new Error('graph-div not found');
}
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
if (bindFunctions) {
@@ -91,7 +100,7 @@
}
}
);
view.parentElement.scrollTop = scroll;
view.parentElement!.scrollTop = scroll;
error = false;
} else if (manualUpdate) {
manualUpdate = false;
+5 -4
View File
@@ -73,12 +73,13 @@ export type HistoryEntry = { id: string; state: State; time: number; url?: strin
}
);
export interface DocConfig {
[key: string]: {
export type DocConfig = Record<
string,
{
code: string;
config?: string;
};
}
}
>;
export type EditorMode = 'code' | 'config';
+7 -6
View File
@@ -8,10 +8,11 @@ const loaders: Record<string, Loader> = {
export const loadDataFromUrl = async (): Promise<void> => {
const searchParams = new URLSearchParams(window.location.search);
let state: Partial<State> = defaultState;
let code: string, config: string;
let code: string | undefined = undefined;
let config: string | undefined = undefined;
let loaded = false;
const codeURL: string = searchParams.get('code');
const configURL: string = searchParams.get('config');
const codeURL: string | undefined = searchParams.get('code') ?? undefined;
const configURL: string | undefined = searchParams.get('config') ?? undefined;
if (codeURL) {
code = await (await fetch(codeURL)).text();
@@ -23,9 +24,6 @@ export const loadDataFromUrl = async (): Promise<void> => {
config = defaultState.mermaid;
}
if (!code) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
for (const [key, value] of searchParams.entries()) {
if (key in loaders) {
try {
@@ -38,6 +36,9 @@ export const loadDataFromUrl = async (): Promise<void> => {
}
}
} else {
if (!codeURL) {
throw new Error('Code URL is not defined');
}
state = {
code,
mermaid: config,
+1 -1
View File
@@ -7,7 +7,7 @@ interface MigrationState {
version: number;
}
const migrations: { [key: string]: () => void } = {
const migrations: Record<string, () => void> = {
injectHistoryIDs
};
+12 -18
View File
@@ -39,16 +39,16 @@ let noWarnings = false;
/**
* List of storages where the warning have already been displayed.
*/
const alreadyWarnFor: Array<string> = [];
const alreadyWarnFor: string[] = [];
/**
* Add a log to indicate that the requested Storage have not been found.
* @param {string} storageName
*/
const warnStorageNotFound = (storageName: string) => {
const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
const isProduction = typeof process !== 'undefined' && process.env.NODE_ENV === 'production';
if (!noWarnings && alreadyWarnFor.indexOf(storageName) === -1 && !isProduction) {
if (!noWarnings && !alreadyWarnFor.includes(storageName) && !isProduction) {
let message = `Unable to find the ${storageName}. No data will be persisted.`;
if (typeof window === 'undefined') {
message +=
@@ -60,17 +60,8 @@ const warnStorageNotFound = (storageName: string) => {
}
};
const allowedClasses = [];
/**
* Add a class to the allowed list of classes to be serialized
* @param classDef The class to add to the list
*/
export const addSerializableClass = (classDef: () => unknown): void => {
allowedClasses.push(classDef);
};
const serialize = (value: unknown): string => ESSerializer.serialize(value);
const deserialize = (value: string): unknown => {
const deserialize = (value?: string | null): unknown => {
// @TODO: to remove in the next major
if (value === 'undefined') {
return undefined;
@@ -78,7 +69,7 @@ const deserialize = (value: string): unknown => {
if (value !== null && value !== undefined) {
try {
return ESSerializer.deserialize(value, allowedClasses);
return ESSerializer.deserialize(value);
} catch (e) {
// Do nothing
// use the value "as is"
@@ -184,7 +175,7 @@ function getBrowserStorage(
browserStorage: Storage,
listenExternalChanges = false
): SelfUpdateStorageInterface<any> {
const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
const listeners: { key: string; listener: (newValue: any) => void }[] = [];
const listenerFunction = (event: StorageEvent) => {
const eventKey = event.key;
if (event.storageArea === browserStorage) {
@@ -196,12 +187,14 @@ function getBrowserStorage(
}
};
const connect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.addEventListener) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('storage', listenerFunction);
}
};
const disconnect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.removeEventListener) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.removeEventListener) {
window.removeEventListener('storage', listenerFunction);
}
};
@@ -240,7 +233,8 @@ function getBrowserStorage(
* @param listenExternalChanges - Update the store if the localStorage is updated from another page
*/
export function localStorage<T>(listenExternalChanges = false): StorageInterface<T> {
if (typeof window !== 'undefined' && window?.localStorage) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (typeof window !== 'undefined' && window.localStorage) {
return getBrowserStorage(window.localStorage, listenExternalChanges);
}
warnStorageNotFound('window.localStorage');
+1 -1
View File
@@ -111,7 +111,7 @@ export const loadState = (data: string): void => {
console.log(`Loading '${data}'`);
try {
state = deserializeState(data);
const mermaidConfig: { [key: string]: string } =
const mermaidConfig: Record<string, string> =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
if (
mermaidConfig.securityLevel &&
+10 -9
View File
@@ -1,6 +1,6 @@
import { browser } from '$app/environment';
import type { AnalyticsInstance } from 'analytics';
export let analytics: AnalyticsInstance;
export let analytics: AnalyticsInstance | undefined;
export const initAnalytics = async (): Promise<void> => {
if (browser && !analytics) {
@@ -27,7 +27,7 @@ export const initAnalytics = async (): Promise<void> => {
}
};
export const detectType = (text: string): string => {
export const detectType = (text: string): string | undefined => {
const possibleDiagramTypes = [
'classDiagram',
'erDiagram',
@@ -49,7 +49,7 @@ export const detectType = (text: string): string => {
};
export const countLines = (code: string): number => {
return (code.match(/\n/g) || '').length + 1;
return (code.match(/\n/g)?.length ?? 0) + 1;
};
export const saveStatistics = (graph: string): void => {
@@ -80,19 +80,20 @@ const delaysPerEvent = {
themeChange: defaultDelay
};
export type AnalyticsEvent = keyof typeof delaysPerEvent;
const timeouts: Record<string, number> = {};
const timeouts: Map<string, number> = new Map<string, number>();
// manual debounce to reduce the number of events sent to analytics
export const logEvent = (name: AnalyticsEvent, data?: unknown): void => {
if (!analytics) {
return;
}
const key = data ? JSON.stringify({ name, data }) : name;
if (timeouts[key] === undefined) {
if (!timeouts.has(key)) {
void analytics.track(name, data);
} else {
clearTimeout(timeouts[key]);
clearTimeout(timeouts.get(key));
}
timeouts[key] = window.setTimeout(() => {
delete timeouts[key];
}, delaysPerEvent[name]);
timeouts.set(
key,
window.setTimeout(() => timeouts.delete(key), delaysPerEvent[name])
);
};
+2 -2
View File
@@ -23,10 +23,10 @@ export const initHandler = async (): Promise<void> => {
syncDiagram();
initURLSubscription();
await initAnalytics();
await analytics?.page();
await analytics.page();
};
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
export const isMac = navigator.platform.toUpperCase().includes('MAC');
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
let count = 0;
+1
View File
@@ -12,6 +12,7 @@
"compilerOptions": {
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"types": ["vitest/importMeta"]
},
"extends": "./.svelte-kit/tsconfig.json"