diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 9acc6de3f..cb004fcf5 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [18.x] + node-version: [20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/content/tutorial/01-svelte/01-introduction/03-dynamic-attributes/README.md b/content/tutorial/01-svelte/01-introduction/03-dynamic-attributes/README.md index e03524b1a..6c325239f 100644 --- a/content/tutorial/01-svelte/01-introduction/03-dynamic-attributes/README.md +++ b/content/tutorial/01-svelte/01-introduction/03-dynamic-attributes/README.md @@ -32,5 +32,5 @@ It's not uncommon to have an attribute where the name and value are the same, li ```svelte /// file: App.svelte -A man dances. +{name} dances. ``` diff --git a/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/README.md b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/README.md index 74e3a581f..49e0654ab 100644 --- a/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/README.md +++ b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/README.md @@ -38,8 +38,9 @@ A simple rule of thumb: the name of the updated variable must appear on the left ```js /// no-file +const obj = { foo: { bar: 1 } }; const foo = obj.foo; -foo.bar = 'baz'; +foo.bar = 2; ``` ...won't trigger reactivity on `obj.foo.bar`, unless you follow it up with `obj = obj`. diff --git a/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/README.md b/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/README.md index 9f8688a0e..a87e34367 100644 --- a/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/README.md @@ -2,13 +2,18 @@ title: Keyed each blocks --- -By default, when you modify the value of an `each` block, it will add and remove items at the _end_ of the block, and update any values that have changed. That might not be what you want. +By default, when you modify the value of an `each` block, it will add and remove DOM nodes at the _end_ of the block, and update any values that have changed. That might not be what you want. -It's easier to show why than to explain. Click the 'Remove first thing' button a few times, and notice what happens: it does not remove the first `` component, but rather the _last_ DOM node. Then it updates the `name` value in the remaining DOM nodes, but not the emoji, which is fixed when each `` is created. +It's easier to show why than to explain. The `` component sets the emoji as a constant on initialization, but the name is passed in via a prop. + +Click the 'Remove first thing' button a few times, and notice what happens: + +1. It removes the last component. +2. It then updates the `name` value in the remaining DOM nodes, but not the emoji, which is fixed when each `` is created. Instead, we'd like to remove only the first `` component and its DOM node, and leave the others unaffected. -To do that, we specify a unique identifier (or "key") for the `each` block: +To do that, we specify a unique identifier (or "key") for each iteration of the `each` block: ```svelte /// file: App.svelte @@ -17,6 +22,6 @@ To do that, we specify a unique identifier (or "key") for the `each` block: {/each} ``` -Here, `(thing.id)` is the _key_, which tells Svelte how to figure out which DOM node to change when the component updates. +Here, `(thing.id)` is the _key_, which tells Svelte how to figure out what to update when the values (`name` in this example) change. > You can use any object as the key, as Svelte uses a `Map` internally — in other words you could do `(thing)` instead of `(thing.id)`. Using a string or number is generally safer, however, since it means identity persists without referential equality, for example when updating with fresh data from an API server. diff --git a/content/tutorial/01-svelte/05-events/02-inline-handlers/README.md b/content/tutorial/01-svelte/05-events/02-inline-handlers/README.md index 7b59d9e56..b0aa0f9bf 100644 --- a/content/tutorial/01-svelte/05-events/02-inline-handlers/README.md +++ b/content/tutorial/01-svelte/05-events/02-inline-handlers/README.md @@ -20,7 +20,7 @@ You can also declare event handlers inline: m = { x: e.clientX, y: e.clientY }; }+++} > - The mouse position is {m.x} x {m.y} + The pointer is at {m.x} x {m.y} ``` diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/App.svelte index b47a06f1e..33d318c91 100644 --- a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/App.svelte @@ -6,6 +6,7 @@ audio.src = horn; function handleClick() { + audio.load(); audio.play(); } diff --git a/content/tutorial/01-svelte/07-lifecycle/01-onmount/README.md b/content/tutorial/01-svelte/07-lifecycle/01-onmount/README.md index e4dfde99a..4195b5c69 100644 --- a/content/tutorial/01-svelte/07-lifecycle/01-onmount/README.md +++ b/content/tutorial/01-svelte/07-lifecycle/01-onmount/README.md @@ -4,7 +4,7 @@ title: onMount Every component has a _lifecycle_ that starts when it is created, and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle. The one you'll use most frequently is `onMount`, which runs after the component is first rendered to the DOM. -In this exercise, we have a `` that we'd like to animate, using the `paint` function in `gradient.js`. Begin by importing the function from `svelte`: +In this exercise, we have a `` that we'd like to animate, using the `paint` function in `gradient.js`. Begin by importing the `onMount` function from `svelte`: ```svelte /// file: App.svelte @@ -14,7 +14,7 @@ In this exercise, we have a `` that we'd like to animate, using the `pai ``` -Then, add a function that runs when the component mounts: +Then, add a callback that runs when the component mounts: ```svelte /// file: App.svelte @@ -41,7 +41,7 @@ So far so good — you should see gently undulating colours in the shape of the ```js /// file: App.svelte onMount(() => { - const canvas = document.querySelector('canvas') + const canvas = document.querySelector('canvas'); const context = canvas.getContext('2d'); +++let frame =+++ requestAnimationFrame(function loop(t) { diff --git a/content/tutorial/01-svelte/07-lifecycle/01-onmount/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/07-lifecycle/01-onmount/app-a/src/lib/App.svelte index 39b42393d..b36b80ce0 100644 --- a/content/tutorial/01-svelte/07-lifecycle/01-onmount/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/07-lifecycle/01-onmount/app-a/src/lib/App.svelte @@ -5,7 +5,7 @@ +> - \ No newline at end of file + diff --git a/content/tutorial/02-advanced-svelte/05-bindings/03-media-elements/app-b/src/lib/AudioPlayer.svelte b/content/tutorial/02-advanced-svelte/05-bindings/03-media-elements/app-b/src/lib/AudioPlayer.svelte index aa995d34a..fcfbc28ce 100644 --- a/content/tutorial/02-advanced-svelte/05-bindings/03-media-elements/app-b/src/lib/AudioPlayer.svelte +++ b/content/tutorial/02-advanced-svelte/05-bindings/03-media-elements/app-b/src/lib/AudioPlayer.svelte @@ -23,17 +23,17 @@ bind:currentTime={time} bind:duration bind:paused - preload="none" + preload="metadata" on:ended={() => { time = 0; }} - /> + >
@@ -69,7 +69,7 @@ }); }} > -
+
{duration ? format(duration) : '--:--'}
diff --git a/content/tutorial/02-advanced-svelte/05-bindings/05-bind-this/app-a/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/05-bindings/05-bind-this/app-a/src/lib/App.svelte index de10e6465..582abc630 100644 --- a/content/tutorial/02-advanced-svelte/05-bindings/05-bind-this/app-a/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/05-bindings/05-bind-this/app-a/src/lib/App.svelte @@ -20,7 +20,7 @@ +> -``` \ No newline at end of file +``` diff --git a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/App.svelte index 594151af4..a9bde36e5 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/App.svelte @@ -14,7 +14,7 @@ Mergers and Aquisitions - 358 Exchange Place, New York, N.Y. 100099 fax 212 555 6390 telex 10 4534 + 358 Exchange Place, New York, N.Y. 10099 fax 212 555 6390 telex 10 4534 diff --git a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/paper.svg b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/paper.svg index f8f704fdf..5dba50c15 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/paper.svg +++ b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-a/src/lib/paper.svg @@ -6,5 +6,5 @@ - - \ No newline at end of file + + diff --git a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-b/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-b/src/lib/App.svelte index 2613efd08..6ddf52dcc 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-b/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/02-named-slots/app-b/src/lib/App.svelte @@ -14,7 +14,7 @@ Mergers and Aquisitions - 358 Exchange Place, New York, N.Y. 100099 fax 212 555 6390 telex 10 4534 + 358 Exchange Place, New York, N.Y. 10099 fax 212 555 6390 telex 10 4534 diff --git a/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/App.svelte index 5bb5882ca..b7b99912f 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/App.svelte @@ -14,7 +14,7 @@ Mergers and Aquisitions - 358 Exchange Place, New York, N.Y. 100099 fax 212 555 6390 telex 10 4534 + 358 Exchange Place, New York, N.Y. 10099 fax 212 555 6390 telex 10 4534 diff --git a/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/paper.svg b/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/paper.svg index f8f704fdf..5dba50c15 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/paper.svg +++ b/content/tutorial/02-advanced-svelte/07-composition/03-slot-fallbacks/app-a/src/lib/paper.svg @@ -6,5 +6,5 @@ - - \ No newline at end of file + + diff --git a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/README.md b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/README.md index 896528c17..c8dbd89a4 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/README.md +++ b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/README.md @@ -12,7 +12,7 @@ Open `FilterableList.svelte`. The `` is being rendered for each filtered i /// file: FilterableList.svelte
{#each data.filter(matches) as item} - + {/each}
``` diff --git a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-a/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-a/src/lib/App.svelte index 9757ff15d..1a7c9c9d5 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-a/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-a/src/lib/App.svelte @@ -10,7 +10,7 @@ field="name" >
- + name hex rgb @@ -18,7 +18,7 @@
- + {row.name} {row.hex} {row.rgb} diff --git a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-b/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-b/src/lib/App.svelte index a21b37016..34c173fff 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-b/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/04-slot-props/app-b/src/lib/App.svelte @@ -9,7 +9,7 @@ let:item={row} >
- + name hex rgb @@ -17,7 +17,7 @@
- + {row.name} {row.hex} {row.rgb} diff --git a/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/README.md b/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/README.md index 7f1d3d859..c9c219f69 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/README.md +++ b/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/README.md @@ -7,7 +7,7 @@ In some cases, you may want to control parts of your component based on whether ```svelte /// file: App.svelte ---
- + name hex rgb @@ -31,7 +31,7 @@ We can fix that by using the special `$$slots` variable in `FilterableList.svelt /// file: FilterableList.svelte +++{#if $$slots.header}+++
- +
+++{/if}+++ ``` diff --git a/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/app-b/src/lib/App.svelte b/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/app-b/src/lib/App.svelte index de5be5138..0dde93327 100644 --- a/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/app-b/src/lib/App.svelte +++ b/content/tutorial/02-advanced-svelte/07-composition/05-optional-slots/app-b/src/lib/App.svelte @@ -9,7 +9,7 @@ let:item={row} >
- + {row.name} {row.hex} {row.rgb} diff --git a/content/tutorial/02-advanced-svelte/09-special-elements/01-svelte-self/README.md b/content/tutorial/02-advanced-svelte/09-special-elements/01-svelte-self/README.md index c117ad4ce..ea4e6a572 100644 --- a/content/tutorial/02-advanced-svelte/09-special-elements/01-svelte-self/README.md +++ b/content/tutorial/02-advanced-svelte/09-special-elements/01-svelte-self/README.md @@ -20,8 +20,8 @@ It's useful for things like this folder tree view, where folders can contain _ot ```svelte /// file: Folder.svelte {#if file.files} - ++++++ + ++++++ {:else} - + {/if} ``` diff --git a/content/tutorial/02-advanced-svelte/09-special-elements/02-svelte-component/README.md b/content/tutorial/02-advanced-svelte/09-special-elements/02-svelte-component/README.md index 0b31b2f81..bc16deb01 100644 --- a/content/tutorial/02-advanced-svelte/09-special-elements/02-svelte-component/README.md +++ b/content/tutorial/02-advanced-svelte/09-special-elements/02-svelte-component/README.md @@ -27,7 +27,7 @@ We _could_ do this with a sequence of `if` blocks... {/each} -++++++ +++++++ ``` The `this` value can be any component constructor, or a falsy value — if it's falsy, no component is rendered. diff --git a/content/tutorial/02-advanced-svelte/09-special-elements/09-svelte-options/README.md b/content/tutorial/02-advanced-svelte/09-special-elements/09-svelte-options/README.md index 145ed9407..6514e388c 100644 --- a/content/tutorial/02-advanced-svelte/09-special-elements/09-svelte-options/README.md +++ b/content/tutorial/02-advanced-svelte/09-special-elements/09-svelte-options/README.md @@ -26,6 +26,6 @@ The options that can be set here are: - `accessors={true}` — adds getters and setters for the component's props - `accessors={false}` — the default - `namespace="..."` — the namespace where this component will be used, most commonly `"svg"` -- `tag="..."` — the name to use when compiling this component as a custom element +- `customElement="..."` — the name to use when compiling this component as a custom element Consult the [API reference](https://svelte.dev/docs) for more information on these options. diff --git a/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-a/src/lib/AudioPlayer.svelte b/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-a/src/lib/AudioPlayer.svelte index 1cc7d1737..85be476d6 100644 --- a/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-a/src/lib/AudioPlayer.svelte +++ b/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-a/src/lib/AudioPlayer.svelte @@ -26,13 +26,13 @@ on:ended={() => { time = 0; }} - /> + >
@@ -68,7 +68,7 @@ }); }} > -
+
{duration ? format(duration) : '--:--'}
diff --git a/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-b/src/lib/AudioPlayer.svelte b/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-b/src/lib/AudioPlayer.svelte index acafe1bbc..ffdb485d8 100644 --- a/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-b/src/lib/AudioPlayer.svelte +++ b/content/tutorial/02-advanced-svelte/10-module-context/01-sharing-code/app-b/src/lib/AudioPlayer.svelte @@ -38,13 +38,13 @@ on:ended={() => { time = 0; }} - /> + >
@@ -80,7 +80,7 @@ }); }} > -
+
{duration ? format(duration) : '--:--'}
diff --git a/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/README.md b/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/README.md index 520567dbb..70def8228 100644 --- a/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/README.md +++ b/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/README.md @@ -21,6 +21,7 @@ We can now import `stopAll` in `App.svelte`... /// file: App.svelte ``` diff --git a/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/app-b/src/lib/AudioPlayer.svelte b/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/app-b/src/lib/AudioPlayer.svelte index 0eaa4a16e..323286b79 100644 --- a/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/app-b/src/lib/AudioPlayer.svelte +++ b/content/tutorial/02-advanced-svelte/10-module-context/02-module-exports/app-b/src/lib/AudioPlayer.svelte @@ -42,13 +42,13 @@ on:ended={() => { time = 0; }} - /> + >
@@ -84,7 +84,7 @@ }); }} > -
+
{duration ? format(duration) : '--:--'}
diff --git a/content/tutorial/02-advanced-svelte/common/src/routes/+error.svelte b/content/tutorial/02-advanced-svelte/common/src/routes/+error.svelte index 930ccfd10..fb5551052 100644 --- a/content/tutorial/02-advanced-svelte/common/src/routes/+error.svelte +++ b/content/tutorial/02-advanced-svelte/common/src/routes/+error.svelte @@ -11,7 +11,7 @@ code {$page.status}

diff --git a/content/tutorial/03-sveltekit/02-routing/02-layouts/README.md b/content/tutorial/03-sveltekit/02-routing/02-layouts/README.md index e7812fead..79898d168 100644 --- a/content/tutorial/03-sveltekit/02-routing/02-layouts/README.md +++ b/content/tutorial/03-sveltekit/02-routing/02-layouts/README.md @@ -14,7 +14,7 @@ src/routes/ └ +page.svelte ``` -...and move the duplicated content from the `+page.svelte` files into the new `+layout.svelte` file. The `` element is where the page content will be rendered: +...and move the duplicated content from the `+page.svelte` files into the new `+layout.svelte` file. The `` element is where the page content will be rendered: ```svelte /// file: src/routes/+layout.svelte @@ -23,7 +23,7 @@ src/routes/ about - + ``` A `+layout.svelte` file applies to every child route, including the sibling `+page.svelte` (if it exists). You can nest layouts to arbitrary depth. diff --git a/content/tutorial/03-sveltekit/03-loading-data/01-page-data/README.md b/content/tutorial/03-sveltekit/03-loading-data/01-page-data/README.md index f23ff32c3..8d9b4049b 100644 --- a/content/tutorial/03-sveltekit/03-loading-data/01-page-data/README.md +++ b/content/tutorial/03-sveltekit/03-loading-data/01-page-data/README.md @@ -7,7 +7,7 @@ At its core, SvelteKit's job boils down to three things: 1. **Routing** — figure out which route matches an incoming request 2. **Loading** — get the data needed by the route -3. **Rendering** - generate some HTML (on the server) or update the DOM (in the browser) +3. **Rendering** — generate some HTML (on the server) or update the DOM (in the browser) We've seen how routing and rendering work. Let's talk about the middle part — loading. diff --git a/content/tutorial/03-sveltekit/03-loading-data/02-layout-data/README.md b/content/tutorial/03-sveltekit/03-loading-data/02-layout-data/README.md index 3b25ecf82..a96aab1ad 100644 --- a/content/tutorial/03-sveltekit/03-loading-data/02-layout-data/README.md +++ b/content/tutorial/03-sveltekit/03-loading-data/02-layout-data/README.md @@ -19,7 +19,7 @@ Now, add a sidebar in the layout for the post page:
- +
+++
+
diff --git a/src/routes/tutorial/[slug]/Output.svelte b/src/routes/tutorial/[slug]/Output.svelte index 0f2526e36..629a2cf5b 100644 --- a/src/routes/tutorial/[slug]/Output.svelte +++ b/src/routes/tutorial/[slug]/Output.svelte @@ -1,7 +1,7 @@ -
{ - if (sessionStorage[copy_enabled]) return; - - /** @type {HTMLElement | null} */ - let node = /** @type {HTMLElement} */ (e.target); +
+
{ + if (sessionStorage[copy_enabled]) return; - while (node && node !== e.currentTarget) { - if (node.nodeName === 'PRE') { - show_modal = true; + /** @type {HTMLElement | null} */ + let node = /** @type {HTMLElement} */ (e.target); - e.preventDefault(); - return; - } + while (node && node !== e.currentTarget) { + if (node.nodeName === 'PRE') { + show_modal = true; - node = /** @type {HTMLElement | null} */ (node.parentNode); - } - }} -> - -
{ - const node = /** @type {HTMLElement} */ (e.target); - - if (node.nodeName === 'CODE') { - const { file } = node.dataset; - if (file) { - dispatch('select', { file }); + e.preventDefault(); + return; } - } - if (node.nodeName === 'SPAN' && node.classList.contains('filename')) { - const file = exercise.scope.prefix + node.textContent; - dispatch('select', { file }); + node = /** @type {HTMLElement | null} */ (node.parentNode); } }} > - {@html exercise.html} + + +
{ + const node = /** @type {HTMLElement} */ (e.target); + + if (node.nodeName === 'CODE') { + const { file } = node.dataset; + if (file) { + dispatch('select', { file }); + } + } + + if (node.nodeName === 'SPAN' && node.classList.contains('filename')) { + const file = exercise.scope.prefix + node.textContent; + dispatch('select', { file }); + } + }} + > + {@html exercise.html} +
+ + {#if exercise.next} +

Next: {exercise.next.title}

+ {/if}
- {#if exercise.next} -

Next: {exercise.next.title}

- {/if} -
- - + +
{#if show_modal} (show_modal = false)}> @@ -113,9 +107,16 @@ {/if} \ No newline at end of file + diff --git a/src/routes/tutorial/[slug]/state.js b/src/routes/tutorial/[slug]/state.js index 629a4d863..e321c3892 100644 --- a/src/routes/tutorial/[slug]/state.js +++ b/src/routes/tutorial/[slug]/state.js @@ -6,19 +6,6 @@ import * as adapter from './adapter.js'; * @typedef {import('svelte/store').Writable} Writable */ -// TODO would be nice if svelte exported this type (maybe it does already?) -/** - * @typedef {{ - * code: string; - * start: { line: number, column: number, character: number }; - * end: { line: number, column: number, character: number }; - * pos: number; - * filename: string; - * frame: string; - * message: string; - * }} CompilerWarning - */ - /** @type {Writable} */ export const files = writable([]); @@ -98,4 +85,4 @@ export function create_directories(name, files) { } return directories; -} \ No newline at end of file +} diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 000000000..eb0536286 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js index 819cf63b7..3b4551d49 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -6,6 +6,10 @@ const config = { kit: { adapter: adapter({ runtime: 'edge' }), + prerender: { + concurrency: 4 + }, + version: { name: child_process.execSync('git rev-parse HEAD').toString().trim() } diff --git a/tests/env_file.spec.ts b/tests/env_file.spec.ts index 7177e40cf..7a546b29c 100644 --- a/tests/env_file.spec.ts +++ b/tests/env_file.spec.ts @@ -1,7 +1,18 @@ -import { expect, test } from '@playwright/test'; +import { expect, test, type Page } from '@playwright/test'; const iframe_selector = 'iframe[src*="webcontainer.io/"]'; +async function disableAnimations(page: Page) { + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + transition-duration: 0s !important; + } + ` + }); +} + test.describe.configure({ mode: 'parallel' }); test('.env file: no timeout error occurs when switching a tutorials without a .env file to one with it', async ({ @@ -11,15 +22,18 @@ test('.env file: no timeout error occurs when switching a tutorials without a .e await page.goto('/tutorial/welcome-to-svelte'); + // disable animations to prevent flakiness + await disableAnimations(page); + const iframe_locator = page.frameLocator(iframe_selector); // wait for the iframe to load await iframe_locator.getByText('Welcome!').waitFor(); // switch to another tutorial with a .env file - await page.click('header > h1', { delay: 200 }); - await page.locator('button', { hasText: 'Part 4: Advanced SvelteKit' }).click({ delay: 200 }); - await page.locator('button', { hasText: 'Environment variables' }).click({ delay: 200 }); + await page.click('header > button > h1', { delay: 200 }); + await page.getByRole('button', { name: 'Part 4: Advanced SvelteKit' }).click({ delay: 200 }); + await page.getByRole('button', { name: 'Environment variables' }).click({ delay: 200 }); await page.locator('a', { hasText: '$env/static/private' }).click({ delay: 200 }); // wait for the iframe to load @@ -41,15 +55,18 @@ test('.env file: environment variables are available when switching a tutorial w await page.goto('/tutorial/welcome-to-svelte'); + // disable animations to prevent flakiness + await disableAnimations(page); + const iframe_locator = page.frameLocator(iframe_selector); // wait for the iframe to load await iframe_locator.getByText('Welcome!').waitFor(); // switch to another tutorial with a .env file - await page.click('header > h1', { delay: 200 }); - await page.locator('button', { hasText: 'Part 4: Advanced SvelteKit' }).click({ delay: 200 }); - await page.locator('button', { hasText: 'Environment variables' }).click({ delay: 200 }); + await page.click('header > button > h1', { delay: 200 }); + await page.getByRole('button', { name: 'Part 4: Advanced SvelteKit' }).click({ delay: 200 }); + await page.getByRole('button', { name: 'Environment variables' }).click({ delay: 200 }); await page.locator('a', { hasText: '$env/dynamic/private' }).click({ delay: 200 }); // wait for the iframe to load diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..fb6340954 --- /dev/null +++ b/vercel.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "headers": [ + { + "source": "_app/immutable/workers/(.*)", + "headers": [ + { + "key": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "key": "cross-origin-embedder-policy", + "value": "require-corp" + }, + { + "key": "cross-origin-resource-policy", + "value": "cross-origin" + } + ] + } + ] +} diff --git a/vite.config.js b/vite.config.js index 1a3c3386d..bf5e4e7dc 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,14 +1,23 @@ -import path from 'path'; import { sveltekit } from '@sveltejs/kit/vite'; +import browserslist from 'browserslist'; +import { browserslistToTargets } from 'lightningcss'; +import path from 'node:path'; /** @type {import('vite').UserConfig} */ export default { + logLevel: 'info', + + css: { + transformer: 'lightningcss', + lightningcss: { + targets: browserslistToTargets(browserslist(['>0.2%', 'not dead'])) + } + }, build: { - target: 'esnext' + target: 'esnext', + cssMinify: 'lightningcss' }, - logLevel: 'info', - plugins: [ // apply cross-origin isolation headers when previewing locally { @@ -28,10 +37,16 @@ export default { server: { fs: { - allow: [path.resolve('.apps')] + allow: [path.resolve('.apps')], + strict: false }, watch: { ignored: ['**/.apps/**'] + }, + headers: { + 'cross-origin-opener-policy': 'same-origin', + 'cross-origin-embedder-policy': 'require-corp', + 'cross-origin-resource-policy': 'cross-origin' } },