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