Merge branch 'master' into blog/new-site

pull/8766/head
Puru Vijay 3 years ago
commit 048fee5382

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: export ComponentType from `svelte` entrypoint

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure identifiers in destructuring contexts don't clash with existing ones

@ -1,5 +0,0 @@
---
'svelte': patch
---
feat: smaller minified output for destructor chunks

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: never use html optimization for mustache tags in hydration mode

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: derived store types

@ -1,5 +0,0 @@
---
'svelte': patch
---
Generate type declarations with dts-buddy

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: ensure types are loaded with all TS settings

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: apply transition to `<svelte:element>` with local transition

@ -1,5 +0,0 @@
---
'svelte': patch
---
breaking: use `CustomEvent` constructor instead of deprecated `createEvent` method

@ -1,18 +0,0 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"svelte": "4.0.0-next.1",
"playground": "0.0.0",
"svelte.dev": "1.0.0"
},
"changesets": [
"beige-boxes-rhyme",
"fair-geese-repeat",
"gentle-pumas-chew",
"green-sheep-learn",
"mighty-suns-occur",
"stale-cougars-wink",
"tame-peaches-destroy"
]
}

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: relax a11y "no redundant role" rule for li, ul, ol

@ -1,5 +0,0 @@
---
'svelte': patch
---
warn on boolean compilerOptions.css

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: export correct SvelteComponent type

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: remove tsconfig.json from published package

@ -12,7 +12,7 @@
}
},
{
"files": ["README.md", "packages/*/README.md"],
"files": ["README.md", "packages/*/README.md", "**/package.json"],
"options": {
"useTabs": false,
"tabWidth": 2

@ -50,7 +50,7 @@ To watch for changes and continually rebuild the package (this is useful if you'
pnpm dev
```
The compiler is written in [TypeScript](https://www.typescriptlang.org/), but don't let that put you off — it's basically just JavaScript with type annotations. You'll pick it up in no time. If you're using an editor other than [Visual Studio Code](https://code.visualstudio.com/), you may need to install a plugin in order to get syntax highlighting and code hints, etc.
The compiler is written in JavaScript and uses [JSDoc](https://jsdoc.app/index.html) comments for type-checking.
### Running Tests

@ -44,9 +44,10 @@ Let's see what that looks like in practice.
<source type='video/mp4' src='https://svelte-technology-assets.surge.sh/just-write-css.mp4'>
</video>
<figcaption>
Is this what they mean by 'use the platform'?
</figcaption>
<!-- prettier-ignore -->
<figcaption>
Is this what they mean by 'use the platform'?
</figcaption>
</figure>

@ -2,6 +2,7 @@
title: Announcing SvelteKit 1.0
description: Web development, streamlined
author: The Svelte team
authorURL: https://svelte.dev/
---
After two years in development, [SvelteKit](https://kit.svelte.dev) has finally reached 1.0. As of today, its the recommended way to build Svelte apps of all shapes and sizes.

@ -0,0 +1,207 @@
---
title: Streaming, snapshots, and other new features since SvelteKit 1.0
description: Exciting improvements in the latest version of SvelteKit
author: Geoff Rich
authorURL: https://geoffrich.net
---
The Svelte team has been hard at work since the release of SvelteKit 1.0. Lets talk about some of the major new features that have shipped since launch: [streaming non-essential data](https://kit.svelte.dev/docs/load#streaming-with-promises), [snapshots](https://kit.svelte.dev/docs/snapshots), and [route-level config](https://kit.svelte.dev/docs/page-options#config).
## Stream non-essential data in load functions
SvelteKit uses [load functions](https://kit.svelte.dev/docs/load) to retrieve data for a given route. When navigating between pages, it first fetches the data, and then renders the page with the result. This could be a problem if some of the data for the page takes longer to load than others, especially if the data isnt essential the user wont see any part of the new page until all the data is ready.
There were ways to work around this. In particular, you could fetch the slow data in the component itself, so it first renders with the data from `load` and then starts fetching the slow data. But this was not ideal: the data is even more delayed since you dont start fetching until the client renders, and youre also having to break SvelteKits `load` convention.
Now, in SvelteKit 1.8, we have a new solution: you can return a nested promise from a server load function, and SvelteKit will start rendering the page before it resolves. Once it completes, the result will be [streamed](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) to the page.
For example, consider the following `load` function:
```ts
// @errors: 2304
export const load: PageServerLoad = () => {
return {
post: fetchPost(),
streamed: {
comments: fetchComments()
}
};
};
```
SvelteKit will automatically await the `fetchPost` call before it starts rendering the page, since its at the top level. However, it wont wait for the nested `fetchComments` call to complete the page will render and `data.streamed.comments` will be a promise that will resolve as the request completes. We can show a loading state in the corresponding `+page.svelte` using Sveltes [await block](https://svelte.dev/docs#template-syntax-await):
```svelte
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<article>
{data.post}
</article>
{#await data.streamed.comments}
Loading...
{:then value}
<ol>
{#each value as comment}
<li>{comment}</li>
{/each}
</ol>
{/await}
```
There is nothing unique about the property `streamed` here all that is needed to trigger the behavior is a promise outside the top level of the returned object.
SvelteKit will only be able to stream responses if your apps hosting platform supports it. In general, any platform built around AWS Lambda (e.g. serverless functions) will not support streaming, but any traditional Node.js server or edge-based runtime will. Check your providers documentation for confirmation.
If your platform does not support streaming, the data will still be available, but the response will be buffered and the page wont start rendering until all data has been fetched.
## How does it work?
In order for data from a server `load` function to get to the browser, we have to _serialize_ it. SvelteKit uses a library called [devalue](https://github.com/Rich-Harris/devalue), which is like `JSON.stringify` but better — it can handle values that JSON can't (like dates and regular expressions), it can serialize objects that contain themselves (or that exist multiple times in the data) without breaking identity, and it protects you against [XSS vulnerabilities](https://github.com/rich-harris/devalue#xss-mitigation).
When we server-render a page, we tell devalue to serialize promises as function calls that create a _deferred_. This is a simplified version of the code SvelteKit adds to the page:
```js
// @errors: 2339 7006
const deferreds = new Map();
window.defer = (id) => {
return new Promise((fulfil, reject) => {
deferreds.set(id, { fulfil, reject });
});
};
window.resolve = (id, data, error) => {
const deferred = deferreds.get(id);
deferreds.delete(id);
if (error) {
deferred.reject(error);
} else {
deferred.fulfil(data);
}
};
// devalue converts your data into a JavaScript expression
const data = {
post: {
title: 'My cool blog post',
content: '...'
},
streamed: {
comments: window.defer(1)
}
};
```
This code, along with the rest of the server-rendered HTML, is sent to the browser immediately, but the connection is kept open. Later, when the promise resolves, SvelteKit pushes an additional chunk of HTML to the browser:
```html
<script>
window.resolve(1, {
data: [{ comment: 'First!' }]
});
</script>
```
For client-side navigation, we use a slightly different mechanism. Data from the server is serialized as [newline delimited JSON](https://dataprotocols.org/ndjson/), and SvelteKit reconstructs the values — using a similar deferred mechanism — with `devalue.parse`:
```json
// this is generated immediately — note the ["Promise",1]...
[{"post":1,"streamed":4},{"title":2,"content":3},"My cool blog post","...",{"comments":5},["Promise",6],1]
// ...then this chunk is sent to the browser once the promise resolves
[{"id":1,"data":2},1,[3],{"comment":4},"First!"]
```
Because promises are natively supported in this way, you can put them anywhere in the data returned from `load` (except at the top level, since we automatically await those for you), and they can resolve with any type of data that devalue supports — including more promises!
One caveat: this feature needs JavaScript. Because of this, we recommend that you only stream in non-essential data so that the core of the experience is available to all users.
For more on this feature, see [the documentation](https://kit.svelte.dev/docs/load#streaming-with-promises). You can see a demo at [sveltekit-on-the-edge.vercel.app](https://sveltekit-on-the-edge.vercel.app/edge) (the location data is artificially delayed and streamed in) or [deploy your own on Vercel](https://vercel.com/templates/svelte/sveltekit-edge-functions), where streaming is supported in both Edge Functions and Serverless Functions.
We're grateful for the inspiration from prior implementations of this idea including Qwik, Remix, Solid, Marko, React and many others.
## Snapshots
Previously in a SvelteKit app, if you navigated away after starting to fill out a form, going back wouldnt restore your form state the form would be recreated with its default values. Depending on the context, this can be frustrating for users. Since SvelteKit 1.5, we have a built-in way to address this: snapshots.
Now, you can export a `snapshot` object from a `+page.svelte` or `+layout.svelte`. This object has two methods: `capture` and `restore`. The `capture` function defines what state you want to store when the user leaves the page. SvelteKit will then associate that state with the current history entry. If the user navigates back to the page, the `restore` function will be called with the state you previously had set.
For example, here is how you would capture and restore the value of a textarea:
```svelte
<script lang="ts">
import type { Snapshot } from './$types';
let comment = '';
export const snapshot: Snapshot = {
capture: () => comment,
restore: (value) => (comment = value)
};
</script>
<form method="POST">
<label for="comment">Comment</label>
<textarea id="comment" bind:value={comment} />
<button>Post comment</button>
</form>
```
While things like form input values and scroll positions are common examples, you can store any JSON-serializable data you like in a snapshot. The snapshot data is stored in [sessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), so it will persist even when the page is reloaded, or if the user navigates to a different site entirely. Because its in `sessionStorage`, you wont be able to access it during server-side rendering.
For more, see [the documentation](https://kit.svelte.dev/docs/snapshots).
## Route-level deployment configuration
SvelteKit uses platform-specific [adapters](https://kit.svelte.dev/docs/adapters) to transform your app code for deployment to production. Until now, you had to configure your deployment on an app-wide level. For instance, you could either deploy your app as an edge function or a serverless function, but not both. This made it impossible to take advantage of the edge for parts of your app if any route needed Node APIs, then you couldnt deploy any of it to the edge. The same is true for other aspects of deployment configuration, such as regions and allocated memory: you had to choose one value that applied to every route in your entire app.
Now, you can export a `config` object in your `+server.js`, `+page(.server).js` and `+layout(.server).js` files to control how those routes are deployed. Doing so in a `+layout.js` will apply the configuration to all child pages. The type of `config` is unique to each adapter, since it depends on the environment youre deploying to.
```ts
// @errors: 2307
import type { Config } from 'some-adapter';
export const config: Config = {
runtime: 'edge'
};
```
Configs are merged at the top level, so you can override values set in a layout for pages further down the tree. For more details, see [the documentation](https://kit.svelte.dev/docs/page-options#config).
If you deploy to Vercel, you can take advantage of this feature by installing the latest versions of SvelteKit and your adapter. This will require a major upgrade to your adapter version, since adapters supporting route-level config require SvelteKit 1.5 or later.
```bash
npm i @sveltejs/kit@latest
npm i @sveltejs/adapter-auto@latest # or @sveltejs/adapter-vercel@latest
```
For now, only the [Vercel adapter](https://kit.svelte.dev/docs/adapter-vercel#deployment-configuration) implements route-specific config, but the building blocks are there to implement this for other platforms. If youre an adapter author, see the changes in [the PR](https://github.com/sveltejs/kit/pull/8740) to see what is required.
## Incremental static regeneration on Vercel
Route-level config also unlocked another much-requested feature you can now use [incremental static regeneration](https://kit.svelte.dev/docs/adapter-vercel#incremental-static-regeneration) (ISR) with SvelteKit apps deployed to Vercel. ISR provides the performance and cost advantages of prerendered content with the flexibility of dynamically rendered content.
To add ISR to a route, include the `isr` property in your `config` object:
```ts
export const config = {
isr: {
// see Vercel adapter docs for the required options
}
};
```
## And much more...
- The [OPTIONS method](https://kit.svelte.dev/docs/routing#server) is now supported in `+server.js` files
- Better error messages when you [export something that belongs in a different file](https://github.com/sveltejs/kit/pull/9055) or [forget to put a slot](https://github.com/sveltejs/kit/pull/8475) in your +layout.svelte.
- You can now [access public environment variables in app.html](https://kit.svelte.dev/docs/project-structure#project-files-src)
- A new [text helper](https://kit.svelte.dev/docs/modules#sveltejs-kit-text) for creating responses
- And a ton of bug fixes see [the changelog](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md) for the full release notes.
Thank you to everyone who has contributed and uses SvelteKit in their projects. Weve said it before, but Svelte is a community project, and it wouldnt be possible without your feedback and contributions.

@ -0,0 +1,59 @@
---
title: "Announcing Svelte 4"
description: 'Updated performance, developer experience, and site'
author: The Svelte team
authorURL: https://svelte.dev/
---
After months in the making, we're excited to announce the stable release of Svelte 4.
Time flies - Svelte 3 was released more than four years ago! In JavaScript-framework-time, that's eons. Sveltes freshness has persisted throughout, but Node.js and browser APIs have evolved during that time and today were updating Svelte to take advantage of some of these improvements. Svelte 4 is mainly a maintenance release, bumping minimum version requirements and tightening up the design in specific areas. It sets the stage for the next generation of Svelte to be released as Svelte 5 - we think youll love it.
If you haven't tried Svelte yet, take it for a spin in our [interactive tutorial](https://learn.svelte.dev/), on [StackBlitz](https://sveltekit.new/), or locally with `npm create svelte@latest`. Svelte lets you easily put together web UIs leveraging the power of HTML, CSS, JS, and the Svelte compiler. Watch [Svelte Radio Live](https://www.youtube.com/watch?v=72TIVhRtyWE) to learn more about this release.
## What's new
### Performance
This release results in smaller and faster hydration code. To see the impact, SvelteKit users can see their compiled output shrink in size by examining the `.svelte-kit/output/client/_app/immutable/nodes` folder. For example, on [kit.svelte.dev](https://kit.svelte.dev) the JS generated across the whole site was reduced in size by 12.7% (126.3 kB to 110.2 kB).
Svelte 4 reduces the Svelte package size by nearly 75% (10.6 MB down to 2.8 MB), which means less waiting on `npm install`. This improvement will be especially noticeable for users who are loading our interactive learning experience on [learn.svelte.dev](https://learn.svelte.dev) for the first time, users of the Svelte REPL, and users with limited connectivity. The majority of the remaining package size is eslint support, which necessitates distributing a CJS build, and once [the eslint rewrite](https://github.com/eslint/eslint/discussions/16557) is completed the Svelte package size can drop by over another 50%.
The number of dependencies in Svelte has been greatly reduced from 61 down to 16. This means faster downloads for our users as well as less susceptibility to supply chain attacks. We also slightly reduced the number of dependencies in the latest versions of SvelteKit as well.
### Developer experience
Svelte 4 makes the Svelte authoring experience more intuitive and consistent: `|local` is now the default for transitions to avoid animations blocking page transitions, preprocessors are now easier to write, and multiple fixes make CSP easier to set up and use.
For users of web components, the largest change is an overhaul of the way you use Svelte to author custom elements. By changing the way they are generated, a whole class of bugs and inconsistencies was eliminated.
Finally, weve also made several [improvements to the IDE authoring experience](https://github.com/sveltejs/svelte/pull/8702):
cmd+click in svelte modules now takes you to the implementation rather than a `.d.ts` file
imports from `svelte/internal` are now hidden and will not clutter autocomplete suggestions
auto-imports now work more reliably
### Updated site, docs, and tutorial
The official [svelte.dev](https://svelte.dev) site has gotten an overhaul. Its now split into multiple pages with improved mobile nav, overhauled typescript docs, dark mode, and an enhanced REPL. The SvelteKit site is also being updated to match. And weve updated all the tutorial links to point to our new [learn.svelte.dev](https://learn.svelte.dev) experience. The old tutorial remains available for users of Safari 16.3 and earlier.
Stay tuned for a more in-depth blog post about all the site changes in the coming days!
## Migrating
Most apps and libraries that are compatible with Svelte 3 should be compatible with Svelte 4. Library authors will need to update the version range to include Svelte 4 if `svelte` is specified in the `peerDependencies`. For application authors, the most common change required will be updating tooling to meet the new minimum version requirements such as Node.js 16. Many other migration steps can be handled with `npx svelte-migrate@latest svelte-4`.
Read the [migration guide](/docs/v4-migration-guide) for full details.
## Svelte 5: the next generation of Svelte
Svelte 5 will be a rewrite of the Svelte compiler and runtime. Svelte 4 was mainly about setting the ground for these future improvements by adopting modern tooling and dropping support for some legacy versions of various technologies such as older bundlers. These changes will help us in a number of ways such as being able to more easily compare the Svelte 5 and Svelte 4 codebases and being able to run the existing tests against the new implementation. Svelte 5 will bring major new features and performance improvements to Svelte. The changes are still baking and not quite ready to share yet, but stay tuned!
## Changelog
See the full list of changes in the [changelog](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md).
## Acknowledgements
First and foremost, thank you to all of the many Svelte maintainers and contributors who made this release possible. Developers contributing multiple PRs to this release were [@dummdidumm](https://github.com/dummdidumm), [@gtm-nayan](https://github.com/gtm-nayan), [@benmccann](https://github.com/benmccann), [@tanhauhau](https://github.com/tanhauhau), [@Karlinator](https://github.com/Karlinator), and [@ngtr6788](https://github.com/ngtr6788). Also, thank you to the many community members who donated to [the Svelte OpenCollective](https://opencollective.com/svelte) - these donations sponsored the site overhaul completed by [PuruVJ](https://github.com/puruvj) as well as a number of recent fixes from [@gtm-nayan](https://github.com/gtm-nayan).
Finally, thank you to the various library maintainers from across the ecosystem who helped prepare for this release. Thank you to [@jessebeach](https://github.com/jessebeach) for the help in getting out new versions of `aria-query` and `axobject-query`, [@jreinhold](https://github.com/jreinhold) for ensuring compatibility with Storybook, and [@yanick](https://github.com/yanick) for updating `svelte-testing-library`. And to ensure things stay working, the [`svelte-ecosystem-ci`](https://github.com/sveltejs/svelte-ecosystem-ci) setup by [@dominikg](https://github.com/dominikg) — which was modeled off his similar work for Vite — has been helping to test against major projects in the ecosystem on an ongoing basis.

@ -62,9 +62,10 @@ An expression might include characters that would cause syntax highlighting to f
When the attribute name and value match (`name={name}`), they can be replaced with `{name}`.
```svelte
<!-- These are equivalent -->
<button {disabled}>...</button>
<button {disabled}>...</button>
<!-- equivalent to
<button disabled={disabled}>...</button>
-->
```
By convention, values passed to components are referred to as _properties_ or _props_ rather than _attributes_, which are a feature of the DOM.

@ -105,12 +105,13 @@ The simplest bindings reflect the value of a property, such as `input.value`.
<input type="checkbox" bind:checked={yes} />
```
If the name matches the value, you can use shorthand.
If the name matches the value, you can use a shorthand.
```svelte
<!-- These are equivalent -->
<input bind:value />
<input bind:value />
<!-- equivalent to
<input bind:value={value} />
-->
```
Numeric input values are coerced; even though `input.value` is a string as far as the DOM is concerned, Svelte will treat it as a number. If the input is empty or invalid (in the case of `type="number"`), the value is `undefined`.
@ -339,8 +340,8 @@ A `class:` directive provides a shorter way of toggling a class on an element.
```svelte
<!-- These are equivalent -->
<div class={active ? 'active' : ''}>...</div>
<div class:active>...</div>
<div class={isActive ? 'active' : ''}>...</div>
<div class:active={isActive}>...</div>
<!-- Shorthand, for when name and value match -->
<div class:active>...</div>
@ -380,7 +381,7 @@ The `style:` directive provides a shorthand for setting multiple styles on an el
<div style:color style:width="12rem" style:background-color={darkMode ? 'black' : 'white'}>...</div>
<!-- Styles can be marked as important -->
<div style:color="red">...</div>
<div style:color|important="red">...</div>
```
When `style:` directives are combined with `style` attributes, the directives will take precedence:

@ -39,8 +39,6 @@ The content is exposed in the child component using the `<slot>` element, which
Note: If you want to render regular `<slot>` element, You can use `<svelte:element this="slot" />`.
Note: If you want to render regular `<slot>` element, You can use `<svelte:element this="slot" />`.
### `<slot name="`_name_`">`
Named slots allow consumers to target specific areas. They can also have fallback content.
@ -328,7 +326,7 @@ The `<svelte:options>` element provides a place to specify per-component compile
## `<svelte:fragment>`
The `<svelte:fragment>` element allows you to place content in a [named slot](/docs/special-elements#slot-name-name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
The `<svelte:fragment>` element allows you to place content in a [named slot](/docs/special-elements#slot-slot-name-name) without wrapping it in a container DOM element. This keeps the flow layout of your document intact.
```svelte
<!-- Widget.svelte -->

@ -223,6 +223,8 @@ Events can be cancelable by passing a third parameter to the dispatch function.
</script>
```
You can type the event dispatcher to define which events it can receive. This will make your code more type safe both within the component (wrong calls are flagged) and when using the component (types of the events are now narrowed). See [here](typescript#script-lang-ts-events) how to do it.
## Types
> TYPES: svelte

@ -4,9 +4,9 @@ title: 'svelte/store'
The `svelte/store` module exports functions for creating [readable](/docs/svelte-store#readable), [writable](/docs/svelte-store#writable) and [derived](/docs/svelte-store#derived) stores.
Keep in mind that you don't _have_ to use these functions to enjoy the [reactive `$store` syntax](/docs/svelte-components#4-prefix-stores-with-$-to-access-their-values) in your components. Any object that correctly implements `.subscribe`, unsubscribe, and (optionally) `.set` is a valid store, and will work both with the special syntax, and with Svelte's built-in [`derived` stores](/docs/svelte-store#derived).
Keep in mind that you don't _have_ to use these functions to enjoy the [reactive `$store` syntax](/docs/svelte-components#script-4-prefix-stores-with-$-to-access-their-values) in your components. Any object that correctly implements `.subscribe`, unsubscribe, and (optionally) `.set` is a valid store, and will work both with the special syntax, and with Svelte's built-in [`derived` stores](/docs/svelte-store#derived).
This makes it possible to wrap almost any other reactive state handling library for use in Svelte. Read more about the [store contract](/docs/svelte-components#4-prefix-stores-with-$-to-access-their-values) to see what a correct implementation looks like.
This makes it possible to wrap almost any other reactive state handling library for use in Svelte. Read more about the [store contract](/docs/svelte-components#script-4-prefix-stores-with-$-to-access-their-values) to see what a correct implementation looks like.
## `writable`
@ -62,6 +62,7 @@ Note that the value of a `writable` is lost when it is destroyed, for example wh
Creates a store whose value cannot be set from 'outside', the first argument is the store's initial value, and the second argument to `readable` is the same as the second argument to `writable`.
```js
<!--- file: App.svelte --->
// ---cut---
import { readable } from 'svelte/store';

@ -29,9 +29,10 @@ An action can have a parameter. If the returned value has an `update` method, it
```svelte
<!--- file: App.svelte --->
<script>
/** @type {string} */
export let bar;
/** @type {import('svelte/action').Action} */
/** @type {import('svelte/action').Action<HTMLElement, string>} */
function foo(node, bar) {
// the node has been mounted in the DOM

@ -29,7 +29,7 @@ const result = compile(source, {
});
```
Refer to [CompileOptions](#type-compileoptions) for all the available options.
Refer to [CompileOptions](#types-compileoptions) for all the available options.
The returned `result` object contains the code for your component, along with useful bits of metadata.
@ -47,7 +47,7 @@ import { compile } from 'svelte/compiler';
const { js, css, ast, warnings, vars, stats } = compile(source);
```
Refer to [CompileResult](#type-compileresult) for a full description of the compile result.
Refer to [CompileResult](#types-compileresult) for a full description of the compile result.
## parse

@ -4,7 +4,7 @@ title: Frequently asked questions
## I'm new to Svelte. Where should I start?
We think the best way to get started is playing through the interactive [tutorial](/tutorial). Each step there is mainly focused on one specific aspect and is easy to follow. You'll be editing and running real Svelte components right in your browser.
We think the best way to get started is playing through the interactive [tutorial](https://learn.svelte.dev/). Each step there is mainly focused on one specific aspect and is easy to follow. You'll be editing and running real Svelte components right in your browser.
Five to ten minutes should be enough to get you up and running. An hour and a half should get you through the entire tutorial.

@ -4,14 +4,23 @@ title: 'Accessibility warnings'
Accessibility (shortened to a11y) isn't always easy to get right, but Svelte will help by warning you at compile time if you write inaccessible markup. However, keep in mind that many accessibility issues can only be identified at runtime using other automated tools and by manually testing your application.
Some warnings may be incorrect in your concrete use case. You can disable such false positives by placing a `<!-- svelte-ignore a11y-<code> -->` comment above the line that causes the warning. Example:
```svelte
<!-- svelte-ignore a11y-autofocus -->
<input autofocus />
```
Here is a list of accessibility checks Svelte will do for you.
## `a11y-accesskey`
Enforce no `accesskey` on element. Access keys are HTML attributes that allow web developers to assign keyboard shortcuts to elements. Inconsistencies between keyboard shortcuts and keyboard commands used by screen reader and keyboard-only users create accessibility complications. To avoid complications, access keys should not be used.
<!-- prettier-ignore -->
```svelte
<!-- A11y: Avoid using accesskey --><div accessKey="z" />
<!-- A11y: Avoid using accesskey -->
<div accessKey="z" />
```
## `a11y-aria-activedescendant-has-tabindex`
@ -69,8 +78,10 @@ The following elements are visually distracting: `<marquee>` and `<blink>`.
Certain DOM elements are useful for screen reader navigation and should not be hidden.
<!-- prettier-ignore -->
```svelte
<!-- A11y: <h2> element should not be hidden --><h2 aria-hidden="true">invisible header</h2>
<!-- A11y: <h2> element should not be hidden -->
<h2 aria-hidden="true">invisible header</h2>
```
## `a11y-img-redundant-alt`
@ -170,8 +181,10 @@ Certain reserved DOM elements do not support ARIA roles, states and properties.
The scope attribute should only be used on `<th>` elements.
<!-- prettier-ignore -->
```svelte
<!-- A11y: The scope attribute should only be used with <th> elements --><div scope="row" />
<!-- A11y: The scope attribute should only be used with <th> elements -->
<div scope="row" />
```
## `a11y-missing-attribute`
@ -267,16 +280,30 @@ A non-interactive element does not support event handlers (mouse and key handler
Tab key navigation should be limited to elements on the page that can be interacted with.
<!-- prettier-ignore -->
```svelte
<!-- A11y: noninteractive element cannot have nonnegative tabIndex value -->
<div tabindex="0" />
```
## a11y-no-static-element-interactions
Elements like `<div>` with interactive handlers like `click` must have an ARIA role.
<!-- prettier-ignore -->
```svelte
<!-- A11y: noninteractive element cannot have nonnegative tabIndex value --><div tabindex="0" />
<!-- A11y: <div> with click handler must have an ARIA role -->
<div on:click={() => ''} />
```
## `a11y-positive-tabindex`
Avoid positive `tabindex` property values. This will move elements out of the expected tab order, creating a confusing experience for keyboard users.
<!-- prettier-ignore -->
```svelte
<!-- A11y: avoid tabindex values above zero --><div tabindex="1" />
<!-- A11y: avoid tabindex values above zero -->
<div tabindex="1" />
```
## `a11y-role-has-required-aria-props`
@ -324,6 +351,8 @@ Enforce that only known ARIA attributes are used. This is based on the [WAI-ARIA
Elements with ARIA roles must use a valid, non-abstract ARIA role. A reference to role definitions can be found at [WAI-ARIA](https://www.w3.org/TR/wai-aria/#role_definitions) site.
<!-- prettier-ignore -->
```svelte
<!-- A11y: Unknown role 'toooltip' (did you mean 'tooltip'?) --><div role="toooltip" />
<!-- A11y: Unknown role 'toooltip' (did you mean 'tooltip'?) -->
<div role="toooltip" />
```

@ -102,12 +102,12 @@ Events can be typed with `createEventDispatcher`:
}>();
function handleClick() {
dispatch('even');
dispatch('event');
dispatch('click', 'hello');
}
function handleType() {
dispatch('even');
dispatch('event');
dispatch('type', Math.random() > 0.5 ? 'world' : null);
}
</script>
@ -176,16 +176,14 @@ You cannot type your reactive declarations with TypeScript in the way you type a
</script>
```
You cannot add a `: TYPE` because it's invalid syntax in this position. Instead, you can use the `as` or move the definition to a `let` statement just above:
You cannot add a `: TYPE` because it's invalid syntax in this position. Instead, you can move the definition to a `let` statement just above:
```svelte
<script lang="ts">
let count = 0;
$: option1 = (count * 2) as number;
let option2: number;
$: option2 = count * 2;
let doubled: number;
$: doubled = count * 2;
</script>
```

@ -2,7 +2,7 @@
title: Svelte 4 migration guide
---
This migration guide provides an overview of how to migrate from Svelte version 3 to 4. See the linked PRs for more details about each change. Use the migration script to migrate some of these automatically: `npx svelte-migrate svelte-4`
This migration guide provides an overview of how to migrate from Svelte version 3 to 4. See the linked PRs for more details about each change. Use the migration script to migrate some of these automatically: `npx svelte-migrate@latest svelte-4`
If you're a library author, consider whether to only support Svelte 4 or if it's possible to support Svelte 3 too. Since most of the breaking changes don't affect many people, this may be easily possible. Also remember to update the version range in your `peerDependencies`.
@ -11,7 +11,8 @@ If you're a library author, consider whether to only support Svelte 4 or if it's
- Upgrade to Node 16 or higher. Earlier versions are no longer supported. ([#8566](https://github.com/sveltejs/svelte/issues/8566))
- If you are using SvelteKit, upgrade to 1.20.4 or newer ([sveltejs/kit#10172](https://github.com/sveltejs/kit/pull/10172))
- If you are using Vite without SvelteKit, upgrade to `vite-plugin-svelte` 2.4.1 or newer ([#8516](https://github.com/sveltejs/svelte/issues/8516))
- If you are using webpack, upgrade to webpack 5 or higher. Earlier versions are no longer supported. ([#8515](https://github.com/sveltejs/svelte/issues/8515))
- If you are using webpack, upgrade to webpack 5 or higher and `svelte-loader` 3.1.8 or higher. Earlier versions are no longer supported. ([#8515](https://github.com/sveltejs/svelte/issues/8515), [198dbcf](https://github.com/sveltejs/svelte/commit/198dbcf))
- If you are using Rollup, upgrade to `rollup-plugin-svelte` 7.1.5 or higher ([198dbcf](https://github.com/sveltejs/svelte/commit/198dbcf))
- If you are using TypeScript, upgrade to TypeScript 5 or higher. Lower versions might still work, but no guarantees are made about that. ([#8488](https://github.com/sveltejs/svelte/issues/8488))
## Browser conditions for bundlers
@ -32,13 +33,13 @@ There are now stricter types for `createEventDispatcher`, `Action`, `ActionRetur
// @errors: 2554 2345
import { createEventDispatcher } from 'svelte';
// Svelte version 3:
const dispatch = createEventDispatcher<{
optional: number | null;
required: string;
noArgument: never;
}>();
// Svelte version 3:
dispatch('optional');
dispatch('required'); // I can still omit the detail argument
dispatch('noArgument', 'surprise'); // I can still add a detail argument
@ -155,4 +156,5 @@ The order in which preprocessors are applied has changed. Now, preprocessors are
- people implementing their own stores from scratch using the `StartStopNotifier` interface (which is passed to the create function of `writable` etc) from `svelte/store` now need to pass an update function in addition to the set function. This has no effect on people using stores or creating stores using the existing Svelte stores. ([#6750](https://github.com/sveltejs/svelte/issues/6750))
- `derived` will now throw an error on falsy values instead of stores passed to it. ([#7947](https://github.com/sveltejs/svelte/issues/7947))
- type definitions for `svelte/internal` were removed to further discourage usage of those internal methods which are not public API. Most of these will likely change for Svelte 5
- Removal of DOM nodes is now batched which slightly changes its order, which might affect the order of events fired if you're using a MutationObserver on these elements ([#8763](https://github.com/sveltejs/svelte/pull/8763))
- Removal of DOM nodes is now batched which slightly changes its order, which might affect the order of events fired if you're using a `MutationObserver` on these elements ([#8763](https://github.com/sveltejs/svelte/pull/8763))
- if you enhanced the global typings through the `svelte.JSX` namespace before, you need to migrate this to use the `svelteHTML` namespace. Similarly if you used the `svelte.JSX` namespace to use type definitions from it, you need to migrate those to use the types from `svelte/elements` instead. You can find more information about what to do [here](https://github.com/sveltejs/language-tools/blob/master/docs/preprocessors/typescript.md#im-getting-deprecation-warnings-for-sveltejsx--i-want-to-migrate-to-the-new-typings)

@ -7,6 +7,7 @@
}
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>

@ -2,6 +2,7 @@
let m = { x: 0, y: 0 };
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:mousemove={(e) => (m = { x: e.clientX, y: e.clientY })}>
The mouse position is {m.x} x {m.y}
</div>

@ -24,6 +24,7 @@
</label>
</div>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<svg
on:mousemove={(e) => coords.set({ x: e.clientX, y: e.clientY })}
on:mousedown={() => size.set(30)}

@ -49,6 +49,7 @@
{#if selected}
{#await selected then d}
<div class="photo" in:receive={{ key: d.id }} out:send={{ key: d.id }}>
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
<img alt={d.alt} src="{ASSETS}/{d.id}.jpg" on:click={() => (selected = null)} />
<p class="credit">

@ -28,8 +28,8 @@
{:else}
<ul>
{#each [...eases] as [name]}
<li class:selected={name === current_ease} on:click={() => (current_ease = name)}>
{name}
<li class:selected={name === current_ease}>
<button on:click={() => (current_ease = name)}> {name}</button>
</li>
{/each}
</ul>
@ -46,8 +46,8 @@
{:else}
<ul>
{#each types as [name, type]}
<li class:selected={type === current_type} on:click={() => (current_type = type)}>
{name}
<li class:selected={type === current_type}>
<button on:click={() => (current_type = type)}> {name}</button>
</li>
{/each}
</ul>

@ -46,5 +46,6 @@
p {
pointer-events: none;
color: black;
}
</style>

@ -10,6 +10,7 @@
}
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:mouseenter={enter} on:mouseleave={leave}>
<slot {hovering} />
</div>

@ -6,12 +6,13 @@
$: if (dialog && showModal) dialog.showModal();
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
<dialog
bind:this={dialog}
on:close={() => (showModal = false)}
on:click|self={() => dialog.close()}
>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:click|stopPropagation>
<slot name="header" />
<hr />

@ -11,7 +11,7 @@
}
</script>
<span class:expanded on:click={toggle}>{name}</span>
<button class:expanded on:click={toggle}>{name}</button>
{#if expanded}
<ul transition:slide={{ duration: 300 }}>
@ -28,12 +28,14 @@
{/if}
<style>
span {
button {
padding: 0 0 0 1.5em;
background: url(/tutorial/icons/folder.svg) 0 0.1em no-repeat;
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
border:none;
font-size:14px;
}
.expanded {

@ -69,9 +69,10 @@ radius of the selected circle.
<button on:click={() => travel(-1)} disabled={i === 0}>undo</button>
<button on:click={() => travel(+1)} disabled={i === undoStack.length - 1}>redo</button>
</div>
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
<svg on:click={handleClick}>
{#each circles as circle}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<circle
cx={circle.cx}
cy={circle.cy}

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

@ -6,23 +6,25 @@
export let todo;
let div;
let btn;
afterUpdate(() => {
flash(div);
flash(btn);
});
</script>
<!-- the text will flash red whenever
the `todo` object changes -->
<div bind:this={div} on:click>
<button bind:this={btn} on:click>
{todo.done ? '👍' : ''}
{todo.text}
</div>
</button>
<style>
div {
button {
cursor: pointer;
line-height: 1.5;
border:none;
background:none;
font-size:14px;
}
</style>

@ -4,23 +4,25 @@
export let todo;
let div;
let btn;
afterUpdate(() => {
flash(div);
flash(btn);
});
</script>
<!-- the text will flash red whenever
the `todo` object changes -->
<div bind:this={div} on:click>
<button bind:this={btn}>
{todo.done ? '👍' : ''}
{todo.text}
</div>
</button>
<style>
div {
button {
cursor: pointer;
line-height: 1.5;
border:none;
background:none;
font-size:14px;
}
</style>

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

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

@ -4,8 +4,9 @@ title: Textarea inputs
The `<textarea>` element behaves similarly to a text input in Svelte — use `bind:value` to create a two-way binding between the `<textarea>` content and the `value` variable:
<!-- prettier-ignore -->
```svelte
<textarea bind:value />
<textarea bind:value={value} />
```
In cases like these, where the names match, we can also use a shorthand form:

@ -17,7 +17,7 @@
{#if showItems}
{#each items.slice(0, i) as item}
<div transition:slide|local>
<div transition:slide|global>
{item}
</div>
{/each}

@ -0,0 +1,17 @@
---
title: Global transitions
---
Ordinarily, transitions will only play on elements when their direct containing block is added or destroyed. In the example here, toggling the visibility of the entire list does not apply transitions to individual list elements.
Instead, we'd like transitions to not only play when individual items are added and removed with the slider but also when we toggle the checkbox.
We can achieve this with a _global_ transition, which plays when _any_ block containing the transitions is added or removed:
```svelte
<div transition:slide|global>
{item}
</div>
```
> In Svelte 3, transitions were global by default and you had to use the `|local` modifier to make them local.

@ -1,15 +0,0 @@
---
title: Local transitions
---
Ordinarily, transitions will play on elements when any container block is added or destroyed. In the example here, toggling the visibility of the entire list also applies transitions to individual list elements.
Instead, we'd like transitions to play only when individual items are added and removed — in other words, when the user drags the slider.
We can achieve this with a _local_ transition, which only plays when the block with the transition itself is added or removed:
```svelte
<div transition:slide|local>
{item}
</div>
```

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

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

@ -20,15 +20,15 @@
},
"license": "MIT",
"devDependencies": {
"@changesets/cli": "^2.26.0",
"@changesets/cli": "^2.26.1",
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"eslint": "^8.40.0",
"eslint-plugin-svelte": "^2.28.0",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"eslint": "^8.43.0",
"eslint-plugin-svelte": "^2.31.0",
"eslint-plugin-unicorn": "^47.0.0",
"playwright": "^1.34.3",
"playwright": "^1.35.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.0"
"prettier-plugin-svelte": "^2.10.1"
},
"packageManager": "pnpm@8.6.0"
"packageManager": "pnpm@8.6.3"
}

@ -7,7 +7,7 @@
"dev": "node --watch start.js"
},
"devDependencies": {
"rollup": "^3.20.2",
"rollup": "^3.25.1",
"rollup-plugin-serve": "^2.0.2",
"svelte": "workspace:*"
}

@ -1,3 +1,7 @@
<script>
import Counter from "./lib/Counter.svelte";
</script>
<div>
Hello world!
</div>

@ -1,13 +1,11 @@
import { readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { watch } from 'rollup';
import serve from 'rollup-plugin-serve';
import * as svelte from '../svelte/src/compiler/index.js';
let __dirname = new URL('.', import.meta.url).pathname;
if (process.platform === 'win32') {
__dirname = __dirname.slice(1); // else path.resolve fucks up
}
const __dirname = fileURLToPath(new URL('.', import.meta.url));
/** @returns {import('rollup').Plugin}*/
function create_plugin(ssr = false) {

@ -1,5 +1,122 @@
# svelte
## 4.0.0
### Major Changes
- breaking: Minimum supported Node version is now Node 16 ([#8566](https://github.com/sveltejs/svelte/pull/8566))
- breaking: Minimum supported webpack version is now webpack 5 ([#8515](https://github.com/sveltejs/svelte/pull/8515))
- breaking: Bundlers must specify the `browser` condition when building a frontend bundle for the browser ([#8516](https://github.com/sveltejs/svelte/pull/8516))
- breaking: Minimum supported vite-plugin-svelte version is now 2.4.1. SvelteKit users can upgrade to 1.20.0 or newer to ensure a compatible version ([#8516](https://github.com/sveltejs/svelte/pull/8516))
- breaking: Minimum supported `rollup-plugin-svelte` version is now 7.1.5 ([198dbcf](https://github.com/sveltejs/svelte/commit/198dbcf))
- breaking: Minimum supported `svelte-loader` is now 3.1.8 ([198dbcf](https://github.com/sveltejs/svelte/commit/198dbcf))
- breaking: Minimum supported TypeScript version is now TypeScript 5 (it will likely work with lower versions, but we make no guarantees about that) ([#8488](https://github.com/sveltejs/svelte/pull/8488))
- breaking: Remove `svelte/register` hook, CJS runtime version and CJS compiler output ([#8613](https://github.com/sveltejs/svelte/pull/8613))
- breaking: Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
- breaking: Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7442](https://github.com/sveltejs/svelte/pull/7442))
- breaking: Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions
(see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136))
- breaking: Overhaul and drastically improve creating custom elements with Svelte (see PR for list of changes and migration instructions) ([#8457](https://github.com/sveltejs/svelte/pull/8457))
- breaking: Deprecate `SvelteComponentTyped` in favor of `SvelteComponent` ([#8512](https://github.com/sveltejs/svelte/pull/8512))
- breaking: Make transitions local by default to prevent confusion around page navigations ([#6686](https://github.com/sveltejs/svelte/issues/6686))
- breaking: Error on falsy values instead of stores passed to `derived` ([#7947](https://github.com/sveltejs/svelte/pull/7947))
- breaking: Custom store implementers now need to pass an `update` function additionally to the `set` function ([#6750](https://github.com/sveltejs/svelte/pull/6750))
- breaking: Do not expose default slot bindings to named slots and vice versa ([#6049](https://github.com/sveltejs/svelte/pull/6049))
- breaking: Change order in which preprocessors are applied ([#8618](https://github.com/sveltejs/svelte/pull/8618))
- breaking: The runtime now makes use of `classList.toggle(name, boolean)` which does not work in very old browsers ([#8629](https://github.com/sveltejs/svelte/pull/8629))
- breaking: apply `inert` to outroing elements ([#8628](https://github.com/sveltejs/svelte/pull/8628))
- breaking: use `CustomEvent` constructor instead of deprecated `createEvent` method ([#8775](https://github.com/sveltejs/svelte/pull/8775))
### Minor Changes
- Add a way to modify attributes for script/style preprocessors ([#8618](https://github.com/sveltejs/svelte/pull/8618))
- Improve hydration speed by adding `data-svelte-h` attribute to detect unchanged HTML elements ([#7426](https://github.com/sveltejs/svelte/pull/7426))
- Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391))
- Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251))
- Allow `#each` to iterate over iterables like `Set`, `Map` etc ([#7425](https://github.com/sveltejs/svelte/issues/7425))
- Improve duplicate key error for keyed `each` blocks ([#8411](https://github.com/sveltejs/svelte/pull/8411))
- Warn about `:` in attributes and props to prevent ambiguity with Svelte directives ([#6823](https://github.com/sveltejs/svelte/issues/6823))
- feat: add version info to `window`. You can opt out by setting `discloseVersion` to `false` in the compiler options ([#8761](https://github.com/sveltejs/svelte/pull/8761))
- feat: smaller minified output for destructor chunks ([#8763](https://github.com/sveltejs/svelte/pull/8763))
### Patch Changes
- Bind `null` option and input values consistently ([#8312](https://github.com/sveltejs/svelte/issues/8312))
- Allow `$store` to be used with changing values including nullish values ([#7555](https://github.com/sveltejs/svelte/issues/7555))
- Initialize stylesheet with `/* empty */` to enable setting CSP directive that also works in Safari ([#7800](https://github.com/sveltejs/svelte/pull/7800))
- Treat slots as if they don't exist when using CSS adjacent and general sibling combinators ([#8284](https://github.com/sveltejs/svelte/issues/8284))
- Fix transitions so that they don't require a `style-src 'unsafe-inline'` Content Security Policy (CSP) ([#6662](https://github.com/sveltejs/svelte/issues/6662)).
- Explicitly disallow `var` declarations extending the reactive statement scope ([#6800](https://github.com/sveltejs/svelte/pull/6800))
- Improve error message when trying to use `animate:` directives on inline components ([#8641](https://github.com/sveltejs/svelte/issues/8641))
- fix: export ComponentType from `svelte` entrypoint ([#8578](https://github.com/sveltejs/svelte/pull/8578))
- fix: never use html optimization for mustache tags in hydration mode ([#8744](https://github.com/sveltejs/svelte/pull/8744))
- fix: derived store types ([#8578](https://github.com/sveltejs/svelte/pull/8578))
- Generate type declarations with dts-buddy ([#8578](https://github.com/sveltejs/svelte/pull/8578))
- fix: ensure types are loaded with all TS settings ([#8721](https://github.com/sveltejs/svelte/pull/8721))
- fix: account for preprocessor source maps when calculating meta info ([#8778](https://github.com/sveltejs/svelte/pull/8778))
- chore: deindent cjs output for compiler ([#8785](https://github.com/sveltejs/svelte/pull/8785))
- warn on boolean compilerOptions.css ([#8710](https://github.com/sveltejs/svelte/pull/8710))
- fix: export correct SvelteComponent type ([#8721](https://github.com/sveltejs/svelte/pull/8721))
## 4.0.0-next.3
### Patch Changes
- feat: smaller minified output for destructor chunks ([#8763](https://github.com/sveltejs/svelte/pull/8763))
- breaking: use `CustomEvent` constructor instead of deprecated `createEvent` method ([#8775](https://github.com/sveltejs/svelte/pull/8775))
- fix: account for preprocessor source maps when calculating meta info ([#8778](https://github.com/sveltejs/svelte/pull/8778))
- chore: deindent cjs output for compiler ([#8785](https://github.com/sveltejs/svelte/pull/8785))
- feat: add version info to `window`. You can opt out by setting `discloseVersion` to `false` in the compiler options ([#8761](https://github.com/sveltejs/svelte/pull/8761))
## 4.0.0-next.2
### Patch Changes
@ -69,7 +186,7 @@
## 3.59.2
* Fix escaping `<textarea bind:value={...}>` values in SSR
- Fix escaping `<textarea bind:value={...}>` values in SSR
## 3.59.1

@ -27,7 +27,7 @@ See [the SvelteKit documentation](https://kit.svelte.dev/docs) to learn more.
## Changelog
[The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md).
[The Changelog for this package is available on GitHub](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md).
## Supporting Svelte

@ -1,12 +1,13 @@
{
"name": "svelte",
"version": "4.0.0-next.2",
"version": "4.0.0",
"description": "Cybernetically enhanced web apps",
"type": "module",
"module": "src/runtime/index.js",
"main": "src/runtime/index.js",
"files": [
"src",
"!src/**/tsconfig.json",
"types",
"compiler.*",
"register.js",
@ -25,40 +26,43 @@
".": {
"types": "./types/index.d.ts",
"browser": {
"import": "./src/runtime/index.js"
"default": "./src/runtime/index.js"
},
"import": "./src/runtime/ssr.js"
"default": "./src/runtime/ssr.js"
},
"./compiler": {
"types": "./types/index.d.ts",
"import": "./src/compiler/index.js",
"require": "./compiler.cjs"
"require": "./compiler.cjs",
"default": "./src/compiler/index.js"
},
"./action": {
"types": "./types/index.d.ts"
},
"./animate": {
"types": "./types/index.d.ts",
"import": "./src/runtime/animate/index.js"
"default": "./src/runtime/animate/index.js"
},
"./easing": {
"types": "./types/index.d.ts",
"import": "./src/runtime/easing/index.js"
"default": "./src/runtime/easing/index.js"
},
"./internal": {
"import": "./src/runtime/internal/index.js"
"default": "./src/runtime/internal/index.js"
},
"./motion": {
"types": "./types/index.d.ts",
"import": "./src/runtime/motion/index.js"
"default": "./src/runtime/motion/index.js"
},
"./store": {
"types": "./types/index.d.ts",
"import": "./src/runtime/store/index.js"
"default": "./src/runtime/store/index.js"
},
"./internal/disclose-version": {
"import": "./src/runtime/internal/disclose-version/index.js"
},
"./transition": {
"types": "./types/index.d.ts",
"import": "./src/runtime/transition/index.js"
"default": "./src/runtime/transition/index.js"
},
"./elements": {
"types": "./elements.d.ts"
@ -100,8 +104,9 @@
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
"acorn": "^8.8.2",
"aria-query": "^5.2.1",
"@jridgewell/trace-mapping": "^0.3.18",
"acorn": "^8.9.0",
"aria-query": "^5.3.0",
"axobject-query": "^3.2.1",
"code-red": "^1.0.3",
"css-tree": "^2.3.1",
@ -112,24 +117,24 @@
"periscopic": "^3.1.0"
},
"devDependencies": {
"@playwright/test": "^1.34.3",
"@playwright/test": "^1.35.1",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.2",
"@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.14.31",
"@types/node": "^14.18.51",
"agadoo": "^3.0.0",
"dts-buddy": "^0.1.7",
"esbuild": "^0.17.19",
"happy-dom": "^9.18.3",
"jsdom": "^21.1.1",
"happy-dom": "^9.20.3",
"jsdom": "^21.1.2",
"kleur": "^4.1.5",
"rollup": "^3.20.2",
"rollup": "^3.25.1",
"source-map": "^0.7.4",
"tiny-glob": "^0.2.9",
"typescript": "^5.0.4",
"vitest": "^0.31.1"
"typescript": "^5.1.3",
"vitest": "^0.31.4"
}
}
}

@ -42,7 +42,8 @@ export default [
file: 'compiler.cjs',
format: 'umd',
name: 'svelte',
sourcemap: false
sourcemap: false,
indent: false
},
external: []
}

@ -4,5 +4,15 @@ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
fs.writeFileSync(
'./src/shared/version.js',
`// generated during release, do not modify\n\n/** @type {string} */\nexport const VERSION = '${pkg.version}';\n`
`// generated during release, do not modify
/**
* The current version, as set in package.json.
*
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '${pkg.version}';
export const PUBLIC_VERSION = '${pkg.version.split('.')[0]}';
`
);

@ -1,10 +1,4 @@
const now =
typeof process !== 'undefined' && process.hrtime
? () => {
const t = process.hrtime();
return t[0] * 1e3 + t[1] / 1e6;
}
: () => self.performance.now();
const now = () => performance.now();
/** @param {any} timings */
function collapse_timings(timings) {

@ -1,3 +1,4 @@
import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping';
import { walk } from 'estree-walker';
import { getLocator } from 'locate-character';
import { reserved, is_valid } from '../utils/names.js';
@ -142,9 +143,20 @@ export default class Component {
/** @type {string} */
file;
/** @type {(c: number) => { line: number; column: number }} */
/**
* Use this for stack traces. It is 1-based and acts on pre-processed sources.
* Use `meta_locate` for metadata on DOM elements.
* @type {(c: number) => { line: number; column: number }}
*/
locate;
/**
* Use this for metadata on DOM elements. It is 1-based and acts on sources that have not been pre-processed.
* Use `locate` for source mappings.
* @type {(c: number) => { line: number; column: number }}
*/
meta_locate;
/** @type {import('./nodes/Element.js').default[]} */
elements = [];
@ -199,7 +211,25 @@ export default class Component {
.replace(process.cwd(), '')
.replace(regex_leading_directory_separator, '')
: compile_options.filename);
// line numbers in stack trace frames are 1-based. source maps are 0-based
this.locate = getLocator(this.source, { offsetLine: 1 });
/** @type {TraceMap | null | undefined} initialise lazy because only used in dev mode */
let tracer;
this.meta_locate = (c) => {
/** @type {{ line: number, column: number }} */
let location = this.locate(c);
if (tracer === undefined) {
// @ts-expect-error - fix the type of CompileOptions.sourcemap
tracer = compile_options.sourcemap ? new TraceMap(compile_options.sourcemap) : null;
}
if (tracer) {
// originalPositionFor returns 1-based lines like locator
location = originalPositionFor(tracer, location);
}
return location;
};
// styles
this.stylesheet = new Stylesheet({
source,

@ -30,7 +30,8 @@ const valid_options = [
'loopGuardTimeout',
'preserveComments',
'preserveWhitespace',
'cssHash'
'cssHash',
'discloseVersion'
];
const valid_css_values = [true, false, 'injected', 'external', 'none'];
const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
@ -112,6 +113,10 @@ function validate_options(options, warnings) {
throw new Error(`Invalid namespace '${namespace}'`);
}
}
if (options.discloseVersion == undefined) {
options.discloseVersion = true;
}
}
/**

@ -694,7 +694,11 @@ export default class Element extends Node {
);
}
// no-redundant-roles
if (current_role === get_implicit_role(this.name, attribute_map)) {
if (
current_role === get_implicit_role(this.name, attribute_map) &&
// <ul role="list"> is ok because CSS list-style:none removes the semantics and this is a way to bring them back
!['ul', 'ol', 'li'].includes(this.name)
) {
component.warn(
attribute,
compiler_warnings.a11y_no_redundant_roles(current_role)

@ -36,6 +36,7 @@ export function unpack_destructuring({
if (in_rest_element) {
context_rest_properties.set(node.name, node);
}
component.used_names.add(node.name);
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (!element) {

@ -62,9 +62,20 @@ export default class Renderer {
/** @type {import('estree').Identifier} */
file_var;
/** @type {(c: number) => { line: number; column: number }} */
/**
* Use this for stack traces. It is 1-based and acts on pre-processed sources.
* Use `meta_locate` for metadata on DOM elements.
* @type {(c: number) => { line: number; column: number }}
*/
locate;
/**
* Use this for metadata on DOM elements. It is 1-based and acts on sources that have not been pre-processed.
* Use `locate` for source mappings.
* @type {(c: number) => { line: number; column: number }}
*/
meta_locate;
/**
* @param {import('../Component.js').default} component
* @param {import('../../interfaces.js').CompileOptions} options
@ -73,6 +84,7 @@ export default class Renderer {
this.component = component;
this.options = options;
this.locate = component.locate; // TODO messy
this.meta_locate = component.meta_locate; // TODO messy
this.file_var = options.dev && this.component.get_unique_name('file');
component.vars
.filter((v) => !v.hoistable || (v.export_name && !v.module))

@ -604,5 +604,17 @@ export default function dom(component, options) {
);
}
}
if (options.discloseVersion === true) {
component.imports.unshift({
type: 'ImportDeclaration',
specifiers: [],
source: {
type: 'Literal',
value: `${options.sveltePath ?? 'svelte'}/internal/disclose-version`
}
});
}
return { js: flatten(body), css };
}

@ -233,6 +233,14 @@ export default class ElementWrapper extends Wrapper {
strip_whitespace,
next_sibling
);
// in the case of `parent_block -> child_dynamic_element_block -> child_dynamic_element`
// `child_dynamic_element_block.add_intro/outro` is called inside `new ElementWrapper()`
// but when `is_local === true` it does not bubble to parent_block
// we manually add transitions back to the parent_block (#8233)
if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local);
// the original svelte:element is never used for rendering, because
// it gets assigned a child_dynamic_element which is used in all rendering logic.
// so doing all of this on the original svelte:element will just cause double
@ -266,10 +274,10 @@ export default class ElementWrapper extends Wrapper {
this.event_handlers = this.node.handlers.map(
(event_handler) => new EventHandler(event_handler, this)
);
if (node.intro || node.outro) {
if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local);
}
if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local);
if (node.animation) {
block.add_animation();
}
@ -585,9 +593,11 @@ export default class ElementWrapper extends Wrapper {
);
}
if (renderer.options.dev) {
const loc = renderer.locate(this.node.start);
const loc = renderer.meta_locate(this.node.start);
block.chunks.hydrate.push(
b`@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${
// TODO this.node.start isn't correct if there's a source map. But since we don't know how the
// original source file looked, there's not much we can do.
this.node.start
});`
);

@ -344,6 +344,12 @@ export interface CompileOptions {
* @default false
*/
preserveWhitespace?: boolean;
/**
* If `true`, exposes the Svelte major version on the global `window` object in the browser.
*
* @default true
*/
discloseVersion?: boolean;
}
export interface ParserOptions {

@ -266,9 +266,6 @@ export function combine_sourcemaps(filename, sourcemap_list) {
if (!map.sources.length) map.sources = [filename];
return map;
}
// browser vs node.js
const b64enc = typeof btoa == 'function' ? btoa : (b) => Buffer.from(b).toString('base64');
const b64dec = typeof atob == 'function' ? atob : (a) => Buffer.from(a, 'base64').toString();
/**
* @param {string} filename
@ -295,7 +292,7 @@ export function apply_preprocessor_sourcemap(filename, svelte_map, preprocessor_
toUrl: {
enumerable: false,
value: function toUrl() {
return 'data:application/json;charset=utf-8;base64,' + b64enc(this.toString());
return 'data:application/json;charset=utf-8;base64,' + btoa(this.toString());
}
}
});
@ -339,7 +336,7 @@ export function parse_attached_sourcemap(processed, tag_name) {
// ignore attached sourcemap
return '';
}
processed.map = b64dec(map_data); // use attached sourcemap
processed.map = atob(map_data); // use attached sourcemap
return ''; // remove from processed.code
}
// sourceMappingURL is path or URL

@ -0,0 +1,5 @@
import { PUBLIC_VERSION } from '../../../shared/version.js';
if (typeof window !== 'undefined')
// @ts-ignore
(window.__svelte || (window.__svelte = { v: new Set() })).v.add(PUBLIC_VERSION);

@ -49,7 +49,7 @@ function tick_spring(ctx, last_value, current_value, target_value) {
* The spring function in Svelte creates a store whose value is animated, with a motion that simulates the behavior of a spring. This means when the value changes, instead of transitioning at a steady rate, it "bounces" like a spring would, depending on the physics parameters provided. This adds a level of realism to the transitions and can enhance the user experience.
*
* https://svelte.dev/docs/svelte-motion#spring
* @template T
* @template [T=any]
* @param {T} [value]
* @param {import('./private.js').SpringOpts} [opts]
* @returns {import('./public.js').Spring<T>}

@ -6,4 +6,5 @@
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '4.0.0-next.2';
export const VERSION = '4.0.0';
export const PUBLIC_VERSION = '4';

Binary file not shown.

@ -147,6 +147,7 @@ export function create_loader(compileOptions, cwd) {
// any imported Svelte components as well. A few edge cases aren't handled but also
// currently unused in the tests, for example `export * from`and live bindings.
let transformed = compiled.js.code
.replace(/^import ['"]([^'"]+)['"]/gm, 'await __import("$1")')
.replace(
/^import \* as (\w+) from ['"]([^'"]+)['"];?/gm,
'const $1 = await __import("$2");'

@ -33,7 +33,12 @@ describe('js-output', () => {
let actual;
try {
const options = Object.assign({}, config.options || {});
const options = Object.assign(
{
discloseVersion: false
},
config.options || {}
);
actual = svelte
.compile(input, options)

@ -6,8 +6,7 @@ import * as svelte from 'svelte/compiler';
import { afterAll, assert, beforeAll, describe, it } from 'vitest';
import { pretty_print_browser_assertion, try_load_config } from '../helpers.js';
const internal = path.resolve('src/runtime/internal/index.js');
const index = path.resolve('src/runtime/index.js');
const assert_file = path.resolve(__dirname, 'assert.js');
/** @type {import('@playwright/test').Browser} */
let browser;
@ -62,9 +61,7 @@ async function run_browser_test(dir) {
alias: {
__MAIN_DOT_SVELTE__: path.resolve(__dirname, 'samples', dir, 'main.svelte'),
__CONFIG__: path.resolve(__dirname, 'samples', dir, '_config.js'),
'assert.js': path.resolve(__dirname, 'assert.js'),
'svelte/internal': internal,
svelte: index
'assert.js': assert_file
},
plugins: [
{
@ -169,9 +166,7 @@ async function run_custom_elements_test(dir) {
entryPoints: [`${cwd}/test.js`],
write: false,
alias: {
'assert.js': path.resolve(__dirname, 'assert.js'),
'svelte/internal': internal,
svelte: index
'assert.js': assert_file
},
plugins: [
{

@ -0,0 +1,25 @@
export default {
get props() {
return {
thePromise: new Promise((_) => {})
};
},
html: `
Waiting...
`,
async test({ assert, component, target }) {
await (component.thePromise = Promise.resolve({ func: 12345 }));
assert.htmlEqual(target.innerHTML, '12345');
try {
await (component.thePromise = Promise.reject({ func: 67890 }));
} catch (e) {
// do nothing
}
assert.htmlEqual(target.innerHTML, '67890');
}
};

@ -0,0 +1,11 @@
<script>
export let thePromise;
</script>
{#await thePromise}
Waiting...
{:then { func }}
{(() => func)()}
{:catch { func: func_1 }}
{(() => func_1)()}
{/await}

@ -0,0 +1,8 @@
<script>
const func = 100;
</script>
{#if true}
{@const [func_1] = [[12, 13, 14]]}
{(() => JSON.stringify(func_1))()}
{/if}

@ -0,0 +1,9 @@
export default {
html: `
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
`
};

@ -0,0 +1,3 @@
{#each [1, 2, 3, 4, 5] as func}
<p>{(() => func)()}</p>
{/each}

@ -0,0 +1,33 @@
import MagicString from 'magic-string';
import * as path from 'node:path';
// fake preprocessor by doing transforms on the source
const str = new MagicString(
`<script>
type Foo = 'foo';
let foo = 'foo';
</script>
<h1>{foo}</h1>
`.replace(/\r\n/g, '\n')
);
str.remove(8, 26); // remove line type Foo = ...
str.remove(55, 56); // remove whitespace before <h1>
export default {
compileOptions: {
dev: true,
sourcemap: str.generateMap({ hires: true })
},
test({ assert, target }) {
const h1 = target.querySelector('h1');
assert.deepEqual(h1.__svelte_meta.loc, {
file: path.relative(process.cwd(), path.resolve(__dirname, 'main.svelte')),
line: 5, // line 4 in main.svelte, but that's the preprocessed code, the original code is above in the fake preprocessor
column: 1, // line 0 in main.svelte, but that's the preprocessed code, the original code is above in the fake preprocessor
char: 38 // TODO this is wrong but we can't backtrace it due to limitations, see add_location function usage comment for more info
});
}
};

@ -19,12 +19,12 @@
<h5 role="heading">heading</h5>
<h6 role="heading">heading</h6>
<hr role="separator" />
<li role="listitem" />
<!-- <li role="listitem" /> allowed since CSS list-style none removes semantic meaning and role brings it back -->
<link role="link" />
<main role="main"></main>
<main role="main" />
<menu role="list" />
<nav role="navigation" />
<ol role="list" />
<!-- <ol role="list" /> allowed, see comment above -->
<optgroup role="group" />
<option role="option" />
<output role="status" />
@ -37,11 +37,11 @@
<tfoot role="rowgroup" />
<thead role="rowgroup" />
<tr role="row" />
<ul role="list" />
<!--<ul role="list" /> allowed, see comment above -->
<!-- Tested header/footer not nested in section/article -->
<header role="banner"></header>
<footer role="contentinfo"></footer>
<header role="banner" />
<footer role="contentinfo" />
<!-- Allowed -->
<!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->

@ -251,18 +251,6 @@
"line": 21
}
},
{
"code": "a11y-no-redundant-roles",
"end": {
"column": 19,
"line": 22
},
"message": "A11y: Redundant role 'listitem'",
"start": {
"column": 4,
"line": 22
}
},
{
"code": "a11y-no-redundant-roles",
"end": {
@ -311,18 +299,6 @@
"line": 26
}
},
{
"code": "a11y-no-redundant-roles",
"end": {
"column": 15,
"line": 27
},
"message": "A11y: Redundant role 'list'",
"start": {
"column": 4,
"line": 27
}
},
{
"code": "a11y-no-redundant-roles",
"end": {
@ -467,18 +443,6 @@
"line": 39
}
},
{
"code": "a11y-no-redundant-roles",
"end": {
"column": 15,
"line": 40
},
"message": "A11y: Redundant role 'list'",
"start": {
"column": 4,
"line": 40
}
},
{
"code": "a11y-no-redundant-roles",
"end": {

File diff suppressed because it is too large Load Diff

@ -18,8 +18,8 @@
},
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"@supabase/supabase-js": "^2.25.0",
"@sveltejs/repl": "0.5.0-next.5",
"@supabase/supabase-js": "^2.26.0",
"@sveltejs/repl": "0.5.0-next.7",
"cookie": "^0.5.0",
"devalue": "^4.3.2",
"do-not-zip": "^1.0.0",
@ -29,28 +29,28 @@
"devDependencies": {
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-vercel": "^3.0.1",
"@sveltejs/kit": "^1.20.4",
"@sveltejs/site-kit": "6.0.0-next.8",
"@sveltejs/vite-plugin-svelte": "^2.4.1",
"@sveltejs/kit": "^1.20.5",
"@sveltejs/site-kit": "6.0.0-next.18",
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"@types/marked": "^5.0.0",
"@types/node": "^20.3.1",
"@types/prettier": "^2.7.3",
"degit": "^2.8.4",
"dotenv": "^16.3.0",
"dotenv": "^16.3.1",
"jimp": "^0.22.8",
"magic-string": "^0.30.0",
"marked": "^5.1.0",
"node-fetch": "^3.3.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1",
"sass": "^1.63.4",
"sass": "^1.63.6",
"satori": "^0.10.1",
"satori-html": "^0.3.2",
"shelljs": "^0.8.5",
"shiki": "^0.14.2",
"shiki-twoslash": "^3.1.2",
"svelte": "workspace:*",
"svelte-check": "^3.4.3",
"svelte-check": "^3.4.4",
"svelte-preprocess": "^5.0.4",
"tiny-glob": "^0.2.9",
"typescript": "^5.1.3",

@ -1,8 +1,9 @@
import { fileURLToPath } from 'node:url';
import { get_examples_data } from '../src/lib/server/examples/index.js';
import fs from 'node:fs';
const examples_data = get_examples_data(
new URL('../../../documentation/examples', import.meta.url).pathname
fileURLToPath(new URL('../../../documentation/examples', import.meta.url))
);
try {

@ -146,7 +146,7 @@ function munge_type_element(member, depth = 1) {
// @ts-ignore
const doc = member.jsDoc?.[0];
if (/private api/i.test(doc?.comment)) return;
if (/(private api|do not use)/i.test(doc?.comment)) return;
/** @type {string[]} */
const children = [];
@ -304,7 +304,7 @@ fs.writeFileSync(
`
/* This file is generated by running \`pnpm generate\`
in the sites/svelte.dev directory do not edit it */
export const modules = /** @type {import('../generated/types').Modules} */ (${JSON.stringify(
export const modules = /** @type {import('@sveltejs/site-kit/markdown').Modules} */ (${JSON.stringify(
modules,
null,
' '

@ -30,6 +30,16 @@
%sveltekit.head%
</head>
<body data-sveltekit-preload-code="hover">
<script>
const themeValue = JSON.parse(localStorage.getItem('svelte:theme'))?.current;
const systemPreferredTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
document.body.classList.remove('light', 'dark');
document.body.classList.add(themeValue ?? systemPreferredTheme);
</script>
<div style="height: 100%">%sveltekit.body%</div>
</body>
</html>

@ -1,6 +1,6 @@
// REPL props
export const svelteUrl = `https://unpkg.com/svelte@3`;
export const svelteUrl = `https://unpkg.com/svelte@4`;
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = '${
import.meta.env.VITE_MAPBOX_ACCESS_TOKEN
}';`;

@ -15,7 +15,7 @@ export interface GitHubUser {
}
export interface Gist {
id: number;
id: string;
name: string;
owner: UserID;
files: Array<{ name: string; type: string; source: string }>;

@ -1,16 +0,0 @@
export type Modules = {
name?: string;
comment?: string;
exempt?: boolean;
types?: Child[];
exports?: Child[];
}[];
type Child = {
name: string;
snippet: string;
comment: string;
deprecated?: string;
bullets?: string[];
children?: Child[];
};

@ -1,23 +1,24 @@
// @ts-check
import { modules } from '$lib/generated/type-info.js';
import { extractFrontmatter } from '@sveltejs/site-kit/markdown';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import { extract_frontmatter } from '../markdown/index.js';
import { render_markdown } from '../markdown/renderer.js';
import { render_content } from '../renderer.js';
/**
* @param {import('./types').BlogData} blog_data
* @param {string} slug
*/
export async function get_processed_blog_post(blog_data, slug) {
const post = blog_data.find((post) => post.slug === slug);
if (!post) return null;
for (const post of blog_data) {
if (post.slug === slug) {
return {
...post,
content: await render_content(post.file, post.content)
};
}
}
return {
...post,
content: await render_markdown(post.file, post.content, { modules })
};
return null;
}
const BLOG_NAME_REGEX = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
@ -31,7 +32,7 @@ export function get_blog_data(base = CONTENT_BASE_PATHS.BLOG) {
if (!BLOG_NAME_REGEX.test(file)) continue;
const { date, date_formatted, slug } = get_date_and_slug(file);
const { metadata, body } = extract_frontmatter(fs.readFileSync(`${base}/${file}`, 'utf-8'));
const { metadata, body } = extractFrontmatter(fs.readFileSync(`${base}/${file}`, 'utf-8'));
blog_posts.push({
date,

@ -1,15 +1,16 @@
import { base as app_base } from '$app/paths';
import { modules } from '$lib/generated/type-info.js';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import {
escape,
extract_frontmatter,
extractFrontmatter,
markedTransform,
normalizeSlugify,
removeMarkdown,
transform
} from '../markdown/index.js';
import { render_markdown } from '../markdown/renderer.js';
replaceExportTypePlaceholders
} from '@sveltejs/site-kit/markdown';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import { render_content } from '../renderer';
/**
* @param {import('./types').DocsData} docs_data
@ -21,7 +22,7 @@ export async function get_parsed_docs(docs_data, slug) {
if (page.slug === slug) {
return {
...page,
content: await render_markdown(page.file, page.content, { modules })
content: await render_content(page.file, page.content)
};
}
}
@ -62,7 +63,7 @@ export function get_docs_data(base = CONTENT_BASE_PATHS.DOCS) {
const page_slug = match[1].replace('.md', '');
const page_data = extract_frontmatter(
const page_data = extractFrontmatter(
fs.readFileSync(`${base}/${category_dir}/${filename}`, 'utf-8')
);
@ -98,27 +99,57 @@ export function get_docs_list(docs_data) {
}));
}
const titled = (str) =>
removeMarkdown(
escape(markedTransform(str, { paragraph: (txt) => txt }))
.replace(/<\/?code>/g, '')
.replace(/&#39;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/<(\/)?(em|b|strong|code)>/g, '')
);
/** @param {string} markdown */
function get_sections(markdown) {
const headingRegex = /^##\s+(.*)$/gm;
/** @type {import('./types').Section[]} */
const secondLevelHeadings = [];
let match;
while ((match = headingRegex.exec(markdown)) !== null) {
secondLevelHeadings.push({
title: removeMarkdown(
escape(transform(match[1], { paragraph: (txt) => txt }))
.replace(/<\/?code>/g, '')
.replace(/&#39;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/<(\/)?(em|b|strong|code)>/g, '')
),
slug: normalizeSlugify(match[1])
});
}
const lines = markdown.split('\n');
const root = /** @type {import('./types').Section} */ ({
title: 'Root',
slug: 'root',
sections: [],
breadcrumbs: [''],
text: ''
});
let currentNodes = [root];
lines.forEach((line) => {
const match = line.match(/^(#{2,4})\s(.*)/);
if (match) {
const level = match[1].length - 2;
const text = titled(match[2]);
const slug = normalizeSlugify(text);
// Prepare new node
/** @type {import('./types').Section} */
const newNode = {
title: text,
slug,
sections: [],
breadcrumbs: [...currentNodes[level].breadcrumbs, text],
text: ''
};
// Add the new node to the tree
currentNodes[level].sections.push(newNode);
// Prepare for potential children of the new node
currentNodes = currentNodes.slice(0, level + 1);
currentNodes.push(newNode);
} else if (line.trim() !== '') {
// Add non-heading line to the text of the current section
currentNodes[currentNodes.length - 1].text += line + '\n';
}
});
return secondLevelHeadings;
return root.sections;
}

@ -5,6 +5,8 @@ export interface Section {
slug: string;
// Currently, we are only going with 2 level headings, so this will be undefined. In future, we may want to support 3 levels, in which case this will be a list of sections
sections?: Section[];
breadcrumbs: string[];
text: string;
}
export type Category = {

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

Loading…
Cancel
Save