Skip to content

Commit e9aab3f

Browse files
authored
Diagnostics (#427)
* flatten * tidy up * start fixing diagnostics * move styles into .css * attempt to wire up errors. doesnt work yet because we need to compile files individually * fix some css * fix * tidy * comment out for now * fix type errors * quick fix * goddammit
1 parent 03febfc commit e9aab3f

File tree

9 files changed

+319
-286
lines changed

9 files changed

+319
-286
lines changed

apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import type { Adapter, Warning } from '$lib/tutorial';
2-
import type { File, Item } from 'editor';
31
import Bundler from '@sveltejs/repl/bundler';
42
// @ts-ignore package exports don't have types
53
import * as yootils from 'yootils';
4+
import type { Adapter } from '$lib/tutorial';
5+
import type { File, Item } from 'editor';
6+
import type { CompileError, Warning } from 'svelte/compiler';
67

78
/** Rollup bundler singleton */
89
let bundler: Bundler;
910

1011
export const state = new (class RollupState {
1112
progress = $state.raw({ value: 0, text: 'initialising' });
1213
bundle = $state.raw<any>(null);
14+
errors = $state.raw<Record<string, CompileError | null>>();
1315
warnings = $state.raw<Record<string, Warning[]>>({});
1416
})();
1517

@@ -51,15 +53,23 @@ export async function create(): Promise<Adapter> {
5153
type: f.name.split('.').pop() ?? 'svelte'
5254
}))
5355
);
56+
5457
state.bundle = result;
5558

56-
const _warnings: Record<string, any> = {};
59+
// TODO this approach is insufficient — we need to get diagnostics for
60+
// individual files, not just the bundle as a whole
61+
state.errors = {};
62+
state.warnings = {};
63+
64+
if (result.error) {
65+
const file = '/src/lib/' + result.error.filename;
66+
state.errors[file] = result.error;
67+
}
68+
5769
for (const warning of result?.warnings ?? []) {
5870
const file = '/src/lib/' + warning.filename;
59-
_warnings[file] = _warnings[file] || [];
60-
_warnings[file].push(warning);
71+
(state.warnings[file] ??= []).push(warning);
6172
}
62-
state.warnings = _warnings;
6373
}
6474

6575
const q = yootils.queue(1);

apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import * as yootils from 'yootils';
66
import { get_depth } from '../../../utils/path.js';
77
import { escape_html } from '../../../utils/escape.js';
88
import { ready } from '../common/index.js';
9-
import type { Adapter, Warning } from '$lib/tutorial';
9+
import type { Adapter } from '$lib/tutorial';
1010
import type { Item, File } from 'editor';
11+
import type { CompileError, Warning } from 'svelte/compiler';
1112

1213
const converter = new AnsiToHtml({
1314
fg: 'var(--sk-text-3)'
@@ -21,6 +22,7 @@ export const state = new (class WCState {
2122
base = $state.raw<string | null>(null);
2223
error = $state.raw<Error | null>(null);
2324
logs = $state.raw<string[]>([]);
25+
errors = $state.raw<Record<string, CompileError | null>>({});
2426
warnings = $state.raw<Record<string, Warning[]>>({});
2527
})();
2628

@@ -47,7 +49,7 @@ export async function create(): Promise<Adapter> {
4749
}
4850
});
4951

50-
let warnings: Record<string, import('$lib/tutorial').Warning[]> = {};
52+
let warnings: Record<string, import('svelte/compiler').Warning[]> = {};
5153
let timeout: any;
5254

5355
function schedule_to_update_warning(msec: number) {
@@ -63,13 +65,20 @@ export async function create(): Promise<Adapter> {
6365
state.logs = [];
6466
} else if (chunk?.startsWith('svelte:warnings:')) {
6567
const warn: Warning = JSON.parse(chunk.slice(16));
66-
const filename = warn.filename.startsWith('/') ? warn.filename : '/' + warn.filename;
68+
const filename = (warn.filename!.startsWith('/') ? warn.filename : '/' + warn.filename)!;
6769
const current = warnings[filename];
6870

6971
if (!current) {
7072
warnings[filename] = [warn];
7173
// the exact same warning may be given multiple times in a row
72-
} else if (!current.some((s) => s.code === warn.code && s.pos === warn.pos)) {
74+
} else if (
75+
!current.some(
76+
(s) =>
77+
s.code === warn.code &&
78+
s.position![0] === warn.position![0] &&
79+
s.position![1] === warn.position![1]
80+
)
81+
) {
7382
current.push(warn);
7483
}
7584

apps/svelte.dev/src/lib/tutorial/index.d.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,6 @@ export interface EditingConstraints {
6464
remove: Set<string>;
6565
}
6666

67-
// TODO replace with `Warning` from `svelte/compiler`
68-
export interface Warning {
69-
code: string;
70-
start: { line: number; column: number; character: number };
71-
end: { line: number; column: number; character: number };
72-
pos: number;
73-
filename: string;
74-
frame: string;
75-
message: string;
76-
}
77-
7867
export interface MenuItem {
7968
icon: string;
8069
label: string;

apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@
316316
<section slot="b" class="editor-container">
317317
<Editor
318318
bind:this={editor}
319+
errors={adapter.adapter_state.errors}
319320
warnings={adapter.adapter_state.warnings}
320321
{workspace}
321322
onchange={async (file, contents) => {

apps/svelte.dev/src/routes/tutorial/[...slug]/adapter.svelte.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ export const adapter_state = new (class {
3131
status: 'initialising'
3232
}
3333
);
34-
/** Compiler warnings */
34+
35+
/** Diagnostics */
36+
errors = $derived((use_rollup ? rollup_state.errors : wc_state.errors) || {});
3537
warnings = $derived((use_rollup ? rollup_state.warnings : wc_state.warnings) || {});
3638
})();
3739

packages/editor/src/lib/Editor.svelte

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,18 @@
1414
import { autocomplete_for_svelte } from '@sveltejs/site-kit/codemirror';
1515
import type { Diagnostic } from '@codemirror/lint';
1616
import { Workspace, type Item, type File } from './Workspace.svelte.js';
17-
import type { Warning } from 'svelte/compiler';
17+
import type { CompileError, Warning } from 'svelte/compiler';
1818
import './codemirror.css';
1919
2020
interface Props {
21-
warnings: Record<string, Warning[]>; // TODO this should include errors as well
21+
errors: Record<string, CompileError | null>;
22+
warnings: Record<string, Warning[]>;
2223
workspace: Workspace;
2324
onchange: (file: File, contents: string) => void;
2425
autocomplete_filter?: (file: File) => boolean;
2526
}
2627
27-
let { warnings, workspace, onchange, autocomplete_filter = () => true }: Props = $props();
28+
let { errors, warnings, workspace, onchange, autocomplete_filter = () => true }: Props = $props();
2829
2930
let container: HTMLDivElement;
3031
@@ -166,25 +167,51 @@
166167
});
167168
168169
$effect(() => {
169-
if (editor_view) {
170-
if (workspace.selected_name) {
171-
const current_warnings = warnings[workspace.selected_name] || [];
172-
const diagnostics = current_warnings.map((warning) => {
173-
/** @type {import('@codemirror/lint').Diagnostic} */
174-
const diagnostic: Diagnostic = {
175-
from: warning.start!.character,
176-
to: warning.end!.character,
177-
severity: 'warning',
178-
message: warning.message
179-
};
180-
181-
return diagnostic;
182-
});
170+
if (!editor_view || !workspace.selected_name) return;
171+
172+
const diagnostics: Diagnostic[] = [];
173+
174+
const error = null; // TODO should be `errors[workspace.selected_name]` but it's currently a Rollup plugin error...
175+
const current_warnings = warnings[workspace.selected_name] || [];
176+
177+
if (error) {
178+
// diagnostics.push({
179+
// severity: 'error',
180+
// from: error.position![0],
181+
// to: error.position![1],
182+
// message: error.message,
183+
// renderMessage: () => {
184+
// // TODO expose error codes, so we can link to docs in future
185+
// const span = document.createElement('span');
186+
// span.innerHTML = `${error.message
187+
// .replace(/&/g, '&amp;')
188+
// .replace(/</g, '&lt;')
189+
// .replace(/`(.+?)`/g, `<code>$1</code>`)}`;
190+
// return span;
191+
// }
192+
// });
193+
}
183194
184-
const transaction = setDiagnostics(editor_view.state, diagnostics);
185-
editor_view.dispatch(transaction);
186-
}
195+
for (const warning of current_warnings) {
196+
diagnostics.push({
197+
severity: 'warning',
198+
from: warning.start!.character,
199+
to: warning.end!.character,
200+
message: warning.message,
201+
renderMessage: () => {
202+
const span = document.createElement('span');
203+
span.innerHTML = `${warning.message
204+
.replace(/&/g, '&amp;')
205+
.replace(/</g, '&lt;')
206+
.replace(/`(.+?)`/g, `<code>$1</code>`)} <strong>(${warning.code})</strong>`;
207+
208+
return span;
209+
}
210+
});
187211
}
212+
213+
const transaction = setDiagnostics(editor_view.state, diagnostics);
214+
editor_view.dispatch(transaction);
188215
});
189216
</script>
190217

0 commit comments

Comments
 (0)