diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 000000000..cb004fcf5 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,32 @@ +name: CI + +on: + push: + branches: ['main'] + pull_request: + branches: ['main'] + +env: + # we call `pnpm playwright install` instead + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2.2.4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + - run: pnpm install --frozen-lockfile + - run: pnpm playwright install chromium + - run: pnpm test diff --git a/.gitignore b/.gitignore index 2e69639ed..f29007301 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ .apps .vercel /content/tutorial/common/.svelte-kit -/content/tutorial/common/node_modules \ No newline at end of file +/content/tutorial/common/node_modules +/test-results \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..0598ad131 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 [these people](https://github.com/sveltejs/learn.svelte.dev/graphs/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index db85c2ead..8866c4475 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,26 @@ A soup-to-nuts interactive tutorial on how to build apps with Svelte. This repo uses [pnpm](https://pnpm.io/). -## Running the app +## Developing the app -First, run `node scripts/create-common-bundle`. This packages up everything that's needed to run a SvelteKit app (Vite, Esbuild, SvelteKit, Svelte compiler etc) which can subsequently be unpacked on a server to create and run and instance of a SvelteKit application (which powers the output window of the tutorial). +First, run `node scripts/create-common-bundle`. This packages up everything that's needed to run a SvelteKit app (Vite, esbuild, SvelteKit, Svelte compiler, etc.) which can subsequently be unpacked on a server to create and run an instance of a SvelteKit application (which powers the output window of the tutorial). Then, run `dev`: -The next steps depend on whether you want to run this locally in filesystem mode, or in WebContainer mode. For now, it works with filesystem mode only locally. In future, it will run both locally and on the web (using [WebContainers](https://blog.stackblitz.com/posts/introducing-webcontainers/)). +```bash +node scripts/create-common-bundle +pnpm dev +``` -### Local/filesystem mode +To build for production and run locally: -1. add an `.env` file with `PUBLIC_USE_FILESYSTEM=true` in it -2. Run the app locally with `pnpm dev` or `pnpm build && pnpm preview`. - -### WebContainer mode - -1. if an `.env` file exists, modify it so there's `PUBLIC_USE_FILESYSTEM=` in it -2. Run the app locally with `pnpm dev` or `pnpm build && pnpm preview`. +```bash +pnpm build +pnpm preview +``` ## Creating new tutorials Tutorials live inside `content`. Each tutorial consists of a `README.md`, which is the text to the left, and `app-a` and `app-b` folders, which represent the initial and solved state. Files that stay the same can be omitted from `app-b`. Files are marked as deleted in `app-b` if they start with `__delete`. Folders that are marked as deleted in `app-b` if they contain a file named `__delete`. + +## Bumping tutorial dependencies + +Bump the dependency (for example Svelte) in both the root and the `content/common` `package.json`. In the root do `pnpm i` (to update `pnpm-lock.yaml`), in `content/common` do `npm i` (to update `package-lock.json`). \ No newline at end of file diff --git a/backend/+server.js b/backend/+server.js deleted file mode 100644 index 951581de2..000000000 --- a/backend/+server.js +++ /dev/null @@ -1,14 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { create } from './apps'; - -/** @type {import('./$types').RequestHandler} */ -export async function POST({ request }) { - return json( - await create({ - files: await request.json() - }), - { - status: 201 // should this be a 200? - } - ); -} diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 39de3f70c..000000000 --- a/backend/README.md +++ /dev/null @@ -1 +0,0 @@ -This used to live in src/routes/backend, but it makes it impossible to use with edge functions. Keeping it here in case we need it in future. diff --git a/backend/[id]/+server.js b/backend/[id]/+server.js deleted file mode 100644 index 918ddfefd..000000000 --- a/backend/[id]/+server.js +++ /dev/null @@ -1,17 +0,0 @@ -import { clear, update } from '../apps'; - -/** @type {import('./$types').RequestHandler} */ -export async function PUT({ url, request, params }) { - const { id } = params; - const files = await request.json(); - - if (url.searchParams.has('reset')) { - clear({ id, files }); - } - - update({ id, files }); - - return new Response(undefined, { - status: 201 - }); -} diff --git a/backend/apps.js b/backend/apps.js deleted file mode 100644 index 80b0669d8..000000000 --- a/backend/apps.js +++ /dev/null @@ -1,208 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { spawn } from 'node:child_process'; -import { createRequire } from 'node:module'; -import * as ports from 'port-authority'; -import { broadcast, ready } from './ws'; - -/** - * @typedef {{ - * process: import('child_process').ChildProcess, - * filenames: string[] - * }} App */ - -fs.rmSync('.apps', { recursive: true, force: true }); -fs.mkdirSync('.apps', { recursive: true }); - -// poor man's HMR, pending https://github.com/vitejs/vite/issues/7887 -// @ts-expect-error -if (globalThis.__apps) { - globalThis.__apps.forEach((app) => { - app.process.kill(); - }); -} - -const require = createRequire(import.meta.url); -const vite_pkg_file = require.resolve('vite/package.json'); -const vite_pkg = JSON.parse(fs.readFileSync(vite_pkg_file, 'utf-8')); -const vite = path.resolve(vite_pkg_file, '..', vite_pkg.bin['vite']); - -/** @type {Map} */ -const apps = new Map(); -globalThis.__apps = apps; - -const hooks_src = `/** @type {import('@sveltejs/kit').Handle} */ -export async function handle({ event, resolve }) { - const response = await resolve(event); - - response.headers.set('cross-origin-opener-policy', 'same-origin'); - response.headers.set('cross-origin-embedder-policy', 'require-corp'); - response.headers.set('cross-origin-resource-policy', 'cross-origin'); - - return response; -}`; - -/** - * @param {{ - * files: import('$lib/types').FileStub[] - * }} options - */ -export async function create({ files }) { - const id = String(Date.now()); - const filenames = write_files(id, files); - - // TODO this enables embedding on cross-origin sites, which is - // necessary for the JSNation talk, but will currently break if an app - // already has a src/hooks.server.js file (though it could be worked - // around easily enough if necessary) - if (!files.find((stub) => stub.name === '/src/hooks.server.js')) { - fs.writeFileSync(`.apps/${id}/src/hooks.server.js`, hooks_src); - } - - const port = await ports.find(3001); - - apps.set(id, { - process: launch(id, String(port)), - filenames - }); - - await ports.waitUntilBusy(port); - - await ready; - - return { - id, - port - }; -} - -/** - * @param {{ - * id: string; - * files: import('$lib/types').FileStub[] - * }} options - */ -export function clear({ id, files }) { - const app = apps.get(id); - - if (!app) { - throw new Error(`app ${id} does not exist`); - } - - const dir = `.apps/${id}`; - const old_filenames = new Set(app.filenames); - - /** @type {string[]} */ - const filenames = []; - - for (const file of files) { - if (file.type === 'file') { - filenames.push(file.name); - old_filenames.delete(file.name); - } - } - - for (const file of old_filenames) { - if (fs.existsSync(dir + file)) { - fs.unlinkSync(dir + file); - } - } - - app.filenames = filenames; -} - -/** - * @param {{ - * id: string; - * files: import('$lib/types').FileStub[] - * }} options - */ -export function update({ id, files }) { - if (!apps.has(id)) { - throw new Error(`app ${id} does not exist`); - } - - write_files(id, files); -} - -/** - * @param {{ id: string }} options - */ -export function destroy({ id }) { - const dir = `.apps/${id}`; - - fs.rmSync(dir, { recursive: true, force: true }); - - apps.get(id)?.process.kill(); - apps.delete(id); -} - -/** - * @param {string} id - * @param {string} port - */ -function launch(id, port) { - const cwd = `.apps/${id}`; - - const process = spawn('node', [vite, 'dev', '--port', port], { - cwd - }); - - process.stdout.on('data', (data) => { - broadcast({ id, data: data.toString(), type: 'stdout' }); - }); - - process.stderr.on('data', (data) => { - broadcast({ id, data: data.toString(), type: 'stderr' }); - }); - - return process; -} - -/** - * - * @param {string} file - * @param {string | Buffer} contents - */ -function write_if_changed(file, contents) { - if (typeof contents === 'string' && fs.existsSync(file)) { - const existing = fs.readFileSync(file, 'utf-8'); - if (contents === existing) return; - } - - fs.mkdirSync(path.dirname(file), { recursive: true }); - fs.writeFileSync(file, contents); -} - -/** - * - * @param {string} id - * @param {import('$lib/types').FileStub[]} files - */ -function write_files(id, files) { - const dir = `.apps/${id}`; - - /** @type {string[]} */ - const filenames = []; - - for (const file of files) { - if (file.type === 'file') { - filenames.push(file.name); - - const dest = `${dir}/${file.name}`; - let content = file.text ? file.contents : Buffer.from(file.contents, 'base64'); - - if (file.name === '/src/app.html' && typeof content === 'string') { - // TODO handle case where config.kit.files.template is different - content = content.replace( - '', - '' - ); - } - - write_if_changed(dest, content); - } - } - - return filenames; -} diff --git a/backend/destroy/+server.js b/backend/destroy/+server.js deleted file mode 100644 index ad9c89efd..000000000 --- a/backend/destroy/+server.js +++ /dev/null @@ -1,14 +0,0 @@ -import { destroy } from '../apps'; - -// this is implemented as a POST handler because it is -// triggered by `navigator.sendBeacon` rather than `fetch` - -/** @type {import('./$types').RequestHandler} */ -export function POST({ url }) { - const id = /** @type {string} */ (url.searchParams.get('id')); - destroy({ id }); - - return new Response(undefined, { - status: 204 - }); -} diff --git a/backend/ws.js b/backend/ws.js deleted file mode 100644 index e02317c0d..000000000 --- a/backend/ws.js +++ /dev/null @@ -1,33 +0,0 @@ -// import './websocket.js'; -import { WebSocketServer } from 'ws'; - -// poor man's HMR, pending https://github.com/vitejs/vite/issues/7887 -// @ts-expect-error -if (globalThis.__wss) globalThis.__wss.close(); - -const wss = new WebSocketServer({ port: 4567 }); -globalThis.__wss = wss; - -/** @type {Set} */ -const connections = new Set(); - -wss.on('connection', (ws) => { - connections.add(ws); - - ws.on('close', () => { - connections.delete(ws); - }); -}); - -export const ready = new Promise((fulfil) => { - wss.on('listening', () => { - fulfil(undefined); - }); -}); - -/** @param {any} data */ -export function broadcast(data) { - for (const connection of connections) { - connection.send(JSON.stringify(data)); - } -} diff --git a/content/tutorial/01-svelte/01-introduction/01-welcome-to-svelte/README.md b/content/tutorial/01-svelte/01-introduction/01-welcome-to-svelte/README.md index b2447d72f..0701a6901 100644 --- a/content/tutorial/01-svelte/01-introduction/01-welcome-to-svelte/README.md +++ b/content/tutorial/01-svelte/01-introduction/01-welcome-to-svelte/README.md @@ -2,7 +2,7 @@ title: Welcome to Svelte --- -Welcome to the Svelte tutorial! This will teach you everything you need to know to easily build fast, small web applications. +Welcome to the Svelte tutorial! This will teach you everything you need to know to easily build web applications of all sizes, with high performance and a small footprint. You can also consult the [API docs](https://svelte.dev/docs) and the [examples](https://svelte.dev/examples), or — if you're impatient to start hacking on your machine locally — create a project with `npm init svelte`. @@ -12,7 +12,7 @@ Svelte is a tool for building web applications. Like other user interface framew These components are _compiled_ into small, efficient JavaScript modules that eliminate overhead traditionally associated with UI frameworks. -You can build your entire app with Svelte (for example, using an application framework like [SvelteKit](https://kit.svelte.dev)), or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere. +You can build your entire app with Svelte (for example, using an application framework like [SvelteKit](https://kit.svelte.dev), which this tutorial will cover), or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere. ## How to use this tutorial @@ -20,11 +20,11 @@ You can build your entire app with Svelte (for example, using an application fra This tutorial is split into four main parts: -- [Welcome to Svelte](/tutorial/welcome-to-svelte) (you are here) -- [Introduction to SvelteKit](/tutorial/introducing-sveltekit) +- [Basic Svelte](/tutorial/welcome-to-svelte) (you are here) - [Advanced Svelte](/tutorial/tweens) +- [Basic SvelteKit](/tutorial/introducing-sveltekit) - [Advanced SvelteKit](/tutorial/optional-params) Each section will present an exercise designed to illustrate a feature. Later exercises build on the knowledge gained in earlier ones, so it's recommended that you go from start to finish. If necessary, you can navigate via the menu above. -If you get stuck, you can click the `solve` button to the left of the editor. (It's disabled on sections like this one that don't include an exercise.) Try not to rely on it too much; you will learn faster by figuring out where to put each suggested code block and manually typing it in to the editor. +If you get stuck, you can click the `solve` button to the left of the editorin the top right of the editor view. (Use the toggle below to switch between tutorial and editor views. The `solve` button is disabled on sections like this one that don't include an exercise.) Try not to rely on it too much; you will learn faster by figuring out where to put each suggested code block and manually typing it in to the editor. diff --git a/content/tutorial/01-svelte/01-introduction/02-your-first-component/README.md b/content/tutorial/01-svelte/01-introduction/02-your-first-component/README.md index 0e1f3d6ac..ac4bb6c52 100644 --- a/content/tutorial/01-svelte/01-introduction/02-your-first-component/README.md +++ b/content/tutorial/01-svelte/01-introduction/02-your-first-component/README.md @@ -11,6 +11,7 @@ A component that just renders some static markup isn't very interesting. Let's a First, add a script tag to your component and declare a `name` variable: ```svelte +/// file: App.svelte ++++++ @@ -21,11 +22,13 @@ First, add a script tag to your component and declare a `name` variable: Then, we can refer to `name` in the markup: ```svelte +/// file: App.svelte

Hello +++{name}+++!

``` Inside the curly braces, we can put any JavaScript we want. Try changing `name` to `name.toUpperCase()` for a shoutier greeting. ```svelte +/// file: App.svelte

Hello {name+++.toUpperCase()+++}!

-``` \ No newline at end of file +``` 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 ae04159c9..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 @@ -7,10 +7,11 @@ Just like you can use curly braces to control text, you can use them to control Our image is missing a `src` — let's add one: ```svelte +/// file: App.svelte ``` -That's better. But Svelte is giving us a warning: +That's better. But if you hover over the `` in the editor, Svelte is giving us a warning: > A11y: <img> element should have an alt attribute @@ -19,7 +20,8 @@ When building web apps, it's important to make sure that they're _accessible_ to In this case, we're missing the `alt` attribute that describes the image for people using screenreaders, or people with slow or flaky internet connections that can't download the image. Let's add one: ```svelte - +/// file: App.svelte + ``` We can use curly braces _inside_ attributes. Try changing it to `"{name} dances."` — remember to declare a `name` variable in the `+++ @@ -15,6 +16,7 @@ Add a ` - - diff --git a/content/tutorial/01-svelte/02-reactivity/02-reactive-declarations/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/02-reactivity/02-reactive-declarations/app-b/src/lib/App.svelte index d031bc187..c48f0c7f3 100644 --- a/content/tutorial/01-svelte/02-reactivity/02-reactive-declarations/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/02-reactivity/02-reactive-declarations/app-b/src/lib/App.svelte @@ -2,12 +2,12 @@ let count = 0; $: doubled = count * 2; - function handleClick() { + function increment() { count += 1; } - diff --git a/content/tutorial/01-svelte/02-reactivity/03-reactive-statements/README.md b/content/tutorial/01-svelte/02-reactivity/03-reactive-statements/README.md index 294cea314..9ee6f4f89 100644 --- a/content/tutorial/01-svelte/02-reactivity/03-reactive-statements/README.md +++ b/content/tutorial/01-svelte/02-reactivity/03-reactive-statements/README.md @@ -5,6 +5,7 @@ title: Statements We're not limited to declaring reactive _values_ — we can also run arbitrary _statements_ reactively. For example, we can log the value of `count` whenever it changes: ```js +/// file: App.svelte let count = 0; +++$: console.log(`the count is ${count}`);+++ @@ -13,15 +14,17 @@ let count = 0; You can easily group statements together with a block: ```js +/// file: App.svelte $: +++{+++ console.log(`the count is ${count}`); - alert(`I SAID THE COUNT IS ${count}`); + console.log(`this will also be logged whenever count changes`); +++}+++ ``` You can even put the `$:` in front of things like `if` blocks: ```js +/// file: App.svelte $: +++if (count >= 10)+++ { alert('count is dangerously high!'); count = 0; 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 19bfa74b3..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 @@ -2,11 +2,12 @@ title: Updating arrays and objects --- -Because Svelte's reactivity is triggered by assignments, using array methods like `push` and `splice` won't automatically cause updates. For example, clicking the 'Add a number' button doesn't currently do anything. +Because Svelte's reactivity is triggered by assignments, using array methods like `push` and `splice` won't automatically cause updates. For example, clicking the 'Add a number' button doesn't currently do anything, even though we're calling `numbers.push(...)` inside `addNumber`. One way to fix that is to add an assignment that would otherwise be redundant: ```js +/// file: App.svelte function addNumber() { numbers.push(numbers.length + 1); +++numbers = numbers;+++ @@ -16,6 +17,7 @@ function addNumber() { But there's a more idiomatic solution: ```js +/// file: App.svelte function addNumber() { numbers = +++[...numbers, numbers.length + 1];+++ } @@ -26,6 +28,7 @@ You can use similar patterns to replace `pop`, `shift`, `unshift` and `splice`. Assignments to _properties_ of arrays and objects — e.g. `obj.foo += 1` or `array[i] = x` — work the same way as assignments to the values themselves. ```js +/// file: App.svelte function addNumber() { numbers[numbers.length] = numbers.length + 1; } @@ -34,8 +37,10 @@ function addNumber() { A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment. For example this... ```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/02-reactivity/04-updating-arrays-and-objects/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-a/src/lib/App.svelte index 77d176923..ab6ef7074 100644 --- a/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-a/src/lib/App.svelte @@ -5,7 +5,7 @@ numbers.push(numbers.length + 1); } - $: sum = numbers.reduce((t, n) => t + n, 0); + $: sum = numbers.reduce((total, currentNumber) => total + currentNumber, 0);

{numbers.join(' + ')} = {sum}

diff --git a/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-b/src/lib/App.svelte index d15f58c4d..730ae8cc7 100644 --- a/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/02-reactivity/04-updating-arrays-and-objects/app-b/src/lib/App.svelte @@ -5,7 +5,7 @@ numbers = [...numbers, numbers.length + 1]; } - $: sum = numbers.reduce((t, n) => t + n, 0); + $: sum = numbers.reduce((total, currentNumber) => total + currentNumber, 0);

{numbers.join(' + ')} = {sum}

diff --git a/content/tutorial/01-svelte/02-reactivity/meta.json b/content/tutorial/01-svelte/02-reactivity/meta.json index e42ac9148..9e1940e2e 100644 --- a/content/tutorial/01-svelte/02-reactivity/meta.json +++ b/content/tutorial/01-svelte/02-reactivity/meta.json @@ -2,7 +2,6 @@ "title": "Reactivity", "scope": { "prefix": "/src/lib/", - "depth": 2, "name": "src" }, "focus": "/src/lib/App.svelte" diff --git a/content/tutorial/01-svelte/03-props/01-declaring-props/README.md b/content/tutorial/01-svelte/03-props/01-declaring-props/README.md index 54f95e4b8..ac9a711a0 100644 --- a/content/tutorial/01-svelte/03-props/01-declaring-props/README.md +++ b/content/tutorial/01-svelte/03-props/01-declaring-props/README.md @@ -7,6 +7,7 @@ So far, we've dealt exclusively with internal state — that is to say, the valu In any real application, you'll need to pass data from one component down to its children. To do that, we need to declare _properties_, generally shortened to 'props'. In Svelte, we do that with the `export` keyword. Edit the `Nested.svelte` component: ```svelte +/// file: Nested.svelte diff --git a/content/tutorial/01-svelte/03-props/01-declaring-props/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/03-props/01-declaring-props/app-b/src/lib/App.svelte deleted file mode 100644 index f64b69a87..000000000 --- a/content/tutorial/01-svelte/03-props/01-declaring-props/app-b/src/lib/App.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/content/tutorial/01-svelte/03-props/02-default-values/README.md b/content/tutorial/01-svelte/03-props/02-default-values/README.md index d6b44849d..2306e80c5 100644 --- a/content/tutorial/01-svelte/03-props/02-default-values/README.md +++ b/content/tutorial/01-svelte/03-props/02-default-values/README.md @@ -5,6 +5,7 @@ title: Default values We can easily specify default values for props in `Nested.svelte`: ```svelte +/// file: Nested.svelte @@ -13,6 +14,7 @@ We can easily specify default values for props in `Nested.svelte`: If we now add a second component _without_ an `answer` prop, it will fall back to the default: ```svelte +/// file: App.svelte -++++++ +++++++ ``` diff --git a/content/tutorial/01-svelte/03-props/03-spread-props/README.md b/content/tutorial/01-svelte/03-props/03-spread-props/README.md index 60ac0b11a..557ee2961 100644 --- a/content/tutorial/01-svelte/03-props/03-spread-props/README.md +++ b/content/tutorial/01-svelte/03-props/03-spread-props/README.md @@ -2,10 +2,25 @@ title: Spread props --- -If you have an object of properties, you can 'spread' them onto a component instead of specifying each one: +In this exercise, we've forgotten to specify the `version` prop expected by `PackageInfo.svelte`, meaning it shows 'version undefined'. + +We _could_ fix it by adding the `version` prop... + +```svelte +/// file: App.svelte + +``` + +...but since the properties of `pkg` correspond to the component's expected props, we can 'spread' them onto the component instead: ```svelte - +/// file: App.svelte + ``` > Conversely, if you need to reference all the props that were passed into a component, including ones that weren't declared with `export`, you can do so by accessing `$$props` directly. It's not generally recommended, as it's difficult for Svelte to optimise, but it's useful in rare cases. diff --git a/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/App.svelte index 2f500a521..2d5d7123b 100644 --- a/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/App.svelte @@ -3,15 +3,14 @@ const pkg = { name: 'svelte', - version: 3, speed: 'blazing', + version: 4, website: 'https://svelte.dev' }; diff --git a/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/PackageInfo.svelte b/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/PackageInfo.svelte index a879f76ca..51a30b8fa 100644 --- a/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/PackageInfo.svelte +++ b/content/tutorial/01-svelte/03-props/03-spread-props/app-a/src/lib/PackageInfo.svelte @@ -8,16 +8,6 @@

- The {name} package is {speed} fast. Download version {version} - from - npm - and learn more here + The {name} package is {speed} fast. Download version {version} from + npm and learn more here

- - diff --git a/content/tutorial/01-svelte/03-props/03-spread-props/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/03-props/03-spread-props/app-b/src/lib/App.svelte index 8518af09e..3ded4fd56 100644 --- a/content/tutorial/01-svelte/03-props/03-spread-props/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/03-props/03-spread-props/app-b/src/lib/App.svelte @@ -3,8 +3,8 @@ const pkg = { name: 'svelte', - version: 3, speed: 'blazing', + version: 4, website: 'https://svelte.dev' }; diff --git a/content/tutorial/01-svelte/03-props/meta.json b/content/tutorial/01-svelte/03-props/meta.json index d03bbc36a..3d316f177 100644 --- a/content/tutorial/01-svelte/03-props/meta.json +++ b/content/tutorial/01-svelte/03-props/meta.json @@ -2,7 +2,6 @@ "title": "Props", "scope": { "prefix": "/src/lib/", - "depth": 2, "name": "src" }, "focus": "/src/lib/App.svelte" diff --git a/content/tutorial/01-svelte/04-logic/01-if-blocks/README.md b/content/tutorial/01-svelte/04-logic/01-if-blocks/README.md index 5fe864050..845b4ab99 100644 --- a/content/tutorial/01-svelte/04-logic/01-if-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/01-if-blocks/README.md @@ -4,20 +4,18 @@ title: If blocks HTML doesn't have a way of expressing _logic_, like conditionals and loops. Svelte does. -To conditionally render some markup, we wrap it in an `if` block: +To conditionally render some markup, we wrap it in an `if` block. Let's add some text that appears when `count` is greater than `10`: ```svelte -+++{#if user.loggedIn}+++ - -+++{/if}+++ +/// file: App.svelte + -+++{#if !user.loggedIn}+++ - -+++{/if}+++ ++++{#if count > 10} +

{count} is greater than 10

+{/if}+++ ``` -Try it — update the component, and click on the buttons. +Try it — update the component, and click on the button. diff --git a/content/tutorial/01-svelte/04-logic/01-if-blocks/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/01-if-blocks/app-a/src/lib/App.svelte index b955f6d0d..bb0ebe43a 100644 --- a/content/tutorial/01-svelte/04-logic/01-if-blocks/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/01-if-blocks/app-a/src/lib/App.svelte @@ -1,15 +1,12 @@ - - - diff --git a/content/tutorial/01-svelte/04-logic/01-if-blocks/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/01-if-blocks/app-b/src/lib/App.svelte index 08f672877..6f6ab28d2 100644 --- a/content/tutorial/01-svelte/04-logic/01-if-blocks/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/01-if-blocks/app-b/src/lib/App.svelte @@ -1,19 +1,16 @@ -{#if user.loggedIn} - -{/if} + -{#if !user.loggedIn} - -{/if} +{#if count > 10} +

{count} is greater than 10

+{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/02-else-blocks/README.md b/content/tutorial/01-svelte/04-logic/02-else-blocks/README.md index a10eb4806..f342c1188 100644 --- a/content/tutorial/01-svelte/04-logic/02-else-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/02-else-blocks/README.md @@ -2,17 +2,14 @@ title: Else blocks --- -Since the two conditions — `if user.loggedIn` and `if !user.loggedIn` — are mutually exclusive, we can simplify this component slightly by using an `else` block: +Just like in JavaScript, an `if` block can have an `else` block: ```svelte -{#if user.loggedIn} - -+++{:else}+++ - +/// file: App.svelte +{#if count > 10} +

{count} is greater than 10

++++{:else} +

{count} is between 0 and 10

+++ {/if} ``` diff --git a/content/tutorial/01-svelte/04-logic/02-else-blocks/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/02-else-blocks/app-a/src/lib/App.svelte deleted file mode 100644 index 08f672877..000000000 --- a/content/tutorial/01-svelte/04-logic/02-else-blocks/app-a/src/lib/App.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - -{#if user.loggedIn} - -{/if} - -{#if !user.loggedIn} - -{/if} diff --git a/content/tutorial/01-svelte/04-logic/02-else-blocks/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/02-else-blocks/app-b/src/lib/App.svelte index 8e3195685..d82dadb10 100644 --- a/content/tutorial/01-svelte/04-logic/02-else-blocks/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/02-else-blocks/app-b/src/lib/App.svelte @@ -1,17 +1,18 @@ -{#if user.loggedIn} - + + +{#if count > 10} +

{count} is greater than 10

{:else} - -{/if} +

{count} is between 0 and 10

+{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/README.md b/content/tutorial/01-svelte/04-logic/03-else-if-blocks/README.md index cd482d603..22ea202a9 100644 --- a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/03-else-if-blocks/README.md @@ -5,11 +5,12 @@ title: Else-if blocks Multiple conditions can be 'chained' together with `else if`: ```svelte -{#if x > 10} -

{x} is greater than 10

-{:+++else if+++ 5 > x} -

{x} is less than 5

+/// file: App.svelte +{#if count > 10} +

{count} is greater than 10

++++{:else if count < 5} +

{count} is less than 5

+++ {:else} -

{x} is between 5 and 10

+

{count} is between +++5+++ and 10

{/if} ``` diff --git a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-a/src/lib/App.svelte deleted file mode 100644 index efffa9af2..000000000 --- a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-a/src/lib/App.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -{#if x > 10} -

{x} is greater than 10

-{:else} - {#if 5 > x} -

{x} is less than 5

- {:else} -

{x} is between 5 and 10

- {/if} -{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-b/src/lib/App.svelte index 9102618ff..0f52805e6 100644 --- a/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/03-else-if-blocks/app-b/src/lib/App.svelte @@ -1,11 +1,20 @@ -{#if x > 10} -

{x} is greater than 10

-{:else if 5 > x} -

{x} is less than 5

+ + +{#if count > 10} +

{count} is greater than 10

+{:else if count < 5} +

{count} is less than 5

{:else} -

{x} is between 5 and 10

+

{count} is between 5 and 10

{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/04-each-blocks/README.md b/content/tutorial/01-svelte/04-logic/04-each-blocks/README.md index f277503ce..3011cbb8f 100644 --- a/content/tutorial/01-svelte/04-logic/04-each-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/04-each-blocks/README.md @@ -2,28 +2,54 @@ title: Each blocks --- -If you need to loop over lists of data, use an `each` block: +When building user interfaces you'll often find yourself working with lists of data. In this exercise, we've repeated the ` + +++{/each}+++ + ``` -> The expression (`cats`, in this case) can be any array or array-like object (i.e. it has a `length` property). You can loop over generic iterables with `each [...iterable]`. +> The expression (`colors`, in this case) can be any array or array-like object (i.e. it has a `length` property). You can loop over generic iterables with `each [...iterable]`. -You can get the current _index_ as a second argument, like so: +Now we need to use the `color` variable in place of `"red"`: ```svelte -{#each cats as cat, i} -
  • - {i + 1}: {cat.name} -
  • -{/each} +/// file: App.svelte +
    + {#each colors as color} + + {/each} +
    ``` -If you prefer, you can use destructuring — `each cats as { id, name }` — and replace `cat.id` and `cat.name` with `id` and `name`. +You can get the current _index_ as a second argument, like so: + +```svelte +/// file: App.svelte +
    + {#each colors as color, +++i}+++ + + {/each} +
    +``` diff --git a/content/tutorial/01-svelte/04-logic/04-each-blocks/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/04-each-blocks/app-a/src/lib/App.svelte index 490c5f6c3..e469ed718 100644 --- a/content/tutorial/01-svelte/04-logic/04-each-blocks/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/04-each-blocks/app-a/src/lib/App.svelte @@ -1,31 +1,63 @@ -

    The Famous Cats of YouTube

    - - +

    Pick a colour

    + +
    + + + + + + + + + + + +
    + + \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/04-each-blocks/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/04-each-blocks/app-b/src/lib/App.svelte index 5676c3c59..468d57b50 100644 --- a/content/tutorial/01-svelte/04-logic/04-each-blocks/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/04-each-blocks/app-b/src/lib/App.svelte @@ -1,31 +1,45 @@ -

    The Famous Cats of YouTube

    +

    Pick a colour

    -
      - {#each cats as { id, name }, i} -
    • - - {i + 1}: {name} - -
    • +
      + {#each colors as color, i} + {/each} -
    + + + \ No newline at end of file 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 d6c0aa3c2..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,20 +2,26 @@ 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 removes the first `` component, but the _last_ DOM node. Then it updates the `name` value in the remaining DOM nodes, but not the emoji. +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 -{#each things as thing (thing.id)} +/// file: App.svelte +{#each things as thing (+++thing.id+++)} {/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/04-logic/05-keyed-each-blocks/app-a/src/lib/Thing.svelte b/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-a/src/lib/Thing.svelte index 8bf862b30..96f48a8d2 100644 --- a/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-a/src/lib/Thing.svelte +++ b/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-a/src/lib/Thing.svelte @@ -10,30 +10,9 @@ // the name is updated whenever the prop value changes... export let name; - // ...but the "emoji" variable is fixed upon initialisation of the component + // ...but the "emoji" variable is fixed upon initialisation + // of the component because it uses `const` instead of `$:` const emoji = emojis[name]; -

    - The emoji for {name} is {emoji} -

    - - +

    {emoji} = {name}

    \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-b/src/lib/Thing.svelte b/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-b/src/lib/Thing.svelte deleted file mode 100644 index 3445e7208..000000000 --- a/content/tutorial/01-svelte/04-logic/05-keyed-each-blocks/app-b/src/lib/Thing.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - -

    - The emoji for {name} is {emoji} -

    - - diff --git a/content/tutorial/01-svelte/04-logic/06-await-blocks/README.md b/content/tutorial/01-svelte/04-logic/06-await-blocks/README.md index 444d292d8..37f5871b8 100644 --- a/content/tutorial/01-svelte/04-logic/06-await-blocks/README.md +++ b/content/tutorial/01-svelte/04-logic/06-await-blocks/README.md @@ -5,13 +5,14 @@ title: Await blocks Most web applications have to deal with asynchronous data at some point. Svelte makes it easy to _await_ the value of [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) directly in your markup: ```svelte -{#await promise} +/// file: App.svelte ++++{#await promise}+++

    ...waiting

    -{:then number} ++++{:then number}

    The number is {number}

    {:catch error}

    {error.message}

    -{/await} +{/await}+++ ``` > Only the most recent `promise` is considered, meaning you don't need to worry about race conditions. @@ -19,7 +20,8 @@ Most web applications have to deal with asynchronous data at some point. Svelte If you know that your promise can't reject, you can omit the `catch` block. You can also omit the first block if you don't want to show anything until the promise resolves: ```svelte -{#await promise then value} -

    the value is {value}

    +/// no-file +{#await promise then number} +

    The number is {number}

    {/await} ``` diff --git a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/App.svelte index 524f9bfa0..a3030a8b3 100644 --- a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/App.svelte @@ -1,16 +1,5 @@ - + - -

    {promise}

    +

    ...waiting

    diff --git a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/utils.js b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/utils.js new file mode 100644 index 000000000..5964cb852 --- /dev/null +++ b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/lib/utils.js @@ -0,0 +1,12 @@ +export async function getRandomNumber() { + // Fetch a random number between 0 and 100 + // (with a delay, so that we can see it) + const res = await fetch('/random-number'); + + if (res.ok) { + return await res.text(); + } else { + // Sometimes the API will fail! + throw new Error('Request failed'); + } +} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/routes/random-number/+server.js b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/routes/random-number/+server.js new file mode 100644 index 000000000..e051d80bd --- /dev/null +++ b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-a/src/routes/random-number/+server.js @@ -0,0 +1,23 @@ +export async function GET(req) { + const query = req.url.searchParams; + let min = query.get('min') || '0'; + let max = query.get('max') || '100'; + min = +min; + max = +max; + + // simulate a long delay + await new Promise((res) => setTimeout(res, 1000)); + + // fail sometimes + if (Math.random() < 0.333) { + return new Response(`Failed to generate random number. Please try again`, { + status: 400, + headers: { 'Access-Control-Allow-Origin': '*' } + }); + } + + const num = min + Math.round(Math.random() * (max - min)); + return new Response(String(num), { + headers: { 'Access-Control-Allow-Origin': '*' } + }); +} \ No newline at end of file diff --git a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-b/src/lib/App.svelte index 7a0cd07fd..c538f17a3 100644 --- a/content/tutorial/01-svelte/04-logic/06-await-blocks/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/04-logic/06-await-blocks/app-b/src/lib/App.svelte @@ -1,16 +1,5 @@ - + {#await promise}

    ...waiting

    diff --git a/content/tutorial/01-svelte/04-logic/meta.json b/content/tutorial/01-svelte/04-logic/meta.json index 337f17523..d5f8e11b9 100644 --- a/content/tutorial/01-svelte/04-logic/meta.json +++ b/content/tutorial/01-svelte/04-logic/meta.json @@ -2,7 +2,6 @@ "title": "Logic", "scope": { "prefix": "/src/lib/", - "depth": 2, "name": "src" }, "focus": "/src/lib/App.svelte" diff --git a/content/tutorial/01-svelte/05-events/01-dom-events/README.md b/content/tutorial/01-svelte/05-events/01-dom-events/README.md index 64cfa3217..3a66bf114 100644 --- a/content/tutorial/01-svelte/05-events/01-dom-events/README.md +++ b/content/tutorial/01-svelte/05-events/01-dom-events/README.md @@ -2,10 +2,11 @@ title: DOM events --- -As we've briefly seen already, you can listen to any event on an element with the `on:` directive: +As we've briefly seen already, you can listen to any DOM event on an element (such as click or [pointermove](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event)) with the `on:` directive: ```svelte -
    - The mouse position is {m.x} x {m.y} +/// file: App.svelte +
    + The pointer is at {m.x} x {m.y}
    ``` diff --git a/content/tutorial/01-svelte/05-events/01-dom-events/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/01-dom-events/app-a/src/lib/App.svelte index 5a020e0ea..e7ebb525c 100644 --- a/content/tutorial/01-svelte/05-events/01-dom-events/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/01-dom-events/app-a/src/lib/App.svelte @@ -1,19 +1,23 @@
    - 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/01-dom-events/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/01-dom-events/app-b/src/lib/App.svelte index 54470a9a5..965ac646e 100644 --- a/content/tutorial/01-svelte/05-events/01-dom-events/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/01-dom-events/app-b/src/lib/App.svelte @@ -1,19 +1,23 @@ -
    - 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/02-inline-handlers/README.md b/content/tutorial/01-svelte/05-events/02-inline-handlers/README.md index 40585909f..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 @@ -5,11 +5,23 @@ title: Inline handlers You can also declare event handlers inline: ```svelte -
    - The mouse position is {m.x} x {m.y} +/// file: App.svelte + + +
    { + m = { x: e.clientX, y: e.clientY }; + }+++} +> + The pointer is at {m.x} x {m.y}
    ``` -The quote marks are optional, but they're helpful for syntax highlighting in some environments. - > In some frameworks you may see recommendations to avoid inline event handlers for performance reasons, particularly inside loops. That advice doesn't apply to Svelte — the compiler will always do the right thing, whichever form you choose. diff --git a/content/tutorial/01-svelte/05-events/02-inline-handlers/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/02-inline-handlers/app-a/src/lib/App.svelte deleted file mode 100644 index 54470a9a5..000000000 --- a/content/tutorial/01-svelte/05-events/02-inline-handlers/app-a/src/lib/App.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - -
    - The mouse position is {m.x} x {m.y} -
    - - diff --git a/content/tutorial/01-svelte/05-events/02-inline-handlers/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/02-inline-handlers/app-b/src/lib/App.svelte index 4c9fd299f..fb776a4c1 100644 --- a/content/tutorial/01-svelte/05-events/02-inline-handlers/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/02-inline-handlers/app-b/src/lib/App.svelte @@ -3,16 +3,20 @@
    { + on:pointermove={(e) => { 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/03-event-modifiers/README.md b/content/tutorial/01-svelte/05-events/03-event-modifiers/README.md index 71954b2fc..5cccee218 100644 --- a/content/tutorial/01-svelte/05-events/03-event-modifiers/README.md +++ b/content/tutorial/01-svelte/05-events/03-event-modifiers/README.md @@ -5,13 +5,8 @@ title: Event modifiers DOM event handlers can have _modifiers_ that alter their behaviour. For example, a handler with a `once` modifier will only run a single time: ```svelte - - - ``` @@ -22,9 +17,9 @@ The full list of modifiers: - `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element - `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so) - `nonpassive` — explicitly set `passive: false` -- `capture` — fires the handler during the _capture_ phase instead of the _bubbling_ phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture)) +- `capture` — fires the handler during the [_capture_](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_capture) phase instead of the [_bubbling_](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling) phase - `once` — remove the handler after the first time it runs - `self` — only trigger handler if event.target is the element itself -- `trusted` — only trigger handler if `event.isTrusted` is `true`. I.e. if the event is triggered by a user action. +- `trusted` — only trigger handler if `event.isTrusted` is `true`, meaning the event was triggered by a user action rather than because some JavaScript called `element.dispatchEvent(...)` You can chain modifiers together, e.g. `on:click|once|capture={...}`. diff --git a/content/tutorial/01-svelte/05-events/03-event-modifiers/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/03-event-modifiers/app-a/src/lib/App.svelte index 0a000c832..74eb81edb 100644 --- a/content/tutorial/01-svelte/05-events/03-event-modifiers/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/03-event-modifiers/app-a/src/lib/App.svelte @@ -1,7 +1,3 @@ - - - + diff --git a/content/tutorial/01-svelte/05-events/03-event-modifiers/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/03-event-modifiers/app-b/src/lib/App.svelte index 3818a1f79..d7014608f 100644 --- a/content/tutorial/01-svelte/05-events/03-event-modifiers/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/05-events/03-event-modifiers/app-b/src/lib/App.svelte @@ -1,9 +1,3 @@ - - - diff --git a/content/tutorial/01-svelte/05-events/04-component-events/README.md b/content/tutorial/01-svelte/05-events/04-component-events/README.md index 0df2bc68f..12457f8ed 100644 --- a/content/tutorial/01-svelte/05-events/04-component-events/README.md +++ b/content/tutorial/01-svelte/05-events/04-component-events/README.md @@ -5,10 +5,11 @@ title: Component events Components can also dispatch events. To do so, they must create an event dispatcher. Update `Inner.svelte`: ```svelte +/// file: Inner.svelte - + diff --git a/content/tutorial/01-svelte/05-events/04-component-events/app-a/src/lib/Inner.svelte b/content/tutorial/01-svelte/05-events/04-component-events/app-a/src/lib/Inner.svelte index df4ab85fe..103fac59b 100644 --- a/content/tutorial/01-svelte/05-events/04-component-events/app-a/src/lib/Inner.svelte +++ b/content/tutorial/01-svelte/05-events/04-component-events/app-a/src/lib/Inner.svelte @@ -1,7 +1,9 @@ diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/README.md b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/README.md index 6932c2994..245d1177b 100644 --- a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/README.md +++ b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/README.md @@ -4,10 +4,11 @@ title: DOM event forwarding Event forwarding works for DOM events too. -We want to get notified of clicks on our `` — to do that, we just need to forward `click` events on the ` ``` 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 f7dc6576b..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 @@ -1,9 +1,14 @@ - + diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/BigRedButton.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/BigRedButton.svelte new file mode 100644 index 000000000..471695245 --- /dev/null +++ b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/BigRedButton.svelte @@ -0,0 +1,25 @@ + + + diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/CustomButton.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/CustomButton.svelte deleted file mode 100644 index d6b1f44f5..000000000 --- a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/CustomButton.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/horn.mp3 b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/horn.mp3 new file mode 100644 index 000000000..3399767ac Binary files /dev/null and b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-a/src/lib/horn.mp3 differ diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/App.svelte deleted file mode 100644 index f7dc6576b..000000000 --- a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/App.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/BigRedButton.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/BigRedButton.svelte new file mode 100644 index 000000000..a7e4c919b --- /dev/null +++ b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/BigRedButton.svelte @@ -0,0 +1,25 @@ + + + diff --git a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/CustomButton.svelte b/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/CustomButton.svelte deleted file mode 100644 index d793155f1..000000000 --- a/content/tutorial/01-svelte/05-events/06-dom-event-forwarding/app-b/src/lib/CustomButton.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/content/tutorial/01-svelte/05-events/meta.json b/content/tutorial/01-svelte/05-events/meta.json index 1ca99233f..032bab895 100644 --- a/content/tutorial/01-svelte/05-events/meta.json +++ b/content/tutorial/01-svelte/05-events/meta.json @@ -2,7 +2,6 @@ "title": "Events", "scope": { "prefix": "/src/lib/", - "depth": 2, "name": "src" }, "focus": "/src/lib/App.svelte" diff --git a/content/tutorial/01-svelte/06-bindings/01-text-inputs/README.md b/content/tutorial/01-svelte/06-bindings/01-text-inputs/README.md index ea806586c..c3a37ccdd 100644 --- a/content/tutorial/01-svelte/06-bindings/01-text-inputs/README.md +++ b/content/tutorial/01-svelte/06-bindings/01-text-inputs/README.md @@ -9,7 +9,8 @@ Sometimes it's useful to break that rule. Take the case of the `` element Instead, we can use the `bind:value` directive: ```svelte - +/// file: App.svelte + ``` This means that not only will changes to the value of `name` update the input value, but changes to the input value will update `name`. diff --git a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/README.md b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/README.md index 9e4ed04b8..3cc1baba7 100644 --- a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/README.md +++ b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/README.md @@ -7,6 +7,14 @@ In the DOM, everything is a string. That's unhelpful when you're dealing with nu With `bind:value`, Svelte takes care of it for you: ```svelte - - +/// file: App.svelte + + + ``` diff --git a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-a/src/lib/App.svelte b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-a/src/lib/App.svelte index 3b790cfee..ad88f30d2 100644 --- a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-a/src/lib/App.svelte +++ b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-a/src/lib/App.svelte @@ -4,46 +4,13 @@

    {a} + {b} = {a + b}

    - - diff --git a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-b/src/lib/App.svelte index e24a9c1a9..15ef23c46 100644 --- a/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/06-bindings/02-numeric-inputs/app-b/src/lib/App.svelte @@ -4,46 +4,13 @@

    {a} + {b} = {a + b}

    - - diff --git a/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/README.md b/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/README.md index 1feac27f9..dc79573de 100644 --- a/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/README.md +++ b/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/README.md @@ -5,5 +5,6 @@ title: Checkbox inputs Checkboxes are used for toggling between states. Instead of binding to `input.value`, we bind to `input.checked`: ```svelte - +/// file: App.svelte + ``` diff --git a/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/app-b/src/lib/App.svelte index eec7e0363..5cc7a437f 100644 --- a/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/app-b/src/lib/App.svelte +++ b/content/tutorial/01-svelte/06-bindings/03-checkbox-inputs/app-b/src/lib/App.svelte @@ -19,4 +19,4 @@

    {/if} - + diff --git a/content/tutorial/01-svelte/06-bindings/04-group-inputs/README.md b/content/tutorial/01-svelte/06-bindings/04-group-inputs/README.md deleted file mode 100644 index e721080df..000000000 --- a/content/tutorial/01-svelte/06-bindings/04-group-inputs/README.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Group inputs ---- - -If you have multiple inputs relating to the same value, you can use `bind:group` along with the `value` attribute. Radio inputs in the same group are mutually exclusive; checkbox inputs in the same group form an array of selected values. - -Add `bind:group` to each input: - -```svelte - -``` - -In this case, we could make the code simpler by moving the checkbox inputs into an `each` block. First, add a `menu` variable to the ` - -

    Size

    - - - - - - - -

    Flavours

    - - - - - - - -{#if flavours.length === 0} -

    Please select at least one flavour

    -{:else if flavours.length > scoops} -

    Can't order more flavours than scoops!

    -{:else} -

    - You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'} - of {join(flavours)} -

    -{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/06-bindings/04-group-inputs/app-b/src/lib/App.svelte b/content/tutorial/01-svelte/06-bindings/04-group-inputs/app-b/src/lib/App.svelte deleted file mode 100644 index d97db41f0..000000000 --- a/content/tutorial/01-svelte/06-bindings/04-group-inputs/app-b/src/lib/App.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - -

    Size

    - - - - - - - -

    Flavours

    - -{#each menu as flavour} - -{/each} - -{#if flavours.length === 0} -

    Please select at least one flavour

    -{:else if flavours.length > scoops} -

    Can't order more flavours than scoops!

    -{:else} -

    - You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'} - of {join(flavours)} -

    -{/if} \ No newline at end of file diff --git a/content/tutorial/01-svelte/06-bindings/06-select-bindings/README.md b/content/tutorial/01-svelte/06-bindings/04-select-bindings/README.md similarity index 73% rename from content/tutorial/01-svelte/06-bindings/06-select-bindings/README.md rename to content/tutorial/01-svelte/06-bindings/04-select-bindings/README.md index fcf4fb9e1..d6c22c998 100644 --- a/content/tutorial/01-svelte/06-bindings/06-select-bindings/README.md +++ b/content/tutorial/01-svelte/06-bindings/04-select-bindings/README.md @@ -2,10 +2,14 @@ title: Select bindings --- -We can also use `bind:value` with `` elements: ```svelte - answer = ''} +> ``` Note that the `
    @@ -46,24 +46,12 @@ .board > input { font-size: 1.4em; grid-column: 1/3; - border-radius: 5px; - background: #f4f4f4; padding: 0.5em; - border: none; + margin: 0 0 1rem 0; } h2 { font-size: 2em; font-weight: 200; } - - .todo { - --label: #222; - --filter: drop-shadow(2px 3px 6px rgba(0,0,0,0.1)); - } - - .done { - --label: #999; - --filter: none; - } diff --git a/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/TodoList.svelte b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/TodoList.svelte new file mode 100644 index 000000000..ddd1fcfe3 --- /dev/null +++ b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/TodoList.svelte @@ -0,0 +1,40 @@ + + +
      + {#each $store.filter((todo) => todo.done === done) as todo (todo.id)} +
    • + +
    • + {/each} +
    + + \ No newline at end of file diff --git a/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/remove.svg b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/remove.svg new file mode 100644 index 000000000..434ab0956 --- /dev/null +++ b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/remove.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/content/tutorial/03-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/todos.js b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/todos.js similarity index 100% rename from content/tutorial/03-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/todos.js rename to content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/todos.js diff --git a/content/tutorial/03-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/transition.js b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/transition.js similarity index 100% rename from content/tutorial/03-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/transition.js rename to content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-a/src/lib/transition.js diff --git a/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-b/src/lib/TodoList.svelte b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-b/src/lib/TodoList.svelte new file mode 100644 index 000000000..680de1fd9 --- /dev/null +++ b/content/tutorial/02-advanced-svelte/02-transitions/09-deferred-transitions/app-b/src/lib/TodoList.svelte @@ -0,0 +1,44 @@ + + +
      + {#each $store.filter((todo) => todo.done === done) as todo (todo.id)} +
    • + +
    • + {/each} +
    + + \ No newline at end of file diff --git a/content/tutorial/03-advanced-svelte/02-transitions/meta.json b/content/tutorial/02-advanced-svelte/02-transitions/meta.json similarity index 89% rename from content/tutorial/03-advanced-svelte/02-transitions/meta.json rename to content/tutorial/02-advanced-svelte/02-transitions/meta.json index 42bc14fd3..94572f005 100644 --- a/content/tutorial/03-advanced-svelte/02-transitions/meta.json +++ b/content/tutorial/02-advanced-svelte/02-transitions/meta.json @@ -2,7 +2,6 @@ "title": "Transitions", "scope": { "prefix": "/src/lib/", - "depth": 2, "name": "src" }, "focus": "/src/lib/App.svelte" diff --git a/content/tutorial/03-advanced-svelte/03-animations/01-animate/README.md b/content/tutorial/02-advanced-svelte/03-animations/01-animate/README.md similarity index 88% rename from content/tutorial/03-advanced-svelte/03-animations/01-animate/README.md rename to content/tutorial/02-advanced-svelte/03-animations/01-animate/README.md index 70c1cbe74..53f0221a1 100644 --- a/content/tutorial/03-advanced-svelte/03-animations/01-animate/README.md +++ b/content/tutorial/02-advanced-svelte/03-animations/01-animate/README.md @@ -9,19 +9,22 @@ To complete the illusion, we also need to apply motion to the elements that _are First, import the `flip` function — flip stands for ['First, Last, Invert, Play'](https://aerotwist.com/blog/flip-your-animations/) — from `svelte/animate` into `TodoList.svelte`: ```svelte +/// file: TodoList.svelte ``` -Then add it to the `