chore: Cleanup, fix theme

This commit is contained in:
Sidharth Vinod
2022-11-15 19:51:00 +05:30
parent 9eb39f31a0
commit 68b589a33a
10 changed files with 111 additions and 73 deletions

View File

@@ -5,7 +5,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
// 'plugin:@typescript-eslint/strict',
'plugin:@typescript-eslint/strict',
'prettier'
],
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
@@ -21,8 +21,17 @@ module.exports = {
'tsconfig.json'
],
overrides: [
{ files: ['*.svelte'], processor: 'svelte3/svelte3' }
// { files: ['*.ts'], extends: ['plugin:@typescript-eslint/recommended-requiring-type-checking'] }
{ files: ['*.svelte'], processor: 'svelte3/svelte3' },
{
files: ['*.ts'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier'
]
}
],
settings: {
'svelte3/typescript': () => require('typescript')

View File

@@ -21,9 +21,9 @@ describe('history', () => {
type: 'manual'
});
const [manualEntry]: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
const [manualEntry] = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualEntry.time).toBe(12345);
expect(manualEntry.type).toBe('manual');
@@ -36,7 +36,9 @@ describe('history', () => {
type: 'auto'
});
const [autoEntry]: HistoryEntry[] = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
const [autoEntry] = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(autoEntry.time).toBe(54321);
expect(autoEntry.type).toBe('auto');
@@ -101,19 +103,23 @@ describe('history migration', () => {
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
let manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
injectHistoryIDs();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});

View File

@@ -24,10 +24,6 @@
'💎 luxury',
'🧛‍♂️ dracula'
];
function checkTheme(theme: string): boolean {
return $themeStore.theme !== undefined && theme.includes($themeStore.theme);
}
</script>
<div class="hidden lg:block dropdown">
@@ -56,7 +52,7 @@
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<ul tabindex="0" class="p-4 menu compact">
{#each themes as theme}
<li class:bordered={checkTheme(theme)}>
<li class:bordered={$themeStore.theme !== undefined && theme.includes($themeStore.theme)}>
<span
class="btn btn-ghost justify-start"
on:click={() => setTheme(theme)}

9
src/lib/types.d.ts vendored
View File

@@ -85,3 +85,12 @@ export type EditorMode = 'code' | 'config';
export type Loader = (url: string) => Promise<State>;
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export interface ErrorHash {
loc: {
first_line: number;
last_line: number;
first_column: number;
last_column: number;
};
}

View File

@@ -42,15 +42,19 @@ interface GistResponse {
}
const getGistData = async (gistURL: string): Promise<GistData> => {
const path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { html_url, files, history }: GistResponse = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
if (isValidGist(files)) {
const code = await getFileContent(files[codeFileName]);
let config: string;
let config = '{}';
if (configFileName in files) {
config = await getFileContent(files[configFileName]);
}
@@ -65,7 +69,7 @@ const getGistData = async (gistURL: string): Promise<GistData> => {
version: currentItem.version.slice(-7)
};
} else {
throw 'Invalid gist provided';
throw new Error('Invalid gist provided');
}
};
@@ -85,35 +89,38 @@ const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State =>
};
export const loadGistData = async (gistURL: string): Promise<State> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { history }: GistResponse = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
const gistHistory: GistData[] = [];
for (const entry of history) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const data: GistData = await getGistData(entry.url).catch(() => undefined);
data && gistHistory.push(data);
}
if (gistHistory.length === 0) {
throw 'Invalid gist provided';
}
gistHistory.reverse();
const state = getStateFromGist(gistHistory.slice(-1).pop(), gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
} catch (err) {
console.error(err);
const path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { history }: GistResponse = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
const gistHistory: GistData[] = [];
for (const entry of history) {
const data: GistData | undefined = await getGistData(entry.url).catch(() => undefined);
data && gistHistory.push(data);
}
if (gistHistory.length === 0) {
throw new Error('Invalid gist provided');
}
gistHistory.reverse();
const entry = gistHistory.slice(-1).pop();
if (!entry) {
throw new Error('Invalid gist provided');
}
const state = getStateFromGist(entry, gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
};

View File

@@ -16,18 +16,22 @@ describe('migrations', () => {
it('should migrate from v0 to v1', async () => {
const { applyMigrations } = await import('./migrations');
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
applyMigrations();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});

View File

@@ -153,7 +153,7 @@ export function persist<T>(
store.set(initialValue);
}
if ((storage as SelfUpdateStorageInterface<T>).addListener) {
if ('addListener' in storage) {
(storage as SelfUpdateStorageInterface<T>).addListener(key, (newValue) => {
store.set(newValue);
});

View File

@@ -36,7 +36,7 @@ const serdes: { [key in SerdeType]: Serde } = {
};
export const serializeState = (state: State, serde: SerdeType = 'pako'): string => {
if (serdes[serde] === undefined) {
if (!(serde in serdes)) {
throw new Error(`Unknown serde type: ${serde}`);
}
const json = JSON.stringify(state);

View File

@@ -5,7 +5,7 @@ import { serializeState, deserializeState } from './serde';
import { cmdKey, errorDebug, AsyncQueue } from './util';
import { parse } from './mermaid';
import type { MarkerData, State, ValidatedState } from '$lib/types';
import type { ErrorHash, MarkerData, State, ValidatedState } from '$lib/types';
import type { MermaidConfig } from 'mermaid';
export const defaultState: State = {
@@ -52,7 +52,7 @@ export const currentState: ValidatedState = (() => {
};
})();
let q: AsyncQueue<State>;
let q: AsyncQueue<State> | undefined;
const processState = async (state: State) => {
const processed: ValidatedState = {
@@ -71,14 +71,19 @@ const processState = async (state: State) => {
processed.error = e;
errorDebug();
console.error(e);
if (e.hash) {
if ('hash' in e) {
const {
loc: { first_line, last_line, first_column, last_column }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
} = e.hash as ErrorHash;
try {
const marker: MarkerData = {
severity: 8, // Error
startLineNumber: e.hash.loc.first_line,
startColumn: e.hash.loc.first_column,
endLineNumber: e.hash.loc.last_line,
endColumn: (e.hash.loc.last_column as number) + 1,
startLineNumber: first_line,
startColumn: first_column,
endLineNumber: last_line,
endColumn: last_column + 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
message: e.str
};
processed.errorMarkers = [marker];
@@ -111,8 +116,10 @@ export const loadState = (data: string): void => {
console.log(`Loading '${data}'`);
try {
state = deserializeState(data);
const mermaidConfig: Record<string, string> =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
const mermaidConfig: MermaidConfig =
typeof state.mermaid === 'string'
? (JSON.parse(state.mermaid) as MermaidConfig)
: state.mermaid;
if (
mermaidConfig.securityLevel &&
mermaidConfig.securityLevel !== 'strict' &&
@@ -182,7 +189,7 @@ export const updateConfig = (config: string): void => {
export const toggleDarkTheme = (dark: boolean): void => {
inputStateStore.update((state) => {
const config: MermaidConfig = JSON.parse(state.mermaid);
const config = JSON.parse(state.mermaid) as MermaidConfig;
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
config.theme = dark ? 'dark' : 'default';
}
@@ -196,7 +203,7 @@ export const initURLSubscription = (): void => {
stateStore.subscribe(({ serialized }) => {
clearTimeout(urlDebounce);
urlDebounce = window.setTimeout(() => {
history.replaceState(undefined, undefined, `#${serialized}`);
history.replaceState(undefined, '', `#${serialized}`);
}, 250);
});
};

View File

@@ -23,7 +23,7 @@ export const initHandler = async (): Promise<void> => {
syncDiagram();
initURLSubscription();
await initAnalytics();
await analytics.page();
await analytics?.page();
};
export const isMac = navigator.platform.toUpperCase().includes('MAC');