Add two-slash to docs pages

pull/8452/head
Puru Vijay 3 years ago
parent 9b439393e6
commit 4a288a5e33

@ -239,7 +239,8 @@ Local variables (that do not represent store values) must _not_ have a `$` prefi
#### Store contract #### Store contract
```js ```ts
// @noErrors
store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void } store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
``` ```

@ -397,7 +397,8 @@ use:action
use:action={parameters} use:action={parameters}
``` ```
```js ```ts
// @noErrors
action = (node: HTMLElement, parameters: any) => { action = (node: HTMLElement, parameters: any) => {
update?: (parameters: any) => void, update?: (parameters: any) => void,
destroy?: () => void destroy?: () => void
@ -471,6 +472,7 @@ transition:fn|local={params}
``` ```
```js ```js
// @noErrors
transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => { transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
delay?: number, delay?: number,
duration?: number, duration?: number,
@ -670,6 +672,7 @@ animate:name={params}
``` ```
```js ```js
// @noErrors
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => { animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
delay?: number, delay?: number,
duration?: number, duration?: number,
@ -680,6 +683,7 @@ animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) =>
``` ```
```ts ```ts
// @noErrors
DOMRect { DOMRect {
bottom: number, bottom: number,
height: number, height: number,

@ -36,6 +36,7 @@ count.update((n) => n + 1); // logs '2'
If a function is passed as the second argument, it will be called when the number of subscribers goes from zero to one (but not from one to two, etc). That function will be passed a `set` function which changes the value of the store. It must return a `stop` function that is called when the subscriber count goes from one to zero. If a function is passed as the second argument, it will be called when the number of subscribers goes from zero to one (but not from one to two, etc). That function will be passed a `set` function which changes the value of the store. It must return a `stop` function that is called when the subscriber count goes from one to zero.
```js ```js
/// file: store.js
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
const count = writable(0, () => { const count = writable(0, () => {
@ -64,8 +65,7 @@ Creates a store whose value cannot be set from 'outside', the first argument is
/// file: store.js /// file: store.js
import { readable } from 'svelte/store'; import { readable } from 'svelte/store';
/** @type {import('svelte/store').Readable<Date>} */ const time = readable(new Date(), (set) => {
const time = readable(null, (set) => {
set(new Date()); set(new Date());
const interval = setInterval(() => { const interval = setInterval(() => {
@ -84,7 +84,18 @@ Derives a store from one or more other stores. The callback runs initially when
In the simplest version, `derived` takes a single store, and the callback returns a derived value. In the simplest version, `derived` takes a single store, and the callback returns a derived value.
```js ```ts
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
declare global {
const a: Writable<number>;
}
export {};
// @filename: index.ts
// ---cut---
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
const doubled = derived(a, ($a) => $a * 2); const doubled = derived(a, ($a) => $a * 2);
@ -94,9 +105,18 @@ The callback can set a value asynchronously by accepting a second argument, `set
In this case, you can also pass a third argument to `derived` — the initial value of the derived store before `set` is first called. In this case, you can also pass a third argument to `derived` — the initial value of the derived store before `set` is first called.
<!-- TODO types -->
```js ```js
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
declare global {
const a: Writable<number>;
}
export {};
// @filename: index.ts
// ---cut---
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
const delayed = derived( const delayed = derived(
@ -104,15 +124,24 @@ const delayed = derived(
($a, set) => { ($a, set) => {
setTimeout(() => set($a), 1000); setTimeout(() => set($a), 1000);
}, },
'one moment...' 2000
); );
``` ```
If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes. If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes.
<!-- TODO types -->
```js ```js
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
declare global {
const frequency: Writable<number>;
}
export {};
// @filename: index.ts
// ---cut---
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
const tick = derived( const tick = derived(
@ -126,15 +155,26 @@ const tick = derived(
clearInterval(interval); clearInterval(interval);
}; };
}, },
'one moment...' 2000
); );
``` ```
In both cases, an array of arguments can be passed as the first argument instead of a single store. In both cases, an array of arguments can be passed as the first argument instead of a single store.
<!-- TODO type --> ```ts
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
```js declare global {
const a: Writable<number>;
const b: Writable<number>;
}
export {};
// @filename: index.ts
// ---cut---
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
const summed = derived([a, b], ([$a, $b]) => $a + $b); const summed = derived([a, b], ([$a, $b]) => $a + $b);
@ -151,7 +191,7 @@ const delayed = derived([a, b], ([$a, $b], set) => {
This simple helper function makes a store readonly. You can still subscribe to the changes from the original one using this new readable store. This simple helper function makes a store readonly. You can still subscribe to the changes from the original one using this new readable store.
```js ```js
import { readonly } from 'svelte/store'; import { readonly, writable } from 'svelte/store';
const writableStore = writable(1); const writableStore = writable(1);
const readableStore = readonly(writableStore); const readableStore = readonly(writableStore);
@ -159,6 +199,7 @@ const readableStore = readonly(writableStore);
readableStore.subscribe(console.log); readableStore.subscribe(console.log);
writableStore.set(2); // console: 2 writableStore.set(2); // console: 2
// @errors: 2339
readableStore.set(2); // ERROR readableStore.set(2); // ERROR
``` ```
@ -171,6 +212,17 @@ Generally, you should read the value of a store by subscribing to it and using t
> This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths. > This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
```js ```js
// @filename: ambient.d.ts
import { type Writable } from 'svelte/store';
declare global {
const store: Writable<string>;
}
export {};
// @filename: index.ts
// ---cut---
import { get } from 'svelte/store'; import { get } from 'svelte/store';
const value = get(store); const value = get(store);

@ -44,7 +44,19 @@ Out of the box, Svelte will interpolate between two numbers, two arrays or two o
If the initial value is `undefined` or `null`, the first value change will take effect immediately. This is useful when you have tweened values that are based on props, and don't want any motion when the component first renders. If the initial value is `undefined` or `null`, the first value change will take effect immediately. This is useful when you have tweened values that are based on props, and don't want any motion when the component first renders.
```js ```ts
// @filename: ambient.d.ts
declare global {
var $size: number;
var big: number;
}
export {};
// @filename: motion.ts
// ---cut---
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const size = tweened(undefined, { const size = tweened(undefined, {
duration: 300, duration: 300,
easing: cubicOut easing: cubicOut
@ -90,6 +102,8 @@ A `spring` store gradually changes to its target value based on its `stiffness`
All of the options above can be changed while the spring is in motion, and will take immediate effect. All of the options above can be changed while the spring is in motion, and will take immediate effect.
```js ```js
import { spring } from 'svelte/motion';
const size = spring(100); const size = spring(100);
size.stiffness = 0.3; size.stiffness = 0.3;
size.damping = 0.4; size.damping = 0.4;
@ -101,6 +115,8 @@ As with [`tweened`](/docs/svelte-motion#tweened) stores, `set` and `update` retu
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`. Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.
```js ```js
import { spring } from 'svelte/motion';
const coords = spring({ x: 50, y: 50 }); const coords = spring({ x: 50, y: 50 });
// updates the value immediately // updates the value immediately
coords.set({ x: 100, y: 200 }, { hard: true }); coords.set({ x: 100, y: 200 }, { hard: true });
@ -131,7 +147,19 @@ coords.update(
If the initial value is `undefined` or `null`, the first value change will take effect immediately, just as with `tweened` values (see above). If the initial value is `undefined` or `null`, the first value change will take effect immediately, just as with `tweened` values (see above).
```js ```ts
// @filename: ambient.d.ts
declare global {
var $size: number;
var big: number;
}
export {};
// @filename: motion.ts
// ---cut---
import { spring } from 'svelte/motion';
const size = spring(); const size = spring();
$: $size = big ? 100 : 10; $: $size = big ? 100 : 10;
``` ```

@ -13,6 +13,15 @@ Nonetheless, it's useful to understand how to use the compiler, since bundler pl
This is where the magic happens. `svelte.compile` takes your component source code, and turns it into a JavaScript module that exports a class. This is where the magic happens. `svelte.compile` takes your component source code, and turns it into a JavaScript module that exports a class.
```js ```js
// @filename: ambient.d.ts
declare global {
var source: string
}
export {}
// @filename: index.ts
// ---cut---
import { compile } from 'svelte/compiler'; import { compile } from 'svelte/compiler';
const result = compile(source, { const result = compile(source, {
@ -74,7 +83,17 @@ The following options can be passed to the compiler. None are required:
The returned `result` object contains the code for your component, along with useful bits of metadata. The returned `result` object contains the code for your component, along with useful bits of metadata.
```js ```ts
// @filename: ambient.d.ts
declare global {
const source: string;
}
export {};
// @filename: main.ts
import { compile } from 'svelte/compiler';
// ---cut---
const { js, css, ast, warnings, vars, stats } = compile(source); const { js, css, ast, warnings, vars, stats } = compile(source);
``` ```
@ -143,6 +162,15 @@ compiled: {
The `parse` function parses a component, returning only its abstract syntax tree. Unlike compiling with the `generate: false` option, this will not perform any validation or other analysis of the component beyond parsing it. Note that the returned AST is not considered public API, so breaking changes could occur at any point in time. The `parse` function parses a component, returning only its abstract syntax tree. Unlike compiling with the `generate: false` option, this will not perform any validation or other analysis of the component beyond parsing it. Note that the returned AST is not considered public API, so breaking changes could occur at any point in time.
```js ```js
// @filename: ambient.d.ts
declare global {
var source: string;
}
export {};
// @filename: main.ts
// ---cut---
import { parse } from 'svelte/compiler'; import { parse } from 'svelte/compiler';
const ast = parse(source, { filename: 'App.svelte' }); const ast = parse(source, { filename: 'App.svelte' });
@ -167,6 +195,15 @@ The `markup` function receives the entire component source text, along with the
> Preprocessor functions should additionally return a `map` object alongside `code` and `dependencies`, where `map` is a sourcemap representing the transformation. > Preprocessor functions should additionally return a `map` object alongside `code` and `dependencies`, where `map` is a sourcemap representing the transformation.
```js ```js
// @filename: ambient.d.ts
declare global {
var source: string;
}
export {};
// @filename: main.ts
// ---cut---
import { preprocess } from 'svelte/compiler'; import { preprocess } from 'svelte/compiler';
import MagicString from 'magic-string'; import MagicString from 'magic-string';
@ -196,10 +233,21 @@ The `script` and `style` functions receive the contents of `<script>` and `<styl
If a `dependencies` array is returned, it will be included in the result object. This is used by packages like [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte) and [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) to watch additional files for changes, in the case where your `<style>` tag has an `@import` (for example). If a `dependencies` array is returned, it will be included in the result object. This is used by packages like [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte) and [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) to watch additional files for changes, in the case where your `<style>` tag has an `@import` (for example).
```js ```ts
// @filename: ambient.d.ts
declare global {
var source: string;
}
export {};
// @filename: main.ts
// @errors: 2322 2345
/// <reference types="@types/node" />
// ---cut---
import { dirname } from 'node:path';
import { preprocess } from 'svelte/compiler'; import { preprocess } from 'svelte/compiler';
import sass from 'sass'; import sass from 'sass';
import { dirname } from 'path';
const { code, dependencies } = await preprocess( const { code, dependencies } = await preprocess(
source, source,
@ -227,6 +275,15 @@ const { code, dependencies } = await preprocess(
Multiple preprocessors can be used together. The output of the first becomes the input to the second. `markup` functions run first, then `script` and `style`. Multiple preprocessors can be used together. The output of the first becomes the input to the second. `markup` functions run first, then `script` and `style`.
```js ```js
// @filename: ambient.d.ts
declare global {
var source: string;
}
export {};
// @filename: main.ts
// ---cut---
import { preprocess } from 'svelte/compiler'; import { preprocess } from 'svelte/compiler';
const { code } = await preprocess( const { code } = await preprocess(
@ -270,6 +327,18 @@ The `walk` function provides a way to walk the abstract syntax trees generated b
The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node. The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node.
```js ```js
// @filename: ambient.d.ts
declare global {
var ast: import('estree').Node;
function do_something(node: import('estree').Node): void;
function do_something_else(node: import('estree').Node): void;
function should_skip_children(node: import('estree').Node): boolean;
}
export {};
// @filename: main.ts
// ---cut---
import { walk } from 'svelte/compiler'; import { walk } from 'svelte/compiler';
walk(ast, { walk(ast, {

@ -4,13 +4,33 @@ title: 'Client-side component API'
## Creating a component ## Creating a component
```js ```ts
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var options: ComponentConstructorOptions<Record<string, any>>;
}
// @filename: index.ts
// ---cut---
const component = new Component(options); const component = new Component(options);
``` ```
A client-side component — that is, a component compiled with `generate: 'dom'` (or the `generate` option left unspecified) is a JavaScript class. A client-side component — that is, a component compiled with `generate: 'dom'` (or the `generate` option left unspecified) is a JavaScript class.
```js ```ts
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare module './App.svelte' {
class Component extends SvelteComponent {}
export default Component;
}
// @filename: index.ts
// ---cut---
import App from './App.svelte'; import App from './App.svelte';
const app = new App({ const app = new App({
@ -42,7 +62,18 @@ Whereas children of `target` are normally left alone, `hydrate: true` will cause
The existing DOM doesn't need to match the component — Svelte will 'repair' the DOM as it goes. The existing DOM doesn't need to match the component — Svelte will 'repair' the DOM as it goes.
```js ```ts
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare module './App.svelte' {
class Component extends SvelteComponent {}
export default Component;
}
// @filename: index.ts
// @errors: 2322
// ---cut---
import App from './App.svelte'; import App from './App.svelte';
const app = new App({ const app = new App({
@ -53,7 +84,20 @@ const app = new App({
## `$set` ## `$set`
```js ```ts
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
var props: Record<string, any>;
}
export {};
// @filename: index.ts
// ---cut---
component.$set(props); component.$set(props);
``` ```
@ -61,22 +105,60 @@ Programmatically sets props on an instance. `component.$set({ x: 1 })` is equiva
Calling this method schedules an update for the next microtask — the DOM is _not_ updated synchronously. Calling this method schedules an update for the next microtask — the DOM is _not_ updated synchronously.
```js ```ts
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
}
export {};
// @filename: index.ts
// ---cut---
component.$set({ answer: 42 }); component.$set({ answer: 42 });
``` ```
## `$on` ## `$on`
```js ```ts
component.$on(event, callback); // @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
var ev: string;
var callback: (event: CustomEvent) => void;
}
export {};
// @filename: index.ts
// ---cut---
component.$on(ev, callback);
``` ```
Causes the `callback` function to be called whenever the component dispatches an `event`. Causes the `callback` function to be called whenever the component dispatches an `event`.
A function is returned that will remove the event listener when called. A function is returned that will remove the event listener when called.
```js ```ts
const off = app.$on('selected', (event) => { // @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
}
export {};
// @filename: index.ts
// ---cut---
const off = component.$on('selected', (event) => {
console.log(event.detail.selection); console.log(event.detail.selection);
}); });
@ -86,6 +168,18 @@ off();
## `$destroy` ## `$destroy`
```js ```js
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
}
export {}
// @filename: index.ts
// ---cut---
component.$destroy(); component.$destroy();
``` ```
@ -94,10 +188,35 @@ Removes a component from the DOM and triggers any `onDestroy` handlers.
## Component props ## Component props
```js ```js
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
}
export {}
// @filename: index.ts
// ---cut---
component.prop; component.prop;
``` ```
```js ```js
// @filename: ambiend.d.ts
import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
var value: unknown;
}
export {}
// @filename: index.ts
// ---cut---
component.prop = value; component.prop = value;
``` ```
@ -106,6 +225,19 @@ If a component is compiled with `accessors: true`, each instance will have gette
By default, `accessors` is `false`, unless you're compiling as a custom element. By default, `accessors` is `false`, unless you're compiling as a custom element.
```js ```js
console.log(app.count); // @filename: ambiend.d.ts
app.count += 1; import { SvelteComponent, ComponentConstructorOptions } from 'svelte';
declare global {
class Component extends SvelteComponent {}
var component: Component;
var props: Record<string, any>;
}
export {}
// @filename: index.ts
// ---cut---
console.log(component.count);
component.count += 1;
``` ```

@ -3,6 +3,7 @@ title: 'Server-side component API'
--- ---
```js ```js
// @noErrors
const result = Component.render(...) const result = Component.render(...)
``` ```
@ -13,6 +14,7 @@ A server-side component exposes a `render` method that can be called with option
You can import a Svelte component directly into Node using [`svelte/register`](/docs/svelte-register). You can import a Svelte component directly into Node using [`svelte/register`](/docs/svelte-register).
```js ```js
// @noErrors
require('svelte/register'); require('svelte/register');
const App = require('./App.svelte').default; const App = require('./App.svelte').default;
@ -36,6 +38,7 @@ The `options` object takes in the following options:
| `context` | `new Map()` | A `Map` of root-level context key-value pairs to supply to the component | | `context` | `new Map()` | A `Map` of root-level context key-value pairs to supply to the component |
```js ```js
// @noErrors
const { head, html, css } = App.render( const { head, html, css } = App.render(
// props // props
{ answer: 42 }, { answer: 42 },

@ -18,6 +18,7 @@ Svelte components can also be compiled to custom elements (aka web components) u
Alternatively, use `tag={null}` to indicate that the consumer of the custom element should name it. Alternatively, use `tag={null}` to indicate that the consumer of the custom element should name it.
```js ```js
// @noErrors
import MyElement from './MyElement.svelte'; import MyElement from './MyElement.svelte';
customElements.define('my-element', MyElement); customElements.define('my-element', MyElement);
@ -38,6 +39,7 @@ By default, custom elements are compiled with `accessors: true`, which means tha
To prevent this, add `accessors={false}` to `<svelte:options>`. To prevent this, add `accessors={false}` to `<svelte:options>`.
```js ```js
// @noErrors
const el = document.querySelector('my-element'); const el = document.querySelector('my-element');
// get the current value of the 'name' prop // get the current value of the 'name' prop

@ -5,11 +5,12 @@ title: 'svelte/register'
To render Svelte components in Node.js without bundling, use `require('svelte/register')`. After that, you can use `require` to include any `.svelte` file. To render Svelte components in Node.js without bundling, use `require('svelte/register')`. After that, you can use `require` to include any `.svelte` file.
```js ```js
// @noErrors
require('svelte/register'); require('svelte/register');
const App = require('./App.svelte').default; const App = require('./App.svelte').default;
... // ...
const { html, css, head } = App.render({ answer: 42 }); const { html, css, head } = App.render({ answer: 42 });
``` ```
@ -19,6 +20,7 @@ const { html, css, head } = App.render({ answer: 42 });
To set compile options, or to use a custom file extension, call the `register` hook as a function: To set compile options, or to use a custom file extension, call the `register` hook as a function:
```js ```js
// @noErrors
require('svelte/register')({ require('svelte/register')({
extensions: ['.customextension'], // defaults to ['.html', '.svelte'] extensions: ['.customextension'], // defaults to ['.html', '.svelte']
preserveComments: true preserveComments: true

@ -23,6 +23,7 @@
"@sveltejs/kit": "^1.15.4", "@sveltejs/kit": "^1.15.4",
"@sveltejs/site-kit": "^4.1.0", "@sveltejs/site-kit": "^4.1.0",
"@types/marked": "^4.0.8", "@types/marked": "^4.0.8",
"@types/node": "^18.15.11",
"@types/prismjs": "^1.26.0", "@types/prismjs": "^1.26.0",
"degit": "^2.8.4", "degit": "^2.8.4",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
@ -35,6 +36,7 @@
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"rollup": "^3.20.2", "rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0", "rollup-plugin-dts": "^5.3.0",
"sass": "^1.62.0",
"satori": "^0.4.7", "satori": "^0.4.7",
"satori-html": "^0.3.2", "satori-html": "^0.3.2",
"shelljs": "^0.8.5", "shelljs": "^0.8.5",
@ -2632,6 +2634,12 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/immutable": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz",
"integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==",
"dev": true
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3804,6 +3812,23 @@
"rimraf": "bin.js" "rimraf": "bin.js"
} }
}, },
"node_modules/sass": {
"version": "1.62.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
"integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/satori": { "node_modules/satori": {
"version": "0.4.7", "version": "0.4.7",
"resolved": "https://registry.npmjs.org/satori/-/satori-0.4.7.tgz", "resolved": "https://registry.npmjs.org/satori/-/satori-0.4.7.tgz",

@ -32,6 +32,7 @@
"@sveltejs/kit": "^1.15.4", "@sveltejs/kit": "^1.15.4",
"@sveltejs/site-kit": "^4.1.0", "@sveltejs/site-kit": "^4.1.0",
"@types/marked": "^4.0.8", "@types/marked": "^4.0.8",
"@types/node": "^18.15.11",
"@types/prismjs": "^1.26.0", "@types/prismjs": "^1.26.0",
"degit": "^2.8.4", "degit": "^2.8.4",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
@ -44,6 +45,7 @@
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"rollup": "^3.20.2", "rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0", "rollup-plugin-dts": "^5.3.0",
"sass": "^1.62.0",
"satori": "^0.4.7", "satori": "^0.4.7",
"satori-html": "^0.3.2", "satori-html": "^0.3.2",
"shelljs": "^0.8.5", "shelljs": "^0.8.5",

@ -1,9 +1,5 @@
// import 'prism-svelte'; import { createShikiHighlighter, renderCodeToHTML, runTwoSlash } from 'shiki-twoslash';
// import 'prismjs/components/prism-bash.js'; import { SHIKI_LANGUAGE_MAP, escape, normalizeSlugify, transform } from '../markdown';
// import 'prismjs/components/prism-diff.js';
// import 'prismjs/components/prism-typescript.js';
import { createShikiHighlighter } from 'shiki-twoslash';
import { SHIKI_LANGUAGE_MAP, normalizeSlugify, transform } from '../markdown';
import { replace_placeholders } from './render.js'; import { replace_placeholders } from './render.js';
// import { parse_route_id } from '../../../../../../packages/kit/src/utils/routing.js'; // import { parse_route_id } from '../../../../../../packages/kit/src/utils/routing.js';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
@ -57,7 +53,6 @@ export async function get_parsed_docs(docs_data, slug) {
/(?:<!---\s*([\w-]+):\s*(.*?)\s*--->|\/\/\/\s*([\w-]+):\s*(.*))\n/gm, /(?:<!---\s*([\w-]+):\s*(.*?)\s*--->|\/\/\/\s*([\w-]+):\s*(.*))\n/gm,
(_, key, value) => { (_, key, value) => {
options[key] = value; options[key] = value;
console.log(options);
return ''; return '';
} }
) )
@ -82,123 +77,112 @@ export async function get_parsed_docs(docs_data, slug) {
version_class = 'js-version'; version_class = 'js-version';
} }
// TODO: Replace later if (language === 'dts') {
html = highlighter.codeToHtml(source, { lang: SHIKI_LANGUAGE_MAP[language] }); html = renderCodeToHTML(
source,
// if (source.includes('$env/')) { 'ts',
// // TODO we're hardcoding static env vars that are used in code examples { twoslash: false },
// // in the types, which isn't... totally ideal, but will do for now { themeName: 'css-variables' },
// injected.push( highlighter
// `declare module '$env/dynamic/private' { export const env: Record<string, string> }`, );
// `declare module '$env/dynamic/public' { export const env: Record<string, string> }`, } else if (language === 'js' || language === 'ts') {
// `declare module '$env/static/private' { export const API_KEY: string }`, try {
// `declare module '$env/static/public' { export const PUBLIC_BASE_URL: string }` const injected = [];
// );
// } // For the snippets that are not proper JS or TS
// if (!source.includes('import')) injected.push('// @errors: 2552 1005 1109 2304');
// if (source.includes('./$types') && !source.includes('@filename: $types.d.ts')) {
// const params = parse_route_id(options.file || `+page.${language}`) if (source.includes('svelte')) {
// .params.map((param) => `${param.name}: string`) injected.push(
// .join(', '); `// @filename: ambient.d.ts`,
`/// <reference types="svelte" />`,
// injected.push( `/// <reference types="svelte/action" />`,
// `// @filename: $types.d.ts`, `/// <reference types="svelte/compiler" />`,
// `import type * as Kit from '@sveltejs/kit';`, `/// <reference types="svelte/easing" />`,
// `export type PageLoad = Kit.Load<{${params}}>;`, `/// <reference types="svelte/motion" />`,
// `export type PageServerLoad = Kit.ServerLoad<{${params}}>;`, `/// <reference types="svelte/transition" />`,
// `export type LayoutLoad = Kit.Load<{${params}}>;`, `/// <reference types="svelte/store" />`
// `export type LayoutServerLoad = Kit.ServerLoad<{${params}}>;`, );
// `export type RequestHandler = Kit.RequestHandler<{${params}}>;`, }
// `export type Action = Kit.Action<{${params}}>;`,
// `export type Actions = Kit.Actions<{${params}}>;` if (page.file.includes('svelte-compiler')) {
// ); injected.push('// @esModuleInterop');
// } }
// // special case — we need to make allowances for code snippets coming if (injected.length) {
// // from e.g. ambient.d.ts const injected_str = injected.join('\n');
// if (file.endsWith('30-modules.md')) { if (source.includes('// @filename:')) {
// injected.push('// @errors: 7006 7031'); source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`);
// } } else {
source = source.replace(
// // another special case /^(?!\/\/ @)/m,
// if (source.includes('$lib/types')) { `${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n`
// injected.push(`declare module '$lib/types' { export interface User {} }`); );
// } }
}
// if (injected.length) {
// const injected_str = injected.join('\n'); const twoslash = runTwoSlash(source, language, {
// if (source.includes('// @filename:')) { defaultCompilerOptions: {
// source = source.replace('// @filename:', `${injected_str}\n\n// @filename:`); allowJs: true,
// } else { checkJs: true,
// source = source.replace( target: ts.ScriptTarget.ES2022
// /^(?!\/\/ @)/m, }
// `${injected_str}\n\n// @filename: index.${language}\n// ---cut---\n` });
// );
// } html = renderCodeToHTML(
// } twoslash.code,
'ts',
// const twoslash = runTwoSlash(source, language, { { twoslash: true },
// defaultCompilerOptions: { // @ts-ignore Why shiki-twoslash requires a theme name?
// allowJs: true, {},
// checkJs: true, highlighter,
// target: 'es2021', twoslash
// }, );
// }); } catch (e) {
console.error(`Error compiling snippet in ${page.file}`);
// html = renderCodeToHTML( console.error(e.code);
// twoslash.code, throw e;
// 'ts', }
// { twoslash: true },
// {}, // we need to be able to inject the LSP attributes as HTML, not text, so we
// highlighter, // turn &lt; into &amp;lt;
// twoslash html = html.replace(
// ); /<data-lsp lsp='([^']*)'([^>]*)>(\w+)<\/data-lsp>/g,
// } catch (e) { (match, lsp, attrs, name) => {
// console.error(`Error compiling snippet in ${file}`); if (!lsp) return name;
// console.error(e.code); return `<data-lsp lsp='${lsp.replace(/&/g, '&amp;')}'${attrs}>${name}</data-lsp>`;
// throw e; }
// } );
// // we need to be able to inject the LSP attributes as HTML, not text, so we // preserve blank lines in output (maybe there's a more correct way to do this?)
// // turn &lt; into &amp;lt; html = html.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>');
// html = html.replace( } else if (language === 'diff') {
// /<data-lsp lsp='([^']*)'([^>]*)>(\w+)<\/data-lsp>/g, const lines = source.split('\n').map((content) => {
// (match, lsp, attrs, name) => { let type = null;
// if (!lsp) return name; if (/^[\+\-]/.test(content)) {
// return `<data-lsp lsp='${lsp.replace(/&/g, '&amp;')}'${attrs}>${name}</data-lsp>`; type = content[0] === '+' ? 'inserted' : 'deleted';
// } content = content.slice(1);
// ); }
// // preserve blank lines in output (maybe there's a more correct way to do this?) return {
// html = html.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>'); type,
// } else if (language === 'diff') { content: escape(content)
// const lines = source.split('\n').map((content) => { };
// let type = null; });
// if (/^[\+\-]/.test(content)) {
// type = content[0] === '+' ? 'inserted' : 'deleted'; html = `<pre class="language-diff"><code>${lines
// content = content.slice(1); .map((line) => {
// } if (line.type) return `<span class="${line.type}">${line.content}\n</span>`;
return line.content + '\n';
// return { })
// type, .join('')}</code></pre>`;
// content: escape(content), } else {
// }; const highlighted = highlighter.codeToHtml(source, {
// }); lang: SHIKI_LANGUAGE_MAP[language]
});
// html = `<pre class="language-diff"><code>${lines
// .map((line) => { html = highlighted.replace(/<div class='line'><\/div>/g, '<div class="line"> </div>');
// if (line.type) return `<span class="${line.type}">${line.content}\n</span>`; }
// return line.content + '\n';
// })
// .join('')}</code></pre>`;
// } else {
// const plang = languages[language];
// const highlighted = plang
// ? PrismJS.highlight(source, PrismJS.languages[plang], language)
// : source.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
// html = `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
// }
if (options.file) { if (options.file) {
html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`; html = `<div class="code-block"><span class="filename">${options.file}</span>${html}</div>`;
@ -417,7 +401,7 @@ function convert_to_ts(js_code, indent = '', offset = '') {
if (variable_statement.name.getText() === 'actions') { if (variable_statement.name.getText() === 'actions') {
code.appendLeft(variable_statement.getEnd(), ` satisfies ${name}`); code.appendLeft(variable_statement.getEnd(), ` satisfies ${name}`);
} else { } else {
code.appendLeft(variable_statement.name.getEnd(), `: ${name}`); code.appendLeft(variable_statement.name.getEnd(), `: ${name}${generics ?? ''}`);
} }
modified = true; modified = true;

@ -144,7 +144,14 @@ export function replace_placeholders(content) {
* @param {string} lang * @param {string} lang
*/ */
function fence(code, lang = 'ts') { function fence(code, lang = 'ts') {
return '\n\n```' + lang + '\n' + code + '\n```\n\n'; return (
'\n\n```' +
lang +
'\n' +
(['js', 'ts'].includes(lang) ? '// @noErrors\n' : '') +
code +
'\n```\n\n'
);
} }
/** /**

@ -0,0 +1,67 @@
<script>
import { tick } from 'svelte';
export let html = '';
export let x = 0;
export let y = 0;
let width = 1;
let tooltip;
// bit of a gross hack but it works — this prevents the
// tooltip from disappearing off the side of the screen
$: if (html && tooltip) {
tick().then(() => {
width = tooltip.getBoundingClientRect().width;
});
}
</script>
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div
on:mouseenter
on:mouseleave
class="tooltip-container"
style="left: {x}px; top: {y}px; --offset: {Math.min(-10, window.innerWidth - (x + width + 10))}px"
>
<div bind:this={tooltip} class="tooltip">
<span>{@html html}</span>
</div>
</div>
<style>
.tooltip-container {
--bg: var(--sk-theme-2);
--arrow-size: 0.4rem;
position: absolute;
transform: translate(var(--offset), calc(2rem + var(--arrow-size)));
}
.tooltip {
margin: 0 2rem 0 0;
background-color: var(--bg);
color: #fff;
text-align: left;
padding: 0.4rem 0.6rem;
border-radius: var(--sk-border-radius);
font-family: var(--sk-font-mono);
font-size: 1.2rem;
white-space: pre-wrap;
z-index: 100;
filter: drop-shadow(2px 4px 6px #67677866);
}
.tooltip::after {
content: '';
position: absolute;
left: calc(-1 * var(--offset) - var(--arrow-size));
top: calc(-2 * var(--arrow-size));
border: var(--arrow-size) solid transparent;
border-bottom-color: var(--bg);
}
.tooltip :global(a) {
color: white;
text-decoration: underline;
}
</style>

@ -0,0 +1,60 @@
import { onMount } from 'svelte';
import Tooltip from './Tooltip.svelte';
export function setup() {
onMount(() => {
let tooltip;
let timeout;
function over(event) {
if (event.target.tagName === 'DATA-LSP') {
clearTimeout(timeout);
if (!tooltip) {
tooltip = new Tooltip({
target: document.body
});
tooltip.$on('mouseenter', () => {
clearTimeout(timeout);
});
tooltip.$on('mouseleave', () => {
clearTimeout(timeout);
tooltip.$destroy();
tooltip = null;
});
}
const rect = event.target.getBoundingClientRect();
const html = event.target.getAttribute('lsp');
const x = (rect.left + rect.right) / 2 + window.scrollX;
const y = rect.top + window.scrollY;
tooltip.$set({
html,
x,
y
});
}
}
function out(event) {
if (event.target.tagName === 'DATA-LSP') {
timeout = setTimeout(() => {
tooltip.$destroy();
tooltip = null;
}, 200);
}
}
window.addEventListener('mouseover', over);
window.addEventListener('mouseout', out);
return () => {
window.removeEventListener('mouseover', over);
window.removeEventListener('mouseout', out);
};
});
}

@ -1,15 +1,75 @@
<script> <script>
import { page } from '$app/stores';
import OnThisPage from './OnThisPage.svelte'; import OnThisPage from './OnThisPage.svelte';
import * as hovers from '$lib/utils/hovers';
export let data; export let data;
$: pages = data.sections.flatMap((section) => section.pages);
$: index = pages.findIndex(({ path }) => path === $page.url.pathname);
$: prev = pages[index - 1];
$: next = pages[index + 1];
hovers.setup();
</script> </script>
<svelte:head> <svelte:head>
<title>{data.page.title} - Svelte</title> <title>{data.page.title} • Docs • SvelteKit</title>
<meta name="twitter:title" content="SvelteKit docs" />
<meta name="twitter:description" content="{data.page.title} • SvelteKit documentation" />
<meta name="Description" content="{data.page.title} • SvelteKit documentation" />
</svelte:head> </svelte:head>
<div class="text"> <div class="text">
{@html data.page.content} {@html data.page.content}
</div> </div>
<div class="controls">
<div>
<span class:faded={!prev}>previous</span>
{#if prev}
<a href={prev.path}>{prev.title}</a>
{/if}
</div>
<div>
<span class:faded={!next}>next</span>
{#if next}
<a href={next.path}>{next.title}</a>
{/if}
</div>
</div>
<OnThisPage details={data.page} /> <OnThisPage details={data.page} />
<style>
.controls {
max-width: calc(var(--sk-line-max-width) + 1rem);
border-top: 1px solid var(--sk-back-4);
padding: 1rem 0 0 0;
display: grid;
grid-template-columns: 1fr 1fr;
margin: 6rem 0 0 0;
}
.controls > :first-child {
text-align: left;
}
.controls > :last-child {
text-align: right;
}
.controls span {
display: block;
font-size: 1.2rem;
text-transform: uppercase;
font-weight: 600;
color: var(--sk-text-3);
}
.controls span.faded {
opacity: 0.4;
}
</style>

Loading…
Cancel
Save