Merge branch 'sveltejs:master' into master

pull/6928/head
andirady 5 years ago committed by GitHub
commit d79fa7e760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

11
.gitignore vendored

@ -23,14 +23,3 @@ node_modules
_actual*.*
_output
/types
/site/.svelte-kit/
/site/build/
/site/.env
/site/static/svelte-app.json
/site/static/contributors.jpg
/site/static/donors.jpg
/site/static/workers/
/site/scripts/svelte-app/
/site/src/routes/_contributors.js
/site/src/routes/_donors.js

@ -1,9 +1,17 @@
# Svelte changelog
## Unreleased
## 3.44.3
* Fix `bind:this` binding inside `onMount` for manually instantiated component ([#6760](https://github.com/sveltejs/svelte/issues/6760))
* Prevent cursor jumps with one-way binding for other `type="text"`-like `<input>`s ([#6941](https://github.com/sveltejs/svelte/pull/6941))
* Exclude `async` loops from `loopGuardTimeout` ([#6945](https://github.com/sveltejs/svelte/issues/6945))
## 3.44.2
* Fix overly restrictive preprocessor types ([#6904](https://github.com/sveltejs/svelte/pull/6904))
* More specific typing for crossfade function - returns a tuple, not an array ([#6926](https://github.com/sveltejs/svelte/issues/6926))
* Add `URLSearchParams` as a known global ([#6938](https://github.com/sveltejs/svelte/pull/6938))
* Add `types` field to `exports` map ([#6939](https://github.com/sveltejs/svelte/issues/6939))
## 3.44.1

@ -64,7 +64,7 @@ npm run test -- -g transition
## svelte.dev
The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [Sapper](https://sapper.svelte.dev).
The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [SvelteKit](https://kit.svelte.dev).
### Is svelte.dev down?

4
package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "svelte",
"version": "3.44.1",
"version": "3.44.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "svelte",
"version": "3.44.1",
"version": "3.44.3",
"license": "MIT",
"devDependencies": {
"@ampproject/remapping": "^0.3.0",

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.44.1",
"version": "3.44.3",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
@ -22,6 +22,7 @@
"exports": {
"./package.json": "./package.json",
".": {
"types": "./types/runtime/index.d.ts",
"browser": {
"import": "./index.mjs",
"require": "./index.js"
@ -34,22 +35,27 @@
"require": "./index.js"
},
"./compiler": {
"types": "./types/compiler/index.d.ts",
"import": "./compiler.mjs",
"require": "./compiler.js"
},
"./animate": {
"types": "./types/runtime/animate/index.d.ts",
"import": "./animate/index.mjs",
"require": "./animate/index.js"
},
"./easing": {
"types": "./types/runtime/easing/index.d.ts",
"import": "./easing/index.mjs",
"require": "./easing/index.js"
},
"./internal": {
"types": "./types/runtime/internal/index.d.ts",
"import": "./internal/index.mjs",
"require": "./internal/index.js"
},
"./motion": {
"types": "./types/runtime/motion/index.d.ts",
"import": "./motion/index.mjs",
"require": "./motion/index.js"
},
@ -57,14 +63,17 @@
"require": "./register.js"
},
"./store": {
"types": "./types/runtime/store/index.d.ts",
"import": "./store/index.mjs",
"require": "./store/index.js"
},
"./transition": {
"types": "./types/runtime/transition/index.d.ts",
"import": "./transition/index.mjs",
"require": "./transition/index.js"
},
"./ssr": {
"types": "./types/runtime/index.d.ts",
"import": "./ssr.mjs",
"require": "./ssr.js"
}

@ -1,7 +0,0 @@
/*
!/Dockerfile
!/package.json
!/package-lock.json
!/build
!/static
!/content

@ -1,13 +0,0 @@
NODE_ENV=
PORT=3000
BASEURL=http://localhost:3000
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
VITE_MAPBOX_ACCESS_TOKEN=
PGHOST=localhost
PGPORT=5432
PGUSER=username
PGPASSWORD=password
PGDATABASE=database_name

@ -1,58 +0,0 @@
module.exports = {
root: true,
rules: {
indent: [2, 'tab', { SwitchCase: 1 }],
semi: [2, 'always'],
'keyword-spacing': [2, { before: true, after: true }],
'space-before-blocks': [2, 'always'],
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
'no-cond-assign': 0,
'no-unused-vars': 2,
'object-shorthand': [2, 'always'],
'no-const-assign': 2,
'no-class-assign': 2,
'no-this-before-super': 2,
'no-var': 2,
'no-unreachable': 2,
'valid-typeof': 2,
'quote-props': [2, 'as-needed'],
'one-var': [2, 'never'],
'prefer-arrow-callback': 2,
'prefer-const': [2, { destructuring: 'all' }],
'arrow-spacing': 2,
'no-inner-declarations': 0,
'require-atomic-updates': 0
},
env: {
es6: true,
browser: true,
node: true,
mocha: true
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings'
],
plugins: ['svelte3'],
overrides: [
{
files: ['*.svelte'],
processor: 'svelte3/svelte3'
}
],
parserOptions: {
ecmaVersion: 9,
sourceType: 'module'
},
settings: {
'import/core-modules': ['svelte'],
'svelte3/compiler': (() => {
try {
return require('svelte/compiler');
} catch (e) {
return null;
}
})()
}
};

@ -1 +0,0 @@
#!include:.dockerignore

@ -1,22 +0,0 @@
# IMPORTANT: Don't use this Dockerfile in your own projects without also looking at the .dockerignore file.
# Without an appropriate .dockerignore, this Dockerfile will copy a large number of unneeded files into your image.
FROM mhart/alpine-node:14
# install dependencies
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
###
# Only copy over the Node pieces we need
# ~> Saves 35MB
###
FROM mhart/alpine-node:slim-14
WORKDIR /app
COPY --from=0 /app .
COPY . .
EXPOSE 3000
CMD ["node", "build"]

@ -1,22 +0,0 @@
HASH := `git rev-parse --short HEAD`
SERVICE := svelte-website
PROJECT := svelte-dev
IMAGE := gcr.io/$(PROJECT)/$(SERVICE):$(HASH)
sveltekit:
@echo "\n~> updating template & contributors list"
@npm run update
@echo "\n~> building SvelteKit app"
@npm run build
docker:
@echo "\n~> building docker image"
@gcloud builds submit --project $(PROJECT) -t $(IMAGE)
deploy: sveltekit docker
@echo "\n~> deploying $(SERVICE) to Cloud Run servers"
@gcloud run deploy $(SERVICE) --project $(PROJECT) --allow-unauthenticated --platform managed --region us-central1 --image $(IMAGE) --memory=512Mi

@ -1,84 +0,0 @@
## Running locally
Set up the site sub-project:
```bash
git clone https://github.com/sveltejs/svelte.git
cd site
npm ci
npm run dev
```
and navigate to [localhost:3000](http://localhost:3000).
The first time you run the site locally, it will update the list of Contributors and REPL dependencies. After this it won't run again unless you force it by running:
```bash
npm run update
```
## Running using the local copy of Svelte
By default, the REPL will fetch the most recent version of Svelte from https://unpkg.com/svelte. When running the site locally, you can also use your local copy of Svelte.
To produce the proper browser-compatible UMD build of the compiler, you will need to run `npm run build` (or `npm run dev`) in the root of this repository with the `PUBLISH` environment variable set to any non-empty string:
```bash
git clone https://github.com/sveltejs/svelte.git
cd svelte
npm ci
PUBLISH=1 npm run build
cd site
npm ci
npm run dev
```
Then visit the REPL at [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). Please note that the local REPL only works with `npm run dev` and not when building the site for production usage.
## REPL GitHub integration
In order for the REPL's GitHub integration to work properly when running locally, you will need to:
- [create a GitHub OAuth app](https://github.com/settings/developers):
- set `Authorization callback URL` to `http://localhost:3000/auth/callback`;
- set `Application name` as you like, and `Homepage URL` as `http://localhost:3000/`;
- create the app and take note of `Client ID` and `Client Secret`
- in this repo, create `site/.env` containing:
```
GITHUB_CLIENT_ID=[your app's Client ID]
GITHUB_CLIENT_SECRET=[your app's Client Secret]
BASEURL=http://localhost:3000
```
## Building the site
To build the website, run `npm run build`. The output can be found in `build`.
## Testing
Tests can be run using `npm run test`.
## Linking `@sveltejs/site-kit` and `@sveltejs/svelte-repl`
This site depends on `@sveltejs/site-kit` (a collection of styles, components and icons used in common by *.svelte.dev websites), and `@sveltejs/svelte-repl`.
In order to work on features that depend on those packages, you need to [link](https://docs.npmjs.com/cli/link) their repositories:
- `cd <somewhere>`
- `git clone https://github.com/sveltejs/site-kit`
- `git clone https://github.com/sveltejs/svelte-repl`
- `cd <somewhere>/site-kit`
- `npm link`
- `cd <somewhere>/svelte-repl`
- `npm link`
- `cd <svelte-repo>/site`
- `npm link @sveltejs/site-kit`
- `npm link @sveltejs/svelte-repl`
## Translating the API docs
Anchors are automatically generated using headings in the documentation and by default (for the english language) they are latinised to make sure the URL is always conforming to RFC3986.
If we need to translate the API documentation to a language using unicode chars, we can setup this app to export the correct anchors by setting up `SLUG_PRESERVE_UNICODE` to `true` in `config.js`.

@ -1,2 +0,0 @@
export const SLUG_PRESERVE_UNICODE = false;
export const SLUG_SEPARATOR = '_';

@ -10,7 +10,7 @@ It's the last "What's new in Svelte" of the year and there's lots to celebrate!
## New features & impactful bug fixes
1. `$$props`, `$$restProps`, and `$$slots` are all now supported in custom web components (**3.29.5**, [Example](https://svelte.dev/repl/ad8e6f39cd20403dacd1be84d71e498d?version=3.29.5)) and `slot` components now support spread props: `<slot {...foo} />` (**3.30.0**)
2. A new `hasContext` lifecycle function makes it easy to check whether a `key` has been set in the context of a parent component (**3.30.0** & **3.30.1**, [Docs](https://svelte.dev/docs#hasContext))
2. A new `hasContext` lifecycle function makes it easy to check whether a `key` has been set in the context of a parent component (**3.30.0** & **3.30.1**, [Docs](https://svelte.dev/docs#run-time-svelte-hascontext))
3. There is now a new `SvelteComponentTyped` class which makes it easier to add strongly typed components that extend base Svelte components. Component library and framework authors rejoice! An example: `export class YourComponent extends SvelteComponentTyped<{aProp: boolean}, {click: MouseEvent}, {default: {aSlot: string}}> {}` (**3.31.0**, [RFC](https://github.com/sveltejs/rfcs/pull/37))
4. Transitions within `{:else}` blocks should now complete successfully (**3.29.5**, [Example](https://svelte.dev/repl/49cef205e5da459594ef2eafcbd41593?version=3.29.5))
5. Svelte now includes an export map, which explicitly states which files can be imported from its npm package (**3.29.5** with some fixes in **3.29.6**, **3.29.7** and **3.30.0**)

@ -10,13 +10,13 @@ Lots to cover this month with releases from across the Svelte ecosystem. Most im
Let's dive into the news 🐬
## What's new in `sveltejs/svelte`
* SSR store handling has been reworked to subscribe and unsubscribe as in DOM mode. SSR stores should work much more consistently now (**3.31.2**, see [custom stores](https://svelte.dev/examples#custom-stores) and [Server-side component API ](https://svelte.dev/docs#Server-side_component_API))
* SSR store handling has been reworked to subscribe and unsubscribe as in DOM mode. SSR stores should work much more consistently now (**3.31.2**, see [custom stores](https://svelte.dev/examples#custom-stores) and [Server-side component API ](https://svelte.dev/docs#run-time-server-side-component-api))
* Multiple instances of the same action are now allowed on an element (**3.32.0**, [example](https://svelte.dev/repl/01a14375951749dab9579cb6860eccde?version=3.32.0))
* The new `foreign` namespace should make it easier for alternative compile targets (like Svelte Native and SvelteGUI) by disabling certain HTML5-specific behaviour and checks (**3.32.0**, [more info](https://github.com/sveltejs/svelte/pull/5652))
* Support for inline comment sourcemaps in code from preprocessors (**3.32.0**)
* Destructured defaults are now allowed to refer to other variables (**3.33.0**, [example](https://svelte.dev/repl/0ee7227e1b45465b9b47d7a5ae2d1252?version=3.33.0))
* Custom elements will now call `onMount` functions when connecting and clean up when disconnecting (**3.33.0**, checkout [this PR](https://github.com/sveltejs/svelte/pull/4522) for an interesting conversation on how folks are using Svelte with Web Components)
* A `cssHash` option has been added to the compiler options to control the classname used for CSS scoping (**3.34.0**, [docs](https://svelte.dev/docs#svelte_compile))
* A `cssHash` option has been added to the compiler options to control the classname used for CSS scoping (**3.34.0**, [docs](https://svelte.dev/docs#compile-time-svelte-compile))
* Continued improvement to Typescript definitions
For a complete list of changes, including bug fixes and links to PRs, check out [the CHANGELOG](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md)

@ -13,7 +13,7 @@ Two projects that have been months (even years) in the making have made their wa
Want to learn more about how to get started, what's different compared to Sapper, new features and migration paths? Check out this week's [episode of Svelte Radio](https://www.svelteradio.com/episodes/svelte-kit-public-beta) for a deep dive with Antony, Kev and Swyx.
## New in Svelte & Language Tools
- Slotted components, including `<svelte:fragment slot="...">` lets component consumers target specific slots with rich content (**Svelte 3.35.0, Language Tools [104.5.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-104.5.0)**, check out the [docs](https://svelte.dev/docs#svelte_fragment) and the [tutorial](https://svelte.dev/tutorial/svelte-fragment))
- Slotted components, including `<svelte:fragment slot="...">` lets component consumers target specific slots with rich content (**Svelte 3.35.0, Language Tools [104.5.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-104.5.0)**, check out the [docs](https://svelte.dev/docs#template-syntax-svelte-fragment) and the [tutorial](https://svelte.dev/tutorial/svelte-fragment))
- Linked editing now works for HTML in Svelte files (**Language Tools, [104.6.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-104.6.0)**)
- Type definitions `svelte.d.ts` are now resolved in order, allowing library authors to ship type definitions with their svelte components (**Language Tools, [104.7.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-104.7.0)**)
- [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte) is available for general use of Svelte in Vite. `npm init @vitejs/app` includes Svelte options using this plugin.

@ -1,5 +1,5 @@
---
title: What's new in Svelte: May 2021
title: "What's new in Svelte: May 2021"
description: Working toward SvelteKit 1.0 and a showcase full of SvelteKit sites!
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: June 2021
title: "What's new in Svelte: June 2021"
description: Progress towards SvelteKit 1.0 and tighter TypeScript/Svelte integrations in language tools
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: July 2021
title: "What's new in Svelte: July 2021"
description: Keeping cool with fixes, TypeScript tooling and tonnes of new features
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: August 2021
title: "What's new in Svelte: August 2021"
description: Shadow DOM, export and await - oh my!
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: September 2021
title: "What's new in Svelte: September 2021"
description: StackOverflow's most loved web framework
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: October 2021
title: "What's new in Svelte: October 2021"
description: A whole year of "What's new in Svelte"
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -1,5 +1,5 @@
---
title: What's new in Svelte: November 2021
title: "What's new in Svelte: November 2021"
description: Over 5000 stars to light up the showcase
author: Daniel Sandoval
authorURL: https://desandoval.net

@ -0,0 +1,91 @@
---
title: "What's new in Svelte: December 2021"
description: "Svelte Summit Fall 2021 Recap, Rich Harris joins Vercel, and Kevin goes full-time on Svelte Society"
author: Daniel Sandoval
authorURL: https://desandoval.net
---
With SvelteKit getting more and more stable each day, there's not much to cover in terms of code changes other than bug fixes... So, in this month's newsletter, we'll be covering Svelte Summit Fall 2021!
If you want to dive deep into the last month's worth of bug fixes, check out the [Svelte](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) and [SvelteKit](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md) changelogs, respectively.
## What happened at Svelte Summit?
If you missed Svelte Summit, you can watch the entire live stream on [YouTube](https://www.youtube.com/watch?v=1Df-9EKvZr0) and catch a recap in the [#svelte-summit channel on Discord](https://discord.gg/YmHcdnhu).
Here are the highlights:
- [Rich Harris](https://twitter.com/rich_harris) took us through a tour of Svelte's history and announced [his move to Vercel](https://vercel.com/blog/vercel-welcomes-rich-harris-creator-of-svelte) - where he will be helping maintain Svelte full-time! ([20:00](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=1200s))
- [Steph Dietz](https://twitter.com/steph_dietz_) explained how Svelte's simple abstractions makes it easy for beginners and experts alike to learn and use JavaScript - without the boilerplate ([29:00](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=1740s))
- [Kevin Bridges](https://twitter.com/kevinast) dove deep into Svelte's reactivity logic by visualizing it through `ReflectiveCounter` and showing how to "fine tune" it, as needed. A full "syllabus" for the presentation is available on [Kevin's site](https://wiibridges.com/presentations/ResponsiveSvelte/). ([42:55](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=2575s))
- [Mateo Morris](https://twitter.com/_mateomorris) launched [Primo](https://primo.af/), an all-in-one SvelteKit CMS to help build and manage static sites ([1:12:34](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=4354s))
- [Guillermo Rauch](https://vercel.com/about/rauchg) explained Vercel's commitment to Svelte, what it means to have Rich on the team, and what's coming next from the company... ([1:21:54](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=4914s))
- [Geoff Rich](https://twitter.com/geoffrich_) introduced various ways to modify motion and transitions within Svelte to be more accessible to all users of the web. Slides and a full transcription of the talk are available on [Geoff's site](https://geoffrich.net/posts/svelte-summit-2021/). ([1:32:30](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=5550s))
- [Dean Fogarty](https://df.id.au/) demoed a number of different use-cases for custom stores - transforming data to and from storage mechanisms within Svelte. Transcript and code is available on [Dean's GitHub](https://github.com/angrytongan/svelte-summit-2021). ([1:43:06](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=6186s))
- [Kellen Mace](https://twitter.com/kellenmace) shared how we can let content creators keep using WordPress, while leveraging Svelte on the frontend to provide a phenomenal user experience ([1:49:30](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=6570ss))
- [Ben Holmes](https://twitter.com/bholmesdev) explained the "islands" architecture and how 11ty + [Slinkity](https://slinkity.dev/) can bring these islands to any HTML template ([2:17:15](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=8235s))
- [Scott Tolinski](https://twitter.com/stolinski) shared the lessons learned from rewriting the React-based LevelUpTutorials in Svelte and "found developer bliss" ([3:16:35](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=11795s))
- [Svelte Sirens](https://sveltesirens.dev) was announced as the new Svelte community for women, non-binary and allies. Their first event was on November 29th - all future events can be found on [the Svelte Sirens website](https://sveltesirens.dev/events) ([3:50:45](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=13845s))
- [Rich Harris](https://twitter.com/rich_harris) discussed creating libraries with SvelteKit, better ways to link packages when developing, and how SvelteKit helps with modern JavaScript library development ([3:56:00](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=14160s))
- [Ken Kunz](https://twitter.com/kennethkunz) explained how finite state machines (and the svelte-fsm library) can make managing Svelte component states more... manageable. Examples from the talk are available on [Ken's GitHub](https://github.com/kenkunz/svelte-fsm/wiki/Examples). ([4:07:18](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=14838s))
- [Austin Crim](https://twitter.com/crim_codes) connected learning to code on the web to learning how to play an instrument. By giving learners early wins and introducing the fundamentals through real-world apps, learning Svelte (and the fundamentals underneath) doesn't have to be a chore ([4:21:50](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=15710s))
- [Jesse Skinner](https://twitter.com/JesseSkinner) brought our legacy apps into the future by explaining how to use (and reuse) Svelte components within React (and even jQuery!) projects ([4:32:30](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=16350s))
- [Jim Fisk](https://twitter.com/jimafisk) and [Stephanie Luz](https://stephanie-luz.medium.com/) introduced [Plenti](https://plenti.co/) and its theming tools to make building new Svelte sites much faster ([4:59:00](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=17940s))
- [Evyatar Alush](https://twitter.com/evyataral) helped us all make (and maintain) better forms using a powerful validation library called [Vest](https://github.com/ealush/vest) ([5:08:55](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=18535s))
- Dominik G. presented a fresh take on icon libraries - one that reduces the bundle size of applications and opens up the entire iconify library for use in any Svelte app ([5:30:04](https://www.youtube.com/watch?v=1Df-9EKvZr0&t=19804s))
Thanks to [Kevin](https://twitter.com/kevmodrome) and all the Svelte Society volunteers for pulling together such an amazing event! Excitingly, [Kevin announced](https://twitter.com/kevmodrome/status/1463151477174714373) after the event that he will now be working full-time on Svelte Society! You can check out all the talks, broken up into individual videos for convenience, in [this Svelte Society YouTube Playlist](https://www.youtube.com/playlist?list=PL8bMgX1kyZTg2bI9IOMgfBc8lrU3v2itt).
If you have feedback on the Svelte Summit, Kev is [looking for feedback on the Svelte subreddit](https://www.reddit.com/r/sveltejs/comments/qzgo3k/svelte_summit_feedback/) 👀
---
## Community Showcase
**Apps & Sites**
- [pixeldrain](https://github.com/Fornaxian/pixeldrain_web) is a free-to-use file sharing platform
- [LifeHash](http://lifehash.info/) generates beautiful visual hashes from Blockchain Commons
- [simple-cloud-music](https://github.com/dufu1991/simple-cloud-music) is a lightweight third-party NetEase cloud music player for modern browsers (likely only works on Chrome)
- [palette.rocks](https://palette.rocks/) is a color palette generator with contrast-checking built-in
- [Kadium](https://github.com/probablykasper/kadium) is an app for staying on top of YouTube channel uploads
- [Multi-Monitor Calculator](https://multimonitorcalculator.com/) is a tool for planning your multi-monitor setup
- [Your Home](https://yourhome.fb.com/) is an interactive overview of Facebook's privacy settings
- [Svelte Crush](https://svelte-crush.netlify.app/) is a Candy Crush style match-3 game
- [100.000 Corona deaths in Germany](https://twitter.com/h_i_g_s_c_h/status/1463767113563353089?s=20) is a visualization made for Spiegel Gesundheit
**Looking for a Svelte project to work on? Interested in helping make Svelte's presence on the web better?** Check out [the list of open issues](https://github.com/svelte-society/sveltesociety-2021/issues) if you'd like to contribute to the Svelte Society rewrite in SvelteKit.
**Videos, Blogs and Podcasts**
- [How To Make and Publish a Svelte Library](https://www.youtube.com/watch?v=_TymiadmPrc)
- [SvelteKit is now fully supported in WebContainers](https://blog.stackblitz.com/posts/sveltekit-supported-in-webcontainers/)
- [Introducing Svelte, and Comparing Svelte with React and Vue](https://joshcollinsworth.com/blog/introducing-svelte-comparing-with-react-vue)
- [Testing a Svelte app with Jest](https://www.roboleary.net/2021/11/18/svelte-app-testing-jest.html)
- [How to create a toast notification library package with SvelteKit](https://www.sarcevic.dev/blog/toasting-in-svelte)
- [Svelte training: Here you can learn Svelte](https://sustainablewww.org/principles/svelte-training-here-you-can-learn-svelte)
- [Introduction to Svelte Actions](https://blog.logrocket.com/svelte-actions-introduction/)
- [Enjoy making DAPPs using SvelteWeb3](https://chiuzon.medium.com/enjoy-making-dapps-using-svelteweb3-b78dfea1d902)
- [Svelte creator: Web development should be more fun](https://www.infoworld.com/article/3639521/svelte-creator-web-development-should-be-more-fun.html)
- [Svelte Radio: Rich Harris is now working full-time on Svelte 🤯](https://share.transistor.fm/s/d9b04961)
- [Web Rush: Svelte and Elder.js with Nick Reese](https://webrush.io/episodes/episode-158-svelte-and-elderjs-with-nick-reese)
- [Building SvelteKit applications with Serverless Redis](https://blog.upstash.com/svelte-with-serverless-redis)
**Libraries, Tools & Components**
- [svelte-cubed](https://github.com/Rich-Harris/svelte-cubed) is a Three.js component library for Svelte - created by Rich Harris for his presentation at Svelte Summit Fall 2021
- [svelte-fsm](https://github.com/kenkunz/svelte-fsm) is a tiny, simple, expressive, pragmatic Finite State Machine (FSM) library, optimized for Svelte
- [bromb](https://github.com/samuelstroschein/bromb) is a feedback widget for websites/web apps that is small and easy to integration/self-host
- [Spaper](https://github.com/Oli8/spaper) is a set of PaperCSS components for Svelte
- [svelte-intl-precompile](https://github.com/cibernox/svelte-intl-precompile) is an i18n library for Svelte that analyzes and compiles your translations at build time
- [svelte-preprocess-svg](https://github.com/svitejs/svelte-preprocess-svg) automatically optimizes inline svg in Svelte components for better performance and reduced file size
- [svelte-subcomponent-preprocessor](https://github.com/srmullen/svelte-subcomponent-preprocessor) allows you to write more than one component within a svelte file
- [svelte-pdfjs](https://github.com/gtm-nayan/svelte-pdfjs) is a crude implementation of a Svelte PDF viewer component
- [svelte-inview](https://github.com/maciekgrzybek/svelte-inview) is a Svelte action that monitors an element enters or leaves the viewport/parent element
- [sveltekit-adapter-wordpress-shortcode](https://github.com/tomatrow/sveltekit-adapter-wordpress-shortcode) is an adapter for SvelteKit which turns your app into a wordpress shortcode
- [svelte-websocket-store](https://github.com/arlac77/svelte-websocket-store) is a Svelte store with a websocket backend
- [Svelte Auto Form](https://github.com/leveluptuts/auto-form) is a fast and fun form library focused on ease of use, rather than flexibility.
- [set-focus](https://www.npmjs.com/package/@svackages/set-focus) is an Svelte action that will set focus on `<a>` or `<button>` elements as soon as they mount - useful for some experiences and testing
Got an idea for SvelteKit? Check out the new [GitHub Discussions](https://github.com/sveltejs/kit/discussions) in the Svelte repo. You can also join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs).
See you next ~~month~~ year!

@ -28,7 +28,7 @@ A `<script>` block contains JavaScript that runs when a component instance is cr
---
Svelte uses the `export` keyword to mark a variable declaration as a *property* or *prop*, which means it becomes accessible to consumers of the component (see the section on [attributes and props](docs#Attributes_and_props) for more information).
Svelte uses the `export` keyword to mark a variable declaration as a *property* or *prop*, which means it becomes accessible to consumers of the component (see the section on [attributes and props](docs#template-syntax-attributes-and-props) for more information).
```sv
<script>
@ -44,7 +44,7 @@ Svelte uses the `export` keyword to mark a variable declaration as a *property*
You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value.
In development mode (see the [compiler options](docs#svelte_compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
In development mode (see the [compiler options](docs#compile-time-svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
```sv
<script>
@ -57,7 +57,7 @@ In development mode (see the [compiler options](docs#svelte_compile)), a warning
If you export a `const`, `class` or `function`, it is readonly from outside the component. Function *expressions* are valid props, however.
Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](docs#bind_element).
Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](docs#template-syntax-element-directives-bind-element).
```sv
<script>
@ -191,7 +191,7 @@ If a statement consists entirely of an assignment to an undeclared variable, Sve
---
A *store* is an object that allows reactive access to a value via a simple *store contract*. The [`svelte/store` module](docs#svelte_store) contains minimal store implementations which fulfil this contract.
A *store* is an object that allows reactive access to a value via a simple *store contract*. The [`svelte/store` module](docs#run-time-svelte-store) contains minimal store implementations which fulfil this contract.
Any time you have a reference to a store, you can access its value inside a component by prefixing it with the `$` character. This causes Svelte to declare the prefixed variable, subscribe to the store at component initialization and unsubscribe when appropriate.
@ -222,7 +222,7 @@ Local variables (that do not represent store values) must *not* have a `$` prefi
store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
```
You can create your own stores without relying on [`svelte/store`](docs#svelte_store), by implementing the *store contract*:
You can create your own stores without relying on [`svelte/store`](docs#run-time-svelte-store), by implementing the *store contract*:
1. A store must contain a `.subscribe` method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling `.subscribe`. All of a store's active subscription functions must later be synchronously called whenever the store's value changes.
2. The `.subscribe` method must return an unsubscribe function. Calling an unsubscribe function must stop its subscription, and its corresponding subscription function must not be called again by the store.
@ -241,7 +241,7 @@ You can `export` bindings from this block, and they will become exports of the c
You cannot `export default`, since the default export is the component itself.
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](docs#svelte_store).
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](docs#run-time-svelte-store).
```sv
<script context="module">

@ -459,7 +459,7 @@ The `{@debug}` tag without any arguments will insert a `debugger` statement that
As well as attributes, elements can have *directives*, which control the element's behaviour in some way.
#### [on:*eventname*](on_element_event)
#### on:*eventname*
```sv
on:eventname={handler}
@ -549,7 +549,7 @@ It's possible to have multiple event listeners for the same event:
<button on:click={increment} on:click={track}>Click me!</button>
```
#### [bind:*property*](bind_element_property)
#### bind:*property*
```sv
bind:property={variable}
@ -770,7 +770,7 @@ Inputs that work together can use `bind:group`.
<input type="checkbox" bind:group={fillings} value="Guac (extra)">
```
#### [bind:this](bind_element)
#### bind:this
```sv
bind:this={dom_node}
@ -929,7 +929,7 @@ The `transition:` directive indicates a *bidirectional* transition, which means
{/if}
```
> By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](docs#Client-side_component_API).
> By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](docs#run-time-client-side-component-api).
##### Transition parameters
@ -1148,9 +1148,9 @@ DOMRect {
---
An animation is triggered when the contents of a [keyed each block](docs#each) are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an *immediate* child of a keyed each block.
An animation is triggered when the contents of a [keyed each block](docs#template-syntax-each) are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an *immediate* child of a keyed each block.
Animations can be used with Svelte's [built-in animation functions](docs#svelte_animate) or [custom animation functions](docs#Custom_animation_functions).
Animations can be used with Svelte's [built-in animation functions](docs#run-time-svelte-animate) or [custom animation functions](docs#template-syntax-element-directives-animate-fn-custom-animation-functions).
```sv
<!-- When `list` is reordered the animation will run-->
@ -1177,7 +1177,7 @@ As with actions and transitions, animations can have parameters.
---
Animations can use custom functions that provide the `node`, an `animation` object and any `paramaters` as arguments. The `animation` parameter is an object containing `from` and `to` properties each containing a [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect#Properties) describing the geometry of the element in its `start` and `end` positions. The `from` property is the DOMRect of the element in its starting position, the `to` property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.
Animations can use custom functions that provide the `node`, an `animation` object and any `parameters` as arguments. The `animation` parameter is an object containing `from` and `to` properties each containing a [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect#Properties) describing the geometry of the element in its `start` and `end` positions. The `from` property is the DOMRect of the element in its starting position, the `to` property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.
If the returned object has a `css` method, Svelte will create a CSS animation that plays on the element.
@ -1249,7 +1249,7 @@ A custom animation function can also return a `tick` function, which is called *
### Component directives
#### [on:*eventname*](on_component_event)
#### on:*eventname*
```sv
on:eventname={handler}
@ -1257,7 +1257,7 @@ on:eventname={handler}
---
Components can emit events using [createEventDispatcher](docs#createEventDispatcher), or by forwarding DOM events. Listening for component events looks the same as listening for DOM events:
Components can emit events using [createEventDispatcher](docs#run-time-svelte-createeventdispatcher), or by forwarding DOM events. Listening for component events looks the same as listening for DOM events:
```sv
<SomeComponent on:whatever={handler}/>
@ -1271,7 +1271,7 @@ As with DOM events, if the `on:` directive is used without a value, the componen
<SomeComponent on:whatever/>
```
#### [--style-props](style_props)
#### --style-props
```sv
--style-props="anycssvalue"
@ -1340,7 +1340,7 @@ Or override it at the consumer level:
<Slider --rail-color="goldenrod"/>
```
#### [bind:*property*](bind_component_property)
#### bind:*property*
```sv
bind:property={variable}
@ -1354,7 +1354,7 @@ You can bind to component props using the same syntax as for elements.
<Keypad bind:value={pin}/>
```
#### [bind:this](bind_component)
#### bind:this
```sv
bind:this={component_instance}
@ -1410,7 +1410,7 @@ The content is exposed in the child component using the `<slot>` element, which
</Widget>
```
#### [`<slot name="`*name*`">`](slot_name)
#### `<slot name="`*name*`">`
---
@ -1453,7 +1453,7 @@ In order to place content in a slot without using a wrapper element, you can use
```
#### [`$$slots`](slots_object)
#### `$$slots`
---
@ -1479,7 +1479,7 @@ Note that explicitly passing in an empty named slot will add that slot's name to
</Card>
```
#### [`<slot key={`*value*`}>`](slot_let)
#### `<slot key={`*value*`}>`
---
@ -1617,7 +1617,7 @@ All except `scrollX` and `scrollY` are readonly.
---
Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave`, which don't fire on `window`. It also lets you use [actions](docs#use_action) on the `<body>` element.
Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave`, which don't fire on `window`. It also lets you use [actions](docs#template-syntax-element-directives-use-action) on the `<body>` element.
`<svelte:body>` also has to appear at the top level of your component.
@ -1657,7 +1657,7 @@ As with `<svelte:window>` and `<svelte:body>`, this element has to appear at the
---
The `<svelte:options>` element provides a place to specify per-component compiler options, which are detailed in the [compiler section](docs#svelte_compile). The possible options are:
The `<svelte:options>` element provides a place to specify per-component compiler options, which are detailed in the [compiler section](docs#compile-time-svelte-compile). The possible options are:
* `immutable={true}` — you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed
* `immutable={false}` — the default. Svelte will be more conservative about whether or not mutable objects have changed
@ -1672,7 +1672,7 @@ The `<svelte:options>` element provides a place to specify per-component compile
### `<svelte:fragment>`
The `<svelte:fragment>` element allows you to place content in a [named slot](docs#slot_name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
The `<svelte:fragment>` element allows you to place content in a [named slot](docs#template-syntax-slot-slot-name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
```sv
<!-- Widget.svelte -->

@ -20,7 +20,7 @@ onMount(callback: () => () => void)
The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component's initialisation (but doesn't need to live *inside* the component; it can be called from an external module).
`onMount` does not run inside a [server-side component](docs#Server-side_component_API).
`onMount` does not run inside a [server-side component](docs#run-time-server-side-component-api).
```sv
<script>
@ -226,7 +226,7 @@ dispatch: ((name: string, detail?: any) => void) = createEventDispatcher();
---
Creates an event dispatcher that can be used to dispatch [component events](docs#on_component_event). Event dispatchers are functions that can take two arguments: `name` and `detail`.
Creates an event dispatcher that can be used to dispatch [component events](docs#template-syntax-component-directives-on-component-event). Event dispatchers are functions that can take two arguments: `name` and `detail`.
Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) and are not cancellable with `event.preventDefault()`. The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data.
@ -256,11 +256,11 @@ Events dispatched from child components can be listened to in their parent. Any
### `svelte/store`
The `svelte/store` module exports functions for creating [readable](docs#readable), [writable](docs#writable) and [derived](docs#derived) stores.
The `svelte/store` module exports functions for creating [readable](docs#run-time-svelte-store-readable), [writable](docs#run-time-svelte-store-writable) and [derived](docs#run-time-svelte-store-derived) stores.
Keep in mind that you don't *have* to use these functions to enjoy the [reactive `$store` syntax](docs#4_Prefix_stores_with_$_to_access_their_values) in your components. Any object that correctly implements `.subscribe`, unsubscribe, and (optionally) `.set` is a valid store, and will work both with the special syntax, and with Svelte's built-in [`derived` stores](docs#derived).
Keep in mind that you don't *have* to use these functions to enjoy the [reactive `$store` syntax](docs#component-format-script-4-prefix-stores-with-$-to-access-their-values) in your components. Any object that correctly implements `.subscribe`, unsubscribe, and (optionally) `.set` is a valid store, and will work both with the special syntax, and with Svelte's built-in [`derived` stores](docs#run-time-svelte-store-derived).
This makes it possible to wrap almost any other reactive state handling library for use in Svelte. Read more about the [store contract](docs#Store_contract) to see what a correct implementation looks like.
This makes it possible to wrap almost any other reactive state handling library for use in Svelte. Read more about the [store contract](docs#component-format-script-4-prefix-stores-with-$-to-access-their-values-store-contract) to see what a correct implementation looks like.
#### `writable`
@ -446,7 +446,7 @@ Tweened stores update their values over a fixed duration. The following options
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the tween lasts
* `easing` (`function`, default `t => t`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `t => t`) — an [easing function](docs#run-time-svelte-easing)
* `interpolate` (`function`) — see below
`store.set` and `store.update` can accept a second `options` argument that will override the options passed in upon instantiation.
@ -537,7 +537,7 @@ A `spring` store gradually changes to its target value based on its `stiffness`
---
As with [`tweened`](docs#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect.
As with [`tweened`](docs#run-time-svelte-motion-tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect.
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.
@ -565,7 +565,7 @@ $: $size = big ? 100 : 10;
### `svelte/transition`
The `svelte/transition` module exports seven functions: `fade`, `blur`, `fly`, `slide`, `scale`, `draw` and `crossfade`. They are for use with Svelte [`transitions`](docs#transition_fn).
The `svelte/transition` module exports seven functions: `fade`, `blur`, `fly`, `slide`, `scale`, `draw` and `crossfade`. They are for use with Svelte [`transitions`](docs#template-syntax-element-directives-transition-fn).
#### `fade`
@ -587,7 +587,7 @@ Animates the opacity of an element from 0 to the current opacity for `in` transi
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `linear`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `linear`) — an [easing function](docs#run-time-svelte-easing)
You can see the `fade` transition in action in the [transition tutorial](tutorial/transition).
@ -623,7 +623,7 @@ Animates a `blur` filter alongside an element's opacity.
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#run-time-svelte-easing)
* `opacity` (`number`, default 0) - the opacity value to animate out to and in from
* `amount` (`number`, default 5) - the size of the blur in pixels
@ -659,7 +659,7 @@ Animates the x and y positions and the opacity of an element. `in` transitions a
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#run-time-svelte-easing)
* `x` (`number`, default 0) - the x offset to animate out to and in from
* `y` (`number`, default 0) - the y offset to animate out to and in from
* `opacity` (`number`, default 0) - the opacity value to animate out to and in from
@ -699,7 +699,7 @@ Slides an element in and out.
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#run-time-svelte-easing)
```sv
<script>
@ -734,7 +734,7 @@ Animates the opacity and scale of an element. `in` transitions animate from an e
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#run-time-svelte-easing)
* `start` (`number`, default 0) - the scale value to animate out to and in from
* `opacity` (`number`, default 0) - the opacity value to animate out to and in from
@ -772,7 +772,7 @@ Animates the stroke of an SVG element, like a snake in a tube. `in` transitions
* `delay` (`number`, default 0) — milliseconds before starting
* `speed` (`number`, default undefined) - the speed of the animation, see below.
* `duration` (`number` | `function`, default 800) — milliseconds the transition lasts
* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#run-time-svelte-easing)
The `speed` parameter is a means of setting the duration of the transition relative to the path's length. It is a modifier that is applied to the length of the path: `duration = length / speed`. A path that is 1000 pixels with a speed of 1 will have a duration of `1000ms`, setting the speed to `0.5` will double that duration and setting it to `2` will halve it.
@ -799,7 +799,7 @@ The `speed` parameter is a means of setting the duration of the transition relat
#### `crossfade`
The `crossfade` function creates a pair of [transitions](docs#transition_fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used.
The `crossfade` function creates a pair of [transitions](docs#template-syntax-element-directives-transition-fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used.
---
@ -807,8 +807,8 @@ The `crossfade` function creates a pair of [transitions](docs#transition_fn) cal
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number` | `function`, default 800) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `fallback` (`function`) — A fallback [transition](docs#transition_fn) to use for send when there is no matching element being received, and for receive when there is no element being sent.
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#run-time-svelte-easing)
* `fallback` (`function`) — A fallback [transition](docs#template-syntax-element-directives-transition-fn) to use for send when there is no matching element being received, and for receive when there is no element being sent.
```sv
<script>
@ -831,7 +831,7 @@ The `crossfade` function creates a pair of [transitions](docs#transition_fn) cal
### `svelte/animate`
The `svelte/animate` module exports one function for use with Svelte [animations](docs#animate_fn).
The `svelte/animate` module exports one function for use with Svelte [animations](docs#template-syntax-element-directives-animate-fn).
#### `flip`
@ -845,10 +845,10 @@ The `flip` function calculates the start and end position of an element and anim
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number` | `function`, default `d => Math.sqrt(d) * 120`) — see below
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#run-time-svelte-easing)
`duration` can be be provided as either:
`duration` can be provided as either:
- a `number`, in milliseconds.
- a function, `distance: number => duration: number`, receiving the distance the element will travel in pixels and returning the duration in milliseconds. This allows you to assign a duration that is relative to the distance travelled by each element.
@ -961,7 +961,7 @@ Existing children of `target` are left where they are.
---
The `hydrate` option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements. It will only work if the component was compiled with the [`hydratable: true` option](docs#svelte_compile). Hydration of `<head>` elements only works properly if the server-side rendering code was also compiled with `hydratable: true`, which adds a marker to each element in the `<head>` so that the component knows which elements it's responsible for removing during hydration.
The `hydrate` option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements. It will only work if the component was compiled with the [`hydratable: true` option](docs#compile-time-svelte-compile). Hydration of `<head>` elements only works properly if the server-side rendering code was also compiled with `hydratable: true`, which adds a marker to each element in the `<head>` so that the component knows which elements it's responsible for removing during hydration.
Whereas children of `target` are normally left alone, `hydrate: true` will cause any children to be removed. For that reason, the `anchor` option cannot be used alongside `hydrate: true`.
@ -1045,7 +1045,7 @@ app.count += 1;
---
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](docs#svelte_options).
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](docs#template-syntax-svelte-options).
```sv
<svelte:options tag="my-element" />
@ -1082,7 +1082,7 @@ document.body.innerHTML = `
---
By default, custom elements are compiled with `accessors: true`, which means that any [props](docs#Attributes_and_props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).
By default, custom elements are compiled with `accessors: true`, which means that any [props](docs#template-syntax-attributes-and-props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).
To prevent this, add `accessors={false}` to `<svelte:options>`.
@ -1119,7 +1119,7 @@ Unlike client-side components, server-side components don't have a lifespan afte
A server-side component exposes a `render` method that can be called with optional props. It returns an object with `head`, `html`, and `css` properties, where `head` contains the contents of any `<svelte:head>` elements encountered.
You can import a Svelte component directly into Node using [`svelte/register`](docs#svelte_register).
You can import a Svelte component directly into Node using [`svelte/register`](docs#run-time-svelte-register).
```js
require('svelte/register');

@ -6,7 +6,7 @@ Typically, you won't interact with the Svelte compiler directly, but will instea
* [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) for users of [Rollup](https://rollupjs.org)
* [svelte-loader](https://github.com/sveltejs/svelte-loader) for users of [webpack](https://webpack.js.org)
* or one of the [community-maintained plugins](https://sveltesociety.dev/tooling)
* or one of the [community-maintained plugins](https://sveltesociety.dev/tools)
Nonetheless, it's useful to understand how to use the compiler, since bundler plugins generally expose compiler options to you.

@ -36,5 +36,10 @@ export default [
{ x: 2013, y: 5.35 },
{ x: 2014, y: 5.28 },
{ x: 2015, y: 4.63 },
{ x: 2016, y: 4.72 }
];
{ x: 2016, y: 4.72 },
{ x: 2017, y: 4.82 },
{ x: 2018, y: 4.79 },
{ x: 2019, y: 4.36 },
{ x: 2020, y: 4 },
{ x: 2021, y: 4.92 }
];

@ -1,39 +1,15 @@
<script>
import { spring } from 'svelte/motion';
import { pannable } from './pannable.js';
import { clickOutside } from "./click_outside.js";
const coords = spring({ x: 0, y: 0 }, {
stiffness: 0.2,
damping: 0.4
});
function handlePanStart() {
coords.stiffness = coords.damping = 1;
}
function handlePanMove(event) {
coords.update($coords => ({
x: $coords.x + event.detail.dx,
y: $coords.y + event.detail.dy
}));
}
function handlePanEnd(event) {
coords.stiffness = 0.2;
coords.damping = 0.4;
coords.set({ x: 0, y: 0 });
}
let showModal = true;
</script>
<div class="box"
use:pannable
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div>
<button on:click={() => (showModal = true)}>Show Modal</button>
{#if showModal}
<div class="box" use:clickOutside on:outclick={() => (showModal = false)}>
Click outside me!
</div>
{/if}
<style>
.box {
@ -46,6 +22,8 @@
top: calc(50% - var(--height) / 2);
border-radius: 4px;
background-color: #ff3e00;
cursor: move;
color: #fff;
text-align: center;
font-weight: bold;
}
</style>
</style>

@ -0,0 +1,15 @@
export function clickOutside(node) {
const handleClick = (event) => {
if (!node.contains(event.target)) {
node.dispatchEvent(new CustomEvent("outclick"));
}
};
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener("click", handleClick, true);
}
};
}

@ -0,0 +1,51 @@
<script>
import { spring } from 'svelte/motion';
import { pannable } from './pannable.js';
const coords = spring({ x: 0, y: 0 }, {
stiffness: 0.2,
damping: 0.4
});
function handlePanStart() {
coords.stiffness = coords.damping = 1;
}
function handlePanMove(event) {
coords.update($coords => ({
x: $coords.x + event.detail.dx,
y: $coords.y + event.detail.dy
}));
}
function handlePanEnd(event) {
coords.stiffness = 0.2;
coords.damping = 0.4;
coords.set({ x: 0, y: 0 });
}
</script>
<div class="box"
use:pannable
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div>
<style>
.box {
--width: 100px;
--height: 100px;
position: absolute;
width: var(--width);
height: var(--height);
left: calc(50% - var(--width) / 2);
top: calc(50% - var(--height) / 2);
border-radius: 4px;
background-color: #ff3e00;
cursor: move;
}
</style>

@ -0,0 +1,3 @@
{
"title": "A more complex action"
}

@ -14,7 +14,7 @@ First, you'll need to integrate Svelte with a build tool. There are officially m
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.
You'll also want to configure your text editor. If you're using VS Code, install the [Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), otherwise follow [this guide](blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting.
You'll also want to configure your text editor. There are [plugins](https://sveltesociety.dev/tools#editor-support) for many popular editors as well as an official [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If your editor does not have a Svelte plugin then you can follow [this guide](blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting.
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:
@ -30,4 +30,4 @@ const app = new App({
});
```
You can then interact with `app` using the [component API](docs#Client-side_component_API) if you need to.
You can then interact with `app` using the [component API](docs#run-time-client-side-component-api) if you need to.

@ -2,7 +2,7 @@
let count = 0;
$: if (count >= 10) {
alert(`count is dangerously high!`);
alert('count is dangerously high!');
count = 9;
}

@ -5,15 +5,15 @@ 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
$: console.log(`the count is ${count}`);
$: console.log('the count is ' + count);
```
You can easily group statements together with a block:
```js
$: {
console.log(`the count is ${count}`);
alert(`I SAID THE COUNT IS ${count}`);
console.log('the count is ' + count);
alert('I SAID THE COUNT IS ' + count);
}
```
@ -21,7 +21,7 @@ You can even put the `$:` in front of things like `if` blocks:
```js
$: if (count >= 10) {
alert(`count is dangerously high!`);
alert('count is dangerously high!');
count = 9;
}
```

@ -2,9 +2,13 @@
title: Binding to component instances
---
Just as you can bind to DOM elements, you can bind to component instances themselves. For example, we can bind the instance of `<InputField>` to a prop named `field` in the same way we did when binding DOM Elements
Just as you can bind to DOM elements, you can bind to component instances themselves. For example, we can bind the instance of `<InputField>` to a variable named `field` in the same way we did when binding DOM Elements
```html
<script>
let field;
</script>
<InputField bind:this={field} />
```

@ -11,4 +11,4 @@ export const elapsed = derived(
);
```
> It's possible to derive a store from multiple inputs, and to explicitly `set` a value instead of returning it (which is useful for deriving values asynchronously). Consult the [API reference](docs#derived) for more information.
> It's possible to derive a store from multiple inputs, and to explicitly `set` a value instead of returning it (which is useful for deriving values asynchronously). Consult the [API reference](docs#run-time-svelte-store-derived) for more information.

@ -6,7 +6,7 @@ Ordinarily, transitions will play on elements when any container block is added
Instead, we'd like transitions to play only when individual items are added and removed — in other words, when the user drags the slider.
We can achieve this with a *local* transition, which only plays when the immediate parent block is added or removed:
We can achieve this with a *local* transition, which only plays when the block with the transition itself is added or removed:
```html
<div transition:slide|local>

@ -1,29 +1,16 @@
<script>
import { spring } from 'svelte/motion';
const coords = spring({ x: 0, y: 0 }, {
stiffness: 0.2,
damping: 0.4
});
function handlePanStart() {
coords.stiffness = coords.damping = 1;
}
function handlePanMove(event) {
coords.update($coords => ({
x: $coords.x + event.detail.dx,
y: $coords.y + event.detail.dy
}));
}
function handlePanEnd(event) {
coords.stiffness = 0.2;
coords.damping = 0.4;
coords.set({ x: 0, y: 0 });
}
let showModal = true;
</script>
<button on:click={() => (showModal = true)}>Show Modal</button>
{#if showModal}
<div class="box" on:outclick={() => (showModal = false)}>
Click outside me!
</div>
{/if}
<style>
.box {
--width: 100px;
@ -35,15 +22,8 @@
top: calc(50% - var(--height) / 2);
border-radius: 4px;
background-color: #ff3e00;
cursor: move;
color: #fff;
text-align: center;
font-weight: bold;
}
</style>
<div class="box"
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div>

@ -1,4 +1,4 @@
export function pannable(node) {
export function clickOutside(node) {
// setup work goes here...
return {
@ -6,4 +6,4 @@ export function pannable(node) {
// ...cleanup goes here
}
};
}
}

@ -1,30 +1,16 @@
<script>
import { spring } from 'svelte/motion';
import { pannable } from './pannable.js';
import { clickOutside } from "./click_outside.js";
const coords = spring({ x: 0, y: 0 }, {
stiffness: 0.2,
damping: 0.4
});
function handlePanStart() {
coords.stiffness = coords.damping = 1;
}
function handlePanMove(event) {
coords.update($coords => ({
x: $coords.x + event.detail.dx,
y: $coords.y + event.detail.dy
}));
}
function handlePanEnd(event) {
coords.stiffness = 0.2;
coords.damping = 0.4;
coords.set({ x: 0, y: 0 });
}
let showModal = true;
</script>
<button on:click={() => (showModal = true)}>Show Modal</button>
{#if showModal}
<div class="box" use:clickOutside on:outclick={() => (showModal = false)}>
Click outside me!
</div>
{/if}
<style>
.box {
--width: 100px;
@ -36,16 +22,8 @@
top: calc(50% - var(--height) / 2);
border-radius: 4px;
background-color: #ff3e00;
cursor: move;
color: #fff;
text-align: center;
font-weight: bold;
}
</style>
<div class="box"
use:pannable
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div>

@ -0,0 +1,15 @@
export function clickOutside(node) {
const handleClick = (event) => {
if (!node.contains(event.target)) {
node.dispatchEvent(new CustomEvent("outclick"));
}
};
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener("click", handleClick, true);
}
};
}

@ -1,47 +0,0 @@
export function pannable(node) {
let x;
let y;
function handleMousedown(event) {
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panstart', {
detail: { x, y }
}));
window.addEventListener('mousemove', handleMousemove);
window.addEventListener('mouseup', handleMouseup);
}
function handleMousemove(event) {
const dx = event.clientX - x;
const dy = event.clientY - y;
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panmove', {
detail: { x, y, dx, dy }
}));
}
function handleMouseup(event) {
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panend', {
detail: { x, y }
}));
window.removeEventListener('mousemove', handleMousemove);
window.removeEventListener('mouseup', handleMouseup);
}
node.addEventListener('mousedown', handleMousedown);
return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
}
};
}

@ -4,85 +4,45 @@ title: The use directive
Actions are essentially element-level lifecycle functions. They're useful for things like:
* interfacing with third-party libraries
* lazy-loaded images
* tooltips
* adding custom event handlers
- interfacing with third-party libraries
- lazy-loaded images
- tooltips
- adding custom event handlers
In this app, we want to make the orange box 'pannable'. It has event handlers for the `panstart`, `panmove` and `panend` events, but these aren't native DOM events. We have to dispatch them ourselves. First, import the `pannable` function...
In this app, we want to make the orange modal close when the user clicks outside it. It has an event handler for the `outclick` event, but it isn't a native DOM event. We have to dispatch it ourselves. First, import the `clickOutside` function...
```js
import { pannable } from './pannable.js';
import { clickOutside } from "./click_outside.js";
```
...then use it with the element:
```html
<div class="box"
use:pannable
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
></div>
<div class="box" use:clickOutside on:outclick="{() => (showModal = false)}">
Click outside me!
</div>
```
Open the `pannable.js` file. Like transition functions, an action function receives a `node` and some optional parameters, and returns an action object. That object can have a `destroy` function, which is called when the element is unmounted.
Open the `click_outside.js` file. Like transition functions, an action function receives a `node` (which is the element that the action is applied to) and some optional parameters, and returns an action object. That object can have a `destroy` function, which is called when the element is unmounted.
We want to fire `panstart` event when the user mouses down on the element, `panmove` events (with `dx` and `dy` properties showing how far the mouse moved) when they drag it, and `panend` events when they mouse up. One possible implementation looks like this:
We want to fire the `outclick` event when the user clicks outside the orange box. One possible implementation looks like this:
```js
export function pannable(node) {
let x;
let y;
function handleMousedown(event) {
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panstart', {
detail: { x, y }
}));
window.addEventListener('mousemove', handleMousemove);
window.addEventListener('mouseup', handleMouseup);
}
function handleMousemove(event) {
const dx = event.clientX - x;
const dy = event.clientY - y;
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panmove', {
detail: { x, y, dx, dy }
}));
}
function handleMouseup(event) {
x = event.clientX;
y = event.clientY;
node.dispatchEvent(new CustomEvent('panend', {
detail: { x, y }
}));
window.removeEventListener('mousemove', handleMousemove);
window.removeEventListener('mouseup', handleMouseup);
}
export function clickOutside(node) {
const handleClick = (event) => {
if (!node.contains(event.target)) {
node.dispatchEvent(new CustomEvent("outclick"));
}
};
node.addEventListener('mousedown', handleMousedown);
document.addEventListener("click", handleClick, true);
return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
}
document.removeEventListener("click", handleClick, true);
},
};
}
```
Update the `pannable` function and try moving the box around.
> This implementation is for demonstration purposes — a more complete one would also consider touch events.
Update the `clickOutside` function, click the button to show the modal and then click outside it to close it.

@ -1,15 +0,0 @@
version: "3.0"
services:
postgres:
image: postgres:13.4
ports:
- "5432:5432"
environment:
- POSTGRES_DB=${PGDATABASE}
- POSTGRES_USER=${PGUSER}
- POSTGRES_PASSWORD=${PGPASSWORD}
adminer:
image: adminer
restart: always
ports:
- "4000:8080"

@ -1,25 +0,0 @@
exports.up = DB => {
DB.sql(`
create table if not exists users (
id serial primary key,
uid character varying(255) not null unique,
name character varying(255),
username character varying(255) not null,
avatar text,
github_token character varying(255),
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone
);
create unique index if not exists users_pkey ON users(id int4_ops);
create unique index if not exists users_uid_key ON users(uid text_ops);
`);
};
exports.down = DB => {
DB.sql(`
drop table if exists users cascade;
drop index if exists users_uid_key;
drop index if exists users_pkey;
`);
};

@ -1,24 +0,0 @@
exports.up = DB => {
DB.sql(`
create table if not exists gists (
id serial primary key,
uid uuid NOT NULL DEFAULT gen_random_uuid(),
user_id integer REFERENCES users(id) not null,
name character varying(255) not null,
files json not null,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone
);
create unique index if not exists gists_pkey ON gists(id int4_ops);
create index if not exists gists_user_id_key ON gists(user_id int4_ops);
`);
};
exports.down = DB => {
DB.sql(`
drop table if exists gists cascade;
drop index if exists gists_user_id_key;
drop index if exists gists_pkey;
`);
};

@ -1,15 +0,0 @@
exports.up = DB => {
DB.sql(`
create table if not exists sessions (
uid uuid NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
user_id integer REFERENCES users(id) not null,
expiry timestamp without time zone DEFAULT now() + interval '1 year'
);
`);
};
exports.down = DB => {
DB.sql(`
drop table if exists sessions;
`);
};

3970
site/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,43 +0,0 @@
{
"name": "svelte.dev",
"version": "1.0.0",
"description": "Docs and examples for Svelte",
"type": "module",
"scripts": {
"dev": "node scripts/update.js && npm run copy-workers && svelte-kit dev",
"copy-workers": "node scripts/copy-workers.js",
"migrate": "node-pg-migrate -r dotenv/config",
"build": "node scripts/update.js && npm run copy-workers && svelte-kit build",
"update": "node scripts/update.js --force=true",
"start": "node build",
"test": "uvu test",
"deploy": "make deploy"
},
"dependencies": {
"cookie": "^0.4.0",
"devalue": "^2.0.0",
"do-not-zip": "^1.0.0",
"flru": "^1.0.2",
"httpie": "^1.1.2",
"jsonwebtoken": "^8.5.1",
"marked": "^3.0.5",
"pg": "^8.7.1",
"prism-svelte": "^0.4.3",
"prismjs": "^1.25.0"
},
"devDependencies": {
"@sindresorhus/slugify": "^0.9.1",
"@sveltejs/adapter-node": "next",
"@sveltejs/kit": "next",
"@sveltejs/site-kit": "^1.4.0",
"@sveltejs/svelte-repl": "^0.3.0",
"degit": "^2.1.4",
"dotenv": "^10.0.0",
"jimp": "^0.8.0",
"node-fetch": "^2.6.1",
"node-pg-migrate": "^6.0.0",
"shelljs": "^0.8.3",
"svelte": "^3.39.0",
"uvu": "^0.5.2"
}
}

@ -1,4 +0,0 @@
import sh from 'shelljs';
sh.rm('-rf', 'static/workers');
sh.cp('-r', 'node_modules/@sveltejs/svelte-repl/workers', 'static');

@ -1,67 +0,0 @@
import fs from 'fs';
import puppeteer from 'puppeteer';
import Jimp from 'jimp';
import c from 'kleur';
const slugs = [];
fs.readdirSync(`content/examples`).forEach(group_dir => {
fs.readdirSync(`content/examples/${group_dir}`).filter(file => file !== 'meta.json').map(example_dir => {
const slug = example_dir.replace(/^\d+-/, '');
slugs.push(slug);
});
});
async function main() {
const browser = await puppeteer.launch({
defaultViewport: {
width: 400 * 10 / 4,
height: 400 + 42,
deviceScaleFactor: 2
}
});
const page = await browser.newPage();
for (const slug of slugs) {
try {
const output_file = `static/examples/thumbnails/${slug}.jpg`;
if (fs.existsSync(output_file)) {
console.log(c.gray(`skipping ${slug}`));
continue;
}
console.log(slug);
await page.goto(`http://localhost:3000/repl/embed?example=${slug}`);
await page.waitForSelector('iframe.inited[title=Result]');
await page.waitFor(1500);
const iframe = await page.$('iframe.inited[title=Result]');
const buffer = await iframe.screenshot();
const image = await Jimp.read(buffer);
console.log(image.bitmap.width, image.bitmap.height);
image.crop(3, 3, image.bitmap.width - 6, image.bitmap.height - 6);
image.autocrop();
// image.scale(0.25);
if (image.bitmap.width > 200 || image.bitmap.height > 200) {
const scale = Math.min(
200 / image.bitmap.width,
200 / image.bitmap.height
);
image.scale(scale);
}
await image.quality(75).write(output_file);
} catch (err) {
console.log(c.bold().red(`failed to screenshot ${slug}`));
console.log(err);
}
}
await browser.close();
}
main();

@ -1,77 +0,0 @@
import sander from 'sander';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(`${__dirname}/../..`);
function extract_frontmatter(markdown) {
const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown);
const frontMatter = match[1];
const content = markdown.slice(match[0].length);
const metadata = {};
frontMatter.split('\n').forEach(pair => {
const colonIndex = pair.indexOf(':');
metadata[pair.slice(0, colonIndex).trim()] = pair
.slice(colonIndex + 1)
.trim();
});
return { metadata, content };
}
const tutorial_sections = sander.readdirSync(`content/tutorial`).map(section_dir => {
const section_slug = section_dir.replace(/^\d+-/, '');
const meta = JSON.parse(sander.readFileSync(`content/tutorial/${section_dir}/meta.json`, { encoding: 'utf-8' }));
const chapters = sander.readdirSync(`content/tutorial/${section_dir}`).map(chapter_dir => {
const app_dir = `content/tutorial/${section_dir}/${chapter_dir}/app-b`;
if (!sander.existsSync(app_dir)) return;
const markdown = sander.readFileSync(`content/tutorial/${section_dir}/${chapter_dir}/text.md`, { encoding: 'utf-8' });
const { metadata } = extract_frontmatter(markdown);
const chapter_slug = chapter_dir.replace(/^\d+-/, '');
return {
slug: chapter_slug,
title: metadata.title,
files: sander.readdirSync(app_dir).map(name => {
return {
name,
source: sander.readFileSync(`${app_dir}/${name}`, { encoding: 'utf-8' })
};
})
};
}).filter(Boolean);
return {
slug: section_slug,
title: meta.title,
chapters
};
}).filter(section => section.chapters.length > 0);
const pad = i => i < 10 ? `0${i}` : i;
tutorial_sections.forEach((section, i) => {
const section_dir = `${pad(i)}-${section.slug}`;
sander.writeFileSync(`content/examples/${section_dir}/meta.json`, JSON.stringify({
title: section.title
}, null, '\t'));
section.chapters.forEach((chapter, i) => {
const chapter_dir = `${pad(i)}-${chapter.slug}`;
sander.writeFileSync(`content/examples/${section_dir}/${chapter_dir}/meta.json`, JSON.stringify({
title: chapter.title
}, null, '\t'));
chapter.files.forEach(file => {
sander.writeFileSync(`content/examples/${section_dir}/${chapter_dir}/${file.name}`, file.source);
});
});
});

@ -1,66 +0,0 @@
import 'dotenv/config';
import fs from 'fs';
import fetch from 'node-fetch';
import Jimp from 'jimp';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(__dirname);
const outputFile = `../src/routes/_contributors.js`;
if (!force && fs.existsSync(outputFile)) {
console.info(`[update/contributors] ${outputFile} exists. Skipping`);
process.exit(0);
}
const base = `https://api.github.com/repos/sveltejs/svelte/contributors`;
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = process.env;
const SIZE = 64;
async function main() {
const contributors = [];
let page = 1;
while (true) {
const res = await fetch(`${base}?client_id=${GITHUB_CLIENT_ID}&client_secret=${GITHUB_CLIENT_SECRET}&per_page=100&page=${page++}`);
const list = await res.json();
if (list.length === 0) break;
contributors.push(...list);
}
const authors = contributors
.filter(({ login }) => !login.includes('[bot]'))
.sort((a, b) => b.contributions - a.contributions);
const sprite = new Jimp(SIZE * authors.length, SIZE);
for (let i = 0; i < authors.length; i += 1) {
const author = authors[i];
console.log(`${i + 1} / ${authors.length}: ${author.login}`);
const image_data = await fetch(author.avatar_url);
const buffer = await image_data.arrayBuffer();
const image = await Jimp.read(buffer);
image.resize(SIZE, SIZE);
sprite.composite(image, i * SIZE, 0);
}
await sprite.quality(80).write(`../static/contributors.jpg`);
// TODO: Optimizing the static/contributors.jpg image should probably get automated as well
console.log('remember to additionally optimize the resulting /static/contributors.jpg image file via e.g. https://squoosh.app ');
const str = `[\n\t${authors.map(a => `'${a.login}'`).join(',\n\t')}\n]`;
fs.writeFileSync(outputFile, `export default ${str};`);
}
main();

@ -1,63 +0,0 @@
import 'dotenv/config';
import fs from 'fs';
import fetch from 'node-fetch';
import Jimp from 'jimp';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(__dirname);
const outputFile = `../src/routes/_donors.js`;
if (!force && fs.existsSync(outputFile)) {
console.info(`[update/donors] ${outputFile} exists. Skipping`);
process.exit(0);
}
const SIZE = 64;
async function main() {
const res = await fetch('https://opencollective.com/svelte/members/all.json');
const donors = await res.json();
const unique = new Map();
donors.forEach(d => unique.set(d.profile, d));
let backers = [...unique.values()]
.filter(({ role }) => role === 'BACKER')
.sort((a, b) => b.totalAmountDonated - a.totalAmountDonated);
const included = [];
for (let i = 0; i < backers.length; i += 1) {
const backer = backers[i];
console.log(`${i} / ${backers.length}: ${backer.name}`);
try {
const image_data = await fetch(backer.image);
const buffer = await image_data.arrayBuffer();
const image = await Jimp.read(buffer);
image.resize(SIZE, SIZE);
included.push({ backer, image });
} catch( err) {
console.log(`Skipping ${backer.name}: no image data`);
}
}
const sprite = new Jimp(SIZE * included.length, SIZE);
for (let i = 0; i < included.length; i += 1) {
sprite.composite(included[i].image, i * SIZE, 0);
}
await sprite.quality(80).write(`../static/donors.jpg`);
// TODO: Optimizing the static/donors.jpg image should probably get automated as well
console.log('remember to additionally optimize the resulting /static/donors.jpg image file via e.g. https://squoosh.app ');
const str = `[\n\t${included.map(a => `${JSON.stringify(a.backer.name)}`).join(',\n\t')}\n]`;
fs.writeFileSync(outputFile, `export default ${str};`);
}
main();

@ -1,9 +0,0 @@
import sh from 'shelljs';
sh.env['FORCE_UPDATE'] = process.argv.includes('--force=true');
Promise.all([
sh.exec('node ./scripts/get_contributors.js'),
sh.exec('node ./scripts/get_donors.js'),
sh.exec('node ./scripts/update_template.js')
]);

@ -1,36 +0,0 @@
import sh from 'shelljs';
import fs from 'fs';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
const force = process.env.FORCE_UPDATE === 'true';
const __dirname = dirname(fileURLToPath(import.meta.url));
sh.cd(path.join(__dirname, '..'));
const outputFile = 'static/svelte-app.json';
if (!force && fs.existsSync(outputFile)) {
console.info(`[update/template] ${outputFile} exists. Skipping`);
process.exit(0);
}
// fetch svelte app
sh.rm('-rf','scripts/svelte-app');
sh.exec('npx degit sveltejs/template scripts/svelte-app');
// remove src (will be recreated client-side) and node_modules
sh.rm('-rf', 'scripts/svelte-app/src');
sh.rm('-rf', 'scripts/svelte-app/node_modules');
// build svelte-app.json
const appPath = 'scripts/svelte-app';
const files = [];
for (const path of sh.find(appPath).filter(p => fs.lstatSync(p).isFile()) ) {
const bytes = fs.readFileSync(path);
const string = bytes.toString();
const data = bytes.compare(Buffer.from(string)) === 0 ? string : [...bytes];
files.push({ path: path.slice(appPath.length + 1), data });
}
fs.writeFileSync(outputFile, JSON.stringify(files));

@ -1,26 +0,0 @@
<!doctype html>
<html lang='en' class="theme-default typo-default">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<meta name='theme-color' content='#ff3e00'>
<base href='/'>
<link rel='stylesheet' href='global.css'>
<link rel='stylesheet' href='prism.css'>
<link rel='manifest' href='manifest.json'>
<link rel='icon' type='image/png' href='favicon.png'>
<meta name='twitter:card' content='summary_large_image'>
<meta name='twitter:site' content='@sveltejs'>
<meta name='twitter:creator' content='@sveltejs'>
<meta name='twitter:image' content='https://svelte.dev/images/twitter-card.png'>
<meta name='og:image' content='https://svelte.dev/images/twitter-card.png'>
%svelte.head%
</head>
<body>
<div id='svelte'>%svelte.body%</div>
</body>
</html>

@ -1,59 +0,0 @@
<script>
import { onMount } from 'svelte';
export let once = false;
export let top = 0;
export let bottom = 0;
export let left = 0;
export let right = 0;
let intersecting = false;
let container;
onMount(() => {
if (typeof IntersectionObserver !== 'undefined') {
const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;
const observer = new IntersectionObserver(entries => {
intersecting = entries[0].isIntersecting;
if (intersecting && once) {
observer.unobserve(container);
}
}, {
rootMargin
});
observer.observe(container);
return () => observer.unobserve(container);
}
function handler() {
const bcr = container.getBoundingClientRect();
intersecting = (
(bcr.bottom + bottom) > 0 &&
(bcr.right + right) > 0 &&
(bcr.top - top) < window.innerHeight &&
(bcr.left - left) < window.innerWidth
);
if (intersecting && once) {
window.removeEventListener('scroll', handler);
}
}
window.addEventListener('scroll', handler);
return () => window.removeEventListener('scroll', handler);
});
</script>
<style>
div {
width: 100%;
height: 100%;
}
</style>
<div bind:this={container}>
<slot {intersecting}></slot>
</div>

@ -1,11 +0,0 @@
<script>
import { onMount } from 'svelte';
let constructor;
onMount(async () => {
constructor = await $$props.this();
});
</script>
<svelte:component this={constructor} {...$$props}/>

@ -1,63 +0,0 @@
<script>
import { onMount } from 'svelte';
let p = 0;
let visible = false;
onMount(() => {
function next() {
visible = true;
p += 0.1;
const remaining = 1 - p;
if (remaining > 0.15) setTimeout(next, 500 / remaining);
}
setTimeout(next, 250);
});
</script>
<style>
.progress-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4px;
z-index: 999;
}
.progress {
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: var(--prime);
transition: width 0.4s;
}
.fade {
position: fixed;
width: 100%;
height: 100%;
background-color: rgba(255,255,255,0.3);
pointer-events: none;
z-index: 998;
animation: fade 0.4s;
}
@keyframes fade {
from { opacity: 0 }
to { opacity: 1 }
}
</style>
{#if visible}
<div class="progress-container">
<div class="progress" style="width: {p * 100}%"></div>
</div>
{/if}
{#if p >= 0.4}
<div class="fade"></div>
{/if}

@ -1,27 +0,0 @@
<script>
export let checked;
</script>
<style>
.input-output-toggle {
display: grid;
user-select: none;
flex: 0;
grid-template-columns: 1fr 40px 1fr;
grid-gap: 0.5em;
align-items: center;
width: 100%;
height: 42px;
border-top: 1px solid var(--second);
}
input { display: block }
span { color: #ccc }
.active { color: #555 }
</style>
<label class="input-output-toggle">
<span class:active={!checked} style="text-align: right">input</span>
<input type="checkbox" bind:checked>
<span class:active={checked}>output</span>
</label>

@ -1,136 +0,0 @@
<script>
import Repl from '@sveltejs/svelte-repl';
import { onMount } from 'svelte';
import { browser } from '$app/env';
import { process_example } from '../../utils/examples';
import InputOutputToggle from './InputOutputToggle.svelte';
export let version = '3';
export let gist = null;
export let example = null;
export let embedded = false;
let repl;
let name = 'loading...';
let width = browser
? window.innerWidth - 32
: 1000;
let checked = false;
onMount(() => {
if (version !== 'local') {
fetch(`https://unpkg.com/svelte@${version}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
}
if (gist) {
fetch(`repl/${gist}.json`).then(r => r.json()).then(data => {
const { description, files } = data;
name = description;
const components = Object.keys(files)
.map(file => {
const dot = file.lastIndexOf('.');
if (!~dot) return;
const source = files[file].content;
return {
name: file.slice(0, dot),
type: file.slice(dot + 1),
source
};
})
.filter(x => x.type === 'svelte' || x.type === 'js')
.sort((a, b) => {
if (a.name === 'App' && a.type === 'svelte') return -1;
if (b.name === 'App' && b.type === 'svelte') return 1;
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
return a.name < b.name ? -1 : 1;
});
repl.set({ components });
});
} else if (example) {
fetch(`examples/${example}.json`).then(async response => {
if (response.ok) {
const data = await response.json();
repl.set({
components: process_example(data.files)
});
}
});
}
});
$: if (embedded) document.title = `${name} • Svelte REPL`;
$: svelteUrl = browser && version === 'local' ?
`${location.origin}/repl/local` :
`https://unpkg.com/svelte@${version}`;
const rollupUrl = `https://unpkg.com/rollup@1/dist/rollup.browser.js`;
$: mobile = width < 560;
</script>
<style>
.repl-outer {
position: relative;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: var(--back);
overflow: hidden;
box-sizing: border-box;
--pane-controls-h: 4.2rem;
}
.viewport {
width: 100%;
height: 100%;
flex: 1;
}
.mobile .viewport {
width: 200%;
height: calc(100% - 42px);
transition: transform 0.3s;
}
.mobile .offset {
transform: translate(-50%, 0);
}
</style>
<div class="repl-outer" bind:clientWidth={width} class:mobile>
<div class="viewport" class:offset={checked}>
{#if browser}
<Repl
bind:this={repl}
workersUrl="workers"
fixed={mobile}
{svelteUrl}
{rollupUrl}
embedded
relaxed
/>
{/if}
</div>
{#if mobile}
<InputOutputToggle bind:checked/>
{/if}
</div>

@ -1,46 +0,0 @@
<script>
export let labels;
export let offset = 0;
</script>
<style>
.toggle {
position: fixed;
bottom: 0;
width: 100%;
height: 4.6rem;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid var(--second);
background-color: white;
}
button {
margin: 0 .15em;
width: 4em;
height: 1em;
padding: .3em .4em;
border-radius: var(--border-r);
line-height: 1em;
box-sizing: content-box;
color: #888;
border: 1px solid var(--back-light);
}
.selected {
background-color: var(--prime);
color: white;
}
</style>
<div class="toggle">
{#each labels as label, index}
<button
class:selected={offset === index}
on:click={() => offset = index}
>
{label}
</button>
{/each}
</div>

@ -1,5 +0,0 @@
// REPL props
export const svelteUrl = `https://unpkg.com/svelte@3`;
export const rollupUrl = `https://unpkg.com/rollup@1/dist/rollup.browser.js`;
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = '${import.meta.env.VITE_MAPBOX_ACCESS_TOKEN}';`;

@ -1,31 +0,0 @@
// need to do this first before importing database, etc.
import dotenv from 'dotenv';
dotenv.config();
import * as cookie from 'cookie';
import { get_user, sanitize_user } from './utils/auth';
import { query } from './utils/db';
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ request, resolve }) {
if (process.env['PGHOST']) {
// this is a convenient time to clear out expired sessions
query('delete from sessions where expiry < now()');
request.locals.cookies = cookie.parse(request.headers.cookie || '');
request.locals.user = await get_user(request.locals.cookies.sid);
}
const response = await resolve(request);
return response;
}
/** @type {import('@sveltejs/kit').GetSession} */
export function getSession(request) {
return request.locals.user
? {
user: sanitize_user(request.locals.user)
}
: {};
}

@ -1,78 +0,0 @@
<script context="module">
/** @type {import('@sveltejs/kit').ErrorLoad} */
export function load({ error, status }) {
return {
props: { error, status }
};
}
</script>
<script>
export let status;
export let error;
// we don't want to use <svelte:window bind:online> here,
// because we only care about the online state when
// the page first loads
const online = typeof navigator !== 'undefined'
? navigator.onLine
: true;
</script>
<style>
.container {
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
}
h1, p { margin: 0 auto }
h1 {
font-size: 2.8em;
font-weight: 300;
margin: 0 0 0.5em 0;
}
p { margin: 1em auto }
.error {
background-color: var(--second);
color: white;
padding: 12px 16px;
font: 600 16px/1.7 var(--font);
border-radius: 2px;
}
/* @media (min-width: 480px) {
h1 { font-size: 4em }
} */
</style>
<svelte:head>
<title>{status}</title>
</svelte:head>
<div class="container">
{#if online}
<h1>Yikes!</h1>
{#if error.message}
<p class="error">{status}: {error.message}</p>
{:else}
<p class="error">Encountered a {status} error</p>
{/if}
{#if import.meta.env.DEV && error.stack}
<pre>{error.stack}</pre>
{:else}
{#if status >= 500}
<p>Please try reloading the page.</p>
{/if}
<p>If the error persists, please drop by <a href="chat">Discord chatroom</a> and let us know, or raise an issue on <a href="https://github.com/sveltejs/svelte">GitHub</a>. Thanks!</p>
{/if}
{:else}
<h1>It looks like you're offline</h1>
<p>Reload the page once you've found the internet.</p>
{/if}
</div>

@ -1,70 +0,0 @@
<script>
import '@sveltejs/site-kit/base.css';
import { setContext } from 'svelte';
import { page, navigating, session } from '$app/stores';
import { Icon, Icons, Nav, NavItem } from '@sveltejs/site-kit';
import PreloadingIndicator from '../components/PreloadingIndicator.svelte';
export let segment;
setContext('app', {
login: () => {
const login_window = window.open(`${window.location.origin}/auth/login`, 'login', 'width=600,height=400');
window.addEventListener('message', function handler(event) {
login_window.close();
window.removeEventListener('message', handler);
$session.user = event.data.user;
});
},
logout: async () => {
const r = await fetch(`/auth/logout`, {
credentials: 'include'
});
if (r.ok) $session.user = null;
}
});
</script>
<Icons/>
{#if $navigating && $navigating.to}
<PreloadingIndicator/>
{/if}
{#if $page.path !== '/repl/embed'}
<Nav {segment} {page} logo="svelte-logo-horizontal.svg">
<NavItem segment="tutorial">Tutorial</NavItem>
<NavItem segment="docs">Docs</NavItem>
<NavItem segment="examples">Examples</NavItem>
<NavItem segment="repl">REPL</NavItem>
<NavItem segment="blog">Blog</NavItem>
<NavItem segment="faq">FAQ</NavItem>
<NavItem external="https://kit.svelte.dev">SvelteKit</NavItem>
<NavItem external="chat" title="Discord Chat">
<Icon name="message-square"/>
</NavItem>
<NavItem external="https://github.com/sveltejs/svelte" title="GitHub Repo">
<Icon name="github"/>
</NavItem>
</Nav>
{/if}
<main>
<slot></slot>
</main>
<style>
main {
position: relative;
margin: 0 auto;
/* padding: var(--nav-h) var(--side-nav) 0 var(--side-nav); */
padding: var(--nav-h) 0 0 0;
overflow-x: hidden;
}
</style>

@ -1,26 +0,0 @@
<script>
import contributors from '../_contributors.js';
</script>
<style>
.contributor {
width: 2.4em;
height: 2.4em;
border-radius: 50%;
text-indent: -9999px;
display: inline-block;
background: no-repeat url(/contributors.jpg);
background-size: auto 102%;
margin: 0 0.5em 0.5em 0;
border: 2px solid var(--second);
}
</style>
{#each contributors as contributor, i}
<a
class="contributor"
style="background-position: {(100 * i) / (contributors.length - 1)}% 0"
href="https://github.com/{contributor}">
{contributor}
</a>
{/each}

@ -1,27 +0,0 @@
<script>
import donors from '../_donors.js';
</script>
<style>
.donor {
width: 2.4em;
height: 2.4em;
border-radius: 50%;
text-indent: -9999px;
display: inline-block;
background: no-repeat url(/donors.jpg);
background-size: auto 102%;
margin: 0 0.5em 0.5em 0;
border: 2px solid var(--second);
}
</style>
{#each donors as donor, i}
<a
class="donor"
style="background-position: {(100 * i) / (donors.length - 1)}% 0"
alt="{donor}"
href="https://opencollective.com/svelte">
{donor}
</a>
{/each}

@ -1,52 +0,0 @@
<script>
import { Section } from '@sveltejs/site-kit';
import IntersectionObserver from '../../components/IntersectionObserver.svelte';
import ReplWidget from '../../components/Repl/ReplWidget.svelte';
export let id;
</script>
<style>
.example {
width: 100%;
}
.example :global(a) {
color: inherit;
}
.example > :global(p) {
margin: 4.4rem 2.4rem 2.4rem 0;
}
.repl-container {
width: 100%;
height: 420px;
border-radius: var(--border-r);
overflow: hidden;
}
@media (min-width: 920px) {
.example {
display: grid;
grid-template-columns: 1fr 3fr;
grid-gap: 0.5em;
align-items: start;
}
}
</style>
<Section>
<div class="example">
<slot></slot>
<div class="repl-container">
<IntersectionObserver once let:intersecting top={400}>
{#if intersecting}
<!-- <Lazy this={loadReplWidget} example={id}/> -->
<ReplWidget example={id}/>
{/if}
</IntersectionObserver>
</div>
</div>
</Section>

@ -1,80 +0,0 @@
export const companies = [
{
href: "https://1password.com",
filename: "1password.svg",
alt: "1Password logo",
},
{
href: "https://www.alaskaair.com/",
style: "background-color: #01426a;",
filename: "alaskaairlines.svg",
alt: "Alaska Airlines logo",
},
{
href: "https://avast.com",
filename: "avast.svg",
alt: "Avast logo",
},
{
href: "https://chess.com",
style: "background-color: #312e2b;",
filename: "chess.svg",
alt: "Chess.com logo",
},
{
href: "https://fusioncharts.com",
filename: "fusioncharts.svg",
alt: "FusionCharts logo",
},
{
href: "https://godaddy.com",
filename: "godaddy.svg",
alt: "GoDaddy logo",
},
{
href: "https://www.ibm.com/",
filename: "ibm.svg",
alt: "IBM logo",
},
{
href: "https://media.lesechos.fr/infographie",
filename: "les-echos.svg",
alt: "Les Echos",
},
{
href: "https://www.philips.co.uk",
filename: "philips.svg",
alt: "Philips logo",
},
{
href: "https://global.rakuten.com/corp/",
filename: "rakuten.svg",
alt: "Rakuten logo",
},
{
href: "https://razorpay.com",
filename: "razorpay.svg",
alt: "Razorpay logo",
},
{
href: "https://www.se.com",
style: " background-color: #3dcd58; ",
filename: "Schneider_Electric.svg",
alt: "Schneider Electric",
},
{
href: "https://squareup.com",
filename: "square.svg",
alt: "Square",
},
{
href: "https://nytimes.com",
filename: "nyt.svg",
alt: "The New York Times logo",
},
{
href: "https://transloadit.com",
filename: "transloadit.svg",
alt: "Transloadit",
},
];

@ -1,66 +0,0 @@
<script>
import { companies } from './WhosUsingSvelte.js';
const randomizer = ({ prominent }) => Math.random();
const doSort = (a, b) => randomizer(b) - randomizer(a);
const sortedCompanies = companies.sort(doSort);
</script>
<div class="logos">
{#each sortedCompanies as { href, filename, alt, style, picture, span }, index}
<a target="_blank" rel="noopener" {href} style={style || ""}>
{#if picture}
<picture>
{#each picture as { type, srcset }}
<source {type} {srcset} />
{/each}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy" />
</picture>
{:else}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy" />
{#if span}
<span>{span}</span>
{/if}
{/if}
</a>
{/each}
</div>
<style>
.logos {
margin: 1em 0 0 0;
display: flex;
flex-wrap: wrap;
}
a {
height: 40px;
margin: 0 0.5em 0.5em 0;
display: flex;
align-items: center;
border: 2px solid var(--second);
padding: 0;
border-radius: 20px;
color: var(--text);
}
picture,
img {
height: 100%;
padding: 5px 10px;
transition: transform 0.2s;
min-width: 0; /* Avoid image overflow in Safari */
}
picture:hover,
img:hover {
transform: scale(1.2);
}
@media (min-width: 540px) {
a {
height: 60px;
border-radius: 30px;
}
picture,
img {
padding: 10px 20px;
}
}
</style>

@ -1,25 +0,0 @@
import { query as db_query } from '../../utils/db';
export async function get({ query, locals }) {
if (locals.user) {
const page_size = 100;
const offset = query.get('offset') ? parseInt(query.get('offset')) : 0;
const rows = await db_query(`
select g.uid, g.name, coalesce(g.updated_at, g.created_at) as updated_at
from gists g
where g.user_id = $1
order by id desc
limit ${page_size + 1}
offset $2
`, [locals.user.id, offset]);
rows.forEach(row => {
row.uid = row.uid.replace(/-/g, '');
});
const more = rows.length > page_size;
return { body: { apps: rows.slice(0, page_size), offset: more ? offset + page_size : null }};
} else {
return { status: 401 };
}
}

@ -1,140 +0,0 @@
<script context="module">
export async function load({ fetch, page, session: { user }}) {
let apps = [];
let offset = null;
if (user) {
let url = 'apps.json';
if (page.query.get('offset')) {
url += `?offset=${encodeURIComponent(page.query.get('offset'))}`;
}
const r = await fetch(url, {
credentials: 'include'
});
if (!r.ok) return { status: r.status, body: await r.text() };
({ apps, offset } = await r.json());
}
return { props: { user, apps, offset }};
}
</script>
<script>
import { getContext } from 'svelte';
export let user;
export let apps;
export let offset;
const { login, logout } = getContext('app');
const formatter = new Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit'
});
const format = str => formatter.format(new Date(str));
</script>
<svelte:head>
<title>Your apps • Svelte</title>
</svelte:head>
<div class="apps">
{#if user}
<header>
<h1>Your apps</h1>
<div class="user">
<img class="avatar" alt="{user.name || user.username} avatar" src="{user.avatar}">
<span>
{user.name || user.username}
(<a on:click|preventDefault={logout} href="auth/logout">log out</a>)
</span>
</div>
</header>
<ul>
{#each apps as app}
<li>
<a href="repl/{app.uid}">
<h2>{app.name}</h2>
<span>updated {format(app.updated_at)}</span>
</a>
</li>
{/each}
</ul>
{#if offset !== null}
<div><a href="apps?offset={offset}">Next page...</a></div>
{/if}
{:else}
<p>Please <a on:click|preventDefault={login} href="auth/login">log in</a> to see your saved apps.</p>
{/if}
</div>
<style>
.apps {
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
max-width: var(--main-width);
margin: 0 auto;
}
header {
margin: 0 0 1em 0;
}
h1 {
font-size: 4rem;
font-weight: 400;
}
.user {
display: flex;
padding: 0 0 0 3.2rem;
position: relative;
margin: 1rem 0 5rem 0;
color: var(--text);
}
.avatar {
position: absolute;
left: 0;
top: 0.1rem;
width: 2.4rem;
height: 2.4rem;
border: 1px solid rgba(0,0,0,0.3);
border-radius: 0.2rem;
}
ul {
list-style: none;
}
li {
margin: 0 0 1em 0;
}
h2 {
color: var(--text);
font-size: var(--h3);
font-weight: 400;
}
li a {
border: none;
}
li a:hover h2 {
color: var(--flash);
}
li span {
font-size: 14px;
color: #999;
}
</style>

@ -1,6 +0,0 @@
export const oauth = 'https://github.com/login/oauth';
export const baseurl = process.env['BASEURL'];
export const secure = baseurl && baseurl.startsWith('https:');
export const client_id = process.env['GITHUB_CLIENT_ID'];
export const client_secret = process.env['GITHUB_CLIENT_SECRET'];

@ -1,54 +0,0 @@
import devalue from 'devalue';
import * as cookie from 'cookie';
import * as httpie from 'httpie';
import { parse, stringify } from 'querystring';
import { sanitize_user, create_user, create_session } from '../../utils/auth';
import { oauth, secure, client_id, client_secret } from './_config.js';
export async function get({ query }) {
try {
// Trade "code" for "access_token"
const r1 = await httpie.post(`${oauth}/access_token?` + stringify({
code: query.get('code'),
client_id,
client_secret,
}));
// Now fetch User details
const { access_token } = parse(r1.data);
const r2 = await httpie.get('https://api.github.com/user', {
headers: {
'User-Agent': 'svelte.dev',
Authorization: `token ${access_token}`
}
});
const user = await create_user(r2.data, access_token);
const session = await create_session(user);
return {
headers: {
'Set-Cookie': cookie.serialize('sid', session.uid, {
maxAge: 31536000,
path: '/',
httpOnly: true,
secure
}),
'Content-Type': 'text/html; charset=utf-8'
},
body: `
<script>
window.opener.postMessage({
user: ${devalue(sanitize_user(user))}
}, window.location.origin);
</script>
`
};
} catch (err) {
console.error('GET /auth/callback', err);
return {
status: 500,
body: err.data
};
}
}

@ -1,35 +0,0 @@
import { stringify } from 'querystring';
import { oauth, baseurl, client_id } from './_config.js';
export const get = client_id
? () => {
const Location = `${oauth}/authorize?` + stringify({
scope: 'read:user',
client_id,
redirect_uri: `${baseurl}/auth/callback`,
});
return {
status: 302,
headers: {
Location
}
};
}
: () => {
return {
status: 500,
headers: {
'Content-Type': 'text/html; charset=utf-8'
},
body: `
<body style="font-family: sans-serif; background: rgb(255,215,215); border: 2px solid red; margin: 0; padding: 1em;">
<h1>Missing .env file</h1>
<p>In order to use GitHub authentication, you will need to <a target="_blank" href="https://github.com/settings/developers">register an OAuth application</a> and create a local .env file:</p>
<pre>GITHUB_CLIENT_ID=[YOUR_APP_ID]\nGITHUB_CLIENT_SECRET=[YOUR_APP_SECRET]\nBASEURL=http://localhost:3000</pre>
<p>The <code>BASEURL</code> variable should match the callback URL specified for your app.</p>
<p>See also <a target="_blank" href="https://github.com/sveltejs/svelte/tree/master/site#repl-github-integration">here</a></p>
</body>
`
};
};

@ -1,18 +0,0 @@
import * as cookie from 'cookie';
import { secure } from './_config.js';
import { delete_session } from '../../utils/auth.js';
export async function get(request) {
await delete_session(request.locals.cookies.sid);
return {
headers: {
'Set-Cookie': cookie.serialize('sid', '', {
maxAge: -1,
path: '/',
httpOnly: true,
secure
})
}
};
}

@ -1,23 +0,0 @@
import get_posts from './_posts.js';
let lookup;
export function get({ params }) {
if (!lookup || process.env.NODE_ENV !== 'production') {
lookup = new Map();
get_posts().forEach(post => {
lookup.set(post.slug, post);
});
}
const post = lookup.get(params.slug);
if (post) {
return {
body: post,
headers: {
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
};
}
}

@ -1,177 +0,0 @@
<script context="module">
export async function load({ fetch, page: { params } }) {
const res = await fetch(`/blog/${params.slug}.json`);
if (res.ok) {
return { props: { post: await res.json() }};
}
}
</script>
<script>
export let post;
</script>
<svelte:head>
<title>{post.metadata.title}</title>
<meta name="twitter:title" content={post.metadata.title}>
<meta name="twitter:description" content={post.metadata.description}>
<meta name="Description" content={post.metadata.description}>
</svelte:head>
<article class='post listify'>
<h1>{post.metadata.title}</h1>
<p class='standfirst'>{post.metadata.description}</p>
<p class='byline'><a href='{post.metadata.authorURL}'>{post.metadata.author}</a> <time datetime='{post.metadata.pubdate}'>{post.metadata.dateString}</time></p>
{@html post.html}
</article>
<style>
.post {
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
max-width: var(--main-width);
margin: 0 auto;
}
h1 {
font-size: 4rem;
font-weight: 400;
}
.standfirst {
font-size: var(--h4);
color: var(--second);
margin: 0 0 1em 0;
}
.byline {
margin: 0 0 6rem 0;
padding: 1.6rem 0 0 0;
border-top: var(--border-w) solid #6767785b;
font-size: var(--h6);
text-transform: uppercase;
}
.byline a {
/* border-bottom: none; */
/* font-weight: 600; */
}
.byline a:hover {
/* border-bottom: 2px solid var(--prime); */
}
.post h1 {
color: var(--second);
max-width: 20em;
margin: 0 0 .8rem 0;
}
.post :global(h2) {
margin: 2em 0 0.5em 0;
/* color: var(--second); */
color: var(--text);
font-size: var(--h3);
font-weight: 300;
}
.post :global(figure) {
margin: 1.6rem 0 3.2rem 0;
}
.post :global(figure) :global(img) {
max-width: 100%;
}
.post :global(figcaption) {
color: var(--second);
text-align: left;
}
.post :global(video) {
width: 100%;
}
.post :global(blockquote) {
max-width: none;
border-left: 4px solid #eee;
background: #f9f9f9;
border-radius: 0 var(--border-r) var(--border-r) 0;
}
.post :global(code) {
padding: .3rem .8rem .3rem;
margin: 0 0.2rem;
top: -.1rem;
background: var(--back-api);
}
.post :global(pre) :global(code) {
padding: 0;
margin: 0;
top: 0;
background: transparent;
}
.post :global(aside) {
float: right;
margin: 0 0 1em 1em;
width: 16rem;
color: var(--second);
z-index: 2;
}
.post :global(.max) {
width: 100%;
}
.post :global(iframe) {
width: 100%;
height: 420px;
margin: 2em 0;
border-radius: var(--border-r);
border: 0.8rem solid var(--second);
}
.post :global(.anchor) {
top: calc((var(--h3) - 24px) / 2);
}
.post :global(a) {
padding: 0;
transition: none;
}
.post :global(a):not(:hover) {
border: none;
}
@media (max-width: 768px) {
.post :global(.anchor) {
transform: scale(0.6);
opacity: 1;
left: -1.0em;
}
}
@media (min-width: 910px) {
.post :global(.max) {
width: calc(100vw - 2 * var(--side-nav));
margin: 0 calc(var(--main-width) / 2 - 50vw);
text-align: center;
}
.post :global(.max) > :global(*) {
width: 100%;
max-width: 1200px;
}
.post :global(iframe) {
width: 100%;
max-width: 1100px;
margin: 2em auto;
}
}
</style>

@ -1,59 +0,0 @@
import fs from 'fs';
import path from 'path';
import { extract_frontmatter, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import marked from 'marked';
import { makeSlugProcessor } from '../../utils/slug';
import { highlight } from '../../utils/highlight';
import { SLUG_PRESERVE_UNICODE } from '../../../config';
const makeSlug = makeSlugProcessor(SLUG_PRESERVE_UNICODE);
export default function get_posts() {
return fs
.readdirSync('content/blog')
.map(file => {
if (path.extname(file) !== '.md') return;
const match = /^(\d+-\d+-\d+)-(.+)\.md$/.exec(file);
if (!match) throw new Error(`Invalid filename '${file}'`);
const [, pubdate, slug] = match;
const markdown = fs.readFileSync(`content/blog/${file}`, 'utf-8');
const { content, metadata } = extract_frontmatter(markdown);
const date = new Date(`${pubdate} EDT`); // cheeky hack
metadata.pubdate = pubdate;
metadata.dateString = date.toDateString();
const renderer = new marked.Renderer();
renderer.link = link_renderer;
renderer.code = highlight;
renderer.heading = (text, level, rawtext) => {
const fragment = makeSlug(rawtext);
return `
<h${level}>
<span id="${fragment}" class="offset-anchor"></span>
<a href="blog/${slug}#${fragment}" class="anchor" aria-hidden="true"></a>
${text}
</h${level}>`;
};
const html = marked(
content.replace(/^\t+/gm, match => match.split('\t').join(' ')),
{ renderer }
);
return {
html,
metadata,
slug
};
})
.sort((a, b) => a.metadata.pubdate < b.metadata.pubdate ? 1 : -1);
}

@ -1,26 +0,0 @@
import get_posts from './_posts.js';
let json;
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
const posts = get_posts()
.filter(post => !post.metadata.draft)
.map(post => {
return {
slug: post.slug,
metadata: post.metadata
};
});
json = JSON.stringify(posts);
}
return {
body: json,
headers: {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
}
}

@ -1,94 +0,0 @@
<script context="module">
export async function load({ fetch }) {
const posts = await fetch(`blog.json`).then(r => r.json());
return {
props: {
posts
}
};
}
</script>
<script>
export let posts;
</script>
<svelte:head>
<title>Blog • Svelte</title>
<link rel="alternate" type="application/rss+xml" title="Svelte blog" href="https://svelte.dev/blog/rss.xml">
<meta name="twitter:title" content="Svelte blog">
<meta name="twitter:description" content="Articles about Svelte and UI development">
<meta name="Description" content="Articles about Svelte and UI development">
</svelte:head>
<h1 class="visually-hidden">Blog</h1>
<div class='posts stretch'>
{#each posts as post}
<article class='post' data-pubdate={post.metadata.dateString}>
<a class="no-underline" sveltekit:prefetch href='blog/{post.slug}' title='Read the article »'>
<h2>{post.metadata.title}</h2>
<p>{post.metadata.description}</p>
</a>
</article>
{/each}
</div>
<style>
.posts {
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
min-height: calc(100vh - var(--nav-h));
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
max-width: var(--main-width);
margin: 0 auto;
}
h2 {
display: inline-block;
margin: 3.2rem 0 0.4rem 0;
color: var(--text);
max-width: 18em;
font-size: var(--h3);
font-weight: 400;
}
.post:first-child {
margin: 0 0 2rem 0;
padding: 0 0 4rem 0;
border-bottom: var(--border-w) solid #6767785b; /* based on --second */
}
.post:first-child h2 {
font-size: 4rem;
font-weight: 400;
color: var(--second);
}
.post:first-child::before,
.post:nth-child(2)::before {
content: 'Latest post • ' attr(data-pubdate);
color: var(--flash);
font-size: var(--h6);
font-weight: 400;
letter-spacing: .05em;
text-transform: uppercase;
}
.post:nth-child(2)::before {
content: 'Older posts';
}
.post p {
font-size: var(--h5);
max-width: 30em;
color: var(--second);
}
.post > a { display: block }
.posts a:hover,
.posts a:hover > h2 {
color: var(--flash)
}
</style>

@ -1,56 +0,0 @@
import get_posts from '../blog/_posts.js';
const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',');
function formatPubdate(str) {
const [y, m, d] = str.split('-');
return `${d} ${months[+m]} ${y} 12:00 +0000`;
}
function escapeHTML(html) {
const chars = {
'"' : 'quot',
"'": '#39',
'&': 'amp',
'<' : 'lt',
'>' : 'gt'
};
return html.replace(/["'&<>]/g, c => `&${chars[c]};`);
}
const rss = `
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Svelte blog</title>
<link>https://svelte.dev/blog</link>
<description>News and information about the magical disappearing UI framework</description>
<image>
<url>https://svelte.dev/favicon.png</url>
<title>Svelte</title>
<link>https://svelte.dev/blog</link>
</image>
${get_posts().filter(post => !post.metadata.draft).map(post => `
<item>
<title>${escapeHTML(post.metadata.title)}</title>
<link>https://svelte.dev/blog/${post.slug}</link>
<description>${escapeHTML(post.metadata.description)}</description>
<pubDate>${formatPubdate(post.metadata.pubdate)}</pubDate>
</item>
`).join('')}
</channel>
</rss>
`.replace(/>[^\S]+/gm, '>').replace(/[^\S]+</gm, '<').trim();
export function get() {
return {
body: rss,
headers: {
'Cache-Control': `max-age=${30 * 60 * 1e3}`,
'Content-Type': 'application/rss+xml'
}
};
}

@ -1,6 +0,0 @@
export function get() {
return {
status: 302,
headers: { Location: 'https://discord.gg/yy75DKs' },
};
}

@ -1,135 +0,0 @@
import fs from 'fs';
import path from 'path';
import { SLUG_PRESERVE_UNICODE, SLUG_SEPARATOR } from '../../../config';
import { extract_frontmatter, extract_metadata, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import { make_session_slug_processor } from '@sveltejs/site-kit/utils/slug';
import { highlight } from '../../utils/highlight';
import marked from 'marked';
const blockTypes = [
'blockquote',
'html',
'heading',
'hr',
'list',
'listitem',
'paragraph',
'table',
'tablerow',
'tablecell'
];
export default function() {
const make_slug = make_session_slug_processor({
preserve_unicode: SLUG_PRESERVE_UNICODE,
separator: SLUG_SEPARATOR
});
return fs
.readdirSync(`content/docs`)
.filter(file => file[0] !== '.' && path.extname(file) === '.md')
.map(file => {
const markdown = fs.readFileSync(`content/docs/${file}`, 'utf-8');
const { content, metadata } = extract_frontmatter(markdown);
const section_slug = make_slug(metadata.title);
const subsections = [];
const renderer = new marked.Renderer();
let block_open = false;
renderer.link = link_renderer;
renderer.hr = () => {
block_open = true;
return '<div class="side-by-side"><div class="copy">';
};
renderer.code = (source, lang) => {
source = source.replace(/^ +/gm, match =>
match.split(' ').join('\t')
);
const lines = source.split('\n');
const meta = extract_metadata(lines[0], lang);
let prefix = '';
let className = 'code-block';
if (meta) {
source = lines.slice(1).join('\n');
const filename = meta.filename || (lang === 'html' && 'App.svelte');
if (filename) {
prefix = `<span class='filename'>${prefix} ${filename}</span>`;
className += ' named';
}
}
if (meta && meta.hidden) return '';
const html = `<div class='${className}'>${prefix}${highlight(source, lang)}</div>`;
if (block_open) {
block_open = false;
return `</div><div class="code">${html}</div></div>`;
}
return html;
};
renderer.heading = (text, level, rawtext) => {
let slug;
const match = /<a href="([^"]+)"[^>]*>(.+)<\/a>/.exec(text);
if (match) {
slug = match[1];
text = match[2];
} else {
slug = make_slug(rawtext);
}
if (level === 3 || level === 4) {
const title = text
.replace(/<\/?code>/g, '')
.replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => {
if ($3) return `.${$1}(...)`;
if ($2) return `.${$1}()`;
return `.${$1}`;
});
subsections.push({ slug, title, level });
}
return `
<h${level}>
<span id="${slug}" class="offset-anchor" ${level > 4 ? 'data-scrollignore' : ''}></span>
<a href="docs#${slug}" class="anchor" aria-hidden="true"></a>
${text}
</h${level}>`;
};
blockTypes.forEach(type => {
const fn = renderer[type];
renderer[type] = function() {
return fn.apply(this, arguments);
};
});
const html = marked(content, { renderer });
const hashes = {};
return {
html: html.replace(/@@(\d+)/g, (m, id) => hashes[id] || m),
metadata,
subsections,
slug: section_slug,
file,
};
});
}

@ -1,13 +0,0 @@
import get_sections from './_sections.js';
let json;
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
json = get_sections();
}
return {
body: json
};
}

@ -1,44 +0,0 @@
<script context="module">
const title_replacements = {
'1_export_creates_a_component_prop': 'props',
'2_Assignments_are_reactive': 'reactive assignments',
'3_$_marks_a_statement_as_reactive': 'reactive statements ($:)',
'4_Prefix_stores_with_$_to_access_their_values': 'accessing stores ($)'
};
export async function load({ fetch }) {
const sections = await fetch(`docs.json`).then(r => r.json());
for (const section of sections) {
for (const subsection of section.subsections) {
const { slug } = subsection;
// show abbreviated title in the table of contents
if (slug in title_replacements) {
subsection.title = title_replacements[slug];
}
}
}
return {
props: {
sections
}
};
}
</script>
<script>
import { Docs } from '@sveltejs/site-kit';
export let sections;
</script>
<svelte:head>
<title>API Docs • Svelte</title>
<meta name="twitter:title" content="Svelte API docs">
<meta name="twitter:description" content="Cybernetically enhanced web apps">
<meta name="Description" content="Cybernetically enhanced web apps">
</svelte:head>
<h1 class="visually-hidden">API Docs</h1>
<Docs {sections}/>

@ -1,20 +0,0 @@
import { get_example } from './_examples.js';
const cache = new Map();
export function get({ params }) {
const { slug } = params;
let example = cache.get(slug);
if (!example || process.env.NODE_ENV !== 'production') {
example = get_example(slug);
if (example) cache.set(slug, example);
}
if (example) {
return {
body: example
};
}
}

@ -1,116 +0,0 @@
<script>
export let sections = [];
export let active_section = null;
export let isLoading = false;
</script>
<style>
.examples-toc {
overflow-y: auto;
height: 100%;
border-right: 1px solid var(--second);
background-color: var(--second);
color: white;
padding: 3rem 3rem 0 3rem;
}
.examples-toc li {
display: block;
line-height: 1.2;
margin: 0 0 4.8rem 0;
}
.section-title {
display: block;
padding: 0 0 0.8rem 0;
font: 400 var(--h6) var(--font);
text-transform: uppercase;
letter-spacing: 0.12em;
font-weight: 700;
}
div {
display: flex;
flex-direction: row;
padding: 0.2rem 3rem;
margin: 0 -3rem;
}
div.active {
background: rgba(0, 0, 0, 0.15) calc(100% - 3rem) 47% no-repeat
url(/icons/arrow-right.svg);
background-size: 1em 1em;
color: white;
}
div.active.loading {
background: rgba(0, 0, 0, 0.1) calc(100% - 3rem) 47% no-repeat
url(/icons/loading.svg);
background-size: 1em 1em;
color: white;
}
a {
display: flex;
flex: 1 1 auto;
position: relative;
color: var(--sidebar-text);
border-bottom: none;
font-size: 1.6rem;
align-items: center;
justify-content: start;
padding: 0;
}
a:hover {
color: white;
}
.repl-link {
flex: 0 1 auto;
font-size: 1.2rem;
font-weight: 700;
margin-right: 2.5rem;
}
.thumbnail {
background-color: white;
object-fit: contain;
width: 5rem;
height: 5rem;
border-radius: 2px;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.13);
margin: 0.2em 0.5em 0.2em 0;
}
</style>
<ul class="examples-toc">
{#each sections as section}
<li>
<span class="section-title">{section.title}</span>
{#each section.examples as example}
<div
class="row"
class:active={example.slug === active_section}
class:loading={isLoading}>
<a
href="examples#{example.slug}"
class="row"
class:active={example.slug === active_section}
class:loading={isLoading}>
<img
class="thumbnail"
alt="{example.title} thumbnail"
src="examples/thumbnails/{example.slug}.jpg" />
<span>{example.title}</span>
</a>
{#if example.slug === active_section}
<a href="repl/{example.slug}" class="repl-link">REPL</a>
{/if}
</div>
{/each}
</li>
{/each}
</ul>

@ -1,50 +0,0 @@
import fs from 'fs';
let lookup;
const titles = new Map();
export function get_examples() {
lookup = new Map();
return fs.readdirSync(`content/examples`).map(group_dir => {
const metadata = JSON.parse(fs.readFileSync(`content/examples/${group_dir}/meta.json`, 'utf-8'));
return {
title: metadata.title,
examples: fs.readdirSync(`content/examples/${group_dir}`).filter(file => file !== 'meta.json').map(example_dir => {
const slug = example_dir.replace(/^\d+-/, '');
if (lookup.has(slug)) throw new Error(`Duplicate example slug "${slug}"`);
lookup.set(slug, `${group_dir}/${example_dir}`);
const metadata = JSON.parse(fs.readFileSync(`content/examples/${group_dir}/${example_dir}/meta.json`, 'utf-8'));
titles.set(slug, metadata.title);
return {
slug,
title: metadata.title
};
})
};
});
}
export function get_example(slug) {
if (!lookup || !lookup.has(slug)) get_examples();
const dir = lookup.get(slug);
const title = titles.get(slug);
if (!dir || !title) return null;
const files = fs.readdirSync(`content/examples/${dir}`)
.filter(name => name[0] !== '.' && name !== 'meta.json')
.map(name => {
return {
name,
source: fs.readFileSync(`content/examples/${dir}/${name}`, 'utf-8')
};
});
return { title, files };
}

@ -1,22 +0,0 @@
import { get_examples } from './_examples.js';
let cached;
export function get() {
if (!cached || process.env.NODE_ENV !== 'production') {
cached = get_examples().filter(section => section.title);
}
try {
return {
body: cached
};
} catch(err) {
return {
status: e.status || 500,
body: {
message: e.message
}
};
}
}

@ -1,183 +0,0 @@
<!-- FIXME sometimes it adds a trailing slash when landing -->
<script context="module">
export async function load({ fetch }) {
const sections = await fetch(`examples.json`).then(r => r.json());
const title_by_slug = sections.reduce((acc, {examples}) => {
examples.forEach(({slug, title}) => {
acc[slug] = title;
});
return acc;
}, {});
return {
props: {
sections,
title_by_slug
}
};
}
</script>
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import Repl from '@sveltejs/svelte-repl';
import ScreenToggle from '../../components/ScreenToggle.svelte';
import {
mapbox_setup, // see site/content/examples/15-context/00-context-api
rollupUrl,
svelteUrl
} from '../../config';
import { process_example } from '../../utils/examples';
import { getFragment } from '@sveltejs/site-kit/utils/navigation';
import TableOfContents from './_TableOfContents.svelte';
export let sections;
export let title_by_slug;
let active_slug;
let width;
let offset = 1;
let repl;
let isLoading = false;
const cache = {};
function showExampleCodeOnChange() {
offset = 1;
}
$: title = title_by_slug[active_slug] || '';
$: first_slug = sections[0].examples[0].slug;
$: mobile = width < 768; // note: same as per media query below
$: replOrientation = (mobile || width > 1080) ? 'columns' : 'rows';
$: if (repl && active_slug) {
if (active_slug in cache) {
repl.set({ components: cache[active_slug] });
showExampleCodeOnChange();
} else {
isLoading = true;
fetch(`examples/${active_slug}.json`)
.then(async response => {
if (response.ok) {
const {files} = await response.json();
return process_example(files);
}
})
.then(components => {
cache[active_slug] = components;
repl.set({components});
showExampleCodeOnChange();
isLoading = false;
})
.catch(() => {
isLoading = false;
});
}
}
onMount(() => {
const onhashchange = () => {
active_slug = getFragment();
};
window.addEventListener('hashchange', onhashchange, false);
const fragment = getFragment();
if (fragment) {
active_slug = fragment;
} else {
active_slug = first_slug;
goto(`examples#${active_slug}`);
}
return () => {
window.removeEventListener('hashchange', onhashchange, false);
};
});
</script>
<svelte:head>
<title>{title} {title ? '•' : ''} Svelte Examples</title>
<meta name="twitter:title" content="Svelte examples">
<meta name="twitter:description" content="Cybernetically enhanced web apps">
<meta name="Description" content="Interactive example Svelte apps">
</svelte:head>
<h1 class="visually-hidden">Examples</h1>
<div class='examples-container' bind:clientWidth={width}>
<div class="viewport offset-{offset}">
<TableOfContents {sections} active_section={active_slug} {isLoading} />
<div class="repl-container" class:loading={isLoading}>
<Repl
bind:this={repl}
workersUrl="workers"
{svelteUrl}
{rollupUrl}
orientation={replOrientation}
fixed={mobile}
relaxed
injectedJS={mapbox_setup}
/>
</div>
</div>
{#if mobile}
<ScreenToggle bind:offset labels={['index', 'input', 'output']}/>
{/if}
</div>
<style>
.examples-container {
position: relative;
height: calc(100vh - var(--nav-h));
overflow: hidden;
padding: 0 0 42px 0;
box-sizing: border-box;
}
.viewport {
display: grid;
width: 300%;
height: 100%;
grid-template-columns: 33.333% 66.666%;
transition: transform .3s;
grid-auto-rows: 100%;
}
.repl-container.loading {
opacity: 0.6;
}
/* temp fix for #2499 and #2550 while waiting for a fix for https://github.com/sveltejs/svelte-repl/issues/8 */
.repl-container :global(.tab-content),
.repl-container :global(.tab-content.visible) {
pointer-events: all;
opacity: 1;
}
.repl-container :global(.tab-content) {
visibility: hidden;
}
.repl-container :global(.tab-content.visible) {
visibility: visible;
}
.offset-1 { transform: translate(-33.333%, 0); }
.offset-2 { transform: translate(-66.666%, 0); }
@media (min-width: 768px) {
.examples-container { padding: 0 }
.viewport {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: var(--sidebar-mid-w) auto;
grid-auto-rows: 100%;
transition: none;
}
.offset-1, .offset-2 { transform: none; }
}
</style>

@ -1,58 +0,0 @@
import fs from 'fs';
import path from 'path';
import { extract_frontmatter, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import marked from 'marked';
import { makeSlugProcessor } from '../../utils/slug';
import { highlight } from '../../utils/highlight';
import { SLUG_PRESERVE_UNICODE } from '../../../config';
const makeSlug = makeSlugProcessor(SLUG_PRESERVE_UNICODE);
export default function get_faqs() {
return fs
.readdirSync('content/faq')
.map(file => {
if (path.extname(file) !== '.md') return;
const match = /^([0-9]+)-(.+)\.md$/.exec(file);
if (!match) throw new Error(`Invalid filename '${file}'`);
const [, order, slug] = match;
const markdown = fs.readFileSync(`content/faq/${file}`, 'utf-8');
const { content, metadata } = extract_frontmatter(markdown);
const renderer = new marked.Renderer();
renderer.link = link_renderer;
renderer.code = highlight;
renderer.heading = (text, level, rawtext) => {
const fragment = makeSlug(rawtext);
return `
<h${level}>
<span id="${fragment}" class="offset-anchor"></span>
<a href="faq#${fragment}" class="anchor" aria-hidden="true"></a>
${text}
</h${level}>`;
};
const answer = marked(
content.replace(/^\t+/gm, match => match.split('\t').join(' ')),
{ renderer }
);
const fragment = makeSlug(slug);
return {
fragment,
order,
answer,
metadata
};
})
.sort((a, b) => a.order - b.order);
}

@ -1,26 +0,0 @@
import get_faqs from './_faqs.js';
let json;
export function get() {
if (!json || process.env.NODE_ENV !== 'production') {
const faqs = get_faqs()
.map(faq => {
return {
fragment: faq.fragment,
answer: faq.answer,
metadata: faq.metadata
};
});
json = JSON.stringify(faqs);
}
return {
body: json,
headers: {
'Content-Type': 'application/json',
'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes
}
};
}

@ -1,93 +0,0 @@
<script context="module">
export async function load({ fetch }) {
const faqs = await fetch(`faq.json`).then(r => r.json());
return {
props: {
faqs
}
};
}
</script>
<script>
const description = "Frequently Asked Questions about Svelte"
export let faqs;
</script>
<svelte:head>
<title>Frequently Asked Questions • Svelte</title>
<meta name="twitter:title" content="Svelte FAQ">
<meta name="twitter:description" content={description}>
<meta name="Description" content={description}>
</svelte:head>
<div class='faqs stretch'>
<h1>Frequently Asked Questions</h1>
{#each faqs as faq}
<article class='faq'>
<h2>
<span id={faq.fragment} class="offset-anchor"></span>
<a class="anchor" href='faq#{faq.fragment}' title='{faq.question}'>&nbsp;</a>
{faq.metadata.question}
</h2>
<p>{@html faq.answer}</p>
</article>
{/each}
</div>
<style>
.faqs {
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
min-height: calc(100vh - var(--nav-h));
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
max-width: var(--main-width);
margin: 0 auto;
}
h2 {
display: inline-block;
margin: 3.2rem 0 1rem 0;
color: var(--text);
max-width: 18em;
font-size: var(--h3);
font-weight: 400;
}
.faq:first-child {
margin: 0 0 2rem 0;
padding: 0 0 4rem 0;
border-bottom: var(--border-w) solid #6767785b; /* based on --second */
}
.faq:first-child h2 {
font-size: 4rem;
font-weight: 400;
color: var(--second);
}
.faq p {
font-size: var(--h5);
max-width: 30em;
color: var(--second);
}
:global(.faqs .faq ul) {
margin-left: 3.2rem;
}
.faqs :global(.anchor) {
top: calc((var(--h3) - 24px) / 2);
}
@media (max-width: 768px) {
.faqs :global(.anchor) {
transform: scale(0.6);
opacity: 1;
left: -1.0em;
}
}
</style>

@ -1,132 +0,0 @@
<script>
import { Blurb, Hero, Section } from '@sveltejs/site-kit';
import Contributors from './_components/Contributors.svelte';
import Donors from './_components/Donors.svelte';
import Example from './_components/Example.svelte';
import WhosUsingSvelte from './_components/WhosUsingSvelte.svelte';
// import Lazy from '../components/Lazy.svelte';
// TODO this causes a Sapper CSS bug...
// function loadReplWidget() {
// console.log('lazy loading');
// return import('../components/Repl/ReplWidget.svelte').then(mod => mod.default);
// }
</script>
<style>
/* darken text for accessibility */
/* TODO does this belong elsewhere? */
:global(.back-light) {
--text: hsl(36, 3%, 44%);
}
.examples {
background: var(--second);
color: white;
overflow: hidden;
}
</style>
<svelte:head>
<title>Svelte • Cybernetically enhanced web apps</title>
<meta name="twitter:title" content="Svelte">
<meta name="twitter:description" content="Cybernetically enhanced web apps">
<meta name="Description" content="Cybernetically enhanced web apps">
</svelte:head>
<h1 class="visually-hidden">Svelte</h1>
<Hero
title="Svelte"
tagline="Cybernetically enhanced web apps"
outline="svelte-logo-outline.svg"
logotype="svelte-logotype.svg"
/>
<Blurb>
<a href="blog/write-less-code" slot="one">
<h2>Write less code</h2>
<p>Build boilerplate-free components using languages you already know — HTML, CSS and JavaScript</p>
<span class="learn-more">learn more</span>
</a>
<a href="blog/virtual-dom-is-pure-overhead" slot="two">
<h2>No virtual DOM</h2>
<p>Svelte compiles your code to tiny, framework-less vanilla JS — your app starts fast and stays fast</p>
<span class="learn-more">learn more</span>
</a>
<a href="blog/svelte-3-rethinking-reactivity" slot="three">
<h2>Truly reactive</h2>
<p>No more complex state management libraries — Svelte brings reactivity to JavaScript itself</p>
<span class="learn-more">learn more</span>
</a>
<div class="description" slot="what">
<p>Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the <em>browser</em>, Svelte shifts that work into a <em>compile step</em> that happens when you build your app.</p>
<p>Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.</p>
<p>We're proud that Svelte was recently voted the <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-web-frameworks">most loved web framework</a> with the <a href="https://2020.stateofjs.com/en-US/technologies/front-end-frameworks/">most satisfied developers</a> in a pair of industry surveys. We think you'll love it too. <a href="blog/svelte-3-rethinking-reactivity">Read the introductory blog post</a> to learn more.</p>
</div>
<div style="grid-area: start; display: flex; flex-direction: column; min-width: 0" slot="how">
<pre class="language-bash" style="margin: 0 0 1em 0; min-width: 0; min-height: 0">
npx degit <a href="https://github.com/sveltejs/template" style="user-select: initial;">sveltejs/template</a> my-svelte-project
<span class="token comment"># or download and extract <a href="https://github.com/sveltejs/template/archive/master.zip">this .zip file</a></span>
cd my-svelte-project
<span class="token comment"># to use <a href="blog/svelte-and-typescript">TypeScript</a> run:</span>
<span class="token comment"># node scripts/setupTypeScript.js</span>
npm install
npm run dev
</pre>
<p style="flex: 1">See the <a href="blog/the-easiest-way-to-get-started">quickstart guide</a> for more information.</p>
<p class="cta"><a sveltekit:prefetch href="tutorial">Learn Svelte</a></p>
</div>
</Blurb>
<div class="examples">
<Example id="hello-world">
<p>Svelte components are built on top of HTML. Just add data.</p>
</Example>
<Example id="nested-components">
<p>CSS is component-scoped by default — no more style collisions or specificity wars. Or you can <a href="/blog/svelte-css-in-js">use your favourite CSS-in-JS library</a>.</p>
</Example>
<Example id="reactive-assignments">
<p>Trigger efficient, granular updates by assigning to local variables. The compiler does the rest.</p>
</Example>
<Example id="svg-transitions">
<p>Build beautiful UIs with a powerful, performant transition engine built right into the framework.</p>
</Example>
</div>
<Section>
<h3>Who's using Svelte?</h3>
<WhosUsingSvelte/>
</Section>
<Section>
<h3>Supporters</h3>
<p>Svelte is free and open source software, made possible by the work of hundreds of volunteers and donors. <a href="https://github.com/sveltejs/svelte">Join us</a> or <a href="https://opencollective.com/svelte">give</a>!</p>
<h3>Contributors</h3>
<Contributors/>
<p></p>
<h3>Donors</h3>
<Donors/>
</Section>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save