Skip to content

Commit 9ca220f

Browse files
authored
Move editor into separate package (#420)
* move editor into separate package * move types over * try this * gah * fix * oops
1 parent 3311eda commit 9ca220f

32 files changed

+497
-163
lines changed

apps/svelte.dev/package.json

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,11 @@
1616
"sync-docs": "tsx scripts/sync-docs/index.ts"
1717
},
1818
"dependencies": {
19-
"@codemirror/autocomplete": "^6.9.0",
20-
"@codemirror/commands": "^6.2.5",
21-
"@codemirror/lang-css": "^6.2.1",
22-
"@codemirror/lang-html": "^6.4.6",
23-
"@codemirror/lang-javascript": "^6.2.1",
24-
"@codemirror/language": "^6.9.0",
25-
"@codemirror/lint": "^6.4.1",
26-
"@codemirror/search": "^6.5.2",
27-
"@codemirror/state": "^6.2.1",
28-
"@codemirror/view": "^6.17.1",
2919
"@jridgewell/sourcemap-codec": "^1.4.15",
3020
"@lezer/common": "^1.0.4",
3121
"@lezer/highlight": "^1.1.6",
3222
"@lezer/javascript": "^1.4.7",
3323
"@lezer/lr": "^1.3.10",
34-
"@replit/codemirror-lang-svelte": "^6.0.0",
35-
"@replit/codemirror-vim": "^6.0.14",
3624
"@rich_harris/svelte-split-pane": "^1.1.3",
3725
"@shikijs/twoslash": "^1.22.0",
3826
"@sveltejs/amp": "^1.1.3",
@@ -46,10 +34,10 @@
4634
"adm-zip": "^0.5.10",
4735
"ansi-to-html": "^0.7.2",
4836
"base64-js": "^1.5.1",
49-
"codemirror": "^6.0.1",
5037
"cookie": "^0.7.0",
5138
"d3-geo": "^3.1.0",
5239
"d3-geo-projection": "^4.0.0",
40+
"editor": "workspace:*",
5341
"flexsearch": "^0.7.43",
5442
"flru": "^1.0.2",
5543
"port-authority": "^2.0.1",

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { Adapter, FileStub, Stub, Warning } from '$lib/tutorial';
1+
import type { Adapter, Warning } from '$lib/tutorial';
2+
import type { File, Item } from 'editor';
23
import Bundler from '@sveltejs/repl/bundler';
34
// @ts-ignore package exports don't have types
45
import * as yootils from 'yootils';
@@ -43,7 +44,7 @@ export async function create(): Promise<Adapter> {
4344
const result = await bundler.bundle(
4445
[...current_stubs.values()]
4546
// TODO we can probably remove all the SvelteKit specific stuff from the tutorial content once this settles down
46-
.filter((f): f is FileStub => f.name.startsWith('/src/lib/') && f.type === 'file')
47+
.filter((f): f is File => f.name.startsWith('/src/lib/') && f.type === 'file')
4748
.map((f) => ({
4849
name: f.name.slice(9).split('.').slice(0, -1).join('.'),
4950
source: f.contents,
@@ -85,7 +86,7 @@ export async function create(): Promise<Adapter> {
8586
};
8687
}
8788

88-
function stubs_to_map(files: Stub[], map = new Map<string, Stub>()) {
89+
function stubs_to_map(files: Item[], map = new Map<string, Item>()) {
8990
for (const file of files) {
9091
map.set(file.name, file);
9192
}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ 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, FileStub, Stub, Warning } from '$lib/tutorial';
9+
import type { Adapter, Warning } from '$lib/tutorial';
10+
import type { Item, File } from 'editor';
1011

1112
const converter = new AnsiToHtml({
1213
fg: 'var(--sk-text-3)'
@@ -27,7 +28,7 @@ export async function create(): Promise<Adapter> {
2728
state.progress = { value: 0, text: 'loading files' };
2829

2930
const q = yootils.queue(1);
30-
const q_per_file = new Map<string, Array<FileStub>>();
31+
const q_per_file = new Map<string, Array<File>>();
3132

3233
/** Paths and contents of the currently loaded file stubs */
3334
let current_stubs = stubs_to_map([]);
@@ -138,7 +139,7 @@ export async function create(): Promise<Adapter> {
138139
return {
139140
reset: (stubs) => {
140141
return q.add(async () => {
141-
const to_write: Stub[] = [];
142+
const to_write: Item[] = [];
142143

143144
const force_delete = [];
144145

@@ -151,7 +152,7 @@ export async function create(): Promise<Adapter> {
151152
continue;
152153
}
153154

154-
const current = current_stubs.get(stub.name) as FileStub;
155+
const current = current_stubs.get(stub.name) as File;
155156

156157
if (current?.contents !== stub.contents) {
157158
to_write.push(stub);
@@ -272,7 +273,7 @@ export async function create(): Promise<Adapter> {
272273
};
273274
}
274275

275-
function is_config(file: Stub) {
276+
function is_config(file: Item) {
276277
return file.type === 'file' && is_config_path(file.name);
277278
}
278279

@@ -299,7 +300,7 @@ function wait_for_restart_vite() {
299300
});
300301
}
301302

302-
function convert_stubs_to_tree(stubs: Stub[], depth = 1) {
303+
function convert_stubs_to_tree(stubs: Item[], depth = 1) {
303304
const tree: FileSystemTree = {};
304305

305306
for (const stub of stubs) {
@@ -319,7 +320,7 @@ function convert_stubs_to_tree(stubs: Stub[], depth = 1) {
319320
return tree;
320321
}
321322

322-
function to_file(file: FileStub) {
323+
function to_file(file: File) {
323324
// special case
324325
if (file.name === '/src/app.html' || file.name === '/src/error.html') {
325326
const contents = file.contents + '<script type="module" src="/src/__client.js"></script>';
@@ -336,7 +337,7 @@ function to_file(file: FileStub) {
336337
};
337338
}
338339

339-
function stubs_to_map(files: Stub[], map = new Map<string, Stub>()) {
340+
function stubs_to_map(files: Item[], map = new Map<string, Item>()) {
340341
for (const file of files) {
341342
map.set(file.name, file);
342343
}

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

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,10 @@
11
import type { Writable } from 'svelte/store';
2-
3-
export interface FileStub {
4-
type: 'file';
5-
name: string;
6-
basename: string;
7-
contents: string;
8-
text: boolean;
9-
}
10-
11-
export interface DirectoryStub {
12-
type: 'directory';
13-
name: string;
14-
basename: string;
15-
}
16-
17-
export type Stub = FileStub | DirectoryStub;
2+
import type { File, Directory, Item } from 'editor';
183

194
export interface Adapter {
205
/** Returns `false` if the reset was in such a way that a reload of the iframe isn't needed */
21-
reset(files: Array<Stub>): Promise<boolean>;
22-
update(file: FileStub): Promise<boolean>;
6+
reset(files: Array<Item>): Promise<boolean>;
7+
update(file: File): Promise<boolean>;
238
}
249

2510
export interface Scope {

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

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
<script lang="ts">
22
import { afterNavigate, beforeNavigate } from '$app/navigation';
33
import { SplitPane } from '@rich_harris/svelte-split-pane';
4-
import { adapter_state, reset } from './adapter.svelte';
5-
import Editor from './Editor.svelte';
4+
import * as adapter from './adapter.svelte';
5+
import { Editor, Workspace } from 'editor';
66
import ContextMenu from './filetree/ContextMenu.svelte';
77
import Filetree from './filetree/Filetree.svelte';
88
import ImageViewer from './ImageViewer.svelte';
99
import Output from './Output.svelte';
1010
import { ScreenToggle } from '@sveltejs/site-kit/components';
1111
import Sidebar from './Sidebar.svelte';
12-
import { solution, Workspace } from './state.svelte';
12+
import { solution } from './state.svelte';
1313
import { create_directories } from './utils';
1414
import { needs_webcontainers, text_files } from './shared';
1515
import OutputRollup from './OutputRollup.svelte';
1616
import { page } from '$app/stores';
1717
import Controls from './Controls.svelte';
18-
import type { Stub } from '$lib/tutorial';
18+
import type { Item } from 'editor';
1919
import type { Snapshot } from './$types.js';
2020
2121
interface Props {
@@ -24,18 +24,16 @@
2424
2525
let { data }: Props = $props();
2626
27-
const workspace = new Workspace();
28-
2927
let path = data.exercise.path;
3028
let show_editor = $state(false);
3129
let show_filetree = $state(false);
3230
let paused = $state(false);
3331
let w = $state(1000);
3432
35-
let previous_files: Stub[] = [];
33+
let previous_files: Item[] = [];
3634
37-
function create_files(map: Record<string, string>): Record<string, Stub> {
38-
const files: Record<string, Stub> = {};
35+
function create_files(map: Record<string, string>): Record<string, Item> {
36+
const files: Record<string, Item> = {};
3937
4038
const to_delete: string[] = [];
4139
@@ -98,13 +96,13 @@
9896
const will_delete = previous_files.some((file) => !(file.name in a));
9997
10098
if (data.exercise.path !== path || will_delete) paused = true;
101-
await reset(workspace.files);
99+
await adapter.reset(workspace.files);
102100
103101
path = data.exercise.path;
104102
paused = false;
105103
});
106104
107-
function is_completed(files: Stub[], solution: Record<string, Stub> | null) {
105+
function is_completed(files: Item[], solution: Record<string, Item> | null) {
108106
if (!solution) return true;
109107
110108
for (const file of files) {
@@ -185,6 +183,19 @@
185183
let a = $derived(create_files(data.exercise.a));
186184
let b = $derived(create_files({ ...data.exercise.a, ...data.exercise.b }));
187185
186+
const workspace = new Workspace({
187+
files: Object.values(a),
188+
selected_name: data.exercise.focus,
189+
onupdate(file) {
190+
adapter.update(file);
191+
},
192+
onreset(items) {
193+
adapter.reset(items);
194+
}
195+
});
196+
197+
solution.set(b);
198+
188199
// for the things we can't do with media queries
189200
let mobile = $derived(w < 800);
190201
@@ -197,7 +208,7 @@
197208
});
198209
199210
$effect(() => {
200-
workspace.selected_name = data.exercise.focus;
211+
workspace.selected_name = data.exercise.focus; // TODO this probably belongs in afterNavigate
201212
});
202213
203214
let completed = $derived(is_completed(workspace.files, b));
@@ -289,7 +300,11 @@
289300
</section>
290301

291302
<section slot="b" class="editor-container">
292-
<Editor exercise={data.exercise} warnings={adapter_state.warnings} {workspace} />
303+
<Editor
304+
exercise={data.exercise}
305+
warnings={adapter.adapter_state.warnings}
306+
{workspace}
307+
/>
293308
<ImageViewer selected={workspace.selected_file} />
294309

295310
{#if mobile && show_filetree}

apps/svelte.dev/src/routes/tutorial/[...slug]/ImageViewer.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
/** @type {import('$lib/tutorial').FileStub | null} */
2+
/** @type {import('editor').File | null} */
33
export let selected;
44
55
const image_types = new Map([

apps/svelte.dev/src/routes/tutorial/[...slug]/Output.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import Loading from './Loading.svelte';
1212
import { adapter_state, subscribe, reset } from './adapter.svelte';
1313
import type { Exercise } from '$lib/tutorial';
14-
import type { Workspace } from './state.svelte';
14+
import type { Workspace } from 'editor';
1515
1616
interface Props {
1717
exercise: Exercise;

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { browser } from '$app/environment';
22
import { page } from '$app/stores';
33
import type { state as WCState } from '$lib/tutorial/adapters/webcontainer/index.svelte';
44
import type { state as RollupState } from '$lib/tutorial/adapters/rollup/index.svelte';
5-
import type { Adapter, FileStub, Stub } from '$lib/tutorial';
5+
import type { Adapter } from '$lib/tutorial';
6+
import type { File, Item } from 'editor';
67
import { needs_webcontainers } from './shared';
78

89
let initial_load = true;
@@ -111,7 +112,7 @@ function publish(event: EventName) {
111112
subscriptions.get(event)?.forEach((fn) => fn());
112113
}
113114

114-
export async function reset(files: Stub[]) {
115+
export async function reset(files: Item[]) {
115116
try {
116117
const adapter = await get_adapter();
117118
const should_reload = await adapter.reset(files);
@@ -126,7 +127,7 @@ export async function reset(files: Stub[]) {
126127
}
127128
}
128129

129-
export async function update(file: FileStub) {
130+
export async function update(file: File) {
130131
const adapter = await get_adapter();
131132
const should_reload = await adapter.update(file);
132133

apps/svelte.dev/src/routes/tutorial/[...slug]/filetree/File.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import Item from './Item.svelte';
44
import file_icon from '$lib/icons/file.svg';
55
import { solution } from '../state.svelte';
6-
import type { FileStub, MenuItem } from '$lib/tutorial';
6+
import type { MenuItem } from '$lib/tutorial';
7+
import type { File } from 'editor';
78
89
interface Props {
9-
file: FileStub;
10+
file: File;
1011
depth: number;
1112
}
1213

apps/svelte.dev/src/routes/tutorial/[...slug]/filetree/Filetree.svelte

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import Folder from './Folder.svelte';
55
import * as context from './context.js';
66
import Modal from '$lib/components/Modal.svelte';
7-
import { solution, Workspace } from '../state.svelte';
7+
import { solution } from '../state.svelte';
88
import { create_directories } from '../utils';
99
import { afterNavigate } from '$app/navigation';
10-
import type { Exercise, Stub } from '$lib/tutorial';
10+
import type { Exercise } from '$lib/tutorial';
11+
import type { Workspace, Item } from 'editor';
1112
1213
interface Props {
1314
exercise: Exercise;
@@ -55,7 +56,7 @@
5556
5657
const basename = name.split('/').pop()!;
5758
58-
const file: Stub =
59+
const file: Item =
5960
type === 'file'
6061
? { type, name, basename, text: true, contents: '' }
6162
: { type, name, basename };
@@ -142,7 +143,7 @@
142143
workspace
143144
});
144145
145-
function is_deleted(file: Stub) {
146+
function is_deleted(file: Item) {
146147
if (file.type === 'directory') return `${file.name}/__delete` in exercise.a;
147148
if (file.text) return file.contents.startsWith('__delete');
148149

0 commit comments

Comments
 (0)