pull/9399/merge
Wes Keiser 3 years ago committed by GitHub
commit 4efa5480a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "svelte-4",
"baseBranch": "main",
"bumpVersionsWithWorkspaceProtocolOnly": true,
"ignore": ["!(@sveltejs/*|svelte)"]
}

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: exclude internal props from spread attributes

@ -0,0 +1,14 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"svelte": "5.0.0-next.0",
"svelte-playgrounds-demo": "0.0.1",
"svelte-playgrounds-sandbox": "0.0.1",
"svelte-5-preview": "0.5.0",
"svelte.dev": "1.0.0"
},
"changesets": [
"rotten-buckets-develop"
]
}

@ -0,0 +1,5 @@
---
'svelte': patch
---
breaking: svelte 5 alpha

@ -0,0 +1,23 @@
# NOTE: In general this should be kept in sync with .eslintignore
**/dist/**
**/config/**
**/build/**
**/playgrounds/sandbox/**
**/npm/**
**/*.js.flow
**/*.d.ts
**/playwright*/**
**/vite.config.js
**/vite.prod.config.js
**/node_modules
**/tests/**
# documentation can contain invalid examples
documentation/**
# contains a fork of the REPL which doesn't adhere to eslint rules
sites/svelte-5-preview/**
# Wasn't checked previously, reenable at some point
sites/svelte.dev/**

@ -0,0 +1,54 @@
module.exports = {
extends: ['@sveltejs'],
// TODO: add runes to eslint-plugin-svelte
globals: {
$state: true,
$derived: true,
$effect: true,
$props: true
},
overrides: [
{
// scripts and playground should be console logging so don't lint against them
files: ['playgrounds/**/*', 'scripts/**/*'],
rules: {
'no-console': 'off'
}
},
{
// the playgrounds can use public naming conventions since they're examples
files: ['playgrounds/**/*'],
rules: {
'lube/svelte-naming-convention': 'off'
}
},
{
files: ['packages/svelte/src/compiler/**/*'],
rules: {
'no-var': 'error'
}
}
],
plugins: ['lube'],
rules: {
'no-console': 'error',
'lube/svelte-naming-convention': ['error', { fixSameNames: true }],
// eslint isn't that well-versed with JSDoc to know that `foo: /** @type{..} */ (foo)` isn't a violation of this rule, so turn it off
'object-shorthand': 'off',
'no-var': 'off',
// TODO: enable these rules and run `pnpm lint:fix`
// skipping that for now so as to avoid impacting real work
'@typescript-eslint/array-type': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'prefer-const': 'off',
'svelte/valid-compile': 'off',
quotes: 'off'
}
};

@ -1,7 +1,7 @@
name: CI
on:
push:
branches: [svelte-4]
branches: [main]
pull_request:
permissions:
contents: read # to fetch code (actions/checkout)
@ -17,11 +17,9 @@ jobs:
strategy:
matrix:
include:
- node-version: 16
os: ubuntu-latest
- node-version: 16
- node-version: 18
os: windows-latest
- node-version: 16
- node-version: 18
os: macOS-latest
- node-version: 18
os: ubuntu-latest
@ -47,6 +45,6 @@ jobs:
- uses: pnpm/action-setup@v2.2.4
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: pnpm
- run: 'pnpm i && pnpm check && pnpm lint'

@ -61,11 +61,11 @@ jobs:
repo: pr.head.repo.full_name
}
- id: generate-token
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 #keep pinned for security reasons, currently 1.8.0
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 #keep pinned for security reasons, currently 1.8.0
with:
app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }}
private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
repository: "${{ github.repository_owner }}/svelte-ecosystem-ci"
repository: '${{ github.repository_owner }}/svelte-ecosystem-ci'
- uses: actions/github-script@v6
id: trigger
env:

@ -3,7 +3,7 @@ name: Release
on:
push:
branches:
- svelte-4
- main
permissions: {}
jobs:

25
.gitignore vendored

@ -1,6 +1,27 @@
# Dependency directories
node_modules/
# IDE related
.idea
.DS_Store
.vscode/*
!.vscode/launch.json
node_modules
# Test coverage
coverage
*.lcov
# Optional eslint cache
.eslintcache
# dotenv environment variables file
.env
.env.test
# build output
dist
.vercel
# OS-specific
.DS_Store
tmp

@ -0,0 +1,39 @@
# NOTE: In general this should be kept in sync with .eslintignore
packages/**/dist/*.js
packages/**/build/*.js
packages/**/npm/**/*
packages/**/config/*.js
packages/svelte/tests/**/*.svelte
packages/svelte/tests/**/_expected*
packages/svelte/tests/**/_actual*
packages/svelte/tests/**/expected*
packages/svelte/tests/**/_output
packages/svelte/tests/**/shards/*.test.js
packages/svelte/tests/hydration/samples/*/_before.html
packages/svelte/tests/hydration/samples/*/_before_head.html
packages/svelte/tests/hydration/samples/*/_after.html
packages/svelte/tests/hydration/samples/*/_after_head.html
packages/svelte/types
packages/svelte/compiler.cjs
playgrounds/demo/src
playgrounds/sandbox/input/**.svelte
playgrounds/sandbox/output
sites/svelte.dev/static/svelte-app.json
sites/svelte.dev/scripts/svelte-app/
sites/svelte.dev/src/routes/_components/Supporters/contributors.jpg
sites/svelte.dev/src/routes/_components/Supporters/contributors.js
sites/svelte.dev/src/routes/_components/Supporters/donors.jpg
sites/svelte.dev/src/routes/_components/Supporters/donors.js
sites/svelte.dev/src/lib/generated
**/node_modules
**/.svelte-kit
**/.vercel
.github/CODEOWNERS
.prettierignore
.eslintignore
.changeset
pnpm-lock.yaml
pnpm-workspace.yaml

@ -18,6 +18,5 @@
"tabWidth": 2
}
}
],
"pluginSearchDirs": ["."]
]
}

@ -14,7 +14,7 @@
"name": "Playground: Server",
"outputCapture": "std",
"program": "start.js",
"cwd": "${workspaceFolder}/packages/playground",
"cwd": "${workspaceFolder}/playgrounds/demo",
"cascadeTerminateToConfigurations": ["Playground: Browser"]
}
],

@ -1,5 +1,5 @@
---
title: "Announcing Svelte 4"
title: 'Announcing Svelte 4'
description: 'Updated performance, developer experience, and site'
author: The Svelte team
authorURL: https://svelte.dev/

@ -1,6 +1,6 @@
---
title: "What's new in Svelte: July 2023"
description: "Svelte 4.0, new website and a tour around the community"
description: 'Svelte 4.0, new website and a tour around the community'
author: Dani Sandoval
authorURL: https://dreamindani.com
---
@ -8,6 +8,7 @@ authorURL: https://dreamindani.com
Svelte 4 is out and folks have been building! There's a bunch of new showcases, libraries and tutorials to share. So let's get right into it...
## What's new in Svelte
The big news this month was the release of Svelte 4.0! You can read all about it in the [Announcing Svelte 4 post](https://svelte.dev/blog/svelte-4). From performance fixes and developer experience improvements to [a brand new site, docs and tutorial](https://svelte.dev/blog/svelte-dev-overhaul)... this new release sets the stage for Svelte 5 with minimal breaking changes.
If you're already on Node.js 16, it's possible you won't see any breaking changes in your project. But be sure to read the [migration guide](https://svelte.dev/docs/v4-migration-guide) for all the details.
@ -15,7 +16,9 @@ If you're already on Node.js 16, it's possible you won't see any breaking change
For a full list of all the changes to the Svelte compiler, including unreleased changes, check out the [CHANGELOG](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md).
## What's new in SvelteKit
This month there were lots of awesome [bug fixes](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md), so be sure to upgrade to the latest version! There are also a few new features to mention:
- The new `event.isSubRequest` boolean indicates whether this is a same-origin fetch request to one of the app's own APIs during a server request (**1.21.0**, [Docs](https://kit.svelte.dev/docs/types#public-types-requestevent), [#10170](https://github.com/sveltejs/kit/pull/10170))
- A new config option, `config.kit.env.privatePrefix` will set a private prefix on environment variables. This defaults to `''` (**1.21.0**, [Docs](https://kit.svelte.dev/docs/configuration), [#9996](https://github.com/sveltejs/kit/pull/9996))
- `VERSION` is now exported and accessible via `@sveltejs/kit`. This can be used for feature detection or anything else that requires knowledge of the current version of SvelteKit (**1.21.0**, [Docs](https://kit.svelte.dev/docs/modules#sveltejs-kit-version), [#9969](https://github.com/sveltejs/kit/pull/9969))
@ -27,6 +30,7 @@ For adapter-specific changes, check out the CHANGELOGs in each of [the `adapter`
## Community Showcase
**Apps & Sites built with Svelte**
- [Heerdle](https://github.com/DreaminDani/heerdle) is a remake of Spotify's now-defunct Heardle - the daily music guessing game
- [Meoweler](https://meoweler.com/) is a travel site filled with cats and helpful facts about popular destinations
- [A tech lead from IKEA](https://www.reddit.com/r/sveltejs/comments/13w4zg3/comment/jmaxial/?utm_source=share&utm_medium=web2x&context=3) gave a few more details on the way they build pages (and page template) using Svelte
@ -39,10 +43,12 @@ For adapter-specific changes, check out the CHANGELOGs in each of [the `adapter`
- [YABin](https://github.com/Yureien/YABin) is Yet Another Pastebin with some very specific features
**Learning Resources**
- [Announcing Svelte 4 post](https://svelte.dev/blog/svelte-4)
- [svelte.dev: A complete overhaul](https://svelte.dev/blog/svelte-dev-overhaul)
_Featuring Svelte Contributors and Ambassadors_
- [Dev Vlog: June 2023](https://www.youtube.com/watch?v=AOXq89h8saI) - Svelte 4.0 with Rich Harris
- [PodRocket: Svelte 4](https://podrocket.logrocket.com/svelte-4) with Geoff
- [This Dot Media: Svelte 4 Launch Party](https://www.youtube.com/watch?v=-9gy_leMmcQ) with Simon, Ben, Geoff, and Puru
@ -60,12 +66,13 @@ _Featuring Svelte Contributors and Ambassadors_
- [Svelte Society - London June 2023](https://www.youtube.com/watch?v=EkH0aMgeIKw)
- [Using The Svelte Context API With Stores](https://www.youtube.com/watch?v=dp-7NvLDrK4), [Impossible FLIP Layout Animations With Svelte And GSAP](https://www.youtube.com/watch?v=ecP8RwpkiQw) and [Create Beautiful Presentations With Svelte](https://www.youtube.com/watch?v=67lqa5kTQkA) by Joy of Code
_To Watch_
- [Server-side filtered, paginated and sorted Table in SvelteKit](https://www.youtube.com/watch?v=VgCU0cVWgJE) by hartenfellerdev
- [Best Icon Library for Svelte and SvelteKit in 2023](https://www.youtube.com/watch?v=qJP6hC4YIhk) by SvelteRust
_To Read_
- [From Zero to Production with SvelteKit](https://www.okupter.com/events/from-zero-to-production-with-sveltekit) by Justin Ahinon
- [Thoughts on Svelte(Kit), one year and 3 billion requests later](https://claudioholanda.ch/en/blog/svelte-kit-after-3-billion-requests/) by Claudio Holanda
- [How I published a gratitude journaling app for iOS and Android using SvelteKit and Capacitor](https://khromov.se/how-i-published-a-gratitude-journaling-app-for-ios-and-android-using-sveltekit-and-capacitor/) by Stanislav Khromov
@ -77,8 +84,8 @@ _To Read_
- [Svelte Realtime Multiplayer Game: User Presence](https://rodneylab.com/svelte-realtime-multiplayer-game/) and [SvelteKit PostCSS Tutorial: use Future CSS Today](https://rodneylab.com/sveltekit-postcss-tutorial/) by Rodney Lab
- [SvelteKits World of Routing: Unleash power of your app using Dynamic Routes and Parameters](https://www.inow.dev/sveltekits-world-of-routing-unleash-power-of-your-app-using-dynamic-routes-and-parameters/) by Igor Nowosad
**Libraries, Tools & Components**
- [The Vercel AI SDK](https://vercel.com/blog/introducing-the-vercel-ai-sdk) is an interoperable, streaming-enabled, edge-ready software development kit for AI apps built with React and Svelte
- [Superforms 1.0](https://superforms.rocks/) has been released. Check out the [migration guide](https://superforms.rocks/migration) and [new feature list](https://superforms.rocks/whats-new-v1) for more details
- [Panda CSS](https://panda-css.com/docs/getting-started/svelte) is CSS-in-JS with build time generated styles, RSC compatibility and multi-variant support

@ -1,6 +1,6 @@
---
title: "What's new in Svelte: August 2023"
description: "Extending Custom Element Classes and new +server exports"
description: 'Extending Custom Element Classes and new +server exports'
author: Dani Sandoval
authorURL: https://dreamindani.com
---
@ -10,6 +10,7 @@ Some sweet new features have dropped in both Svelte and SvelteKit, this month. I
More on all that down below...
## What's new in Svelte & Language Tools
There's been a bunch of minor bugfixes since the Svelte 4 release. You can find them in the [CHANGELOG](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md).
The **4.1.0 release** added the ability to further customize the custom element class that wraps the underlying Svelte component. Check out the [Custom Elements API docs](https://svelte.dev/docs/custom-elements-api) or the [PR](https://github.com/sveltejs/svelte/pull/8991) for more info!
@ -17,6 +18,7 @@ The **4.1.0 release** added the ability to further customize the custom element
In addition to supporting SvelteKit's new `HEAD` server method, Svelte's language tools now support Prettier v3 (**extensions-107.9.0**) and workspace trust settings are now used to support all settings in workspace (**extensions-107.8.0**).
## What's new in SvelteKit
- The `HEAD` server method is now available in API routes (**1.22.0**, [Docs](https://kit.svelte.dev/docs/routing#server), [#9753](https://github.com/sveltejs/kit/pull/9753))
- Responses with `Vary` headers are now cached, too (except for `Vary: *`) (**1.22.0**, [Docs](https://kit.svelte.dev/docs/routing#server-content-negotiation), [#9993](https://github.com/sveltejs/kit/pull/9993))
- There's now a more helpful error for preview if SvelteKit's build output doesn't exist (**1.22.2**, [#10337](https://github.com/sveltejs/kit/pull/10337))
@ -28,6 +30,7 @@ For all the patches and performance updates from this month, check out the [Svel
## Community Showcase
**Apps & Sites built with Svelte**
- [GitLight](https://github.com/ColinLienard/gitlight) brings GitHub & GitLab notifications to your desktop
- [Days](https://github.com/paprikka/days) is paprikka's life in days, inspired by Buster Benson's Life in Weeks
- [Mofi](https://mofi.loud.red/) is a content-aware fill and trim for music
@ -37,14 +40,14 @@ For all the patches and performance updates from this month, check out the [Svel
- [Maktaba](https://www.maktaba.digital/) is a bookmark manager that "you will actually use"
- [Whispering](https://github.com/braden-w/whispering-extension) is a Chrome extension that lets you access OpenAI's Whisper API for fast transcription in the browser (including ChatGPT)
- [DocuTalk](https://docutalk.co/) is an AI Customer Support chatbot for your website
- [Krello](https://github.com/iamrishupatel/trello-clone) is a Trello clone built with Svelte, Appwrite and Flowbite
- [Krello](https://github.com/iamrishupatel/trello-clone) is a Trello clone built with Svelte, Appwrite and Flowbite
- [Been](https://beeneverywhere.net/) is a map builder with travel stats like visited countries, extreme visited points, etc.
- [image-to-social-media-thumbnail](https://brody.fyi/tools/image-to-social-media-thumbnail) lets you convert any image to a social media thumbnail
- [Svelte Capacitor Store](https://github.com/sdekna/svelte-capacitor-store) is a persistent store that uses capacitor (preferences) storage on native devices, and localStorage otherwise, making it ideal for multi-platform projects
**Learning Resources**
_Featuring Svelte Contributors and Ambassadors_
- [Exploring Svelte 4 w/ Kevin AK: Performance, Compatibility, & Web Component Support | Modern Web Pod](https://www.youtube.com/watch?v=YOL0HGGVib4) by This Dot Media
- [Svelte Sirens Stream Design Systems: Lessons Learned](https://www.youtube.com/live/YHZaiIGSqsE?feature=share) featuring Eric Liu, creator of Carbon Components Svelte and the `sveld` docgen library
- This Week in Svelte:
@ -52,8 +55,8 @@ _Featuring Svelte Contributors and Ambassadors_
- [2023 July 7](https://www.youtube.com/watch?v=0tq1ph4DDFA) - Svelte 4.0.5, Kit 1.22.1, Svelte 5, local storage and markdown
- [2023 July 21](https://www.youtube.com/watch?v=AG4_3kon3zU) - Svelte 4.1.1, SvelteKit 1.22.3, Progressive enhancement
_To Watch/Hear_
- [What is The Transitional Web? with Chris Ferdinandi](https://www.smashingmagazine.com/2023/07/smashing-podcast-episode-63/?ref=dailydevbytes.com) by Smashing Podcast
- [SvelteKit in 100 seconds](https://www.youtube.com/watch?v=H1eEFfAkIik) by Fireship
- [Primo V2 Introduction](https://www.youtube.com/watch?v=ThInVXgxJ1Q) by Primo (a [visual CMS](https://primocms.org/) based on Svelte)
@ -62,8 +65,8 @@ _To Watch/Hear_
- [Markdown in SvelteKit with custom Components: mdsvex](https://www.youtube.com/watch?v=VJFkyGd0FEA) by hartenfellerdev
- [How To Add Confetti for Svelte and Sveltekit 🎉](https://www.youtube.com/watch?v=gXtWSb94704) and [Make Your SvelteKit Code 10x Faster With Rust and WebAssembly](https://www.youtube.com/watch?v=Vn2bIv_J_UE) by SvelteRust
_To Read_
- [SvelteJS: My ecosystem is bigger than yours](https://hackmd.io/@roguegpu/r1RKQMdt3) by roguegpu
- [Avoid shared state on the server in SvelteKit](https://blog.aakashgoplani.in/avoid-shared-state-on-the-server-in-sveltekit) by Aakash Goplani
- [SvelteKit Fontaine: Reduce Custom Font CLS](https://rodneylab.com/sveltekit-fontaine/) by Rodney Lab
@ -74,8 +77,8 @@ _To Read_
- [Deploying Sveltekit on IIS](https://dev.to/nnutnonn/deploying-sveltekit-on-iis--5gf6) by Nutchapon Makelai
- [Streamlined Authentication and Secrets Management](https://eman.hashnode.dev/streamlined-authentication-and-secrets-management) by Eman
**Libraries, Tools & Components**
- [Melt UI](https://github.com/melt-ui/melt-ui) is a set of headless, accessible component builders for Svelte
- [MDsveX](https://github.com/pngwn/MDsveX/releases/tag/mdsvex%400.11.0) has been updated to work with Svelte 4
- [Svelte Sonner](https://github.com/wobsoriano/svelte-sonner) is an opinionated toast component for Svelte
@ -85,7 +88,7 @@ _To Read_
- [better-svelte-writable](https://github.com/tnthung/better-svelte-writable) provides a type-safe writable which gives you more control over the container
- [Svetch.ts](https://github.com/Bewinxed/svetch#readme) is a client/types/schema/docs generator for your API endpoints
- [sveltekit-localize-url](https://github.com/rinart73/sveltekit-localize-url) handles URL localization and routing
- [elegua](https://github.com/howesteve/elegua) is a small, reactive PWA router for Svelte
- [elegua](https://github.com/howesteve/elegua) is a small, reactive PWA router for Svelte
- [Molly](https://github.com/renefournier/molly/tree/main) is a bash script and npm module that helps you clean up unused Svelte components in your project
- [sveltekit-bot](https://github.com/begoon/sveltekit-bot) is a Telegram bot made with SvelteKit and Vercel

@ -133,12 +133,15 @@ We can also customize this page transition using CSS animation. In the style blo
}
:root::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
animation:
90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
:root::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms cubic-bezier(0.4, 0, 0.2, 1) both
slide-from-right;
animation:
210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
```
@ -233,17 +236,15 @@ While this may be the safest option, reduced motion does not necessarily mean no
```css
@media (prefers-reduced-motion: no-preference) {
:root::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 300ms cubic-bezier(0.4, 0, 0.2, 1) both
slide-to-left;
animation:
90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
:root::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms cubic-bezier(
0.4,
0,
0.2,
1
) both slide-from-right;
animation:
210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
}
```

@ -10,9 +10,11 @@ Happy September y'all! With all the [sneak peeks at what's coming soon in Svelte
Before we jump in, a warm welcome to the new Svelte Ambassadors: [@cainux](https://github.com/cainux) and [@grischaerbe](https://github.com/grischaerbe)! Welcome to the crew ⛴️
## What's new in Svelte & Language Tools
- `svelteHTML` has moved from language-tools into Svelte core so that `svelte/element` types will now load correctly (**4.2.0** in Svelte, **107.10.0** in Language Tools)
## What's new in SvelteKit
- `URL` is now accepted in the `redirect` function (**1.23.0**, [Docs](https://kit.svelte.dev/docs/modules#sveltejs-kit-redirect), [#10570](https://github.com/sveltejs/kit/pull/10570))
- Mistyped route filenames will now throw a warning (**1.23.0**, [#10558](https://github.com/sveltejs/kit/pull/10558))
- The new `onNavigate` lifecycle function enables view transitions - Check out the [blog post](https://svelte.dev/blog/view-transitions) for more info (**1.24.0**, [Docs](https://kit.svelte.dev/docs/modules#app-navigation-onnavigate), [#9605](https://github.com/sveltejs/kit/pull/9605))
@ -24,6 +26,7 @@ But that's just the new features! For all the patches and performance updates fr
## Community Showcase
**Apps & Sites built with Svelte**
- [Planet Of The Bugs](https://planetofthebugs.xyz/) allows developers to practice and hone their skill-sets by exposing them to an endless supply of unique, curated issues and bugs from popular open-source projects on Github
- [Minesweeper](https://github.com/ProductionPanic/minesweeper/tree/main) is an Android game built with SvelteKit, Capacitor, TailwindCSS and DaisyUI (check it out on the [Google Play Store](https://play.google.com/store/apps/details?id=com.production.panic.minesweeper&pli=1))
- [Pendor](https://www.pendor.ai/) is an AI component generator for Svelte
@ -36,10 +39,9 @@ But that's just the new features! For all the patches and performance updates fr
- [Ubuntu 22.04 in Svelte](https://github.com/manhhungpc/ubuntu2204-svelte) aims to replicate the Ubuntu 22.04 desktop experience on the web
- [My Queue](https://www.myqueue.so/) creates a playlist of written articles by turning them into audio stories
**Learning Resources**
_Featuring Svelte Contributors and Ambassadors_
- [Svelte Society - London August 2023](https://www.youtube.com/watch?v=90Psdk5rAnU)
- [Building a Blog using SvelteKit and Nostr as a CMS (Part 1](https://kevinak.se/blog/building-a-blog-using-sveltekit-and-nostr-as-a-cms-part-1-1690807337563)) by Kev
- [Mastering SvelteKit with Geoff Rich | JS Drops](https://www.youtube.com/watch?v=MaF8kRbHbi0) by This Dot Media
@ -60,15 +62,15 @@ _Featuring Svelte Contributors and Ambassadors_
- [Medusa and SvelteKit E-Commerce Stack](https://www.youtube.com/watch?v=rVVHxows9dY) with Lacey Pevey
- [Design Systems: Lessons Learned](https://www.youtube.com/watch?v=YHZaiIGSqsE) with Eric Liu
_To Watch_
- [Image optimization in SvelteKit with vite-imagetools](https://www.youtube.com/watch?v=285vSLe9LQ8) by hartenfellerdev
- [Building a Todo App with Rust and SvelteKit: Complete Tutorial](https://www.youtube.com/watch?v=w7is2bCTUg0) and [Stripe Payment In SvelteKit With Dynamic Pricing](https://www.youtube.com/watch?v=o8gvCLgz1vs) by SvelteRust
- [Building a Todo App with Rust and SvelteKit: Complete Tutorial](https://www.youtube.com/watch?v=w7is2bCTUg0) and [Stripe Payment In SvelteKit With Dynamic Pricing](https://www.youtube.com/watch?v=o8gvCLgz1vs) by SvelteRust
- [Leaflet maps in SvelteKit like it's 2023 (HowTo)](https://www.youtube.com/watch?v=JFctWXEzFZw)
ShipBit
ShipBit
_To Read_
- [Internationalization in SvelteKit (Series)](https://blog.aakashgoplani.in/series/i18n-in-sveltekit) by Aakash Goplani
- [The easiest Chatbot you will ever build](https://simon-prammer.vercel.app/blog/post/sveltekit-langchain) and [Intro to LangSmith🦜🛠](https://simon-prammer.vercel.app/blog/post/langsmith) by Simon Prammer
- [SvelteKit: How to make code-based router, instead of file-based router [August 2023]](https://dev.to/maxcore/sveltekit-how-to-make-code-based-router-instead-of-file-based-router-august-2023-5f9) by Max Core
@ -81,9 +83,8 @@ _To Read_
- [Type-safe User Authentication in SvelteKit with Lucia, Planetscale, and Upstash Redis](https://upstash.com/blog/lucia-sveltekit) by Chris Jayden
- [Document Svelte Projects with HTML and JSDoc Comments](https://blog.robino.dev/posts/doc-comments-svelte) by Ross Robino
**Libraries, Tools & Components**
- [Carta](https://github.com/BearToCode/carta-md) is a lightweight, fast and extensible Svelte Markdown editor and viewer, based on Marked
- [Threlte](https://threlte.xyz/), the 3D framework built from Svelte and Three.js has released version 6
- [vite-plugin-web-extension](https://vite-plugin-web-extension.aklinker1.io/guide/frontend-frameworks.html#svelte-integration) works great with Svelte to make building browser extensions easier

@ -1,6 +1,6 @@
---
title: "Hacktoberfest 2023 with SvelteKit"
description: "SvelteKit joins in the Hacktoberfest event in 2023"
title: 'Hacktoberfest 2023 with SvelteKit'
description: 'SvelteKit joins in the Hacktoberfest event in 2023'
author: Willow (GHOST) & Braden Wiggins
authorURL: https://ghostdev.xyz
---
@ -21,4 +21,4 @@ It's a good idea to communicate clearly and often about what you're trying to so
Join our [Discord](https://svelte.dev/chat) and ask questions in the dedicated `#hacktoberfest` channel. We're happy to help you get started!
We're excited to see what you've got in store for SvelteKit! Happy hacking! 🎃
We're excited to see what you've got in store for SvelteKit! Happy hacking! 🎃

@ -1,6 +1,6 @@
---
title: "What's new in Svelte: October 2023"
description: "Reactions to Runes and SvelteKit +server fallbacks"
description: 'Reactions to Runes and SvelteKit +server fallbacks'
author: Dani Sandoval
authorURL: https://dreamindani.com
---
@ -8,10 +8,12 @@ authorURL: https://dreamindani.com
Svelte 5 isn't out yet (you can, however, [preview it now](https://svelte-5-preview.vercel.app/)), but that doesn't mean we don't get a sneak peek! Most notably are [Runes](https://svelte.dev/blog/runes) - a simpler way to manage reactive variables in Svelte code. There's lots of links the showcase section for deeper dives on all things Runes, but let's talk about what else been released this month...
## What's new in Svelte & Language Tools
- [Svelte 4.2.1](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md#421) was released with a bunch of fixes to HTML, CSS and sourcemap compilation
- [The latest version of the Svelte language tools](https://github.com/sveltejs/language-tools/releases/tag/extensions-107.11.0) [enhances component references](https://github.com/sveltejs/language-tools/pull/2157) in the "Find All References" command, [fixes a persistent issue with automated types going missing](https://github.com/sveltejs/language-tools/pull/2160) after restarting a project and [adds fallback handling to auto-types](https://github.com/sveltejs/language-tools/issues/2156) (like those found in SvelteKit's `+server.js` files)
## What's new in SvelteKit
- `+server.js` now has a catch-all handler that handles all unimplemented valid server requests. Just export a `fallback` function! (**1.25.0**, [Docs](https://kit.svelte.dev/docs/routing#server-fallback-method-handler), [#9755](https://github.com/sveltejs/kit/pull/9755))
That's all for the new features! If you're looking for other patches and performance updates, check out the [SvelteKit CHANGELOG](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md). You can also find adapter-specific CHANGELOGs in each of [the `adapter` directories](https://github.com/sveltejs/kit/tree/master/packages).
@ -25,6 +27,7 @@ That's all for the new features! If you're looking for other patches and perform
Threlte [is throwing a hackathon](https://threlte.xyz/hackathon) (**motion warning for the landing page** - it will respect Reduce Motion settings). The kickoff event is on Sunday, 15 October 2023 16:00 UTC.
**Apps & Sites built with Svelte**
- [game-of-life-svelte](https://github.com/StephenGunn/game-of-life-svelte) is a Conway's Game of Life implementation using SvelteKit tech
- [Limey](https://limey.io/) is an easy-to-use website builder for simple sites and landing pages
- [Appwrite's new landing page](https://appwrite.io/) is now written with SvelteKit (previously covered was their [console UI](https://github.com/appwrite/console) in Svelte)
@ -34,10 +37,10 @@ Threlte [is throwing a hackathon](https://threlte.xyz/hackathon) (**motion warni
- [Dithering](https://www.sigrist.dev/dithering) is a tool to dither photos with plenty of options
- [Rocky Mountain Slam](https://www.rockymountainslam.com/) is an interactive map to follow Jason Heyn as he attempts to complete the first ever Rocky Mountain Slam ([code](https://github.com/martyheyn/rocky-mnt-slam))
**Learning Resources**
_Featuring Svelte Contributors and Ambassadors_
- [Svelte 5: Introducing Runes... with Rich Harris](https://www.youtube.com/watch?v=RVnxF3j3N8U) and its follow-up: [Svelte 5 runes: what's the deal with getters and setters?](https://www.youtube.com/watch?v=NR8L5m73dtE)
- [Conditionally stream data in SvelteKit](https://geoffrich.net/posts/conditionally-stream-data/) by Geoff Rich
- [Svelte Runes Change How Reactivity Works In Svelte](https://www.youtube.com/watch?v=TOTUXiYZhf4), [Make A 3D GitHub Skyline With Svelte To Flex On Your Peers](https://www.youtube.com/watch?v=f9fd1L1FEts), [Simple Page Transitions Using The View Transitions API With SvelteKit](https://www.youtube.com/watch?v=q_2irZO4SS8) and [Using JavaScript Libraries With Svelte Is Easy](https://www.youtube.com/watch?v=N9OjaQ0XtKQ) by Joy of Code
@ -49,16 +52,16 @@ _Featuring Svelte Contributors and Ambassadors_
- [2023 September 15](https://www.youtube.com/watch?v=qH2FavwhU88) - SvelteKit 1.25.0, deserialize form data, magic is coming
- [2023 September 22](https://www.youtube.com/watch?v=ek7KE1EDu2w) - Svelte 5 Runes!
_To Watch_
- [RUNES - Coming in Svelte v5 | My Take](https://www.youtube.com/watch?v=iCK1coch1wA) by Coding Garden
- [Don't Sleep on Svelte 5](https://www.youtube.com/watch?v=DgNWssn2vpc) and [Level Up Your Svelte Stores](https://www.youtube.com/watch?v=-vjNAyL2JCQ) by Huntabyte
- [Introduction To Svelte Runes (Every Svelte Rune Explained)](https://www.youtube.com/watch?v=gihSBVfyFbI) by Cooper Codes
- [Svelte Runes: Awesome or Awful?](https://www.youtube.com/watch?v=JRZCqUOmFwY) by Jack Herrington
- [Let Build A Youtube Clone With SvelteKit (Svelte, Tailwind Css, RapidApi, Shadcn Svelte, Axios, etc)](https://www.youtube.com/watch?v=65yMfpsoH4o) by Lawal Adebola
_To Read_
- [Create the Perfect Sharable Rune in Svelte](https://dev.to/jdgamble555/create-the-perfect-sharable-rune-in-svelte-ij8) by Jonathan Gamble
- [You Don't Need to "Learn" Svelte](https://kaviisuri.com/you-dont-need-to-learn-svelte) by KaviiSuri
- [Build Websites with Prismic and SvelteKit](https://prismic.io/blog/sveltekit-prismic-integration) by Angelo Ashmore
@ -67,8 +70,8 @@ _To Read_
- [Integrate Storybook in Svelte: Doing it the Svelte-way](https://mainmatter.com/blog/2023/09/18/integrate-storybook-in-svelte-doing-it-the-svelte-way/) by Oscar Dominguez
- [The Sveltekit tutorial: Part 1 | What, why, and how?](https://tntman.tech/posts/sveltekit-guide-part-1) by Suyashtnt
**Libraries, Tools & Components**
- [KitForStartups](https://github.com/okupter/kitforstartups) is an Open Source SvelteKit SaaS boilerplate
- [SuperNavigation](https://github.com/0xDjole/super-navigation) is a mobile-like navigation UX for the web
- [skeleton-material-theme](https://github.com/plasmatech8/skeleton-material-theme) is a Material theme for the Skeleton UI library

@ -1,6 +1,6 @@
---
title: "What's new in Svelte: November 2023"
description: "Svelte Summit on Nov 11 and better DevEx for all!"
description: 'Svelte Summit on Nov 11 and better DevEx for all!'
author: Dani Sandoval
authorURL: https://dreamindani.com
---
@ -12,10 +12,12 @@ Every month, maintainers within the Svelte ecosystem fix bugs, improve performan
Let's take a closer look 👀...
## What's new in Svelte & Language Tools
- Svelte 4.2.2 cleans up a few element-specific features ([Release Notes](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md#422))
- Extensions 107.12.0 improves block folding for functions, if statements and more ([Release Notes](https://github.com/sveltejs/language-tools/releases/tag/extensions-107.12.0), [PR](https://github.com/sveltejs/language-tools/pull/2169))
## What's new in SvelteKit
- Route parameter types will now be inferred from the applicable matcher's guard check (**kit@1.26.0**, [Docs](https://kit.svelte.dev/docs/advanced-routing#matching), [#10755](https://github.com/sveltejs/kit/pull/10755))
- The new `invalidateAll` boolean option lets you turn on and off the `invalidateAll()` form function within the `enhance` callback (**kit@1.27.0**, [Docs](https://kit.svelte.dev/docs/form-actions#progressive-enhancement-use-enhance), [#9476](https://github.com/sveltejs/kit/issues/9476))
- The output of the project creation wizard will now reflect which package manager you're using (**create-svelte@5.1.1**, [#10811](https://github.com/sveltejs/kit/pull/10811))
@ -27,6 +29,7 @@ For a complete list of bug fixes and performance updates, check out the [SvelteK
## Community Showcase
**Apps & Sites built with Svelte**
- [4THSEX](https://4thsex.com/) is a creative website for the producer / creative director with the same name
- [Syntax.fm](https://github.com/syntaxfm/website) has been redesigned from the ground up with SvelteKit
- [GitContext](https://gitcontext.com/) is an early-access tool to improve the process of reviewing code
@ -38,10 +41,10 @@ For a complete list of bug fixes and performance updates, check out the [SvelteK
- [Sessionic](https://github.com/navorite/sessionic) is a web extension to easily save browser sessions and manage them
- [Pilink](https://pil.ink/) is a "suckless" link shortener
**Learning Resources**
_Featuring Svelte Contributors and Ambassadors_
- [Wolfensvelte 3D and the Svelte Language Server in the Browser with Jason Bradnick](https://www.svelteradio.com/episodes/wolfensvelte-3d-and-the-svelte-language-server-in-the-browser-with-jason-bradnick) by Svelte Radio
- [This Is How You Sveltify Any JavaScript Library](https://www.youtube.com/watch?v=RuM4KHTZqD4), [Svelte Actions Make Svelte The Best JavaScript Framework](https://www.youtube.com/watch?v=LGOqg0Y7sAc) and [How Svelte Stores Make State Management Easy](https://www.youtube.com/watch?v=L3uBfL-4dDM) by Joy of Code
- Svelte Society Talks
@ -55,13 +58,14 @@ _Featuring Svelte Contributors and Ambassadors_
- [2023 October 20](https://www.youtube.com/watch?v=O13bGtOV-aA) - Kit 1.26.0, Svelte 4.2.2, dynamically-loaded components
_To Watch_
- [SvelteKit & TailwindCSS Tutorial Build & Deploy a Web Portfolio](https://www.youtube.com/watch?v=-2UjwQzxvBQ) by freeCodeCamp.org
- [Why SvelteKit? [Intro to SvelteKit 1.0, part 1]](https://www.youtube.com/watch?v=FP4AylVsiT8) by Jeffrey Codes Javascript
- [Build an AI Chatbot - it's that easy?!](https://www.youtube.com/watch?v=FcDj9_590Xg) by Simon Prammer
- [Introduction to SvelteKit | FREE 5 HOUR SVELTE WORKSHOP 2023 | Lessons + Coding Exercises](https://www.youtube.com/watch?v=wWRhX_Hzyf8) by This Dot Media
_To Read_
- [What we learned from migrating our web app to SvelteKit](https://blog.datawrapper.de/migrating-our-web-app-to-sveltekit/) by Marten Sigwart
- [SvelteKit Tutorial: Build a Website From Scratch](https://prismic.io/blog/svelte-sveltekit-tutorial) by Prismic has been updated based on the latest SvelteKit features
- [Svelte by Example](https://sveltebyexample.com/) is a succinct, gentle introduction to Svelte & SvelteKit
@ -73,6 +77,7 @@ _To Read_
- [Open Neovim From Your Browser - Integrating nvim with Sveltes Inspector](https://theosteiner.de/open-neovim-from-your-browser-integrating-nvim-with-sveltes-inspector) by Theo Steiner
**Libraries, Tools & Components**
- Work to [support SvelteKit in Deno](https://github.com/denoland/deno/issues/17248) is ongoing and [Deno now supports](https://github.com/denoland/deno/pull/21026) creating SvelteKit projects out-of-the-box!
- [Purplix](https://github.com/WardPearce/Purplix.io) is an open-source collection of tools dedicated to user privacy and creating trust with your audience
- [Obra Icons](https://github.com/Obra-Studio/obra-icons-svelte-public) is a simple, consistent set of icons, perfect for user interfaces

@ -72,7 +72,7 @@ Sometimes actions emit custom events and apply custom attributes to the element
}
</script>
<div use:foo={{ prop: 'someValue' }} on:emit={handleEmit} />
<div on:emit={handleEmit} use:foo={{ prop: 'someValue' }} />
```
## Types

@ -18,6 +18,7 @@ If you're a library author, consider whether to only support Svelte 4 or if it's
## Browser conditions for bundlers
Bundlers must now specify the `browser` condition when building a frontend bundle for the browser. SvelteKit and Vite will handle this automatically for you. If you're using any others, you may observe lifecycle callbacks such as `onMount` not get called and you'll need to update the module resolution configuration.
- For Rollup this is done within the `@rollup/plugin-node-resolve` plugin by setting `browser: true` in its options. See the [`rollup-plugin-svelte`](https://github.com/sveltejs/rollup-plugin-svelte/#usage) documentation for more details
- For wepback this is done by adding `"browser"` to the `conditionNames` array. You may also have to update your `alias` config, if you have set it. See the [`svelte-loader`](https://github.com/sveltejs/svelte-loader#usage) documentation for more details

@ -31,22 +31,28 @@
const reply = eliza.transform(text);
setTimeout(() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
}, 500 + Math.random() * 500);
}, 200 + Math.random() * 200);
setTimeout(
() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(
() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
},
500 + Math.random() * 500
);
},
200 + Math.random() * 200
);
}
}
</script>

@ -34,8 +34,8 @@
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
border:none;
font-size:14px;
border: none;
font-size: 14px;
}
.expanded {

@ -27,10 +27,10 @@
<h2>Immutable</h2>
{#each todos as todo}
<ImmutableTodo {todo} on:click={() => toggle(todo.id)} /><br>
<ImmutableTodo {todo} on:click={() => toggle(todo.id)} /><br />
{/each}
<h2>Mutable</h2>
{#each todos as todo}
<MutableTodo {todo} on:click={() => toggle(todo.id)} /><br>
<MutableTodo {todo} on:click={() => toggle(todo.id)} /><br />
{/each}

@ -23,8 +23,8 @@
<style>
button {
cursor: pointer;
border:none;
background:none;
font-size:14px;
border: none;
background: none;
font-size: 14px;
}
</style>

@ -21,8 +21,8 @@
<style>
button {
cursor: pointer;
border:none;
background:none;
font-size:14px;
border: none;
background: none;
font-size: 14px;
}
</style>

@ -13,6 +13,6 @@
<style>
button {
width:200px;
width: 200px;
}
</style>

@ -13,6 +13,6 @@
<style>
button {
width:200px;
width: 200px;
}
</style>

@ -34,7 +34,7 @@
padding: 0.2em 1em 0.3em;
text-align: center;
border-radius: 0.2em;
color:#333333;
color: #333333;
background-color: #ffdfd3;
}
</style>

@ -34,7 +34,7 @@
padding: 0.2em 1em 0.3em;
text-align: center;
border-radius: 0.2em;
color:#333333;
color: #333333;
background-color: #ffdfd3;
}
</style>

@ -31,22 +31,28 @@
const reply = eliza.transform(text);
setTimeout(() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
}, 500 + Math.random() * 500);
}, 200 + Math.random() * 200);
setTimeout(
() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(
() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
},
500 + Math.random() * 500
);
},
200 + Math.random() * 200
);
}
}
</script>

@ -31,22 +31,28 @@
const reply = eliza.transform(text);
setTimeout(() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
}, 500 + Math.random() * 500);
}, 200 + Math.random() * 200);
setTimeout(
() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(
() => {
comments = comments
.filter((comment) => !comment.placeholder)
.concat({
author: 'eliza',
text: reply
});
},
500 + Math.random() * 500
);
},
200 + Math.random() * 200
);
}
}
</script>

@ -6,9 +6,9 @@ Key blocks destroy and recreate their contents when the value of an expression c
```svelte
{#key number}
<span style="display: inline-block" in:fly={{ y: -20 }}>
{number}
</span>
<span style="display: inline-block" in:fly={{ y: -20 }}>
{number}
</span>
{/key}
```

@ -17,7 +17,7 @@
padding: 1em;
margin: 0 0 1em 0;
background-color: #eee;
color: black;
color: black;
}
.active {

@ -37,7 +37,7 @@
padding: 1em;
margin: 0 0 1em 0;
background-color: #eee;
color: black;
color: black;
}
.active {

@ -4,31 +4,44 @@
"description": "monorepo for svelte and friends",
"private": true,
"type": "module",
"license": "MIT",
"packageManager": "pnpm@8.6.12",
"engines": {
"pnpm": "^8.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/svelte.git"
},
"scripts": {
"test": "pnpm test -r --filter=./packages/*",
"start": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run dev --prefix playgrounds/demo\"",
"build": "pnpm -r --filter=./packages/* build",
"build:sites": "pnpm -r --filter=./sites/* build",
"preview-site": "npm run build --prefix sites/svelte-5-preview",
"check": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r check",
"lint": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r lint",
"format": "pnpm -r format",
"format": "prettier --write --plugin prettier-plugin-svelte .",
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint ./",
"test": "vitest run --coverage",
"test-output": "vitest run --reporter=json --outputFile=sites/svelte-5-preview/src/routes/status/results.json",
"changeset:version": "changeset version && pnpm -r generate:version && git add --all",
"changeset:publish": "changeset publish"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/svelte.git"
},
"license": "MIT",
"devDependencies": {
"@changesets/cli": "^2.26.1",
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"eslint": "^8.44.0",
"eslint-plugin-svelte": "^2.32.2",
"eslint-plugin-unicorn": "^47.0.0",
"@sveltejs/eslint-config": "^6.0.4",
"@types/node": "^18.18.8",
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@vitest/coverage-v8": "^0.34.6",
"concurrently": "^8.2.0",
"cross-env": "^7.0.3",
"eslint": "^8.49.0",
"eslint-plugin-lube": "^0.1.7",
"jsdom": "22.0.0",
"playwright": "^1.35.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1"
},
"packageManager": "pnpm@8.6.3"
"prettier": "^3.0.1",
"prettier-plugin-svelte": "^3.0.3",
"typescript": "^5.2.2",
"vitest": "^0.34.6"
}
}

@ -1,17 +0,0 @@
**/_actual.js
**/expected.js
_output
test/*/samples/*/output.js
# automatically generated
internal_exports.js
# output files
animate/*.js
esing/*.js
internal/*.js
motion/*.js
store/*.js
transition/*.js
index.js
compiler.js

@ -1,7 +0,0 @@
module.exports = {
root: true,
extends: ['@sveltejs'],
rules: {
'@typescript-eslint/no-non-null-assertion': 'off'
}
};

@ -1,18 +1,12 @@
*.map
/src/compiler/compile/internal_exports.js
/compiler.cjs
/scratch/
/test/*/samples/_
/test/runtime/shards
_actual*.*
_output
/types
/compiler.cjs
action.d.ts
animate.d.ts
compiler.d.ts
easing.d.ts
index.d.ts
motion.d.ts
store.d.ts
transition.d.ts
/action.d.ts
/animate.d.ts
/compiler.d.ts
/easing.d.ts
/index.d.ts
/legacy.d.ts
/motion.d.ts
/store.d.ts
/transition.d.ts

@ -1,22 +0,0 @@
/*
!/elements
!/scripts
!/src
src/compiler/compile/internal_exports.js
src/shared/version.js
!/test
!documentation
!sites
sites/svelte.dev/src/lib/generated/*.js
sites/svelte.dev/.svelte-kit
sites/svelte.dev/.vercel
/test/**/*.svelte
/test/**/_expected*
/test/**/_actual*
/test/**/expected*
/test/**/_output
/test/**/shards/*.test.js
/test/hydration/samples/raw-repair/_after.html
/types
!rollup.config.js
!vitest.config.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,9 +1,7 @@
[![Cybernetically enhanced web apps: Svelte](https://sveltejs.github.io/assets/banner.png)](https://svelte.dev)
[![npm version](https://img.shields.io/npm/v/svelte.svg)](https://www.npmjs.com/package/svelte) [![license](https://img.shields.io/npm/l/svelte.svg)](LICENSE.md) [![Chat](https://img.shields.io/discord/457912077277855764?label=chat&logo=discord)](https://svelte.dev/chat)
## What is Svelte?
Svelte is a new way to build web applications. It's a compiler that takes your declarative components and converts them into efficient JavaScript that surgically updates the DOM.

@ -66,146 +66,350 @@ export type MessageEventHandler<T extends EventTarget> = EventHandler<MessageEve
export interface DOMAttributes<T extends EventTarget> {
// Clipboard Events
'on:copy'?: ClipboardEventHandler<T> | undefined | null;
oncopy?: ClipboardEventHandler<T> | undefined | null;
oncopycapture?: ClipboardEventHandler<T> | undefined | null;
'on:cut'?: ClipboardEventHandler<T> | undefined | null;
oncut?: ClipboardEventHandler<T> | undefined | null;
oncutcapture?: ClipboardEventHandler<T> | undefined | null;
'on:paste'?: ClipboardEventHandler<T> | undefined | null;
onpaste?: ClipboardEventHandler<T> | undefined | null;
onpastecapture?: ClipboardEventHandler<T> | undefined | null;
// Composition Events
'on:compositionend'?: CompositionEventHandler<T> | undefined | null;
oncompositionend?: CompositionEventHandler<T> | undefined | null;
oncompositionendcapture?: CompositionEventHandler<T> | undefined | null;
'on:compositionstart'?: CompositionEventHandler<T> | undefined | null;
oncompositionstart?: CompositionEventHandler<T> | undefined | null;
oncompositionstartcapture?: CompositionEventHandler<T> | undefined | null;
'on:compositionupdate'?: CompositionEventHandler<T> | undefined | null;
oncompositionupdate?: CompositionEventHandler<T> | undefined | null;
oncompositionupdatecapture?: CompositionEventHandler<T> | undefined | null;
// Focus Events
'on:focus'?: FocusEventHandler<T> | undefined | null;
onfocus?: FocusEventHandler<T> | undefined | null;
onfocuscapture?: FocusEventHandler<T> | undefined | null;
'on:focusin'?: FocusEventHandler<T> | undefined | null;
onfocusin?: FocusEventHandler<T> | undefined | null;
onfocusincapture?: FocusEventHandler<T> | undefined | null;
'on:focusout'?: FocusEventHandler<T> | undefined | null;
onfocusout?: FocusEventHandler<T> | undefined | null;
onfocusoutcapture?: FocusEventHandler<T> | undefined | null;
'on:blur'?: FocusEventHandler<T> | undefined | null;
onblur?: FocusEventHandler<T> | undefined | null;
onblurcapture?: FocusEventHandler<T> | undefined | null;
// Form Events
'on:change'?: FormEventHandler<T> | undefined | null;
onchange?: FormEventHandler<T> | undefined | null;
onchangecapture?: FormEventHandler<T> | undefined | null;
'on:beforeinput'?: EventHandler<InputEvent, T> | undefined | null;
onbeforeinput?: EventHandler<InputEvent, T> | undefined | null;
onbeforeinputcapture?: EventHandler<InputEvent, T> | undefined | null;
'on:input'?: FormEventHandler<T> | undefined | null;
oninput?: FormEventHandler<T> | undefined | null;
oninputcapture?: FormEventHandler<T> | undefined | null;
'on:reset'?: FormEventHandler<T> | undefined | null;
onreset?: FormEventHandler<T> | undefined | null;
onresetcapture?: FormEventHandler<T> | undefined | null;
'on:submit'?: EventHandler<SubmitEvent, T> | undefined | null;
onsubmit?: EventHandler<SubmitEvent, T> | undefined | null;
onsubmitcapture?: EventHandler<SubmitEvent, T> | undefined | null;
'on:invalid'?: EventHandler<Event, T> | undefined | null;
oninvalid?: EventHandler<Event, T> | undefined | null;
oninvalidcapture?: EventHandler<Event, T> | undefined | null;
'on:formdata'?: EventHandler<FormDataEvent, T> | undefined | null;
onformdata?: EventHandler<FormDataEvent, T> | undefined | null;
onformdatacapture?: EventHandler<FormDataEvent, T> | undefined | null;
// Image Events
'on:load'?: EventHandler | undefined | null;
onload?: EventHandler | undefined | null;
onloadcapture?: EventHandler | undefined | null;
'on:error'?: EventHandler | undefined | null; // also a Media Event
onerror?: EventHandler | undefined | null; // also a Media Event
onerrorcapture?: EventHandler | undefined | null; // also a Media Event
// Detail Events
'on:toggle'?: EventHandler<Event, T> | undefined | null;
ontoggle?: EventHandler<Event, T> | undefined | null;
ontogglecapture?: EventHandler<Event, T> | undefined | null;
// Keyboard Events
'on:keydown'?: KeyboardEventHandler<T> | undefined | null;
onkeydown?: KeyboardEventHandler<T> | undefined | null;
onkeydowncapture?: KeyboardEventHandler<T> | undefined | null;
'on:keypress'?: KeyboardEventHandler<T> | undefined | null;
onkeypress?: KeyboardEventHandler<T> | undefined | null;
onkeypresscapture?: KeyboardEventHandler<T> | undefined | null;
'on:keyup'?: KeyboardEventHandler<T> | undefined | null;
onkeyup?: KeyboardEventHandler<T> | undefined | null;
onkeyupcapture?: KeyboardEventHandler<T> | undefined | null;
// Media Events
'on:abort'?: EventHandler<Event, T> | undefined | null;
onabort?: EventHandler<Event, T> | undefined | null;
onabortcapture?: EventHandler<Event, T> | undefined | null;
'on:canplay'?: EventHandler<Event, T> | undefined | null;
oncanplay?: EventHandler<Event, T> | undefined | null;
oncanplaycapture?: EventHandler<Event, T> | undefined | null;
'on:canplaythrough'?: EventHandler<Event, T> | undefined | null;
oncanplaythrough?: EventHandler<Event, T> | undefined | null;
oncanplaythroughcapture?: EventHandler<Event, T> | undefined | null;
'on:cuechange'?: EventHandler<Event, T> | undefined | null;
oncuechange?: EventHandler<Event, T> | undefined | null;
oncuechangecapture?: EventHandler<Event, T> | undefined | null;
'on:durationchange'?: EventHandler<Event, T> | undefined | null;
ondurationchange?: EventHandler<Event, T> | undefined | null;
ondurationchangecapture?: EventHandler<Event, T> | undefined | null;
'on:emptied'?: EventHandler<Event, T> | undefined | null;
onemptied?: EventHandler<Event, T> | undefined | null;
onemptiedcapture?: EventHandler<Event, T> | undefined | null;
'on:encrypted'?: EventHandler<Event, T> | undefined | null;
onencrypted?: EventHandler<Event, T> | undefined | null;
onencryptedcapture?: EventHandler<Event, T> | undefined | null;
'on:ended'?: EventHandler<Event, T> | undefined | null;
onended?: EventHandler<Event, T> | undefined | null;
onendedcapture?: EventHandler<Event, T> | undefined | null;
'on:loadeddata'?: EventHandler<Event, T> | undefined | null;
onloadeddata?: EventHandler<Event, T> | undefined | null;
onloadeddatacapture?: EventHandler<Event, T> | undefined | null;
'on:loadedmetadata'?: EventHandler<Event, T> | undefined | null;
onloadedmetadata?: EventHandler<Event, T> | undefined | null;
onloadedmetadatacapture?: EventHandler<Event, T> | undefined | null;
'on:loadstart'?: EventHandler<Event, T> | undefined | null;
onloadstart?: EventHandler<Event, T> | undefined | null;
onloadstartcapture?: EventHandler<Event, T> | undefined | null;
'on:pause'?: EventHandler<Event, T> | undefined | null;
onpause?: EventHandler<Event, T> | undefined | null;
onpausecapture?: EventHandler<Event, T> | undefined | null;
'on:play'?: EventHandler<Event, T> | undefined | null;
onplay?: EventHandler<Event, T> | undefined | null;
onplaycapture?: EventHandler<Event, T> | undefined | null;
'on:playing'?: EventHandler<Event, T> | undefined | null;
onplaying?: EventHandler<Event, T> | undefined | null;
onplayingcapture?: EventHandler<Event, T> | undefined | null;
'on:progress'?: EventHandler<Event, T> | undefined | null;
onprogress?: EventHandler<Event, T> | undefined | null;
onprogresscapture?: EventHandler<Event, T> | undefined | null;
'on:ratechange'?: EventHandler<Event, T> | undefined | null;
onratechange?: EventHandler<Event, T> | undefined | null;
onratechangecapture?: EventHandler<Event, T> | undefined | null;
'on:seeked'?: EventHandler<Event, T> | undefined | null;
onseeked?: EventHandler<Event, T> | undefined | null;
onseekedcapture?: EventHandler<Event, T> | undefined | null;
'on:seeking'?: EventHandler<Event, T> | undefined | null;
onseeking?: EventHandler<Event, T> | undefined | null;
onseekingcapture?: EventHandler<Event, T> | undefined | null;
'on:stalled'?: EventHandler<Event, T> | undefined | null;
onstalled?: EventHandler<Event, T> | undefined | null;
onstalledcapture?: EventHandler<Event, T> | undefined | null;
'on:suspend'?: EventHandler<Event, T> | undefined | null;
onsuspend?: EventHandler<Event, T> | undefined | null;
onsuspendcapture?: EventHandler<Event, T> | undefined | null;
'on:timeupdate'?: EventHandler<Event, T> | undefined | null;
ontimeupdate?: EventHandler<Event, T> | undefined | null;
ontimeupdatecapture?: EventHandler<Event, T> | undefined | null;
'on:volumechange'?: EventHandler<Event, T> | undefined | null;
onvolumechange?: EventHandler<Event, T> | undefined | null;
onvolumechangecapture?: EventHandler<Event, T> | undefined | null;
'on:waiting'?: EventHandler<Event, T> | undefined | null;
onwaiting?: EventHandler<Event, T> | undefined | null;
onwaitingcapture?: EventHandler<Event, T> | undefined | null;
// MouseEvents
'on:auxclick'?: MouseEventHandler<T> | undefined | null;
onauxclick?: MouseEventHandler<T> | undefined | null;
onauxclickcapture?: MouseEventHandler<T> | undefined | null;
'on:click'?: MouseEventHandler<T> | undefined | null;
onclick?: MouseEventHandler<T> | undefined | null;
onclickcapture?: MouseEventHandler<T> | undefined | null;
'on:contextmenu'?: MouseEventHandler<T> | undefined | null;
oncontextmenu?: MouseEventHandler<T> | undefined | null;
oncontextmenucapture?: MouseEventHandler<T> | undefined | null;
'on:dblclick'?: MouseEventHandler<T> | undefined | null;
ondblclick?: MouseEventHandler<T> | undefined | null;
ondblclickcapture?: MouseEventHandler<T> | undefined | null;
'on:drag'?: DragEventHandler<T> | undefined | null;
ondrag?: DragEventHandler<T> | undefined | null;
ondragcapture?: DragEventHandler<T> | undefined | null;
'on:dragend'?: DragEventHandler<T> | undefined | null;
ondragend?: DragEventHandler<T> | undefined | null;
ondragendcapture?: DragEventHandler<T> | undefined | null;
'on:dragenter'?: DragEventHandler<T> | undefined | null;
ondragenter?: DragEventHandler<T> | undefined | null;
ondragentercapture?: DragEventHandler<T> | undefined | null;
'on:dragexit'?: DragEventHandler<T> | undefined | null;
ondragexit?: DragEventHandler<T> | undefined | null;
ondragexitcapture?: DragEventHandler<T> | undefined | null;
'on:dragleave'?: DragEventHandler<T> | undefined | null;
ondragleave?: DragEventHandler<T> | undefined | null;
ondragleavecapture?: DragEventHandler<T> | undefined | null;
'on:dragover'?: DragEventHandler<T> | undefined | null;
ondragover?: DragEventHandler<T> | undefined | null;
ondragovercapture?: DragEventHandler<T> | undefined | null;
'on:dragstart'?: DragEventHandler<T> | undefined | null;
ondragstart?: DragEventHandler<T> | undefined | null;
ondragstartcapture?: DragEventHandler<T> | undefined | null;
'on:drop'?: DragEventHandler<T> | undefined | null;
ondrop?: DragEventHandler<T> | undefined | null;
ondropcapture?: DragEventHandler<T> | undefined | null;
'on:mousedown'?: MouseEventHandler<T> | undefined | null;
onmousedown?: MouseEventHandler<T> | undefined | null;
onmousedowncapture?: MouseEventHandler<T> | undefined | null;
'on:mouseenter'?: MouseEventHandler<T> | undefined | null;
onmouseenter?: MouseEventHandler<T> | undefined | null;
'on:mouseleave'?: MouseEventHandler<T> | undefined | null;
onmouseleave?: MouseEventHandler<T> | undefined | null;
'on:mousemove'?: MouseEventHandler<T> | undefined | null;
onmousemove?: MouseEventHandler<T> | undefined | null;
onmousemovecapture?: MouseEventHandler<T> | undefined | null;
'on:mouseout'?: MouseEventHandler<T> | undefined | null;
onmouseout?: MouseEventHandler<T> | undefined | null;
onmouseoutcapture?: MouseEventHandler<T> | undefined | null;
'on:mouseover'?: MouseEventHandler<T> | undefined | null;
onmouseover?: MouseEventHandler<T> | undefined | null;
onmouseovercapture?: MouseEventHandler<T> | undefined | null;
'on:mouseup'?: MouseEventHandler<T> | undefined | null;
onmouseup?: MouseEventHandler<T> | undefined | null;
onmouseupcapture?: MouseEventHandler<T> | undefined | null;
// Selection Events
'on:select'?: EventHandler<Event, T> | undefined | null;
onselect?: EventHandler<Event, T> | undefined | null;
onselectcapture?: EventHandler<Event, T> | undefined | null;
'on:selectionchange'?: EventHandler<Event, T> | undefined | null;
onselectionchange?: EventHandler<Event, T> | undefined | null;
onselectionchangecapture?: EventHandler<Event, T> | undefined | null;
'on:selectstart'?: EventHandler<Event, T> | undefined | null;
onselectstart?: EventHandler<Event, T> | undefined | null;
onselectstartcapture?: EventHandler<Event, T> | undefined | null;
// Touch Events
'on:touchcancel'?: TouchEventHandler<T> | undefined | null;
ontouchcancel?: TouchEventHandler<T> | undefined | null;
ontouchcancelcapture?: TouchEventHandler<T> | undefined | null;
'on:touchend'?: TouchEventHandler<T> | undefined | null;
ontouchend?: TouchEventHandler<T> | undefined | null;
ontouchendcapture?: TouchEventHandler<T> | undefined | null;
'on:touchmove'?: TouchEventHandler<T> | undefined | null;
ontouchmove?: TouchEventHandler<T> | undefined | null;
ontouchmovecapture?: TouchEventHandler<T> | undefined | null;
'on:touchstart'?: TouchEventHandler<T> | undefined | null;
ontouchstart?: TouchEventHandler<T> | undefined | null;
ontouchstartcapture?: TouchEventHandler<T> | undefined | null;
// Pointer Events
'on:gotpointercapture'?: PointerEventHandler<T> | undefined | null;
ongotpointercapture?: PointerEventHandler<T> | undefined | null;
ongotpointercapturecapture?: PointerEventHandler<T> | undefined | null;
'on:pointercancel'?: PointerEventHandler<T> | undefined | null;
onpointercancel?: PointerEventHandler<T> | undefined | null;
onpointercancelcapture?: PointerEventHandler<T> | undefined | null;
'on:pointerdown'?: PointerEventHandler<T> | undefined | null;
onpointerdown?: PointerEventHandler<T> | undefined | null;
onpointerdowncapture?: PointerEventHandler<T> | undefined | null;
'on:pointerenter'?: PointerEventHandler<T> | undefined | null;
onpointerenter?: PointerEventHandler<T> | undefined | null;
onpointerentercapture?: PointerEventHandler<T> | undefined | null;
'on:pointerleave'?: PointerEventHandler<T> | undefined | null;
onpointerleave?: PointerEventHandler<T> | undefined | null;
onpointerleavecapture?: PointerEventHandler<T> | undefined | null;
'on:pointermove'?: PointerEventHandler<T> | undefined | null;
onpointermove?: PointerEventHandler<T> | undefined | null;
onpointermovecapture?: PointerEventHandler<T> | undefined | null;
'on:pointerout'?: PointerEventHandler<T> | undefined | null;
onpointerout?: PointerEventHandler<T> | undefined | null;
onpointeroutcapture?: PointerEventHandler<T> | undefined | null;
'on:pointerover'?: PointerEventHandler<T> | undefined | null;
onpointerover?: PointerEventHandler<T> | undefined | null;
onpointerovercapture?: PointerEventHandler<T> | undefined | null;
'on:pointerup'?: PointerEventHandler<T> | undefined | null;
onpointerup?: PointerEventHandler<T> | undefined | null;
onpointerupcapture?: PointerEventHandler<T> | undefined | null;
'on:lostpointercapture'?: PointerEventHandler<T> | undefined | null;
onlostpointercapture?: PointerEventHandler<T> | undefined | null;
onlostpointercapturecapture?: PointerEventHandler<T> | undefined | null;
// UI Events
'on:scroll'?: UIEventHandler<T> | undefined | null;
onscroll?: UIEventHandler<T> | undefined | null;
onscrollcapture?: UIEventHandler<T> | undefined | null;
'on:resize'?: UIEventHandler<T> | undefined | null;
onresize?: UIEventHandler<T> | undefined | null;
onresizecapture?: UIEventHandler<T> | undefined | null;
// Wheel Events
'on:wheel'?: WheelEventHandler<T> | undefined | null;
onwheel?: WheelEventHandler<T> | undefined | null;
onwheelcapture?: WheelEventHandler<T> | undefined | null;
// Animation Events
'on:animationstart'?: AnimationEventHandler<T> | undefined | null;
onanimationstart?: AnimationEventHandler<T> | undefined | null;
onanimationstartcapture?: AnimationEventHandler<T> | undefined | null;
'on:animationend'?: AnimationEventHandler<T> | undefined | null;
onanimationend?: AnimationEventHandler<T> | undefined | null;
onanimationendcapture?: AnimationEventHandler<T> | undefined | null;
'on:animationiteration'?: AnimationEventHandler<T> | undefined | null;
onanimationiteration?: AnimationEventHandler<T> | undefined | null;
onanimationiterationcapture?: AnimationEventHandler<T> | undefined | null;
// Transition Events
'on:transitionstart'?: TransitionEventHandler<T> | undefined | null;
ontransitionstart?: TransitionEventHandler<T> | undefined | null;
ontransitionstartcapture?: TransitionEventHandler<T> | undefined | null;
'on:transitionrun'?: TransitionEventHandler<T> | undefined | null;
ontransitionrun?: TransitionEventHandler<T> | undefined | null;
ontransitionruncapture?: TransitionEventHandler<T> | undefined | null;
'on:transitionend'?: TransitionEventHandler<T> | undefined | null;
ontransitionend?: TransitionEventHandler<T> | undefined | null;
ontransitionendcapture?: TransitionEventHandler<T> | undefined | null;
'on:transitioncancel'?: TransitionEventHandler<T> | undefined | null;
ontransitioncancel?: TransitionEventHandler<T> | undefined | null;
ontransitioncancelcapture?: TransitionEventHandler<T> | undefined | null;
// Svelte Transition Events
'on:outrostart'?: EventHandler<CustomEvent<null>, T> | undefined | null;
onoutrostart?: EventHandler<CustomEvent<null>, T> | undefined | null;
onoutrostartcapture?: EventHandler<CustomEvent<null>, T> | undefined | null;
'on:outroend'?: EventHandler<CustomEvent<null>, T> | undefined | null;
onoutroend?: EventHandler<CustomEvent<null>, T> | undefined | null;
onoutroendcapture?: EventHandler<CustomEvent<null>, T> | undefined | null;
'on:introstart'?: EventHandler<CustomEvent<null>, T> | undefined | null;
onintrostart?: EventHandler<CustomEvent<null>, T> | undefined | null;
onintrostartcapture?: EventHandler<CustomEvent<null>, T> | undefined | null;
'on:introend'?: EventHandler<CustomEvent<null>, T> | undefined | null;
onintroend?: EventHandler<CustomEvent<null>, T> | undefined | null;
onintroendcapture?: EventHandler<CustomEvent<null>, T> | undefined | null;
// Message Events
'on:message'?: MessageEventHandler<T> | undefined | null;
onmessage?: MessageEventHandler<T> | undefined | null;
onmessagecapture?: MessageEventHandler<T> | undefined | null;
'on:messageerror'?: MessageEventHandler<T> | undefined | null;
onmessageerror?: MessageEventHandler<T> | undefined | null;
onmessageerrorcapture?: MessageEventHandler<T> | undefined | null;
// Document Events
'on:visibilitychange'?: EventHandler<Event, T> | undefined | null;
onvisibilitychange?: EventHandler<Event, T> | undefined | null;
onvisibilitychangecapture?: EventHandler<Event, T> | undefined | null;
// Global Events
'on:cancel'?: EventHandler<Event, T> | undefined | null;
oncancel?: EventHandler<Event, T> | undefined | null;
oncancelcapture?: EventHandler<Event, T> | undefined | null;
'on:close'?: EventHandler<Event, T> | undefined | null;
onclose?: EventHandler<Event, T> | undefined | null;
onclosecapture?: EventHandler<Event, T> | undefined | null;
'on:fullscreenchange'?: EventHandler<Event, T> | undefined | null;
onfullscreenchange?: EventHandler<Event, T> | undefined | null;
onfullscreenchangecapture?: EventHandler<Event, T> | undefined | null;
'on:fullscreenerror'?: EventHandler<Event, T> | undefined | null;
onfullscreenerror?: EventHandler<Event, T> | undefined | null;
onfullscreenerrorcapture?: EventHandler<Event, T> | undefined | null;
}
// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
@ -828,6 +1032,7 @@ export interface HTMLInputAttributes extends HTMLAttributes<HTMLInputElement> {
width?: number | string | undefined | null;
'on:change'?: ChangeEventHandler<HTMLInputElement> | undefined | null;
onchange?: ChangeEventHandler<HTMLInputElement> | undefined | null;
'bind:checked'?: boolean | undefined | null;
'bind:value'?: any;
@ -1021,6 +1226,7 @@ export interface HTMLSelectAttributes extends HTMLAttributes<HTMLSelectElement>
value?: any;
'on:change'?: ChangeEventHandler<HTMLSelectElement> | undefined | null;
onchange?: ChangeEventHandler<HTMLSelectElement> | undefined | null;
'bind:value'?: any;
}
@ -1071,6 +1277,7 @@ export interface HTMLTextareaAttributes extends HTMLAttributes<HTMLTextAreaEleme
wrap?: string | undefined | null;
'on:change'?: ChangeEventHandler<HTMLTextAreaElement> | undefined | null;
onchange?: ChangeEventHandler<HTMLTextAreaElement> | undefined | null;
'bind:value'?: any;
}
@ -1140,36 +1347,56 @@ export interface SvelteWindowAttributes extends HTMLAttributes<Window> {
'bind:scrollY'?: Window['scrollY'] | undefined | null;
readonly 'bind:online'?: Window['navigator']['onLine'] | undefined | null;
// SvelteKit
'on:sveltekit:start'?: EventHandler<CustomEvent, Window> | undefined | null;
'on:sveltekit:navigation-start'?: EventHandler<CustomEvent, Window> | undefined | null;
'on:sveltekit:navigation-end'?: EventHandler<CustomEvent, Window> | undefined | null;
'on:devicelight'?: EventHandler<Event, Window> | undefined | null;
ondevicelight?: EventHandler<Event, Window> | undefined | null;
'on:beforeinstallprompt'?: EventHandler<Event, Window> | undefined | null;
onbeforeinstallprompt?: EventHandler<Event, Window> | undefined | null;
'on:deviceproximity'?: EventHandler<Event, Window> | undefined | null;
ondeviceproximity?: EventHandler<Event, Window> | undefined | null;
'on:paint'?: EventHandler<Event, Window> | undefined | null;
onpaint?: EventHandler<Event, Window> | undefined | null;
'on:userproximity'?: EventHandler<Event, Window> | undefined | null;
onuserproximity?: EventHandler<Event, Window> | undefined | null;
'on:beforeprint'?: EventHandler<Event, Window> | undefined | null;
onbeforeprint?: EventHandler<Event, Window> | undefined | null;
'on:afterprint'?: EventHandler<Event, Window> | undefined | null;
onafterprint?: EventHandler<Event, Window> | undefined | null;
'on:languagechange'?: EventHandler<Event, Window> | undefined | null;
onlanguagechange?: EventHandler<Event, Window> | undefined | null;
'on:orientationchange'?: EventHandler<Event, Window> | undefined | null;
onorientationchange?: EventHandler<Event, Window> | undefined | null;
'on:message'?: EventHandler<MessageEvent, Window> | undefined | null;
onmessage?: EventHandler<MessageEvent, Window> | undefined | null;
'on:messageerror'?: EventHandler<MessageEvent, Window> | undefined | null;
onmessageerror?: EventHandler<MessageEvent, Window> | undefined | null;
'on:offline'?: EventHandler<Event, Window> | undefined | null;
onoffline?: EventHandler<Event, Window> | undefined | null;
'on:online'?: EventHandler<Event, Window> | undefined | null;
ononline?: EventHandler<Event, Window> | undefined | null;
'on:beforeunload'?: EventHandler<BeforeUnloadEvent, Window> | undefined | null;
onbeforeunload?: EventHandler<BeforeUnloadEvent, Window> | undefined | null;
'on:unload'?: EventHandler<Event, Window> | undefined | null;
onunload?: EventHandler<Event, Window> | undefined | null;
'on:storage'?: EventHandler<StorageEvent, Window> | undefined | null;
onstorage?: EventHandler<StorageEvent, Window> | undefined | null;
'on:hashchange'?: EventHandler<HashChangeEvent, Window> | undefined | null;
onhashchange?: EventHandler<HashChangeEvent, Window> | undefined | null;
'on:pagehide'?: EventHandler<PageTransitionEvent, Window> | undefined | null;
onpagehide?: EventHandler<PageTransitionEvent, Window> | undefined | null;
'on:pageshow'?: EventHandler<PageTransitionEvent, Window> | undefined | null;
onpageshow?: EventHandler<PageTransitionEvent, Window> | undefined | null;
'on:popstate'?: EventHandler<PopStateEvent, Window> | undefined | null;
onpopstate?: EventHandler<PopStateEvent, Window> | undefined | null;
'on:devicemotion'?: EventHandler<DeviceMotionEvent> | undefined | null;
ondevicemotion?: EventHandler<DeviceMotionEvent> | undefined | null;
'on:deviceorientation'?: EventHandler<DeviceOrientationEvent, Window> | undefined | null;
ondeviceorientation?: EventHandler<DeviceOrientationEvent, Window> | undefined | null;
'on:deviceorientationabsolute'?: EventHandler<DeviceOrientationEvent, Window> | undefined | null;
ondeviceorientationabsolute?: EventHandler<DeviceOrientationEvent, Window> | undefined | null;
'on:unhandledrejection'?: EventHandler<PromiseRejectionEvent, Window> | undefined | null;
onunhandledrejection?: EventHandler<PromiseRejectionEvent, Window> | undefined | null;
'on:rejectionhandled'?: EventHandler<PromiseRejectionEvent, Window> | undefined | null;
onrejectionhandled?: EventHandler<PromiseRejectionEvent, Window> | undefined | null;
}
export interface SVGAttributes<T extends EventTarget> extends AriaAttributes, DOMAttributes<T> {
@ -1454,7 +1681,7 @@ export interface SVGAttributes<T extends EventTarget> extends AriaAttributes, DO
z?: number | string | undefined | null;
zoomAndPan?: string | undefined | null;
// allow any data- attribute
// allow any data- attribute
[key: `data-${string}`]: any;
}

@ -1,142 +1,127 @@
{
"name": "svelte",
"version": "4.2.3",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.0.0-next.1",
"type": "module",
"module": "src/runtime/index.js",
"main": "src/runtime/index.js",
"types": "./types/index.d.ts",
"engines": {
"node": ">=18"
},
"files": [
"src",
"!src/**/tsconfig.json",
"types",
"compiler.*",
"register.js",
"index.d.ts",
"store.d.ts",
"animate.d.ts",
"transition.d.ts",
"easing.d.ts",
"motion.d.ts",
"action.d.ts",
"elements.d.ts",
"svelte-html.d.ts",
"compiler.cjs",
"*.d.ts",
"README.md"
],
"module": "src/main/main-client.js",
"main": "src/main/main-client.js",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./types/index.d.ts",
"browser": {
"default": "./src/runtime/index.js"
},
"default": "./src/runtime/ssr.js"
},
"./compiler": {
"types": "./types/index.d.ts",
"require": "./compiler.cjs",
"default": "./src/compiler/index.js"
"browser": "./src/main/main-client.js",
"default": "./src/main/main-server.js"
},
"./package.json": "./package.json",
"./action": {
"types": "./types/index.d.ts"
},
"./animate": {
"types": "./types/index.d.ts",
"default": "./src/runtime/animate/index.js"
"default": "./src/animate/index.js"
},
"./compiler": {
"types": "./types/index.d.ts",
"require": "./compiler.cjs",
"default": "./src/compiler/index.js"
},
"./easing": {
"types": "./types/index.d.ts",
"default": "./src/runtime/easing/index.js"
"default": "./src/easing/index.js"
},
"./elements": {
"types": "./elements.d.ts"
},
"./internal": {
"default": "./src/runtime/internal/index.js"
"default": "./src/internal/index.js"
},
"./internal/disclose-version": {
"default": "./src/internal/disclose-version.js"
},
"./internal/server": {
"default": "./src/internal/server/index.js"
},
"./legacy": {
"types": "./types/index.d.ts",
"browser": "./src/legacy/legacy-client.js",
"default": "./src/legacy/legacy-server.js"
},
"./motion": {
"types": "./types/index.d.ts",
"default": "./src/runtime/motion/index.js"
"default": "./src/motion/index.js"
},
"./store": {
"./server": {
"types": "./types/index.d.ts",
"default": "./src/runtime/store/index.js"
"default": "./src/server/index.js"
},
"./internal/disclose-version": {
"default": "./src/runtime/internal/disclose-version/index.js"
"./store": {
"types": "./types/index.d.ts",
"default": "./src/store/index.js"
},
"./transition": {
"types": "./types/index.d.ts",
"default": "./src/runtime/transition/index.js"
},
"./elements": {
"types": "./elements.d.ts"
"default": "./src/transition/index.js"
}
},
"engines": {
"node": ">=16"
},
"types": "types/index.d.ts",
"scripts": {
"format": "prettier . --cache --plugin-search-dir=. --write",
"check": "tsc --noEmit",
"test": "vitest run && echo \"manually check that there are no type errors in test/types by opening the files in there\"",
"build": "rollup -c && pnpm types",
"generate:version": "node ./scripts/generate-version.js",
"dev": "rollup -cw",
"posttest": "agadoo src/internal/index.js",
"prepublishOnly": "pnpm build",
"types": "node ./scripts/generate-dts.js",
"lint": "prettier . --cache --plugin-search-dir=. --check && eslint \"{scripts,src,test}/**/*.js\" --cache --fix"
},
"repository": {
"type": "git",
"url": "https://github.com/sveltejs/svelte.git",
"url": "git+https://github.com/sveltejs/svelte.git",
"directory": "packages/svelte"
},
"bugs": {
"url": "https://github.com/sveltejs/svelte/issues"
},
"homepage": "https://svelte.dev",
"keywords": [
"UI",
"framework",
"templates",
"templating"
],
"author": "Rich Harris",
"license": "MIT",
"bugs": {
"url": "https://github.com/sveltejs/svelte/issues"
"scripts": {
"build": "rollup -c && node scripts/build.js",
"watch": "rollup -cw",
"check": "tsc && cd ./tests/types && tsc",
"check:watch": "tsc --watch",
"generate:version": "node ./scripts/generate-version.js",
"prepublishOnly": "pnpm build"
},
"devDependencies": {
"@jridgewell/trace-mapping": "^0.3.19",
"@playwright/test": "^1.35.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@types/aria-query": "^5.0.3",
"@types/estree": "^1.0.5",
"dts-buddy": "^0.4.0",
"esbuild": "^0.19.2",
"rollup": "^4.1.5",
"source-map": "^0.7.4",
"tiny-glob": "^0.2.9"
},
"homepage": "https://svelte.dev",
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@jridgewell/trace-mapping": "^0.3.18",
"acorn": "^8.9.0",
"acorn": "^8.10.0",
"aria-query": "^5.3.0",
"axobject-query": "^3.2.1",
"code-red": "^1.0.3",
"css-tree": "^2.3.1",
"estree-walker": "^3.0.3",
"axobject-query": "^4.0.0",
"esm-env": "^1.0.0",
"esrap": "^1.1.1",
"is-reference": "^3.0.1",
"locate-character": "^3.0.0",
"magic-string": "^0.30.4",
"periscopic": "^3.1.0"
},
"devDependencies": {
"@playwright/test": "^1.35.1",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.1.0",
"@sveltejs/eslint-config": "^6.0.4",
"@types/aria-query": "^5.0.1",
"@types/estree": "^1.0.1",
"@types/node": "^14.18.51",
"agadoo": "^3.0.0",
"dts-buddy": "^0.1.7",
"esbuild": "^0.18.11",
"eslint-plugin-lube": "^0.1.7",
"happy-dom": "^9.20.3",
"jsdom": "22.0.0",
"kleur": "^4.1.5",
"rollup": "^3.26.2",
"source-map": "^0.7.4",
"tiny-glob": "^0.2.9",
"typescript": "^5.1.3",
"vitest": "^0.33.0"
"zimmerframe": "^1.1.0"
}
}

@ -1,50 +1,17 @@
import fs from 'node:fs';
import { createRequire } from 'node:module';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import { defineConfig } from 'rollup';
// runs the version generation as a side-effect of importing
import './scripts/generate-version.js';
const require = createRequire(import.meta.url);
const internal = await import('./src/runtime/internal/index.js');
fs.writeFileSync(
'src/compiler/compile/internal_exports.js',
`// This file is automatically generated\n` +
`export default new Set(${JSON.stringify(Object.keys(internal))});`
);
/**
* @type {import("rollup").RollupOptions[]}
*/
export default [
// Generate UMD build of the compiler so that Eslint/Prettier (which need CJS) and REPL (which needs UMD because browser) can use it
{
input: 'src/compiler/index.js',
plugins: [
{
resolveId(id) {
// Must import from the `css-tree` browser bundled distribution due to `createRequire` usage if importing from css-tree directly
if (id === 'css-tree') {
return require.resolve('./node_modules/css-tree/dist/csstree.esm.js');
}
}
},
resolve(),
commonjs({
include: ['../../node_modules/**', 'node_modules/**']
}),
json()
],
output: {
file: 'compiler.cjs',
format: 'umd',
name: 'svelte',
sourcemap: false,
indent: false
},
external: []
}
];
export default defineConfig({
input: 'src/compiler/index.js',
output: {
file: 'compiler.cjs',
format: 'umd',
name: 'svelte'
},
plugins: [resolve(), commonjs(), terser()]
});

@ -1,6 +0,0 @@
{
"plugins": ["lube"],
"rules": {
"lube/svelte-naming-convention": ["error", { "fixSameNames": true }]
}
}

@ -0,0 +1,39 @@
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { createBundle } from 'dts-buddy';
const dir = fileURLToPath(new URL('..', import.meta.url));
const pkg = JSON.parse(fs.readFileSync(`${dir}/package.json`, 'utf-8'));
// For people not using moduleResolution: 'bundler', we need to generate these files. Think about removing this in Svelte 6 or 7
// It may look weird, but the imports MUST be ending with index.js to be properly resolved in all TS modes
for (const name of ['action', 'animate', 'easing', 'motion', 'store', 'transition', 'legacy']) {
fs.writeFileSync(`${dir}/${name}.d.ts`, "import './types/index.js';\n");
}
fs.writeFileSync(`${dir}/index.d.ts`, "import './types/index.js';\n");
fs.writeFileSync(`${dir}/compiler.d.ts`, "import './types/index.js';\n");
// TODO: Remove these in Svelte 6. They are here so that tooling (which historically made use of these) can support Svelte 4-6 in one minor version
fs.mkdirSync(`${dir}/types/compiler`, { recursive: true });
fs.writeFileSync(`${dir}/types/compiler/preprocess.d.ts`, "import '../index.js';\n");
fs.writeFileSync(`${dir}/types/compiler/interfaces.d.ts`, "import '../index.js';\n");
await createBundle({
output: `${dir}/types/index.d.ts`,
modules: {
[pkg.name]: `${dir}/src/main/public.d.ts`,
[`${pkg.name}/action`]: `${dir}/src/action/public.d.ts`,
[`${pkg.name}/animate`]: `${dir}/src/animate/public.d.ts`,
[`${pkg.name}/compiler`]: `${dir}/src/compiler/index.js`,
[`${pkg.name}/easing`]: `${dir}/src/easing/index.js`,
[`${pkg.name}/legacy`]: `${dir}/src/legacy/public.d.ts`,
[`${pkg.name}/motion`]: `${dir}/src/motion/public.d.ts`,
[`${pkg.name}/server`]: `${dir}/src/server/index.js`,
[`${pkg.name}/store`]: `${dir}/src/store/public.d.ts`,
[`${pkg.name}/transition`]: `${dir}/src/transition/public.d.ts`,
// TODO remove in Svelte 6
[`${pkg.name}/types/compiler/preprocess`]: `${dir}/src/compiler/preprocess/legacy-public.d.ts`,
[`${pkg.name}/types/compiler/interfaces`]: `${dir}/src/compiler/types/legacy-interfaces.d.ts`
}
});

@ -1,40 +0,0 @@
// Compile all Svelte files in a directory to JS and CSS files
// Usage: node scripts/compile-test.js <directory>
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import glob from 'tiny-glob/sync.js';
import { compile } from '../src/compiler/index.js';
const cwd = path.resolve(process.argv[2]);
const options = [
['normal', {}],
['hydrate', { hydratable: true }],
['ssr', { generate: 'ssr' }]
];
for (const file of glob('**/*.svelte', { cwd })) {
const contents = readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r/g, '');
let w;
for (const [name, opts] of options) {
const dir = `${cwd}/_output/${name}`;
const { js, css, warnings } = compile(contents, {
...opts,
filename: file
});
if (warnings.length) {
w = warnings;
}
mkdirSync(dir, { recursive: true });
js.code && writeFileSync(`${dir}/${file.replace(/\.svelte$/, '.js')}`, js.code);
css.code && writeFileSync(`${dir}/${file.replace(/\.svelte$/, '.css')}`, css.code);
}
if (w) {
console.log(`Warnings for ${file}:`);
console.log(w);
}
}

@ -1,42 +0,0 @@
import * as fs from 'node:fs';
import { createBundle } from 'dts-buddy';
// It may look weird, but the imports MUST be ending with index.js to be properly resolved in all TS modes
for (const name of ['action', 'animate', 'easing', 'motion', 'store', 'transition']) {
fs.writeFileSync(`${name}.d.ts`, "import './types/index.js';");
}
fs.writeFileSync('index.d.ts', "import './types/index.js';");
fs.writeFileSync('compiler.d.ts', "import './types/index.js';");
// TODO: some way to mark these as deprecated
fs.mkdirSync('./types/compiler', { recursive: true });
fs.writeFileSync('./types/compiler/preprocess.d.ts', "import '../index.js';");
fs.writeFileSync('./types/compiler/interfaces.d.ts', "import '../index.js';");
await createBundle({
output: 'types/index.d.ts',
compilerOptions: {
strict: true
},
modules: {
svelte: 'src/runtime/public.d.ts',
'svelte/compiler': 'src/compiler/public.d.ts',
'svelte/types/compiler/preprocess': 'src/compiler/preprocess/public.d.ts',
'svelte/types/compiler/interfaces': 'src/compiler/interfaces.d.ts',
'svelte/action': 'src/runtime/action/public.d.ts',
'svelte/animate': 'src/runtime/animate/public.d.ts',
'svelte/easing': 'src/runtime/easing/index.js',
'svelte/motion': 'src/runtime/motion/public.d.ts',
'svelte/store': 'src/runtime/store/public.d.ts',
'svelte/transition': 'src/runtime/transition/public.d.ts'
}
});
// There's no way to tell in JS that a class can have arbitrary properties, so we need to add that manually
const types = fs.readFileSync('types/index.d.ts', 'utf-8');
fs.writeFileSync(
'types/index.d.ts',
// same line to not affect source map
types.replace(/export class SvelteComponent<[^{]*{/, '$& [prop: string]: any;')
);

@ -3,7 +3,7 @@ import fs from 'node:fs';
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
fs.writeFileSync(
'./src/shared/version.js',
'./src/version.js',
`// generated during release, do not modify
/**

@ -1,96 +0,0 @@
/** ----------------------------------------------------------------------
This script gets a list of global objects/functions of browser.
This process is simple for now, so it is handled without AST parser.
Please run `node scripts/globals-extractor.js` at the project root.
see: https://github.com/microsoft/TypeScript/tree/main/lib
---------------------------------------------------------------------- */
import http from 'node:https';
import fs from 'node:fs';
const GLOBAL_TS_PATH = './src/compiler/utils/globals.js';
// MEMO: add additional objects/functions which existed in `src/compiler/utils/names.ts`
// before this script was introduced but could not be retrieved by this process.
const SPECIALS = ['global', 'globalThis', 'InternalError', 'process', 'undefined'];
const get_url = (name) =>
`https://raw.githubusercontent.com/microsoft/TypeScript/main/src/lib/${name}.d.ts`;
const extract_name = (split) => split.match(/^[a-zA-Z0-9_$]+/)[0];
const extract_functions_and_references = (name, data) => {
const functions = [];
const references = [];
data.split('\n').forEach((line) => {
const trimmed = line.trim();
const split = trimmed.replace(/[\s+]/, ' ').split(' ');
if (split[0] === 'declare' && split[1] !== 'type') {
functions.push(extract_name(split[2]));
} else if (trimmed.startsWith('/// <reference')) {
const matched = trimmed.match(/ lib="(.+)"/);
const reference = matched && matched[1];
if (reference) references.push(reference);
}
});
return { functions, references };
};
const do_get = (url) =>
new Promise((resolve, reject) => {
http
.get(url, (res) => {
let body = '';
res.setEncoding('utf8');
res.on('data', (chunk) => (body += chunk));
res.on('end', () => resolve(body));
})
.on('error', (e) => {
console.error(e.message);
reject(e);
});
});
const fetched_names = new Set();
const get_functions = async (name) => {
const res = [];
if (fetched_names.has(name)) return res;
fetched_names.add(name);
const body = await do_get(get_url(name));
const { functions, references } = extract_functions_and_references(name, body);
res.push(...functions);
const chile_functions = await Promise.all(references.map(get_functions));
chile_functions.forEach((i) => res.push(...i));
return res;
};
const build_output = (functions) => {
const sorted = Array.from(new Set(functions.sort()));
return `\
/** ----------------------------------------------------------------------
This file is automatically generated by \`scripts/globals-extractor.js\`.
Generated At: ${new Date().toISOString()}
---------------------------------------------------------------------- */
export default new Set([
${sorted.map((i) => `\t'${i}'`).join(',\n')}
]);
`;
};
const get_exists_globals = () => {
const regexp = /^\s*["'](.+)["'],?\s*$/;
return fs
.readFileSync(GLOBAL_TS_PATH, 'utf8')
.split('\n')
.filter((line) => line.match(regexp))
.map((line) => line.match(regexp)[1]);
};
(async () => {
const globals = get_exists_globals();
const new_globals = await get_functions('es2021.full');
globals.forEach((g) => new_globals.push(g));
SPECIALS.forEach((g) => new_globals.push(g));
fs.writeFileSync(GLOBAL_TS_PATH, build_output(new_globals));
})();

@ -1,6 +0,0 @@
{
"plugins": ["lube"],
"rules": {
"lube/svelte-naming-convention": ["error", { "fixSameNames": true }]
}
}

@ -0,0 +1,73 @@
/**
* Actions can return an object containing the two properties defined in this interface. Both are optional.
* - update: An action can have a parameter. This method will be called whenever that parameter changes,
* immediately after Svelte has applied updates to the markup. `ActionReturn` and `ActionReturn<undefined>` both
* mean that the action accepts no parameters.
* - destroy: Method that is called after the element is unmounted
*
* Additionally, you can specify which additional attributes and events the action enables on the applied element.
* This applies to TypeScript typings only and has no effect at runtime.
*
* Example usage:
* ```ts
* interface Attributes {
* newprop?: string;
* 'on:event': (e: CustomEvent<boolean>) => void;
* }
*
* export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn<Parameter, Attributes> {
* // ...
* return {
* update: (updatedParameter) => {...},
* destroy: () => {...}
* };
* }
* ```
*
* Docs: https://svelte.dev/docs/svelte-action
*/
export interface ActionReturn<
Parameter = undefined,
Attributes extends Record<string, any> = Record<never, any>
> {
update?: (parameter: Parameter) => void;
destroy?: () => void;
/**
* ### DO NOT USE THIS
* This exists solely for type-checking and has no effect at runtime.
* Set this through the `Attributes` generic instead.
*/
$$_attributes?: Attributes;
}
/**
* Actions are functions that are called when an element is created.
* You can use this interface to type such actions.
* The following example defines an action that only works on `<div>` elements
* and optionally accepts a parameter which it has a default value for:
* ```ts
* export const myAction: Action<HTMLDivElement, { someProperty: boolean } | undefined> = (node, param = { someProperty: true }) => {
* // ...
* }
* ```
* `Action<HTMLDivElement>` and `Action<HTMLDiveElement, undefined>` both signal that the action accepts no parameters.
*
* You can return an object with methods `update` and `destroy` from the function and type which additional attributes and events it has.
* See interface `ActionReturn` for more details.
*
* Docs: https://svelte.dev/docs/svelte-action
*/
export interface Action<
Element = HTMLElement,
Parameter = undefined,
Attributes extends Record<string, any> = Record<never, any>
> {
<Node extends Element>(
...args: undefined extends Parameter
? [node: Node, parameter?: Parameter]
: [node: Node, parameter: Parameter]
): void | ActionReturn<Parameter, Attributes>;
}
// Implementation notes:
// - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode

@ -0,0 +1,32 @@
import { cubicOut } from '../easing/index.js';
/**
* The flip function calculates the start and end position of an element and animates between them, translating the x and y values.
* `flip` stands for [First, Last, Invert, Play](https://aerotwist.com/blog/flip-your-animations/).
*
* https://svelte.dev/docs/svelte-animate#flip
* @param {Element} node
* @param {{ from: DOMRect; to: DOMRect }} fromTo
* @param {import('./public.js').FlipParams} params
* @returns {import('./public.js').AnimationConfig}
*/
export function flip(node, { from, to }, params = {}) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
const dx = from.left + (from.width * ox) / to.width - (to.left + ox);
const dy = from.top + (from.height * oy) / to.height - (to.top + oy);
const { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
return {
delay,
duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
easing,
css: (t, u) => {
const x = u * dx;
const y = u * dy;
const sx = t + (u * from.width) / to.width;
const sy = t + (u * from.height) / to.height;
return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
}
};
}

@ -1,83 +0,0 @@
const now = () => performance.now();
/** @param {any} timings */
function collapse_timings(timings) {
const result = {};
timings.forEach((timing) => {
result[timing.label] = Object.assign(
{
total: timing.end - timing.start
},
timing.children && collapse_timings(timing.children)
);
});
return result;
}
export default class Stats {
/**
* @typedef {Object} Timing
* @property {string} label
* @property {number} start
* @property {number} end
* @property {Timing[]} children
*/
/** @type {number} */
start_time;
/** @type {Timing} */
current_timing;
/** @type {Timing[]} */
current_children;
/** @type {Timing[]} */
timings;
/** @type {Timing[]} */
stack;
constructor() {
this.start_time = now();
this.stack = [];
this.current_children = this.timings = [];
}
/** @param {any} label */
start(label) {
const timing = {
label,
start: now(),
end: null,
children: []
};
this.current_children.push(timing);
this.stack.push(timing);
this.current_timing = timing;
this.current_children = timing.children;
}
/** @param {any} label */
stop(label) {
if (label !== this.current_timing.label) {
throw new Error(
`Mismatched timing labels (expected ${this.current_timing.label}, got ${label})`
);
}
this.current_timing.end = now();
this.stack.pop();
this.current_timing = this.stack[this.stack.length - 1];
this.current_children = this.current_timing ? this.current_timing.children : this.timings;
}
render() {
const timings = Object.assign(
{
total: now() - this.start_time
},
collapse_timings(this.timings)
);
return {
timings
};
}
}

File diff suppressed because it is too large Load Diff

@ -1,356 +0,0 @@
// All compiler errors should be listed and accessed from here
/**
* @internal
*/
export default {
invalid_binding_elements: /**
* @param {string} element
* @param {string} binding
*/ (element, binding) => ({
code: 'invalid-binding',
message: `'${binding}' is not a valid binding on <${element}> elements`
}),
invalid_binding_element_with: /**
* @param {string} elements
* @param {string} binding
*/ (elements, binding) => ({
code: 'invalid-binding',
message: `'${binding}' binding can only be used with ${elements}`
}),
invalid_binding_on: /**
* @param {string} binding
* @param {string} element
* @param {string} [post]
*/ (binding, element, post) => ({
code: 'invalid-binding',
message: `'${binding}' is not a valid binding on ${element}` + (post || '')
}),
invalid_binding_foreign: /** @param {string} binding */ (binding) => ({
code: 'invalid-binding',
message: `'${binding}' is not a valid binding. Foreign elements only support bind:this`
}),
invalid_binding_no_checkbox: /**
* @param {string} binding
* @param {boolean} is_radio
*/ (binding, is_radio) => ({
code: 'invalid-binding',
message:
`'${binding}' binding can only be used with <input type="checkbox">` +
(is_radio ? ' — for <input type="radio">, use \'group\' binding' : '')
}),
invalid_binding: /** @param {string} binding */ (binding) => ({
code: 'invalid-binding',
message: `'${binding}' is not a valid binding`
}),
invalid_binding_window: /** @param {string[]} parts */ (parts) => ({
code: 'invalid-binding',
message: `Bindings on <svelte:window> must be to top-level properties, e.g. '${
parts[parts.length - 1]
}' rather than '${parts.join('.')}'`
}),
invalid_binding_let: {
code: 'invalid-binding',
message: 'Cannot bind to a variable declared with the let: directive'
},
invalid_binding_await: {
code: 'invalid-binding',
message: 'Cannot bind to a variable declared with {#await ... then} or {:catch} blocks'
},
invalid_binding_const: {
code: 'invalid-binding',
message: 'Cannot bind to a variable declared with {@const ...}'
},
invalid_binding_writable: {
code: 'invalid-binding',
message: 'Cannot bind to a variable which is not writable'
},
binding_undeclared: /** @param {string} name */ (name) => ({
code: 'binding-undeclared',
message: `${name} is not declared`
}),
invalid_type: {
code: 'invalid-type',
message: "'type' attribute cannot be dynamic if input uses two-way binding"
},
missing_type: {
code: 'missing-type',
message: "'type' attribute must be specified"
},
dynamic_multiple_attribute: {
code: 'dynamic-multiple-attribute',
message: "'multiple' attribute cannot be dynamic if select uses two-way binding"
},
missing_contenteditable_attribute: {
code: 'missing-contenteditable-attribute',
message:
"'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings"
},
dynamic_contenteditable_attribute: {
code: 'dynamic-contenteditable-attribute',
message: "'contenteditable' attribute cannot be dynamic if element uses two-way binding"
},
invalid_event_modifier_combination: /**
* @param {string} modifier1
* @param {string} modifier2
*/ (modifier1, modifier2) => ({
code: 'invalid-event-modifier',
message: `The '${modifier1}' and '${modifier2}' modifiers cannot be used together`
}),
invalid_event_modifier_legacy: /** @param {string} modifier */ (modifier) => ({
code: 'invalid-event-modifier',
message: `The '${modifier}' modifier cannot be used in legacy mode`
}),
invalid_event_modifier: /** @param {string} valid */ (valid) => ({
code: 'invalid-event-modifier',
message: `Valid event modifiers are ${valid}`
}),
invalid_event_modifier_component: {
code: 'invalid-event-modifier',
message: "Event modifiers other than 'once' can only be used on DOM elements"
},
textarea_duplicate_value: {
code: 'textarea-duplicate-value',
message:
'A <textarea> can have either a value attribute or (equivalently) child content, but not both'
},
illegal_attribute: /** @param {string} name */ (name) => ({
code: 'illegal-attribute',
message: `'${name}' is not a valid attribute name`
}),
invalid_slot_attribute: {
code: 'invalid-slot-attribute',
message: 'slot attribute cannot have a dynamic value'
},
duplicate_slot_attribute: /** @param {string} name */ (name) => ({
code: 'duplicate-slot-attribute',
message: `Duplicate '${name}' slot`
}),
invalid_slotted_content: {
code: 'invalid-slotted-content',
message:
"Element with a slot='...' attribute must be a child of a component or a descendant of a custom element"
},
invalid_attribute_head: {
code: 'invalid-attribute',
message: '<svelte:head> should not have any attributes or directives'
},
invalid_action: {
code: 'invalid-action',
message: 'Actions can only be applied to DOM elements, not components'
},
invalid_animation: {
code: 'invalid-animation',
message: 'Animations can only be applied to DOM elements, not components'
},
invalid_class: {
code: 'invalid-class',
message: 'Classes can only be applied to DOM elements, not components'
},
invalid_transition: {
code: 'invalid-transition',
message: 'Transitions can only be applied to DOM elements, not components'
},
invalid_let: {
code: 'invalid-let',
message: 'let directive value must be an identifier or an object/array pattern'
},
invalid_slot_directive: {
code: 'invalid-slot-directive',
message: '<slot> cannot have directives'
},
dynamic_slot_name: {
code: 'dynamic-slot-name',
message: '<slot> name cannot be dynamic'
},
invalid_slot_name: {
code: 'invalid-slot-name',
message: 'default is a reserved word — it cannot be used as a slot name'
},
invalid_slot_attribute_value_missing: {
code: 'invalid-slot-attribute',
message: 'slot attribute value is missing'
},
invalid_slotted_content_fragment: {
code: 'invalid-slotted-content',
message: '<svelte:fragment> must be a child of a component'
},
illegal_attribute_title: {
code: 'illegal-attribute',
message: '<title> cannot have attributes'
},
illegal_structure_title: {
code: 'illegal-structure',
message: '<title> can only contain text and {tags}'
},
duplicate_transition: /**
* @param {string} directive
* @param {string} parent_directive
*/ (directive, parent_directive) => {
/** @param {string} _directive */
function describe(_directive) {
return _directive === 'transition' ? "a 'transition'" : `an '${_directive}'`;
}
const message =
directive === parent_directive
? `An element can only have one '${directive}' directive`
: `An element cannot have both ${describe(parent_directive)} directive and ${describe(
directive
)} directive`;
return {
code: 'duplicate-transition',
message
};
},
contextual_store: {
code: 'contextual-store',
message:
'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
},
default_export: {
code: 'default-export',
message: 'A component cannot have a default export'
},
illegal_declaration: {
code: 'illegal-declaration',
message: 'The $ prefix is reserved, and cannot be used for variable and import names'
},
illegal_subscription: {
code: 'illegal-subscription',
message: 'Cannot reference store value inside <script context="module">'
},
illegal_global: /** @param {string} name */ (name) => ({
code: 'illegal-global',
message: `${name} is an illegal variable name`
}),
illegal_variable_declaration: {
code: 'illegal-variable-declaration',
message: 'Cannot declare same variable name which is imported inside <script context="module">'
},
cyclical_reactive_declaration: /** @param {string[]} cycle */ (cycle) => ({
code: 'cyclical-reactive-declaration',
message: `Cyclical dependency detected: ${cycle.join(' → ')}`
}),
invalid_tag_property: {
code: 'invalid-tag-property',
message: "tag name must be two or more words joined by the '-' character"
},
invalid_customElement_attribute: {
code: 'invalid-customElement-attribute',
message:
"'customElement' must be a string literal defining a valid custom element name or an object of the form " +
"{ tag: string; shadow?: 'open' | 'none'; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }"
},
invalid_tag_attribute: {
code: 'invalid-tag-attribute',
message: "'tag' must be a string literal"
},
invalid_shadow_attribute: {
code: 'invalid-shadow-attribute',
message: "'shadow' must be either 'open' or 'none'"
},
invalid_props_attribute: {
code: 'invalid-props-attribute',
message:
"'props' must be a statically analyzable object literal of the form " +
"'{ [key: string]: { attribute?: string; reflect?: boolean; type?: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object' }'"
},
invalid_namespace_property: /**
* @param {string} namespace
* @param {string} [suggestion]
*/ (namespace, suggestion) => ({
code: 'invalid-namespace-property',
message:
`Invalid namespace '${namespace}'` + (suggestion ? ` (did you mean '${suggestion}'?)` : '')
}),
invalid_namespace_attribute: {
code: 'invalid-namespace-attribute',
message: "The 'namespace' attribute must be a string literal representing a valid namespace"
},
invalid_attribute_value: /** @param {string} name */ (name) => ({
code: `invalid-${name}-value`,
message: `${name} attribute must be true or false`
}),
invalid_options_attribute_unknown: /** @param {string} name */ (name) => ({
code: 'invalid-options-attribute',
message: `<svelte:options> unknown attribute '${name}'`
}),
invalid_options_attribute: {
code: 'invalid-options-attribute',
message:
"<svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes"
},
css_invalid_global: {
code: 'css-invalid-global',
message: ':global(...) can be at the start or end of a selector sequence, but not in the middle'
},
css_invalid_global_selector: {
code: 'css-invalid-global-selector',
message: ':global(...) must contain a single selector'
},
css_invalid_global_selector_position: {
code: 'css-invalid-global-selector-position',
message:
':global(...) not at the start of a selector sequence should not contain type or universal selectors'
},
css_invalid_selector: /** @param {string} selector */ (selector) => ({
code: 'css-invalid-selector',
message: `Invalid selector "${selector}"`
}),
duplicate_animation: {
code: 'duplicate-animation',
message: "An element can only have one 'animate' directive"
},
invalid_animation_immediate: {
code: 'invalid-animation',
message:
'An element that uses the animate directive must be the immediate child of a keyed each block'
},
invalid_animation_key: {
code: 'invalid-animation',
message:
'An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?'
},
invalid_animation_sole: {
code: 'invalid-animation',
message:
'An element that uses the animate directive must be the sole child of a keyed each block'
},
invalid_animation_dynamic_element: {
code: 'invalid-animation',
message: '<svelte:element> cannot have a animate directive'
},
invalid_directive_value: {
code: 'invalid-directive-value',
message:
'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)'
},
invalid_const_placement: {
code: 'invalid-const-placement',
message:
'{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>'
},
invalid_const_declaration: /** @param {string} name */ (name) => ({
code: 'invalid-const-declaration',
message: `'${name}' has already been declared`
}),
invalid_const_update: /** @param {string} name */ (name) => ({
code: 'invalid-const-update',
message: `'${name}' is declared using {@const ...} and is read-only`
}),
cyclical_const_tags: /** @param {string[]} cycle */ (cycle) => ({
code: 'cyclical-const-tags',
message: `Cyclical dependency detected: ${cycle.join(' → ')}`
}),
invalid_component_style_directive: {
code: 'invalid-component-style-directive',
message: 'Style directives cannot be used on components'
},
invalid_var_declaration: {
code: 'invalid_var_declaration',
message: '"var" scope should not extend outside the reactive block'
},
invalid_style_directive_modifier: /** @param {string} valid */ (valid) => ({
code: 'invalid-style-directive-modifier',
message: `Valid modifiers for style directives are: ${valid}`
})
};

@ -1,310 +0,0 @@
/**
* @internal
*/
export default {
tag_option_deprecated: {
code: 'tag-option-deprecated',
message: "'tag' option is deprecated — use 'customElement' instead"
},
unused_export_let: /**
* @param {string} component
* @param {string} property
*/ (component, property) => ({
code: 'unused-export-let',
message: `${component} has unused export property '${property}'. If it is for external reference only, please consider using \`export const ${property}\``
}),
module_script_reactive_declaration: {
code: 'module-script-reactive-declaration',
message: '$: has no effect in a module script'
},
non_top_level_reactive_declaration: {
code: 'non-top-level-reactive-declaration',
message: '$: has no effect outside of the top-level'
},
module_script_variable_reactive_declaration: /** @param {string[]} names */ (names) => ({
code: 'module-script-reactive-declaration',
message: `${names.map((name) => `"${name}"`).join(', ')} ${
names.length > 1 ? 'are' : 'is'
} declared in a module script and will not be reactive`
}),
missing_declaration: /**
* @param {string} name
* @param {boolean} has_script
*/ (name, has_script) => ({
code: 'missing-declaration',
message:
`'${name}' is not defined` +
(has_script
? ''
: `. Consider adding a <script> block with 'export let ${name}' to declare a prop`)
}),
missing_custom_element_compile_options: {
code: 'missing-custom-element-compile-options',
message:
"The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?"
},
css_unused_selector: /** @param {string} selector */ (selector) => ({
code: 'css-unused-selector',
message: `Unused CSS selector "${selector}"`
}),
empty_block: {
code: 'empty-block',
message: 'Empty block'
},
reactive_component: /** @param {string} name */ (name) => ({
code: 'reactive-component',
message: `<${name}/> will not be reactive if ${name} changes. Use <svelte:component this={${name}}/> if you want this reactivity.`
}),
component_name_lowercase: /** @param {string} name */ (name) => ({
code: 'component-name-lowercase',
message: `<${name}> will be treated as an HTML element unless it begins with a capital letter`
}),
avoid_is: {
code: 'avoid-is',
message: "The 'is' attribute is not supported cross-browser and should be avoided"
},
invalid_html_attribute: /**
* @param {string} name
* @param {string} suggestion
*/ (name, suggestion) => ({
code: 'invalid-html-attribute',
message: `'${name}' is not a valid HTML attribute. Did you mean '${suggestion}'?`
}),
a11y_aria_attributes: /** @param {string} name */ (name) => ({
code: 'a11y-aria-attributes',
message: `A11y: <${name}> should not have aria-* attributes`
}),
a11y_incorrect_attribute_type: /**
* @param {import('aria-query').ARIAPropertyDefinition} schema
* @param {string} attribute
*/ (schema, attribute) => {
let message;
switch (schema.type) {
case 'boolean':
message = `The value of '${attribute}' must be exactly one of true or false`;
break;
case 'id':
message = `The value of '${attribute}' must be a string that represents a DOM element ID`;
break;
case 'idlist':
message = `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs`;
break;
case 'tristate':
message = `The value of '${attribute}' must be exactly one of true, false, or mixed`;
break;
case 'token':
message = `The value of '${attribute}' must be exactly one of ${(schema.values || []).join(
', '
)}`;
break;
case 'tokenlist':
message = `The value of '${attribute}' must be a space-separated list of one or more of ${(
schema.values || []
).join(', ')}`;
break;
default:
message = `The value of '${attribute}' must be of type ${schema.type}`;
}
return {
code: 'a11y-incorrect-aria-attribute-type',
message: `A11y: ${message}`
};
},
a11y_unknown_aria_attribute: /**
* @param {string} attribute
* @param {string} [suggestion]
*/ (attribute, suggestion) => ({
code: 'a11y-unknown-aria-attribute',
message:
`A11y: Unknown aria attribute 'aria-${attribute}'` +
(suggestion ? ` (did you mean '${suggestion}'?)` : '')
}),
a11y_hidden: /** @param {string} name */ (name) => ({
code: 'a11y-hidden',
message: `A11y: <${name}> element should not be hidden`
}),
a11y_misplaced_role: /** @param {string} name */ (name) => ({
code: 'a11y-misplaced-role',
message: `A11y: <${name}> should not have role attribute`
}),
a11y_unknown_role: /**
* @param {string | boolean} role
* @param {string} [suggestion]
*/ (role, suggestion) => ({
code: 'a11y-unknown-role',
message: `A11y: Unknown role '${role}'` + (suggestion ? ` (did you mean '${suggestion}'?)` : '')
}),
a11y_no_abstract_role: /** @param {string | boolean} role */ (role) => ({
code: 'a11y-no-abstract-role',
message: `A11y: Abstract role '${role}' is forbidden`
}),
a11y_no_redundant_roles: /** @param {string | boolean} role */ (role) => ({
code: 'a11y-no-redundant-roles',
message: `A11y: Redundant role '${role}'`
}),
a11y_no_static_element_interactions: /**
* @param {string} element
* @param {string[]} handlers
*/ (element, handlers) => ({
code: 'a11y-no-static-element-interactions',
message: `A11y: <${element}> with ${handlers.join(', ')} ${
handlers.length === 1 ? 'handler' : 'handlers'
} must have an ARIA role`
}),
a11y_no_interactive_element_to_noninteractive_role: /**
* @param {string | boolean} role
* @param {string} element
*/ (role, element) => ({
code: 'a11y-no-interactive-element-to-noninteractive-role',
message: `A11y: <${element}> cannot have role '${role}'`
}),
a11y_no_noninteractive_element_interactions: /** @param {string} element */ (element) => ({
code: 'a11y-no-noninteractive-element-interactions',
message: `A11y: Non-interactive element <${element}> should not be assigned mouse or keyboard event listeners.`
}),
a11y_no_noninteractive_element_to_interactive_role: /**
* @param {string | boolean} role
* @param {string} element
*/ (role, element) => ({
code: 'a11y-no-noninteractive-element-to-interactive-role',
message: `A11y: Non-interactive element <${element}> cannot have interactive role '${role}'`
}),
a11y_role_has_required_aria_props: /**
* @param {string} role
* @param {string[]} props
*/ (role, props) => ({
code: 'a11y-role-has-required-aria-props',
message: `A11y: Elements with the ARIA role "${role}" must have the following attributes defined: ${props
.map((name) => `"${name}"`)
.join(', ')}`
}),
a11y_role_supports_aria_props: /**
* @param {string} attribute
* @param {string} role
* @param {boolean} is_implicit
* @param {string} name
*/ (attribute, role, is_implicit, name) => {
let message = `The attribute '${attribute}' is not supported by the role '${role}'.`;
if (is_implicit) {
message += ` This role is implicit on the element <${name}>.`;
}
return {
code: 'a11y-role-supports-aria-props',
message: `A11y: ${message}`
};
},
a11y_accesskey: {
code: 'a11y-accesskey',
message: 'A11y: Avoid using accesskey'
},
a11y_autofocus: {
code: 'a11y-autofocus',
message: 'A11y: Avoid using autofocus'
},
a11y_misplaced_scope: {
code: 'a11y-misplaced-scope',
message: 'A11y: The scope attribute should only be used with <th> elements'
},
a11y_positive_tabindex: {
code: 'a11y-positive-tabindex',
message: 'A11y: avoid tabindex values above zero'
},
a11y_invalid_attribute: /**
* @param {string} href_attribute
* @param {string} href_value
*/ (href_attribute, href_value) => ({
code: 'a11y-invalid-attribute',
message: `A11y: '${href_value}' is not a valid ${href_attribute} attribute`
}),
a11y_missing_attribute: /**
* @param {string} name
* @param {string} article
* @param {string} sequence
*/ (name, article, sequence) => ({
code: 'a11y-missing-attribute',
message: `A11y: <${name}> element should have ${article} ${sequence} attribute`
}),
a11y_autocomplete_valid: /**
* @param {null | true | string} type
* @param {null | true | string} value
*/ (type, value) => ({
code: 'a11y-autocomplete-valid',
message: `A11y: The value '${value}' is not supported by the attribute 'autocomplete' on element <input type="${
type || '...'
}">`
}),
a11y_img_redundant_alt: {
code: 'a11y-img-redundant-alt',
message: 'A11y: Screenreaders already announce <img> elements as an image.'
},
a11y_interactive_supports_focus: /** @param {string} role */ (role) => ({
code: 'a11y-interactive-supports-focus',
message: `A11y: Elements with the '${role}' interactive role must have a tabindex value.`
}),
a11y_label_has_associated_control: {
code: 'a11y-label-has-associated-control',
message: 'A11y: A form label must be associated with a control.'
},
a11y_media_has_caption: {
code: 'a11y-media-has-caption',
message: 'A11y: <video> elements must have a <track kind="captions">'
},
a11y_distracting_elements: /** @param {string} name */ (name) => ({
code: 'a11y-distracting-elements',
message: `A11y: Avoid <${name}> elements`
}),
a11y_structure_immediate: {
code: 'a11y-structure',
message: 'A11y: <figcaption> must be an immediate child of <figure>'
},
a11y_structure_first_or_last: {
code: 'a11y-structure',
message: 'A11y: <figcaption> must be first or last child of <figure>'
},
a11y_mouse_events_have_key_events: /**
* @param {string} event
* @param {string} accompanied_by
*/ (event, accompanied_by) => ({
code: 'a11y-mouse-events-have-key-events',
message: `A11y: on:${event} must be accompanied by on:${accompanied_by}`
}),
a11y_click_events_have_key_events: {
code: 'a11y-click-events-have-key-events',
message:
'A11y: visible, non-interactive elements with an on:click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as <button type="button"> or <a> might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details.'
},
a11y_missing_content: /** @param {string} name */ (name) => ({
code: 'a11y-missing-content',
message: `A11y: <${name}> element should have child content`
}),
a11y_no_noninteractive_tabindex: {
code: 'a11y-no-noninteractive-tabindex',
message: 'A11y: noninteractive element cannot have nonnegative tabIndex value'
},
a11y_aria_activedescendant_has_tabindex: {
code: 'a11y-aria-activedescendant-has-tabindex',
message: 'A11y: Elements with attribute aria-activedescendant should have tabindex value'
},
redundant_event_modifier_for_touch: {
code: 'redundant-event-modifier',
message: "Touch event handlers that don't use the 'event' object are passive by default"
},
redundant_event_modifier_passive: {
code: 'redundant-event-modifier',
message: 'The passive modifier only works with wheel and touch events'
},
invalid_rest_eachblock_binding: /** @param {string} rest_element_name */ (rest_element_name) => ({
code: 'invalid-rest-eachblock-binding',
message: `The rest operator (...) will create a new object and binding '${rest_element_name}' with the original object will not work`
}),
avoid_mouse_events_on_document: {
code: 'avoid-mouse-events-on-document',
message:
'Mouse enter/leave events on the document are not supported in all browsers and should be avoided'
},
illegal_attribute_character: {
code: 'illegal-attribute-character',
message:
"Attributes should not contain ':' characters to prevent ambiguity with Svelte directives"
}
};

@ -1,156 +0,0 @@
import { b } from 'code-red';
/**
* @param {any} program
* @param {import('estree').Identifier} name
* @param {string} banner
* @param {any} svelte_path
* @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
* @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
* @param {import('estree').ImportDeclaration[]} imports
* @param {Export[]} module_exports
* @param {import('estree').ExportNamedDeclaration[]} exports_from
*/
export default function create_module(
program,
name,
banner,
svelte_path = 'svelte',
helpers,
globals,
imports,
module_exports,
exports_from
) {
const internal_path = `${svelte_path}/internal`;
helpers.sort((a, b) => (a.name < b.name ? -1 : 1));
globals.sort((a, b) => (a.name < b.name ? -1 : 1));
return esm(
program,
name,
banner,
svelte_path,
internal_path,
helpers,
globals,
imports,
module_exports,
exports_from
);
}
/**
* @param {any} source
* @param {any} svelte_path
*/
function edit_source(source, svelte_path) {
return source === 'svelte' || source.startsWith('svelte/')
? source.replace('svelte', svelte_path)
: source;
}
/**
* @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
* @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
*/
function get_internal_globals(globals, helpers) {
return (
globals.length > 0 && {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'ObjectPattern',
properties: globals.map((g) => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: { type: 'Identifier', name: g.name },
value: g.alias,
kind: 'init'
}))
},
init: helpers.find(({ name }) => name === 'globals').alias
}
]
}
);
}
/**
* @param {any} program
* @param {import('estree').Identifier} name
* @param {string} banner
* @param {string} svelte_path
* @param {string} internal_path
* @param {Array<{ name: string; alias: import('estree').Identifier }>} helpers
* @param {Array<{ name: string; alias: import('estree').Identifier }>} globals
* @param {import('estree').ImportDeclaration[]} imports
* @param {Export[]} module_exports
* @param {import('estree').ExportNamedDeclaration[]} exports_from
*/
function esm(
program,
name,
banner,
svelte_path,
internal_path,
helpers,
globals,
imports,
module_exports,
exports_from
) {
const import_declaration = {
type: 'ImportDeclaration',
specifiers: helpers.map((h) => ({
type: 'ImportSpecifier',
local: h.alias,
imported: { type: 'Identifier', name: h.name }
})),
source: { type: 'Literal', value: internal_path }
};
const internal_globals = get_internal_globals(globals, helpers);
// edit user imports
/** @param {any} node */
function rewrite_import(node) {
const value = edit_source(node.source.value, svelte_path);
if (node.source.value !== value) {
node.source.value = value;
node.source.raw = null;
}
}
imports.forEach(rewrite_import);
exports_from.forEach(rewrite_import);
const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration',
specifiers: module_exports.map((x) => ({
type: 'Specifier',
local: { type: 'Identifier', name: x.name },
exported: { type: 'Identifier', name: x.as }
}))
};
program.body = b`
/* ${banner} */
${import_declaration}
${internal_globals}
${imports}
${exports_from}
${program.body}
export default ${name};
${exports}
`;
}
/**
* @typedef {Object} Export
* @property {string} name
* @property {string} as
*/

@ -1,833 +0,0 @@
import { gather_possible_values, UNKNOWN } from './gather_possible_values.js';
import compiler_errors from '../compiler_errors.js';
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns.js';
const BlockAppliesToNode = /** @type {const} */ ({
NotPossible: 0,
Possible: 1,
UnknownSelectorType: 2
});
const NodeExist = /** @type {const} */ ({
Probably: 0,
Definitely: 1
});
/** @typedef {typeof NodeExist[keyof typeof NodeExist]} NodeExistsValue */
const whitelist_attribute_selector = new Map([
['details', new Set(['open'])],
['dialog', new Set(['open'])]
]);
const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/;
export default class Selector {
/** @type {import('./private.js').CssNode} */
node;
/** @type {import('./Stylesheet.js').default} */
stylesheet;
/** @type {Block[]} */
blocks;
/** @type {Block[]} */
local_blocks;
/** @type {boolean} */
used;
/**
* @param {import('./private.js').CssNode} node
* @param {import('./Stylesheet.js').default} stylesheet
*/
constructor(node, stylesheet) {
this.node = node;
this.stylesheet = stylesheet;
this.blocks = group_selectors(node);
// take trailing :global(...) selectors out of consideration
let i = this.blocks.length;
while (i > 0) {
if (!this.blocks[i - 1].global) break;
i -= 1;
}
this.local_blocks = this.blocks.slice(0, i);
const host_only = this.blocks.length === 1 && this.blocks[0].host;
const root_only = this.blocks.length === 1 && this.blocks[0].root;
this.used = this.local_blocks.length === 0 || host_only || root_only;
}
/** @param {import('../nodes/Element.js').default} node */
apply(node) {
/** @type {Array<{ node: import('../nodes/Element.js').default; block: Block }>} */
const to_encapsulate = [];
apply_selector(this.local_blocks.slice(), node, to_encapsulate);
if (to_encapsulate.length > 0) {
to_encapsulate.forEach(({ node, block }) => {
this.stylesheet.nodes_with_css_class.add(node);
block.should_encapsulate = true;
});
this.used = true;
}
}
/** @param {import('magic-string').default} code */
minify(code) {
/** @type {number} */
let c = null;
this.blocks.forEach((block, i) => {
if (i > 0) {
if (block.start - c > 1) {
code.update(c, block.start, block.combinator.name || ' ');
}
}
c = block.end;
});
}
/**
* @param {import('magic-string').default} code
* @param {string} attr
* @param {number} max_amount_class_specificity_increased
*/
transform(code, attr, max_amount_class_specificity_increased) {
const amount_class_specificity_to_increase =
max_amount_class_specificity_increased -
this.blocks.filter((block) => block.should_encapsulate).length;
/** @param {import('./private.js').CssNode} selector */
function remove_global_pseudo_class(selector) {
const first = selector.children[0];
const last = selector.children[selector.children.length - 1];
code.remove(selector.start, first.start).remove(last.end, selector.end);
}
/**
* @param {Block} block
* @param {string} attr
*/
function encapsulate_block(block, attr) {
for (const selector of block.selectors) {
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
remove_global_pseudo_class(selector);
}
}
let i = block.selectors.length;
while (i--) {
const selector = block.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') {
if (selector.name !== 'root' && selector.name !== 'host') {
if (i === 0) code.prependRight(selector.start, attr);
}
continue;
}
if (selector.type === 'TypeSelector' && selector.name === '*') {
code.update(selector.start, selector.end, attr);
} else {
code.appendLeft(selector.end, attr);
}
break;
}
}
this.blocks.forEach((block, index) => {
if (block.global) {
remove_global_pseudo_class(block.selectors[0]);
}
if (block.should_encapsulate)
encapsulate_block(
block,
index === this.blocks.length - 1
? attr.repeat(amount_class_specificity_to_increase + 1)
: attr
);
});
}
/** @param {import('../Component.js').default} component */
validate(component) {
let start = 0;
let end = this.blocks.length;
for (; start < end; start += 1) {
if (!this.blocks[start].global) break;
}
for (; end > start; end -= 1) {
if (!this.blocks[end - 1].global) break;
}
for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) {
return component.error(this.blocks[i].selectors[0], compiler_errors.css_invalid_global);
}
}
this.validate_global_with_multiple_selectors(component);
this.validate_global_compound_selector(component);
this.validate_invalid_combinator_without_selector(component);
}
/** @param {import('../Component.js').default} component */
validate_global_with_multiple_selectors(component) {
if (this.blocks.length === 1 && this.blocks[0].selectors.length === 1) {
// standalone :global() with multiple selectors is OK
return;
}
for (const block of this.blocks) {
for (const selector of block.selectors) {
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
if (regex_is_single_css_selector.test(selector.children[0].value)) {
component.error(selector, compiler_errors.css_invalid_global_selector);
}
}
}
}
}
/** @param {import('../Component.js').default} component */
validate_invalid_combinator_without_selector(component) {
for (let i = 0; i < this.blocks.length; i++) {
const block = this.blocks[i];
if (block.combinator && block.selectors.length === 0) {
component.error(
this.node,
compiler_errors.css_invalid_selector(
component.source.slice(this.node.start, this.node.end)
)
);
}
if (!block.combinator && block.selectors.length === 0) {
component.error(
this.node,
compiler_errors.css_invalid_selector(
component.source.slice(this.node.start, this.node.end)
)
);
}
}
}
/** @param {import('../Component.js').default} component */
validate_global_compound_selector(component) {
for (const block of this.blocks) {
for (let index = 0; index < block.selectors.length; index++) {
const selector = block.selectors[index];
if (
selector.type === 'PseudoClassSelector' &&
selector.name === 'global' &&
index !== 0 &&
selector.children &&
selector.children.length > 0 &&
!/[.:#\s]/.test(selector.children[0].value[0])
) {
component.error(selector, compiler_errors.css_invalid_global_selector_position);
}
}
}
}
get_amount_class_specificity_increased() {
let count = 0;
for (const block of this.blocks) {
if (block.should_encapsulate) {
count++;
}
}
return count;
}
}
/**
* @param {Block[]} blocks
* @param {import('../nodes/Element.js').default} node
* @param {Array<{ node: import('../nodes/Element.js').default; block: Block }>} to_encapsulate
* @returns {boolean}
*/
function apply_selector(blocks, node, to_encapsulate) {
const block = blocks.pop();
if (!block) return false;
if (!node) {
return (
(block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0)
);
}
switch (block_might_apply_to_node(block, node)) {
case BlockAppliesToNode.NotPossible:
return false;
case BlockAppliesToNode.UnknownSelectorType:
// bail. TODO figure out what these could be
to_encapsulate.push({ node, block });
return true;
}
if (block.combinator) {
if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') {
for (const ancestor_block of blocks) {
if (ancestor_block.global) {
continue;
}
if (ancestor_block.host) {
to_encapsulate.push({ node, block });
return true;
}
let parent = node;
while ((parent = get_element_parent(parent))) {
if (
block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible
) {
to_encapsulate.push({ node: parent, block: ancestor_block });
}
}
if (to_encapsulate.length) {
to_encapsulate.push({ node, block });
return true;
}
}
if (blocks.every((block) => block.global)) {
to_encapsulate.push({ node, block });
return true;
}
return false;
} else if (block.combinator.name === '>') {
const has_global_parent = blocks.every((block) => block.global);
if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
to_encapsulate.push({ node, block });
return true;
}
return false;
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
let has_match = false;
// NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the
// css-tree limitation that does not parse the inner selector of :global
// so unless we are sure there will be no sibling to match, we will consider it as matched
const has_global = blocks.some((block) => block.global);
if (has_global) {
if (siblings.size === 0 && get_element_parent(node) !== null) {
return false;
}
to_encapsulate.push({ node, block });
return true;
}
for (const possible_sibling of siblings.keys()) {
if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
to_encapsulate.push({ node, block });
has_match = true;
}
}
return has_match;
}
// TODO other combinators
to_encapsulate.push({ node, block });
return true;
}
to_encapsulate.push({ node, block });
return true;
}
const regex_backslash_and_following_character = /\\(.)/g;
/**
* @param {Block} block
* @param {import('../nodes/Element.js').default} node
* @returns {typeof BlockAppliesToNode[keyof typeof BlockAppliesToNode]}
*/
function block_might_apply_to_node(block, node) {
let i = block.selectors.length;
while (i--) {
const selector = block.selectors[i];
const name =
typeof selector.name === 'string' &&
selector.name.replace(regex_backslash_and_following_character, '$1');
if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) {
return BlockAppliesToNode.NotPossible;
}
if (
block.selectors.length === 1 &&
selector.type === 'PseudoClassSelector' &&
name === 'global'
) {
return BlockAppliesToNode.NotPossible;
}
if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') {
continue;
}
if (selector.type === 'ClassSelector') {
if (
!attribute_matches(node, 'class', name, '~=', false) &&
!node.classes.some((c) => c.name === name)
)
return BlockAppliesToNode.NotPossible;
} else if (selector.type === 'IdSelector') {
if (!attribute_matches(node, 'id', name, '=', false)) return BlockAppliesToNode.NotPossible;
} else if (selector.type === 'AttributeSelector') {
if (
!(
whitelist_attribute_selector.has(node.name.toLowerCase()) &&
whitelist_attribute_selector
.get(node.name.toLowerCase())
.has(selector.name.name.toLowerCase())
) &&
!attribute_matches(
node,
selector.name.name,
selector.value && unquote(selector.value),
selector.matcher,
selector.flags
)
) {
return BlockAppliesToNode.NotPossible;
}
} else if (selector.type === 'TypeSelector') {
if (
node.name.toLowerCase() !== name.toLowerCase() &&
name !== '*' &&
!node.is_dynamic_element
)
return BlockAppliesToNode.NotPossible;
} else {
return BlockAppliesToNode.UnknownSelectorType;
}
}
return BlockAppliesToNode.Possible;
}
/**
* @param {any} operator
* @param {any} expected_value
* @param {any} case_insensitive
* @param {any} value
*/
function test_attribute(operator, expected_value, case_insensitive, value) {
if (case_insensitive) {
expected_value = expected_value.toLowerCase();
value = value.toLowerCase();
}
switch (operator) {
case '=':
return value === expected_value;
case '~=':
return value.split(/\s/).includes(expected_value);
case '|=':
return `${value}-`.startsWith(`${expected_value}-`);
case '^=':
return value.startsWith(expected_value);
case '$=':
return value.endsWith(expected_value);
case '*=':
return value.includes(expected_value);
default:
throw new Error("this shouldn't happen");
}
}
/**
* @param {import('./private.js').CssNode} node
* @param {string} name
* @param {string} expected_value
* @param {string} operator
* @param {boolean} case_insensitive
*/
function attribute_matches(node, name, expected_value, operator, case_insensitive) {
const spread = node.attributes.find((attr) => attr.type === 'Spread');
if (spread) return true;
if (node.bindings.some((binding) => binding.name === name)) return true;
const attr = node.attributes.find((attr) => attr.name === name);
if (!attr) return false;
if (attr.is_true) return operator === null;
if (expected_value == null) return true;
if (attr.chunks.length === 1) {
const value = attr.chunks[0];
if (!value) return false;
if (value.type === 'Text')
return test_attribute(operator, expected_value, case_insensitive, value.data);
}
const possible_values = new Set();
let prev_values = [];
for (const chunk of attr.chunks) {
const current_possible_values = new Set();
if (chunk.type === 'Text') {
current_possible_values.add(chunk.data);
} else {
gather_possible_values(chunk.node, current_possible_values);
}
// impossible to find out all combinations
if (current_possible_values.has(UNKNOWN)) return true;
if (prev_values.length > 0) {
const start_with_space = [];
const remaining = [];
current_possible_values.forEach((current_possible_value) => {
if (regex_starts_with_whitespace.test(current_possible_value)) {
start_with_space.push(current_possible_value);
} else {
remaining.push(current_possible_value);
}
});
if (remaining.length > 0) {
if (start_with_space.length > 0) {
prev_values.forEach((prev_value) => possible_values.add(prev_value));
}
const combined = [];
prev_values.forEach((prev_value) => {
remaining.forEach((value) => {
combined.push(prev_value + value);
});
});
prev_values = combined;
start_with_space.forEach((value) => {
if (regex_ends_with_whitespace.test(value)) {
possible_values.add(value);
} else {
prev_values.push(value);
}
});
continue;
} else {
prev_values.forEach((prev_value) => possible_values.add(prev_value));
prev_values = [];
}
}
current_possible_values.forEach((current_possible_value) => {
if (regex_ends_with_whitespace.test(current_possible_value)) {
possible_values.add(current_possible_value);
} else {
prev_values.push(current_possible_value);
}
});
if (prev_values.length < current_possible_values.size) {
prev_values.push(' ');
}
if (prev_values.length > 20) {
// might grow exponentially, bail out
return true;
}
}
prev_values.forEach((prev_value) => possible_values.add(prev_value));
if (possible_values.has(UNKNOWN)) return true;
for (const value of possible_values) {
if (test_attribute(operator, expected_value, case_insensitive, value)) return true;
}
return false;
}
/** @param {import('./private.js').CssNode} value */
function unquote(value) {
if (value.type === 'Identifier') return value.name;
const str = value.value;
if ((str[0] === str[str.length - 1] && str[0] === "'") || str[0] === '"') {
return str.slice(1, str.length - 1);
}
return str;
}
/**
* @param {import('../nodes/Element.js').default} node
* @returns {any}
*/
function get_element_parent(node) {
/** @type {import('../nodes/interfaces.js').INode} */
let parent = node;
while ((parent = parent.parent) && parent.type !== 'Element');
return /** @type {import('../nodes/Element.js').default | null} */ (parent);
}
/**
* Finds the given node's previous sibling in the DOM
*
* The Svelte <slot> is just a placeholder and is not actually real. Any children nodes
* in <slot> are 'flattened' and considered as the same level as the <slot>'s siblings
*
* e.g.
* <h1>Heading 1</h1>
* <slot>
* <h2>Heading 2</h2>
* </slot>
*
* is considered to look like:
* <h1>Heading 1</h1>
* <h2>Heading 2</h2>
* @param {import('../nodes/interfaces.js').INode} node
* @returns {import('../nodes/interfaces.js').INode}
*/
function find_previous_sibling(node) {
/** @type {import('../nodes/interfaces.js').INode} */
let current_node = node;
do {
if (current_node.type === 'Slot') {
const slot_children = current_node.children;
if (slot_children.length > 0) {
current_node = slot_children.slice(-1)[0]; // go to its last child first
continue;
}
}
while (!current_node.prev && current_node.parent && current_node.parent.type === 'Slot') {
current_node = current_node.parent;
}
current_node = current_node.prev;
} while (current_node && current_node.type === 'Slot');
return current_node;
}
/**
* @param {import('../nodes/interfaces.js').INode} node
* @param {boolean} adjacent_only
* @returns {Map<import('../nodes/Element.js').default, NodeExistsValue>}
*/
function get_possible_element_siblings(node, adjacent_only) {
/** @type {Map<import('../nodes/Element.js').default, NodeExistsValue>} */
const result = new Map();
/** @type {import('../nodes/interfaces.js').INode} */
let prev = node;
while ((prev = find_previous_sibling(prev))) {
if (prev.type === 'Element') {
if (
!prev.attributes.find(
(attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot'
)
) {
result.set(prev, NodeExist.Definitely);
}
if (adjacent_only) {
break;
}
} else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
const possible_last_child = get_possible_last_child(prev, adjacent_only);
add_to_map(possible_last_child, result);
if (adjacent_only && has_definite_elements(possible_last_child)) {
return result;
}
}
}
if (!prev || !adjacent_only) {
/** @type {import('../nodes/interfaces.js').INode} */
let parent = node;
let skip_each_for_last_child = node.type === 'ElseBlock';
while (
(parent = parent.parent) &&
(parent.type === 'EachBlock' ||
parent.type === 'IfBlock' ||
parent.type === 'ElseBlock' ||
parent.type === 'AwaitBlock')
) {
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
add_to_map(possible_siblings, result);
if (parent.type === 'EachBlock') {
// first child of each block can select the last child of each block as previous sibling
if (skip_each_for_last_child) {
skip_each_for_last_child = false;
} else {
add_to_map(get_possible_last_child(parent, adjacent_only), result);
}
} else if (parent.type === 'ElseBlock') {
skip_each_for_last_child = true;
parent = parent.parent;
}
if (adjacent_only && has_definite_elements(possible_siblings)) {
break;
}
}
}
return result;
}
/**
* @param {import('../nodes/EachBlock.js').default | import('../nodes/IfBlock.js').default | import('../nodes/AwaitBlock.js').default} block
* @param {boolean} adjacent_only
* @returns {Map<import('../nodes/Element.js').default, NodeExistsValue>}
*/
function get_possible_last_child(block, adjacent_only) {
/** @typedef {Map<import('../nodes/Element.js').default, NodeExistsValue>} NodeMap */
/** @type {NodeMap} */
const result = new Map();
if (block.type === 'EachBlock') {
/** @type {NodeMap} */
const each_result = loop_child(block.children, adjacent_only);
/** @type {NodeMap} */
const else_result = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
const not_exhaustive = !has_definite_elements(else_result);
if (not_exhaustive) {
mark_as_probably(each_result);
mark_as_probably(else_result);
}
add_to_map(each_result, result);
add_to_map(else_result, result);
} else if (block.type === 'IfBlock') {
/** @type {NodeMap} */
const if_result = loop_child(block.children, adjacent_only);
/** @type {NodeMap} */
const else_result = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
if (not_exhaustive) {
mark_as_probably(if_result);
mark_as_probably(else_result);
}
add_to_map(if_result, result);
add_to_map(else_result, result);
} else if (block.type === 'AwaitBlock') {
/** @type {NodeMap} */
const pending_result = block.pending
? loop_child(block.pending.children, adjacent_only)
: new Map();
/** @type {NodeMap} */
const then_result = block.then ? loop_child(block.then.children, adjacent_only) : new Map();
/** @type {NodeMap} */
const catch_result = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map();
const not_exhaustive =
!has_definite_elements(pending_result) ||
!has_definite_elements(then_result) ||
!has_definite_elements(catch_result);
if (not_exhaustive) {
mark_as_probably(pending_result);
mark_as_probably(then_result);
mark_as_probably(catch_result);
}
add_to_map(pending_result, result);
add_to_map(then_result, result);
add_to_map(catch_result, result);
}
return result;
}
/**
* @param {Map<import('../nodes/Element.js').default, NodeExistsValue>} result
* @returns {boolean}
*/
function has_definite_elements(result) {
if (result.size === 0) return false;
for (const exist of result.values()) {
if (exist === NodeExist.Definitely) {
return true;
}
}
return false;
}
/**
* @param {Map<import('../nodes/Element.js').default, NodeExistsValue>} from
* @param {Map<import('../nodes/Element.js').default, NodeExistsValue>} to
* @returns {void}
*/
function add_to_map(from, to) {
from.forEach((exist, element) => {
to.set(element, higher_existence(exist, to.get(element)));
});
}
/**
* @param {NodeExistsValue | null} exist1
* @param {NodeExistsValue | null} exist2
* @returns {NodeExistsValue}
*/
function higher_existence(exist1, exist2) {
if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
return exist1 > exist2 ? exist1 : exist2;
}
/** @param {Map<import('../nodes/Element.js').default, NodeExistsValue>} result */
function mark_as_probably(result) {
for (const key of result.keys()) {
result.set(key, NodeExist.Probably);
}
}
/**
* @param {import('../nodes/interfaces.js').INode[]} children
* @param {boolean} adjacent_only
*/
function loop_child(children, adjacent_only) {
/** @type {Map<import('../nodes/Element.js').default, NodeExistsValue>} */
const result = new Map();
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
if (child.type === 'Element') {
result.set(child, NodeExist.Definitely);
if (adjacent_only) {
break;
}
} else if (
child.type === 'EachBlock' ||
child.type === 'IfBlock' ||
child.type === 'AwaitBlock'
) {
const child_result = get_possible_last_child(child, adjacent_only);
add_to_map(child_result, result);
if (adjacent_only && has_definite_elements(child_result)) {
break;
}
}
}
return result;
}
class Block {
/** @type {boolean} */
host;
/** @type {boolean} */
root;
/** @type {import('./private.js').CssNode} */
combinator;
/** @type {import('./private.js').CssNode[]} */
selectors;
/** @type {number} */
start;
/** @type {number} */
end;
/** @type {boolean} */
should_encapsulate;
/** @param {import('./private.js').CssNode} combinator */
constructor(combinator) {
this.combinator = combinator;
this.host = false;
this.root = false;
this.selectors = [];
this.start = null;
this.end = null;
this.should_encapsulate = false;
}
/** @param {import('./private.js').CssNode} selector */
add(selector) {
if (this.selectors.length === 0) {
this.start = selector.start;
this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host';
}
this.root = this.root || (selector.type === 'PseudoClassSelector' && selector.name === 'root');
this.selectors.push(selector);
this.end = selector.end;
}
get global() {
return (
this.selectors.length >= 1 &&
this.selectors[0].type === 'PseudoClassSelector' &&
this.selectors[0].name === 'global' &&
this.selectors.every(
(selector) =>
selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector'
)
);
}
}
/** @param {import('./private.js').CssNode} selector */
function group_selectors(selector) {
/** @type {Block} */
let block = new Block(null);
const blocks = [block];
selector.children.forEach((child) => {
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
block = new Block(child);
blocks.push(block);
} else {
block.add(child);
}
});
return blocks;
}

@ -1,514 +0,0 @@
import MagicString from 'magic-string';
import { walk } from 'estree-walker';
import Selector from './Selector.js';
import hash from '../utils/hash.js';
import compiler_warnings from '../compiler_warnings.js';
import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore.js';
import { push_array } from '../../utils/push_array.js';
import { regex_only_whitespaces, regex_whitespace } from '../../utils/patterns.js';
const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/;
/**
* @param {string} name
* @returns {string}
*/
function remove_css_prefix(name) {
return name.replace(regex_css_browser_prefix, '');
}
/** @param {import('./private.js').CssNode} node */
const is_keyframes_node = (node) => remove_css_prefix(node.name) === 'keyframes';
/**
* @param {import('./private.js').CssNode} param
* @returns {true}
*/
const at_rule_has_declaration = ({ block }) =>
block && block.children && block.children.find((node) => node.type === 'Declaration');
/**
* @param {import('magic-string').default} code
* @param {number} start
* @param {Declaration[]} declarations
* @returns {number}
*/
function minify_declarations(code, start, declarations) {
let c = start;
declarations.forEach((declaration, i) => {
const separator = i > 0 ? ';' : '';
if (declaration.node.start - c > separator.length) {
code.update(c, declaration.node.start, separator);
}
declaration.minify(code);
c = declaration.node.end;
});
return c;
}
class Rule {
/** @type {import('./Selector.js').default[]} */
selectors;
/** @type {Declaration[]} */
declarations;
/** @type {import('./private.js').CssNode} */
node;
/** @type {Atrule} */
parent;
/**
* @param {import('./private.js').CssNode} node
* @param {any} stylesheet
* @param {Atrule} [parent]
*/
constructor(node, stylesheet, parent) {
this.node = node;
this.parent = parent;
this.selectors = node.prelude.children.map((node) => new Selector(node, stylesheet));
this.declarations = node.block.children.map((node) => new Declaration(node));
}
/** @param {import('../nodes/Element.js').default} node */
apply(node) {
this.selectors.forEach((selector) => selector.apply(node)); // TODO move the logic in here?
}
/** @param {boolean} dev */
is_used(dev) {
if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node))
return true;
if (this.declarations.length === 0) return dev;
return this.selectors.some((s) => s.used);
}
/**
* @param {import('magic-string').default} code
* @param {boolean} _dev
*/
minify(code, _dev) {
let c = this.node.start;
let started = false;
this.selectors.forEach((selector) => {
if (selector.used) {
const separator = started ? ',' : '';
if (selector.node.start - c > separator.length) {
code.update(c, selector.node.start, separator);
}
selector.minify(code);
c = selector.node.end;
started = true;
}
});
code.remove(c, this.node.block.start);
c = this.node.block.start + 1;
c = minify_declarations(code, c, this.declarations);
code.remove(c, this.node.block.end - 1);
}
/**
* @param {import('magic-string').default} code
* @param {string} id
* @param {Map<string, string>} keyframes
* @param {number} max_amount_class_specificity_increased
*/
transform(code, id, keyframes, max_amount_class_specificity_increased) {
if (this.parent && this.parent.node.type === 'Atrule' && is_keyframes_node(this.parent.node))
return true;
const attr = `.${id}`;
this.selectors.forEach((selector) =>
selector.transform(code, attr, max_amount_class_specificity_increased)
);
this.declarations.forEach((declaration) => declaration.transform(code, keyframes));
}
/** @param {import('../Component.js').default} component */
validate(component) {
this.selectors.forEach((selector) => {
selector.validate(component);
});
}
/** @param {(selector: import('./Selector.js').default) => void} handler */
warn_on_unused_selector(handler) {
this.selectors.forEach((selector) => {
if (!selector.used) handler(selector);
});
}
get_max_amount_class_specificity_increased() {
return Math.max(
...this.selectors.map((selector) => selector.get_amount_class_specificity_increased())
);
}
}
class Declaration {
/** @type {import('./private.js').CssNode} */
node;
/** @param {import('./private.js').CssNode} node */
constructor(node) {
this.node = node;
}
/**
* @param {import('magic-string').default} code
* @param {Map<string, string>} keyframes
*/
transform(code, keyframes) {
const property = this.node.property && remove_css_prefix(this.node.property.toLowerCase());
if (property === 'animation' || property === 'animation-name') {
this.node.value.children.forEach((block) => {
if (block.type === 'Identifier') {
const name = block.name;
if (keyframes.has(name)) {
code.update(block.start, block.end, keyframes.get(name));
}
}
});
}
}
/** @param {import('magic-string').default} code */
minify(code) {
if (!this.node.property) return; // @apply, and possibly other weird cases?
const c = this.node.start + this.node.property.length;
const first = this.node.value.children ? this.node.value.children[0] : this.node.value;
// Don't minify whitespace in custom properties, since some browsers (Chromium < 99)
// treat --foo: ; and --foo:; differently
if (first.type === 'Raw' && regex_only_whitespaces.test(first.value)) return;
let start = first.start;
while (regex_whitespace.test(code.original[start])) start += 1;
if (start - c > 1) {
code.update(c, start, ':');
}
}
}
class Atrule {
/** @type {import('./private.js').CssNode} */
node;
/** @type {Array<Atrule | Rule>} */
children;
/** @type {Declaration[]} */
declarations;
/** @param {import('./private.js').CssNode} node */
constructor(node) {
this.node = node;
this.children = [];
this.declarations = [];
}
/** @param {import('../nodes/Element.js').default} node */
apply(node) {
if (
this.node.name === 'container' ||
this.node.name === 'media' ||
this.node.name === 'supports' ||
this.node.name === 'layer'
) {
this.children.forEach((child) => {
child.apply(node);
});
} else if (is_keyframes_node(this.node)) {
this.children.forEach((/** @type {Rule} */ rule) => {
rule.selectors.forEach((selector) => {
selector.used = true;
});
});
}
}
/** @param {boolean} _dev */
is_used(_dev) {
return true; // TODO
}
/**
* @param {import('magic-string').default} code
* @param {boolean} dev
*/
minify(code, dev) {
if (this.node.name === 'media') {
const expression_char = code.original[this.node.prelude.start];
let c = this.node.start + (expression_char === '(' ? 6 : 7);
if (this.node.prelude.start > c) code.remove(c, this.node.prelude.start);
this.node.prelude.children.forEach((query) => {
// TODO minify queries
c = query.end;
});
code.remove(c, this.node.block.start);
} else if (this.node.name === 'supports') {
let c = this.node.start + 9;
if (this.node.prelude.start - c > 1) code.update(c, this.node.prelude.start, ' ');
this.node.prelude.children.forEach((query) => {
// TODO minify queries
c = query.end;
});
code.remove(c, this.node.block.start);
} else {
let c = this.node.start + this.node.name.length + 1;
if (this.node.prelude) {
if (this.node.prelude.start - c > 1) code.update(c, this.node.prelude.start, ' ');
c = this.node.prelude.end;
}
if (this.node.block && this.node.block.start - c > 0) {
code.remove(c, this.node.block.start);
}
}
// TODO other atrules
if (this.node.block) {
let c = this.node.block.start + 1;
if (this.declarations.length) {
c = minify_declarations(code, c, this.declarations);
// if the atrule has children, leave the last declaration semicolon alone
if (this.children.length) c++;
}
this.children.forEach((child) => {
if (child.is_used(dev)) {
code.remove(c, child.node.start);
child.minify(code, dev);
c = child.node.end;
}
});
code.remove(c, this.node.block.end - 1);
}
}
/**
* @param {import('magic-string').default} code
* @param {string} id
* @param {Map<string, string>} keyframes
* @param {number} max_amount_class_specificity_increased
*/
transform(code, id, keyframes, max_amount_class_specificity_increased) {
if (is_keyframes_node(this.node)) {
this.node.prelude.children.forEach(({ type, name, start, end }) => {
if (type === 'Identifier') {
if (name.startsWith('-global-')) {
code.remove(start, start + 8);
this.children.forEach((/** @type {Rule} */ rule) => {
rule.selectors.forEach((selector) => {
selector.used = true;
});
});
} else {
code.update(start, end, keyframes.get(name));
}
}
});
}
this.children.forEach((child) => {
child.transform(code, id, keyframes, max_amount_class_specificity_increased);
});
}
/** @param {import('../Component.js').default} component */
validate(component) {
this.children.forEach((child) => {
child.validate(component);
});
}
/** @param {(selector: import('./Selector.js').default) => void} handler */
warn_on_unused_selector(handler) {
if (this.node.name !== 'media') return;
this.children.forEach((child) => {
child.warn_on_unused_selector(handler);
});
}
get_max_amount_class_specificity_increased() {
return Math.max(
...this.children.map((rule) => rule.get_max_amount_class_specificity_increased())
);
}
}
/** @param {any} params */
const get_default_css_hash = ({ css, hash }) => {
return `svelte-${hash(css)}`;
};
export default class Stylesheet {
/** @type {string} */
source;
/** @type {import('../../interfaces.js').Ast} */
ast;
/** @type {string} */
filename;
/** @type {boolean} */
dev;
/** @type {boolean} */
has_styles;
/** @type {string} */
id;
/** @type {Array<Rule | Atrule>} */
children = [];
/** @type {Map<string, string>} */
keyframes = new Map();
/** @type {Set<import('./private.js').CssNode>} */
nodes_with_css_class = new Set();
/**
* @param {{
* source: string;
* ast: import('../../interfaces.js').Ast;
* filename: string | undefined;
* component_name: string | undefined;
* dev: boolean;
* get_css_hash: import('../../interfaces.js').CssHashGetter;
* }} params
*/
constructor({ source, ast, component_name, filename, dev, get_css_hash = get_default_css_hash }) {
this.source = source;
this.ast = ast;
this.filename = filename;
this.dev = dev;
if (ast.css && ast.css.children.length) {
this.id = get_css_hash({
filename,
name: component_name,
css: ast.css.content.styles,
hash
});
this.has_styles = true;
/** @type {Atrule[]} */
const stack = [];
let depth = 0;
/** @type {Atrule} */
let current_atrule = null;
walk(/** @type {any} */ (ast.css), {
enter: (/** @type {any} */ node) => {
if (node.type === 'Atrule') {
const atrule = new Atrule(node);
stack.push(atrule);
if (current_atrule) {
current_atrule.children.push(atrule);
} else if (depth <= 1) {
this.children.push(atrule);
}
if (is_keyframes_node(node)) {
node.prelude.children.forEach((expression) => {
if (expression.type === 'Identifier' && !expression.name.startsWith('-global-')) {
this.keyframes.set(expression.name, `${this.id}-${expression.name}`);
}
});
} else if (at_rule_has_declaration(node)) {
const at_rule_declarations = node.block.children
.filter((node) => node.type === 'Declaration')
.map((node) => new Declaration(node));
push_array(atrule.declarations, at_rule_declarations);
}
current_atrule = atrule;
}
if (node.type === 'Rule') {
const rule = new Rule(node, this, current_atrule);
if (current_atrule) {
current_atrule.children.push(rule);
} else if (depth <= 1) {
this.children.push(rule);
}
}
depth += 1;
},
leave: (/** @type {any} */ node) => {
if (node.type === 'Atrule') {
stack.pop();
current_atrule = stack[stack.length - 1];
}
depth -= 1;
}
});
} else {
this.has_styles = false;
}
}
/** @param {import('../nodes/Element.js').default} node */
apply(node) {
if (!this.has_styles) return;
for (let i = 0; i < this.children.length; i += 1) {
const child = this.children[i];
child.apply(node);
}
}
reify() {
this.nodes_with_css_class.forEach((node) => {
node.add_css_class();
});
}
/** @param {string} file */
render(file) {
if (!this.has_styles) {
return { code: null, map: null };
}
const code = new MagicString(this.source);
walk(/** @type {any} */ (this.ast.css), {
enter: (/** @type {any} */ node) => {
code.addSourcemapLocation(node.start);
code.addSourcemapLocation(node.end);
}
});
const max = Math.max(
...this.children.map((rule) => rule.get_max_amount_class_specificity_increased())
);
this.children.forEach((child) => {
child.transform(code, this.id, this.keyframes, max);
});
let c = 0;
this.children.forEach((child) => {
if (child.is_used(this.dev)) {
code.remove(c, child.node.start);
child.minify(code, this.dev);
c = child.node.end;
}
});
code.remove(c, this.source.length);
return {
code: code.toString(),
map: code.generateMap({
includeContent: true,
source: this.filename,
file
})
};
}
/** @param {import('../Component.js').default} component */
validate(component) {
this.children.forEach((child) => {
child.validate(component);
});
}
/** @param {import('../Component.js').default} component */
warn_on_unused_selectors(component) {
const ignores = !this.ast.css
? []
: extract_ignores_above_position(this.ast.css.start, this.ast.html.children);
component.push_ignores(ignores);
this.children.forEach((child) => {
child.warn_on_unused_selector((selector) => {
component.warn(
selector.node,
compiler_warnings.css_unused_selector(
this.source.slice(selector.node.start, selector.node.end)
)
);
});
});
component.pop_ignores();
}
}

@ -1,16 +0,0 @@
export const UNKNOWN = {};
/**
* @param {import("estree").Node} node
* @param {Set<string | {}>} set
*/
export function gather_possible_values(node, set) {
if (node.type === 'Literal') {
set.add(node.value);
} else if (node.type === 'ConditionalExpression') {
gather_possible_values(node.consequent, set);
gather_possible_values(node.alternate, set);
} else {
set.add(UNKNOWN);
}
}

@ -1,6 +0,0 @@
export interface CssNode {
type: string;
start: number;
end: number;
[prop_name: string]: any;
}

@ -1,155 +0,0 @@
import Stats from '../Stats.js';
import parse from '../parse/index.js';
import render_dom from './render_dom/index.js';
import render_ssr from './render_ssr/index.js';
import Component from './Component.js';
import fuzzymatch from '../utils/fuzzymatch.js';
import get_name_from_filename from './utils/get_name_from_filename.js';
import { valid_namespaces } from '../utils/namespaces.js';
const valid_options = [
'name',
'filename',
'sourcemap',
'enableSourcemap',
'generate',
'errorMode',
'varsReport',
'outputFilename',
'cssOutputFilename',
'sveltePath',
'dev',
'accessors',
'immutable',
'hydratable',
'legacy',
'customElement',
'namespace',
'tag',
'css',
'loopGuardTimeout',
'preserveComments',
'preserveWhitespace',
'cssHash',
'discloseVersion'
];
const valid_css_values = [true, false, 'injected', 'external', 'none'];
const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
const regex_starts_with_lowercase_character = /^[a-z]/;
let warned_of_format = false;
let warned_boolean_css = false;
/**
* @param {import('../interfaces.js').CompileOptions} options
* @param {import('../interfaces.js').Warning[]} warnings
*/
function validate_options(options, warnings) {
if (/** @type {any} */ (options).format) {
if (!warned_of_format) {
warned_of_format = true;
console.warn(
'The format option has been removed in Svelte 4, the compiler only outputs ESM now. Remove "format" from your compiler options. ' +
'If you did not set this yourself, bump the version of your bundler plugin (vite-plugin-svelte/rollup-plugin-svelte/svelte-loader)'
);
}
delete (/** @type {any} */ (options).format);
}
const { name, filename, loopGuardTimeout, dev, namespace, css } = options;
Object.keys(options).forEach((key) => {
if (!valid_options.includes(key)) {
const match = fuzzymatch(key, valid_options);
let message = `Unrecognized option '${key}'`;
if (match) message += ` (did you mean '${match}'?)`;
throw new Error(message);
}
});
if (name && !regex_valid_identifier.test(name)) {
throw new Error(`options.name must be a valid identifier (got '${name}')`);
}
if (name && regex_starts_with_lowercase_character.test(name)) {
const message = 'options.name should be capitalised';
warnings.push({
code: 'options-lowercase-name',
message,
filename,
toString: () => message
});
}
if (loopGuardTimeout && !dev) {
const message = 'options.loopGuardTimeout is for options.dev = true only';
warnings.push({
code: 'options-loop-guard-timeout',
message,
filename,
toString: () => message
});
}
if (css === true || css === false) {
options.css = css === true ? 'injected' : 'external';
if (!warned_boolean_css) {
console.warn(
`compilerOptions.css as a boolean is deprecated. Use '${options.css}' instead of ${css}.`
);
warned_boolean_css = true;
}
}
if (!valid_css_values.includes(options.css)) {
throw new Error(
`compilerOptions.css must be 'injected', 'external' or 'none' (got '${options.css}').`
);
}
if (namespace && valid_namespaces.indexOf(namespace) === -1) {
const match = fuzzymatch(namespace, valid_namespaces);
if (match) {
throw new Error(`Invalid namespace '${namespace}' (did you mean '${match}'?)`);
} else {
throw new Error(`Invalid namespace '${namespace}'`);
}
}
if (options.discloseVersion == undefined) {
options.discloseVersion = true;
}
}
/**
* `compile` takes your component source code, and turns it into a JavaScript module that exports a class.
*
* https://svelte.dev/docs/svelte-compiler#svelte-compile
* @param {string} source
* @param {import('../interfaces.js').CompileOptions} options
*/
export default function compile(source, options = {}) {
options = Object.assign(
{ generate: 'dom', dev: false, enableSourcemap: true, css: 'injected' },
options
);
const stats = new Stats();
const warnings = [];
validate_options(options, warnings);
stats.start('parse');
const ast = parse(source, options);
stats.stop('parse');
stats.start('create component');
const component = new Component(
ast,
source,
options.name || get_name_from_filename(options.filename) || 'Component',
options,
stats,
warnings
);
stats.stop('create component');
const result =
options.generate === false
? null
: options.generate === 'ssr'
? render_ssr(component, options)
: render_dom(component, options);
return component.generate(result);
}

@ -1,2 +0,0 @@
// This file is automatically generated
export default new Set(["HtmlTag","HtmlTagHydration","ResizeObserverSingleton","SvelteComponent","SvelteComponentDev","SvelteComponentTyped","SvelteElement","action_destroyer","add_attribute","add_classes","add_flush_callback","add_iframe_resize_listener","add_location","add_render_callback","add_styles","add_transform","afterUpdate","append","append_dev","append_empty_stylesheet","append_hydration","append_hydration_dev","append_styles","assign","attr","attr_dev","attribute_to_object","beforeUpdate","bind","binding_callbacks","blank_object","bubble","check_outros","children","claim_comment","claim_component","claim_element","claim_html_tag","claim_space","claim_svg_element","claim_text","clear_loops","comment","component_subscribe","compute_rest_props","compute_slots","construct_svelte_component","construct_svelte_component_dev","contenteditable_truthy_values","createEventDispatcher","create_animation","create_bidirectional_transition","create_component","create_custom_element","create_in_transition","create_out_transition","create_slot","create_ssr_component","current_component","custom_event","dataset_dev","debug","destroy_block","destroy_component","destroy_each","detach","detach_after_dev","detach_before_dev","detach_between_dev","detach_dev","dirty_components","dispatch_dev","each","element","element_is","empty","end_hydrating","ensure_array_like","ensure_array_like_dev","escape","escape_attribute_value","escape_object","exclude_internal_props","fix_and_destroy_block","fix_and_outro_and_destroy_block","fix_position","flush","flush_render_callbacks","getAllContexts","getContext","get_all_dirty_from_scope","get_binding_group_value","get_current_component","get_custom_elements_slots","get_root_for_style","get_slot_changes","get_spread_object","get_spread_update","get_store_value","get_svelte_dataset","globals","group_outros","handle_promise","hasContext","has_prop","head_selector","identity","init","init_binding_group","init_binding_group_dynamic","insert","insert_dev","insert_hydration","insert_hydration_dev","intros","invalid_attribute_name_character","is_client","is_crossorigin","is_empty","is_function","is_promise","is_void","listen","listen_dev","loop","loop_guard","merge_ssr_styles","missing_component","mount_component","noop","not_equal","now","null_to_empty","object_without_properties","onDestroy","onMount","once","outro_and_destroy_block","prevent_default","prop_dev","query_selector_all","raf","resize_observer_border_box","resize_observer_content_box","resize_observer_device_pixel_content_box","run","run_all","safe_not_equal","schedule_update","select_multiple_value","select_option","select_options","select_value","self","setContext","set_attributes","set_current_component","set_custom_element_data","set_custom_element_data_map","set_data","set_data_contenteditable","set_data_contenteditable_dev","set_data_dev","set_data_maybe_contenteditable","set_data_maybe_contenteditable_dev","set_dynamic_element_data","set_input_type","set_input_value","set_now","set_raf","set_store_value","set_style","set_svg_attributes","space","split_css_unit","spread","src_url_equal","srcset_url_equal","start_hydrating","stop_immediate_propagation","stop_propagation","subscribe","svg_element","text","tick","time_ranges_to_array","to_number","toggle_class","transition_in","transition_out","trusted","update_await_block_branch","update_keyed_each","update_slot","update_slot_base","validate_component","validate_dynamic_element","validate_each_keys","validate_slots","validate_store","validate_void_dynamic_element","xlink_attr"]);

@ -1,36 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
/** @extends Node<'Action'> */
export default class Action extends Node {
/** @type {string} */
name;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {boolean} */
uses_context;
/** @type {import('./shared/TemplateScope.js').default} */
template_scope;
/**
* @param {import('../Component.js').default} component *
* @param {import('./shared/Node.js').default} parent *
* @param {import('./shared/TemplateScope.js').default} scope *
* @param {import('../../interfaces.js').Directive} info undefined
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
const object = info.name.split('.')[0];
component.warn_if_undefined(object, info, scope);
this.name = info.name;
component.add_reference(/** @type {any} */ (this), object);
this.expression = info.expression
? new Expression(component, this, scope, info.expression)
: null;
this.template_scope = scope;
this.uses_context = this.expression && this.expression.uses_context;
}
}

@ -1,43 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
import compiler_errors from '../compiler_errors.js';
/** @extends Node<'Animation'> */
export default class Animation extends Node {
/** @type {string} */
name;
/** @type {import('./shared/Expression.js').default} */
expression;
/**
* @param {import('../Component.js').default} component *
* @param {import('./Element.js').default} parent *
* @param {import('./shared/TemplateScope.js').default} scope *
* @param {import('../../interfaces.js').TemplateNode} info undefined
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
component.warn_if_undefined(info.name, info, scope);
this.name = info.name;
component.add_reference(/** @type {any} */ (this), info.name.split('.')[0]);
if (parent.animation) {
component.error(this, compiler_errors.duplicate_animation);
return;
}
const block = parent.parent;
if (!block || block.type !== 'EachBlock') {
// TODO can we relax the 'immediate child' rule?
component.error(this, compiler_errors.invalid_animation_immediate);
return;
}
if (!block.key) {
component.error(this, compiler_errors.invalid_animation_key);
return;
}
/** @type {import('./EachBlock.js').default} */ (block).has_animation = true;
this.expression = info.expression
? new Expression(component, this, scope, info.expression, true)
: null;
}
}

@ -1,136 +0,0 @@
import { string_literal } from '../utils/stringify.js';
import add_to_set from '../utils/add_to_set.js';
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
import { x } from 'code-red';
import compiler_warnings from '../compiler_warnings.js';
/** @extends Node<'Attribute' | 'Spread', import('./Element.js').default> */
export default class Attribute extends Node {
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {string} */
name;
/** @type {boolean} */
is_spread;
/** @type {boolean} */
is_true;
/** @type {boolean} */
is_static;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {Array<import('./Text.js').default | import('./shared/Expression.js').default>} */
chunks;
/** @type {Set<string>} */
dependencies;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope;
if (info.type === 'Spread') {
this.name = null;
this.is_spread = true;
this.is_true = false;
this.expression = new Expression(component, this, scope, info.expression);
this.dependencies = this.expression.dependencies;
this.chunks = null;
this.is_static = false;
} else {
this.name = info.name;
this.is_true = info.value === true;
this.is_static = true;
this.dependencies = new Set();
this.chunks = this.is_true
? []
: info.value.map((node) => {
if (node.type === 'Text') return node;
this.is_static = false;
const expression = new Expression(component, this, scope, node.expression);
add_to_set(this.dependencies, expression.dependencies);
return expression;
});
}
if (this.dependencies.size > 0) {
parent.cannot_use_innerhtml();
parent.not_static_content();
}
// TODO Svelte 5: Think about moving this into the parser and make it an error
if (
this.name &&
this.name.includes(':') &&
!this.name.startsWith('xmlns:') &&
!this.name.startsWith('xlink:') &&
!this.name.startsWith('xml:')
) {
component.warn(this, compiler_warnings.illegal_attribute_character);
}
}
get_dependencies() {
if (this.is_spread) return this.expression.dynamic_dependencies();
/** @type {Set<string>} */
const dependencies = new Set();
this.chunks.forEach((chunk) => {
if (chunk.type === 'Expression') {
add_to_set(dependencies, chunk.dynamic_dependencies());
}
});
return Array.from(dependencies);
}
/** @param {any} block */
get_value(block) {
if (this.is_true) return x`true`;
if (this.chunks.length === 0) return x`""`;
if (this.chunks.length === 1) {
return this.chunks[0].type === 'Text'
? string_literal(/** @type {import('./Text.js').default} */ (this.chunks[0]).data)
: /** @type {import('./shared/Expression.js').default} */ (this.chunks[0]).manipulate(
block
);
}
let expression = this.chunks
.map(
/** @param {any} chunk */ (chunk) =>
chunk.type === 'Text' ? string_literal(chunk.data) : chunk.manipulate(block)
)
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
if (this.chunks[0].type !== 'Text') {
expression = x`"" + ${expression}`;
}
return expression;
}
get_static_value() {
if (!this.is_static) return null;
return this.is_true
? true
: this.chunks[0]
? // method should be called only when `is_static = true`
/** @type {import('./Text.js').default} */ (this.chunks[0]).data
: '';
}
should_cache() {
return this.is_static
? false
: this.chunks.length === 1
? // @ts-ignore todo: probably error
this.chunks[0].node.type !== 'Identifier' || this.scope.names.has(this.chunks[0].node.name)
: true;
}
}

@ -1,74 +0,0 @@
import Node from './shared/Node.js';
import PendingBlock from './PendingBlock.js';
import ThenBlock from './ThenBlock.js';
import CatchBlock from './CatchBlock.js';
import Expression from './shared/Expression.js';
import { unpack_destructuring } from './shared/Context.js';
/** @extends Node<'AwaitBlock'> */
export default class AwaitBlock extends Node {
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('./shared/Context.js').Context[]} */
then_contexts;
/** @type {import('./shared/Context.js').Context[]} */
catch_contexts;
/** @type {import('estree').Node | null} */
then_node;
/** @type {import('estree').Node | null} */
catch_node;
/** @type {import('./PendingBlock.js').default} */
pending;
/** @type {import('./ThenBlock.js').default} */
then;
/** @type {import('./CatchBlock.js').default} */
catch;
/** @type {Map<string, import('estree').Node>} */
context_rest_properties = new Map();
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();
this.expression = new Expression(component, this, scope, info.expression);
this.then_node = info.value;
this.catch_node = info.error;
if (this.then_node) {
this.then_contexts = [];
unpack_destructuring({
contexts: this.then_contexts,
node: info.value,
scope,
component,
context_rest_properties: this.context_rest_properties
});
}
if (this.catch_node) {
this.catch_contexts = [];
unpack_destructuring({
contexts: this.catch_contexts,
node: info.error,
scope,
component,
context_rest_properties: this.context_rest_properties
});
}
this.pending = new PendingBlock(component, this, scope, info.pending);
this.then = new ThenBlock(component, this, scope, info.then);
this.catch = new CatchBlock(component, this, scope, info.catch);
}
}

@ -1,132 +0,0 @@
import Node from './shared/Node.js';
import get_object from '../utils/get_object.js';
import Expression from './shared/Expression.js';
import { regex_dimensions, regex_box_size } from '../../utils/patterns.js';
import { clone } from '../../utils/clone.js';
import compiler_errors from '../compiler_errors.js';
import compiler_warnings from '../compiler_warnings.js';
// TODO this should live in a specific binding
const read_only_media_attributes = new Set([
'duration',
'buffered',
'seekable',
'played',
'seeking',
'ended',
'videoHeight',
'videoWidth',
'naturalWidth',
'naturalHeight',
'readyState'
]);
/** @extends Node<'Binding'> */
export default class Binding extends Node {
/** @type {string} */
name;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('estree').Node} */
raw_expression; // TODO exists only for bind:this — is there a more elegant solution?
/** @type {boolean} */
is_contextual;
/** @type {boolean} */
is_readonly;
/**
* @param {import('../Component.js').default} component
* @param {import('./Element.js').default | import('./InlineComponent.js').default | import('./Window.js').default | import('./Document.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
if (info.expression.type !== 'Identifier' && info.expression.type !== 'MemberExpression') {
component.error(info, compiler_errors.invalid_directive_value);
return;
}
this.name = info.name;
this.expression = new Expression(component, this, scope, info.expression);
this.raw_expression = clone(info.expression);
const { name } = get_object(this.expression.node);
this.is_contextual = Array.from(this.expression.references).some((name) =>
scope.names.has(name)
);
if (this.is_contextual) this.validate_binding_rest_properties(scope);
// make sure we track this as a mutable ref
if (scope.is_let(name)) {
component.error(this, compiler_errors.invalid_binding_let);
return;
} else if (scope.names.has(name)) {
if (scope.is_await(name)) {
component.error(this, compiler_errors.invalid_binding_await);
return;
}
if (scope.is_const(name)) {
component.error(this, compiler_errors.invalid_binding_const);
}
scope.dependencies_for_name.get(name).forEach((name) => {
const variable = component.var_lookup.get(name);
if (variable) {
variable.mutated = true;
}
});
} else {
const variable = component.var_lookup.get(name);
if (!variable || variable.global) {
component.error(
/** @type {any} */ (this.expression.node),
compiler_errors.binding_undeclared(name)
);
return;
}
variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
if (info.expression.type === 'Identifier' && !variable.writable) {
component.error(
/** @type {any} */ (this.expression.node),
compiler_errors.invalid_binding_writable
);
return;
}
}
const type = parent.get_static_attribute_value('type');
this.is_readonly =
regex_dimensions.test(this.name) ||
regex_box_size.test(this.name) ||
(is_element(parent) &&
((parent.is_media_node() && read_only_media_attributes.has(this.name)) ||
(parent.name === 'input' && type === 'file'))) /* TODO others? */;
}
is_readonly_media_attribute() {
return read_only_media_attributes.has(this.name);
}
/** @param {import('./shared/TemplateScope.js').default} scope */
validate_binding_rest_properties(scope) {
this.expression.references.forEach((name) => {
const each_block = scope.get_owner(name);
if (each_block && each_block.type === 'EachBlock') {
const rest_node = each_block.context_rest_properties.get(name);
if (rest_node) {
this.component.warn(
/** @type {any} */ (rest_node),
compiler_warnings.invalid_rest_eachblock_binding(name)
);
}
}
});
}
}
/**
* @param {import('./shared/Node.js').default} node
* @returns {node is import('./Element.js').default}
*/
function is_element(node) {
return !!(/** @type {any} */ (node).is_media_node);
}

@ -1,31 +0,0 @@
import Node from './shared/Node.js';
import EventHandler from './EventHandler.js';
import Action from './Action.js';
/** @extends Node<'Body'> */
export default class Body extends Node {
/** @type {import('./EventHandler.js').default[]} */
handlers = [];
/** @type {import('./Action.js').default[]} */
actions = [];
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').Element} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
});
}
}

@ -1,32 +0,0 @@
import AbstractBlock from './shared/AbstractBlock.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends AbstractBlock<'CatchBlock'> */
export default class CatchBlock extends AbstractBlock {
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/**
* @param {import('../Component.js').default} component
* @param {import('./AwaitBlock.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope.child();
if (parent.catch_node) {
parent.catch_contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
[this.const_tags, this.children] = get_const_tags(info.children, component, this, parent);
if (!info.skip) {
this.warn_if_empty_block();
}
}
}

@ -1,25 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
/** @extends Node<'Class'> */
export default class Class extends Node {
/** @type {string} */
name;
/** @type {import('./shared/Expression.js').default} */
expression;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.name = info.name;
this.expression = info.expression
? new Expression(component, this, scope, info.expression)
: null;
}
}

@ -1,22 +0,0 @@
import Node from './shared/Node.js';
/** @extends Node<'Comment'> */
export default class Comment extends Node {
/** @type {string} */
data;
/** @type {string[]} */
ignores;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.data = info.data;
this.ignores = info.ignores;
}
}

@ -1,104 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
import { unpack_destructuring } from './shared/Context.js';
import { walk } from 'estree-walker';
import { extract_identifiers } from 'periscopic';
import is_reference from 'is-reference';
import get_object from '../utils/get_object.js';
import compiler_errors from '../compiler_errors.js';
const allowed_parents = new Set([
'EachBlock',
'CatchBlock',
'ThenBlock',
'InlineComponent',
'SlotTemplate',
'IfBlock',
'ElseBlock'
]);
/** @extends Node<'ConstTag'> */
export default class ConstTag extends Node {
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('./shared/Context.js').Context[]} */
contexts = [];
/** @type {import('../../interfaces.js').ConstTag} */
node;
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {Map<string, import('estree').Node>} */
context_rest_properties = new Map();
/** @type {Set<string>} */
assignees = new Set();
/** @type {Set<string>} */
dependencies = new Set();
/**
* @param {import('../Component.js').default} component
* @param {import('./interfaces.js').INodeAllowConstTag} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').ConstTag} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
if (!allowed_parents.has(parent.type)) {
component.error(info, compiler_errors.invalid_const_placement);
}
this.node = info;
this.scope = scope;
const { assignees, dependencies } = this;
extract_identifiers(info.expression.left).forEach(({ name }) => {
assignees.add(name);
const owner = this.scope.get_owner(name);
if (owner === parent) {
component.error(info, compiler_errors.invalid_const_declaration(name));
}
});
walk(info.expression.right, {
/**
* @type {import('estree-walker').SyncHandler}
*/
enter(node, parent) {
if (
is_reference(
/** @type {import('is-reference').NodeWithPropertyDefinition} */ (node),
/** @type {import('is-reference').NodeWithPropertyDefinition} */ (parent)
)
) {
const identifier = get_object(node);
const { name } = identifier;
dependencies.add(name);
}
}
});
}
parse_expression() {
unpack_destructuring({
contexts: this.contexts,
node: this.node.expression.left,
scope: this.scope,
component: this.component,
context_rest_properties: this.context_rest_properties
});
this.expression = new Expression(this.component, this, this.scope, this.node.expression.right);
this.contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
const owner = this.scope.get_owner(context.key.name);
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
this.component.error(
this.node,
compiler_errors.invalid_const_declaration(context.key.name)
);
}
this.scope.add(context.key.name, this.expression.dependencies, this);
});
}
}

@ -1,23 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
/** @extends Node<'DebugTag'> */
export default class DebugTag extends Node {
/** @type {import('./shared/Expression.js').default[]} */
expressions;
/**
* @param {import('../Component.js').default} component
* @param {import('./interfaces.js').INode} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.expressions = info.identifiers.map(
/** @param {import('estree').Node} node */ (node) => {
return new Expression(component, parent, scope, node);
}
);
}
}

@ -1,75 +0,0 @@
import Node from './shared/Node.js';
import Binding from './Binding.js';
import EventHandler from './EventHandler.js';
import fuzzymatch from '../../utils/fuzzymatch.js';
import Action from './Action.js';
import list from '../../utils/list.js';
import compiler_warnings from '../compiler_warnings.js';
import compiler_errors from '../compiler_errors.js';
const valid_bindings = ['fullscreenElement', 'visibilityState'];
/** @extends Node<'Document'> */
export default class Document extends Node {
/** @type {import('./EventHandler.js').default[]} */
handlers = [];
/** @type {import('./Binding.js').default[]} */
bindings = [];
/** @type {import('./Action.js').default[]} */
actions = [];
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').Element} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (!~valid_bindings.indexOf(node.name)) {
const match = fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` (did you mean '${match}'?)`
)
);
} else {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` — valid bindings are ${list(valid_bindings)}`
)
);
}
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
});
this.validate();
}
/** @private */
validate() {
const handlers_map = new Set();
this.handlers.forEach((handler) => handlers_map.add(handler.name));
if (handlers_map.has('mouseenter') || handlers_map.has('mouseleave')) {
this.component.warn(this, compiler_warnings.avoid_mouse_events_on_document);
}
}
}

@ -1,114 +0,0 @@
import ElseBlock from './ElseBlock.js';
import Expression from './shared/Expression.js';
import AbstractBlock from './shared/AbstractBlock.js';
import { unpack_destructuring } from './shared/Context.js';
import compiler_errors from '../compiler_errors.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends AbstractBlock<'EachBlock'> */
export default class EachBlock extends AbstractBlock {
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('estree').Node} */
context_node;
/** @type {string} */
iterations;
/** @type {string} */
index;
/** @type {string} */
context;
/** @type {import('./shared/Expression.js').default} */
key;
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./shared/Context.js').Context[]} */
contexts;
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/** @type {boolean} */
has_animation;
/** */
has_binding = false;
/** */
has_index_binding = false;
/** @type {Map<string, import('estree').Node>} */
context_rest_properties;
/** @type {import('./ElseBlock.js').default} */
else;
/**
* @param {import('../Component.js').default} component
* @param {import('estree').Node} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();
this.expression = new Expression(component, this, scope, info.expression);
this.context = info.context.name || 'each'; // TODO this is used to facilitate binding; currently fails with destructuring
this.context_node = info.context;
this.index = info.index;
this.scope = scope.child();
this.context_rest_properties = new Map();
this.contexts = [];
unpack_destructuring({
contexts: this.contexts,
node: info.context,
scope,
component,
context_rest_properties: this.context_rest_properties
});
this.contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, this.expression.dependencies, this);
});
if (this.index) {
// index can only change if this is a keyed each block
const dependencies = info.key ? this.expression.dependencies : new Set([]);
this.scope.add(this.index, dependencies, this);
}
this.key = info.key ? new Expression(component, this, this.scope, info.key) : null;
this.has_animation = false;
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
if (this.has_animation) {
this.children = this.children.filter(
(child) => !is_empty_node(child) && !is_comment_node(child)
);
if (this.children.length !== 1) {
const child = this.children.find(
(child) => !!(/** @type {import('./Element.js').default} */ (child).animation)
);
component.error(
/** @type {import('./Element.js').default} */ (child).animation,
compiler_errors.invalid_animation_sole
);
return;
}
}
this.warn_if_empty_block();
this.else = info.else ? new ElseBlock(component, this, this.scope, info.else) : null;
}
}
/** @param {import('./interfaces.js').INode} node */
function is_empty_node(node) {
return node.type === 'Text' && node.data.trim() === '';
}
/** @param {import('./interfaces.js').INode} node */
function is_comment_node(node) {
return node.type === 'Comment';
}

File diff suppressed because it is too large Load Diff

@ -1,24 +0,0 @@
import AbstractBlock from './shared/AbstractBlock.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends AbstractBlock<'ElseBlock'> */
export default class ElseBlock extends AbstractBlock {
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope.child();
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
this.warn_if_empty_block();
}
}

@ -1,82 +0,0 @@
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
import { sanitize } from '../../utils/names.js';
const regex_contains_term_function_expression = /FunctionExpression/;
/** @extends Node<'EventHandler'> */
export default class EventHandler extends Node {
/** @type {string} */
name;
/** @type {Set<string>} */
modifiers;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('estree').Identifier} */
handler_name;
/** */
uses_context = false;
/** */
can_make_passive = false;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} template_scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, template_scope, info) {
super(component, parent, template_scope, info);
this.name = info.name;
this.modifiers = new Set(info.modifiers);
if (info.expression) {
this.expression = new Expression(component, this, template_scope, info.expression);
this.uses_context = this.expression.uses_context;
if (
regex_contains_term_function_expression.test(info.expression.type) &&
info.expression.params.length === 0
) {
// TODO make this detection more accurate — if `event.preventDefault` isn't called, and
// `event` is passed to another function, we can make it passive
this.can_make_passive = true;
} else if (info.expression.type === 'Identifier') {
let node = component.node_for_declaration.get(info.expression.name);
if (node) {
if (node.type === 'VariableDeclaration') {
// for `const handleClick = () => {...}`, we want the [arrow] function expression node
const declarator = node.declarations.find(
(d) => /** @type {import('estree').Identifier} */ (d.id).name === info.expression.name
);
node = declarator && declarator.init;
}
if (
node &&
(node.type === 'FunctionExpression' ||
node.type === 'FunctionDeclaration' ||
node.type === 'ArrowFunctionExpression') &&
node.params.length === 0
) {
this.can_make_passive = true;
}
}
}
} else {
this.handler_name = component.get_unique_name(`${sanitize(this.name)}_handler`);
}
}
/** @returns {boolean} */
get reassigned() {
if (!this.expression) {
return false;
}
const node = this.expression.node;
if (regex_contains_term_function_expression.test(node.type)) {
return false;
}
return this.expression.dynamic_dependencies().length > 0;
}
}

@ -1,26 +0,0 @@
import Node from './shared/Node.js';
import map_children from './shared/map_children.js';
import TemplateScope from './shared/TemplateScope.js';
/** @extends Node<'Fragment'> */
export default class Fragment extends Node {
/** @type {import('../render_dom/Block.js').default} */
block;
/** @type {import('./interfaces.js').INode[]} */
children;
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/**
* @param {import('../Component.js').default} component
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, info) {
const scope = new TemplateScope();
super(component, null, scope, info);
this.scope = scope;
this.children = map_children(component, this, scope, info.children);
}
}

@ -1,40 +0,0 @@
import Node from './shared/Node.js';
import map_children from './shared/map_children.js';
import hash from '../utils/hash.js';
import compiler_errors from '../compiler_errors.js';
import { regex_non_whitespace_character } from '../../utils/patterns.js';
/** @extends Node<'Head'> */
export default class Head extends Node {
/** @type {any[]} */
children; // TODO
/** @type {string} */
id;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
if (info.attributes.length) {
component.error(info.attributes[0], compiler_errors.invalid_attribute_head);
return;
}
this.children = map_children(
component,
parent,
scope,
info.children.filter((child) => {
return child.type !== 'Text' || regex_non_whitespace_character.test(child.data);
})
);
if (this.children.length > 0) {
this.id = `svelte-${hash(this.component.source.slice(this.start, this.end))}`;
}
}
}

@ -1,36 +0,0 @@
import ElseBlock from './ElseBlock.js';
import Expression from './shared/Expression.js';
import AbstractBlock from './shared/AbstractBlock.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends AbstractBlock<'IfBlock'> */
export default class IfBlock extends AbstractBlock {
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('./ElseBlock.js').default} */
else;
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope.child();
this.cannot_use_innerhtml();
this.not_static_content();
this.expression = new Expression(component, this, this.scope, info.expression);
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
this.else = info.else ? new ElseBlock(component, this, scope, info.else) : null;
this.warn_if_empty_block();
}
}

@ -1,199 +0,0 @@
import Node from './shared/Node.js';
import Attribute from './Attribute.js';
import map_children from './shared/map_children.js';
import Binding from './Binding.js';
import EventHandler from './EventHandler.js';
import Expression from './shared/Expression.js';
import compiler_errors from '../compiler_errors.js';
import { regex_only_whitespaces } from '../../utils/patterns.js';
/** @extends Node<'InlineComponent'> */
export default class InlineComponent extends Node {
/** @type {string} */
name;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {import('./Binding.js').default[]} */
bindings = [];
/** @type {import('./EventHandler.js').default[]} */
handlers = [];
/** @type {import('./Attribute.js').default[]} */
css_custom_properties = [];
/** @type {import('./interfaces.js').INode[]} */
children;
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {string} */
namespace;
/** @type {Attribute[]} */
let_attributes;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();
if (info.name !== 'svelte:component' && info.name !== 'svelte:self') {
const name = info.name.split('.')[0]; // accommodate namespaces
component.warn_if_undefined(name, info, scope);
component.add_reference(/** @type {any} */ (this), name);
}
this.name = info.name;
this.namespace = get_namespace(parent, component.namespace);
this.expression =
this.name === 'svelte:component'
? new Expression(component, this, scope, info.expression)
: null;
const let_attributes = (this.let_attributes = []);
info.attributes.forEach(
/** @param {import('../../interfaces.js').BaseDirective | import('../../interfaces.js').Attribute | import('../../interfaces.js').SpreadAttribute} node */ (
node
) => {
/* eslint-disable no-fallthrough */
switch (node.type) {
case 'Action':
return component.error(node, compiler_errors.invalid_action);
case 'Attribute':
if (node.name.startsWith('--')) {
this.css_custom_properties.push(new Attribute(component, this, scope, node));
break;
}
// fallthrough
case 'Spread':
this.attributes.push(new Attribute(component, this, scope, node));
break;
case 'Binding':
this.bindings.push(new Binding(component, this, scope, node));
break;
case 'Class':
return component.error(node, compiler_errors.invalid_class);
case 'EventHandler':
this.handlers.push(new EventHandler(component, this, scope, node));
break;
case 'Let':
let_attributes.push(node);
break;
case 'Transition':
return component.error(node, compiler_errors.invalid_transition);
case 'StyleDirective':
return component.error(node, compiler_errors.invalid_component_style_directive);
case 'Animation':
return component.error(node, compiler_errors.invalid_animation);
default:
throw new Error(`Not implemented: ${node.type}`);
}
/* eslint-enable no-fallthrough */
}
);
this.scope = scope;
this.handlers.forEach((handler) => {
handler.modifiers.forEach((modifier) => {
if (modifier !== 'once') {
return component.error(handler, compiler_errors.invalid_event_modifier_component);
}
});
});
const children = [];
for (let i = info.children.length - 1; i >= 0; i--) {
const child = info.children[i];
if (child.type === 'SlotTemplate') {
children.push(child);
info.children.splice(i, 1);
} else if (
(child.type === 'Element' || child.type === 'InlineComponent' || child.type === 'Slot') &&
child.attributes.find((attribute) => attribute.name === 'slot')
) {
const slot_template = {
start: child.start,
end: child.end,
type: 'SlotTemplate',
name: 'svelte:fragment',
attributes: [],
children: [child]
};
// transfer attributes
for (let i = child.attributes.length - 1; i >= 0; i--) {
const attribute = child.attributes[i];
if (attribute.type === 'Let') {
slot_template.attributes.push(attribute);
child.attributes.splice(i, 1);
} else if (attribute.type === 'Attribute' && attribute.name === 'slot') {
slot_template.attributes.push(attribute);
}
}
// transfer const
for (let i = child.children.length - 1; i >= 0; i--) {
const child_child = child.children[i];
if (child_child.type === 'ConstTag') {
slot_template.children.push(child_child);
child.children.splice(i, 1);
}
}
children.push(slot_template);
info.children.splice(i, 1);
} else if (child.type === 'Comment' && children.length > 0) {
children[children.length - 1].children.unshift(child);
}
}
if (info.children.some((node) => not_whitespace_text(node))) {
children.push({
start: info.start,
end: info.end,
type: 'SlotTemplate',
name: 'svelte:fragment',
attributes: [],
children: info.children
});
}
if (let_attributes.length) {
// copy let: attribute from <Component /> to <svelte:fragment slot="default" />
// as they are for `slot="default"` only
children.forEach((child) => {
const slot = child.attributes.find((attribute) => attribute.name === 'slot');
if (!slot || slot.value[0].data === 'default') {
child.attributes.push(...let_attributes);
}
});
}
this.children = map_children(component, this, this.scope, children);
}
get slot_template_name() {
return /** @type {string} */ (
this.attributes.find((attribute) => attribute.name === 'slot').get_static_value()
);
}
}
/** @param {any} node */
function not_whitespace_text(node) {
return !(node.type === 'Text' && regex_only_whitespaces.test(node.data));
}
/**
* @param {import('./shared/Node.js').default} parent
* @param {string} explicit_namespace
*/
function get_namespace(parent, explicit_namespace) {
const parent_element = parent.find_nearest(/^Element/);
if (!parent_element) {
return explicit_namespace;
}
return parent_element.namespace;
}

@ -1,24 +0,0 @@
import Expression from './shared/Expression.js';
import map_children from './shared/map_children.js';
import AbstractBlock from './shared/AbstractBlock.js';
/** @extends AbstractBlock<'KeyBlock'> */
export default class KeyBlock extends AbstractBlock {
/** @type {import('./shared/Expression.js').default} */
expression;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();
this.expression = new Expression(component, this, scope, info.expression);
this.children = map_children(component, this, scope, info.children);
this.warn_if_empty_block();
}
}

@ -1,52 +0,0 @@
import Node from './shared/Node.js';
import { walk } from 'estree-walker';
import compiler_errors from '../compiler_errors.js';
const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']);
/** @extends Node<'Let'> */
export default class Let extends Node {
/** @type {import('estree').Identifier} */
name;
/** @type {import('estree').Identifier} */
value;
/** @type {string[]} */
names = [];
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.name = { type: 'Identifier', name: info.name };
const { names } = this;
if (info.expression) {
this.value = info.expression;
walk(info.expression, {
/** @param {import('estree').Identifier | import('estree').BasePattern} node */
enter(node) {
if (!applicable.has(node.type)) {
return component.error(/** @type {any} */ (node), compiler_errors.invalid_let);
}
if (node.type === 'Identifier') {
names.push(/** @type {import('estree').Identifier} */ (node).name);
}
// slightly unfortunate hack
if (node.type === 'ArrayExpression') {
node.type = 'ArrayPattern';
}
if (node.type === 'ObjectExpression') {
node.type = 'ObjectPattern';
}
}
});
} else {
names.push(this.name.name);
}
}
}

@ -1,4 +0,0 @@
import Tag from './shared/Tag.js';
/** @extends Tag<'MustacheTag'> */
export default class MustacheTag extends Tag {}

@ -1,4 +0,0 @@
import Node from './shared/Node.js';
/** @extends Node<'Options'> */
export default class Options extends Node {}

@ -1,19 +0,0 @@
import map_children from './shared/map_children.js';
import AbstractBlock from './shared/AbstractBlock.js';
/** @extends AbstractBlock<'PendingBlock'> */
export default class PendingBlock extends AbstractBlock {
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = map_children(component, parent, scope, info.children);
if (!info.skip) {
this.warn_if_empty_block();
}
}
}

@ -1,16 +0,0 @@
import Tag from './shared/Tag.js';
/** @extends Tag<'RawMustacheTag'> */
export default class RawMustacheTag extends Tag {
/**
* @param {any} component
* @param {any} parent
* @param {any} scope
* @param {any} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();
}
}

@ -1,46 +0,0 @@
import Element from './Element.js';
import Attribute from './Attribute.js';
import compiler_errors from '../compiler_errors.js';
/** @extends Element */
export default class Slot extends Element {
/** @type {'Slot'} */
// @ts-ignore Slot elements have the 'Slot' type, but TypeScript doesn't allow us to have 'Slot' when it extends Element
type = 'Slot';
/** @type {string} */
slot_name;
/** @type {Map<string, import('./Attribute.js').default>} */
values = new Map();
/**
* @param {import('../Component.js').default} component
* @param {import('./interfaces.js').INode} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach((attr) => {
if (attr.type !== 'Attribute' && attr.type !== 'Spread') {
return component.error(attr, compiler_errors.invalid_slot_directive);
}
if (attr.name === 'name') {
if (attr.value.length !== 1 || attr.value[0].type !== 'Text') {
return component.error(attr, compiler_errors.dynamic_slot_name);
}
this.slot_name = attr.value[0].data;
if (this.slot_name === 'default') {
return component.error(attr, compiler_errors.invalid_slot_name);
}
}
this.values.set(attr.name, new Attribute(component, this, scope, attr));
});
if (!this.slot_name) this.slot_name = 'default';
component.slots.set(this.slot_name, this);
this.cannot_use_innerhtml();
this.not_static_content();
}
}

@ -1,75 +0,0 @@
import Node from './shared/Node.js';
import Let from './Let.js';
import Attribute from './Attribute.js';
import compiler_errors from '../compiler_errors.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends Node<'SlotTemplate'> */
export default class SlotTemplate extends Node {
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./interfaces.js').INode[]} */
children;
/** @type {import('./Let.js').default[]} */
lets = [];
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/** @type {import('./Attribute.js').default} */
slot_attribute;
/** @type {string} */
slot_template_name = 'default';
/**
* @param {import('../Component.js').default} component
* @param {import('./interfaces.js').INode} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {any} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.validate_slot_template_placement();
scope = scope.child();
info.attributes.forEach((node) => {
switch (node.type) {
case 'Let': {
const l = new Let(component, this, scope, node);
this.lets.push(l);
const dependencies = new Set([l.name.name]);
l.names.forEach((name) => {
scope.add(name, dependencies, this);
});
break;
}
case 'Attribute': {
if (node.name === 'slot') {
this.slot_attribute = new Attribute(component, this, scope, node);
if (!this.slot_attribute.is_static) {
return component.error(node, compiler_errors.invalid_slot_attribute);
}
const value = this.slot_attribute.get_static_value();
if (typeof value === 'boolean') {
return component.error(node, compiler_errors.invalid_slot_attribute_value_missing);
}
this.slot_template_name = /** @type {string} */ (value);
break;
}
throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`);
}
default:
throw new Error(`Not implemented: ${node.type}`);
}
});
this.scope = scope;
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
}
validate_slot_template_placement() {
if (this.parent.type !== 'InlineComponent') {
return this.component.error(this, compiler_errors.invalid_slotted_content_fragment);
}
}
}

@ -1,64 +0,0 @@
import list from '../../utils/list.js';
import compiler_errors from '../compiler_errors.js';
import { nodes_to_template_literal } from '../utils/nodes_to_template_literal.js';
import Expression from './shared/Expression.js';
import Node from './shared/Node.js';
const valid_modifiers = new Set(['important']);
/** @extends Node<'StyleDirective'> */
export default class StyleDirective extends Node {
/** @type {string} */
name;
/** @type {Set<string>} */
modifiers;
/** @type {import('./shared/Expression.js').default} */
expression;
/** @type {boolean} */
should_cache;
/**
* @param {import('../Component.js').default} component
* @param {import('./shared/Node.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.name = info.name;
this.modifiers = new Set(info.modifiers);
for (const modifier of this.modifiers) {
if (!valid_modifiers.has(modifier)) {
component.error(
this,
compiler_errors.invalid_style_directive_modifier(list([...valid_modifiers]))
);
}
}
// Convert the value array to an expression so it's easier to handle
// the StyleDirective going forward.
if (info.value === true || (info.value.length === 1 && info.value[0].type === 'MustacheTag')) {
const identifier =
info.value === true
? {
type: 'Identifier',
start: info.end - info.name.length,
end: info.end,
name: info.name
}
: info.value[0].expression;
this.expression = new Expression(component, this, scope, identifier);
this.should_cache = false;
} else {
const raw_expression = nodes_to_template_literal(info.value);
this.expression = new Expression(component, this, scope, raw_expression);
this.should_cache = raw_expression.expressions.length > 0;
}
}
get important() {
return this.modifiers.has('important');
}
}

@ -1,68 +0,0 @@
import Node from './shared/Node.js';
import { regex_non_whitespace_character } from '../../utils/patterns.js';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elements_without_text = new Set(['audio', 'datalist', 'dl', 'optgroup', 'select', 'video']);
const regex_ends_with_svg = /svg$/;
const regex_non_whitespace_characters = /[\S\u00A0]/;
/** @extends Node<'Text'> */
export default class Text extends Node {
/** @type {string} */
data;
/** @type {boolean} */
synthetic;
/**
* @param {import('../Component.js').default} component
* @param {import('./interfaces.js').INode} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.data = info.data;
this.synthetic = info.synthetic || false;
}
should_skip() {
if (regex_non_whitespace_character.test(this.data)) return false;
const parent_element = this.find_nearest(/(?:Element|InlineComponent|SlotTemplate|Head)/);
if (!parent_element) return false;
if (parent_element.type === 'Head') return true;
if (parent_element.type === 'InlineComponent')
return parent_element.children.length === 1 && this === parent_element.children[0];
// svg namespace exclusions
if (regex_ends_with_svg.test(parent_element.namespace)) {
if (this.prev && this.prev.type === 'Element' && this.prev.name === 'tspan') return false;
}
return parent_element.namespace || elements_without_text.has(parent_element.name);
}
/** @returns {boolean} */
keep_space() {
if (this.component.component_options.preserveWhitespace) return true;
return this.within_pre();
}
/** @returns {boolean} */
within_pre() {
let node = this.parent;
while (node) {
if (node.type === 'Element' && node.name === 'pre') {
return true;
}
node = node.parent;
}
return false;
}
/** @returns {boolean} */
use_space() {
if (this.component.compile_options.preserveWhitespace) return false;
if (regex_non_whitespace_characters.test(this.data)) return false;
return !this.within_pre();
}
}

@ -1,32 +0,0 @@
import AbstractBlock from './shared/AbstractBlock.js';
import get_const_tags from './shared/get_const_tags.js';
/** @extends AbstractBlock<'ThenBlock'> */
export default class ThenBlock extends AbstractBlock {
/** @type {import('./shared/TemplateScope.js').default} */
scope;
/** @type {import('./ConstTag.js').default[]} */
const_tags;
/**
* @param {import('../Component.js').default} component
* @param {import('./AwaitBlock.js').default} parent
* @param {import('./shared/TemplateScope.js').default} scope
* @param {import('../../interfaces.js').TemplateNode} info
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope.child();
if (parent.then_node) {
parent.then_contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
[this.const_tags, this.children] = get_const_tags(info.children, component, this, parent);
if (!info.skip) {
this.warn_if_empty_block();
}
}
}

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

Loading…
Cancel
Save