fix: Make svelte-check pass and resolve TS6/Svelte 5.56 type errors
Adding the `svelte-check` CI gate surfaced pre-existing type errors and
warnings under develop's TypeScript 6 / Svelte 5.56 bump. Fixes them so
`pnpm check` reports 0 errors / 0 warnings:
- Add `lang="ts"` to Share/Privacy (and a script to PrivacyPolicyLink) so
importers get real declarations instead of implicit `any`.
- Replace the deprecated `monaco.languages.json` with the new top-level
`monaco.json` namespace (proper API, no casts).
- Navbar: use `resolve('/', {})` instead of the deprecated `base`.
- Type the promo `component` as `Component<{ closeBanner: Snippet }>` and
MainMenu's `renderer` as `Snippet<[Omit<MenuItem, 'renderer'>]>`.
- Index-by-string casts in state.ts / Preset / DiagramDocumentationButton.
- Make Actions' clipboard handler accept an optional event.
- toggle-group: expose variant/size via getters to fix the
state_referenced_locally warning.
- Make the History e2e save/delete cases change state via a sample
diagram (deterministic) instead of editor typing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a3a3f8ffbe
commit
64e8822e32
@@ -199,7 +199,10 @@ ${svgString}`);
|
||||
};
|
||||
};
|
||||
|
||||
const onCopyClipboard = async (event: Event) => {
|
||||
const onCopyClipboard = async (event?: Event) => {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
await exportImage(event, clipboardCopy);
|
||||
logEvent('copyClipboard');
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
throw new Error('divEl is undefined');
|
||||
}
|
||||
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
monaco.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
enableSchemaRequest: true,
|
||||
schemas: [
|
||||
|
||||
@@ -97,7 +97,9 @@
|
||||
return { key: '', url: docURLBase };
|
||||
}
|
||||
const key = standardizeDiagramType(diagramType);
|
||||
const docConfig = docMap[key] ?? { code: '' };
|
||||
const docConfig: { code: string; config?: string } = docMap[key as keyof typeof docMap] ?? {
|
||||
code: ''
|
||||
};
|
||||
const url = docURLBase + (docConfig[editorMode] ?? docConfig.code ?? '');
|
||||
return { key, url };
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
sharesData?: boolean;
|
||||
checkDiagramType?: boolean;
|
||||
isSectionEnd?: boolean;
|
||||
renderer: (item: Omit<MenuItem, 'renderer'>) => ReturnType<Snippet>;
|
||||
renderer: Snippet<[Omit<MenuItem, 'renderer'>]>;
|
||||
}
|
||||
|
||||
const menuItems: MenuItem[] = $derived([
|
||||
@@ -89,7 +89,7 @@
|
||||
]);
|
||||
</script>
|
||||
|
||||
{#snippet menuItem(options: MenuItem)}
|
||||
{#snippet menuItem(options: Omit<MenuItem, 'renderer'>)}
|
||||
<a
|
||||
href={options.href}
|
||||
target="_blank"
|
||||
@@ -104,7 +104,7 @@
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
{#snippet mcMenuItem(item: MenuItem)}
|
||||
{#snippet mcMenuItem(item: Omit<MenuItem, 'renderer'>)}
|
||||
<McWrapper
|
||||
side="right"
|
||||
labelPrefix={item.sharesData === false ? 'Opens a new tab in' : undefined}
|
||||
@@ -114,7 +114,7 @@
|
||||
</McWrapper>
|
||||
{/snippet}
|
||||
|
||||
{#snippet darkModeMenuItem(options: MenuItem)}
|
||||
{#snippet darkModeMenuItem(options: Omit<MenuItem, 'renderer'>)}
|
||||
<div
|
||||
class={cn(
|
||||
'flex cursor-pointer items-center justify-between border-b-2 px-3 py-2 hover:bg-muted',
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import { Button } from '$/components/ui/button';
|
||||
import { Separator } from '$/components/ui/separator';
|
||||
import { dismissPromotion, getActivePromotion } from '$lib/util/promos/promo';
|
||||
import type { ComponentProps, Snippet } from 'svelte';
|
||||
import { untrack, type ComponentProps, type Snippet } from 'svelte';
|
||||
import MermaidIcon from '~icons/custom/mermaid';
|
||||
import CloseIcon from '~icons/material-symbols/close-rounded';
|
||||
import GithubIcon from '~icons/mdi/github';
|
||||
@@ -41,7 +41,7 @@
|
||||
}
|
||||
];
|
||||
|
||||
let activePromotion = $state(hidePromotion ? undefined : getActivePromotion());
|
||||
let activePromotion = $state(untrack(() => (hidePromotion ? undefined : getActivePromotion())));
|
||||
|
||||
const trackBannerClick = () => {
|
||||
if (!activePromotion) {
|
||||
@@ -84,7 +84,7 @@
|
||||
<div class="flex flex-1 items-center gap-2">
|
||||
<MainMenu />
|
||||
<MermaidIcon class="size-6" />
|
||||
<a href={resolve('/')} class="whitespace-nowrap text-accent">
|
||||
<a href={resolve('/', {})} class="whitespace-nowrap text-accent">
|
||||
{#if !mobileToggle}
|
||||
Mermaid
|
||||
{/if}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
const samples = { ...getSampleDiagrams(), ...extras } as const;
|
||||
const loadSampleDiagram = (diagramType: string): void => {
|
||||
updateCode(samples[diagramType], {
|
||||
updateCode(samples[diagramType as keyof typeof samples], {
|
||||
resetPanZoom: true,
|
||||
updateDiagram: true
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import ExternalLinkWrapper from '$/components/ExternalLinkWrapper.svelte';
|
||||
import * as Dialog from '$/components/ui/dialog';
|
||||
import { env } from '$/util/env';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { buttonVariants } from '$/components/ui/button';
|
||||
import * as Dialog from '$/components/ui/dialog';
|
||||
import { Separator } from '$/components/ui/separator';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<script lang="ts"></script>
|
||||
|
||||
<div class="text-center">
|
||||
<a
|
||||
href="https://mermaid.ai/privacy-policy"
|
||||
|
||||
@@ -24,8 +24,12 @@
|
||||
}: ToggleGroupPrimitive.RootProps & ToggleVariants = $props();
|
||||
|
||||
setToggleGroupCtx({
|
||||
variant,
|
||||
size
|
||||
get variant() {
|
||||
return variant;
|
||||
},
|
||||
get size() {
|
||||
return size;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { env } from '$lib/util/env';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import type { Component } from 'svelte';
|
||||
import type { Component, Snippet } from 'svelte';
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
import { localStorage, persist } from '../persist';
|
||||
import April2025 from './April2025.svelte';
|
||||
@@ -12,7 +12,7 @@ dayjs.extend(duration);
|
||||
interface Promotion {
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
component: Component;
|
||||
component: Component<{ closeBanner: Snippet }>;
|
||||
hideDurationMs: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ function getUnsafePaths(object: object, unsafeKeys: string[], path: string[] = [
|
||||
}
|
||||
}
|
||||
Object.keys(object).forEach((key) => {
|
||||
const value = object[key] as unknown;
|
||||
const value = (object as Record<string, unknown>)[key];
|
||||
const currentPath = [...path, key];
|
||||
// Prototype pollution check.
|
||||
if (key.startsWith('__')) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import { typeInEditor } from './utils';
|
||||
|
||||
const config = '{\n "theme": "default"\n}';
|
||||
|
||||
@@ -93,8 +92,9 @@ test.describe('History', () => {
|
||||
await expect(page.getByText('State already saved.')).toBeVisible();
|
||||
await expect(page.locator('#historyList li')).toHaveCount(1);
|
||||
|
||||
// A real edit produces a new entry.
|
||||
await typeInEditor(page, ' Z[Extra]', { newline: true });
|
||||
// Loading a different sample changes the state, so it saves as a new entry.
|
||||
await page.getByRole('button', { name: 'Sequence', exact: true }).click();
|
||||
await expect(page.locator('#view')).not.toContainText('Christmas');
|
||||
await page.locator('#saveHistory').click();
|
||||
await expect(page.locator('#historyList li')).toHaveCount(2);
|
||||
});
|
||||
@@ -112,7 +112,8 @@ test.describe('History', () => {
|
||||
test('deletes a single entry and clears all after confirmation', async ({ page }) => {
|
||||
await openHistory(page);
|
||||
await page.locator('#saveHistory').click();
|
||||
await typeInEditor(page, ' Z[Another]', { newline: true });
|
||||
await page.getByRole('button', { name: 'Sequence', exact: true }).click();
|
||||
await expect(page.locator('#view')).not.toContainText('Christmas');
|
||||
await page.locator('#saveHistory').click();
|
||||
await expect(page.locator('#historyList li')).toHaveCount(2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user