Merge branch 'sites' into feat/ts-in-docs

pull/8452/head
Puru Vijay 3 years ago
commit 01d666b8c0

@ -4,6 +4,9 @@
* Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752))
* Add support for resize observer bindings (`<div bind:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize>`) ([#8022](https://github.com/sveltejs/svelte/pull/8022))
* Update interpolated style directive properly when using spread ([#8438](https://github.com/sveltejs/svelte/issues/8438))
* Remove style directive property when value is `undefined` ([#8462](https://github.com/sveltejs/svelte/issues/8462))
* Ensure version is typed as `string` instead of the literal `__VERSION__` ([#8498](https://github.com/sveltejs/svelte/issues/8498))
## 3.58.0

@ -1078,11 +1078,17 @@ export interface SvelteMediaTimeRange {
end: number;
}
export interface SvelteDocumentAttributes extends HTMLAttributes<Document> {
readonly 'bind:fullscreenElement'?: Document['fullscreenElement'] | undefined | null;
readonly 'bind:visibilityState'?: Document['visibilityState'] | undefined | null;
}
export interface SvelteWindowAttributes extends HTMLAttributes<Window> {
readonly 'bind:innerWidth'?: Window['innerWidth'] | undefined | null;
readonly 'bind:innerHeight'?: Window['innerHeight'] | undefined | null;
readonly 'bind:outerWidth'?: Window['outerWidth'] | undefined | null;
readonly 'bind:outerHeight'?: Window['outerHeight'] | undefined | null;
readonly 'bind:devicePixelRatio'?: Window['devicePixelRatio'] | undefined | null;
'bind:scrollX'?: Window['scrollX'] | undefined | null;
'bind:scrollY'?: Window['scrollY'] | undefined | null;
readonly 'bind:online'?: Window['navigator']['onLine'] | undefined | null;
@ -1591,7 +1597,7 @@ export interface SvelteHTMLElements {
// Svelte specific
'svelte:window': SvelteWindowAttributes;
'svelte:document': HTMLAttributes<Document>;
'svelte:document': SvelteDocumentAttributes;
'svelte:body': HTMLAttributes<HTMLElement>;
'svelte:fragment': { slot?: string };
'svelte:options': { [name: string]: any };

@ -36,7 +36,9 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_
</script>
```
You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value.
---
You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that if the values of props are subsequently updated, then any prop whose value is not specified will be set to `undefined` (rather than its initial value).
In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.

@ -298,6 +298,8 @@ Inputs that work together can use `bind:group`.
<input type="checkbox" bind:group={fillings} value="Guac (extra)" />
```
> `bind:group` only works if the inputs are in the same Svelte component.
## bind:this
```svelte

@ -241,6 +241,7 @@ You can also bind to the following properties:
- `scrollX`
- `scrollY`
- `online` — an alias for `window.navigator.onLine`
- `devicePixelRatio`
All except `scrollX` and `scrollY` are readonly.
@ -256,6 +257,10 @@ All except `scrollX` and `scrollY` are readonly.
<svelte:document on:event={handler}/>
```
```svelte
<svelte:document bind:prop={value}/>
```
Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document`, such as `visibilitychange`, which don't fire on `window`. It also lets you use [actions](/docs/element-directives#use-action) on `document`.
As with `<svelte:window>`, this element may only appear the top level of your component and must never be inside a block or element.
@ -267,6 +272,13 @@ As with `<svelte:window>`, this element may only appear the top level of your co
/>
```
You can also bind to the following properties:
- `fullscreenElement`
- `visibilityState`
All are readonly.
## `<svelte:body>`
```svelte

File diff suppressed because it is too large Load Diff

@ -17,7 +17,7 @@
"test": "uvu -r ts-node/register src/lib/server/markdown"
},
"dependencies": {
"@supabase/supabase-js": "^2.20.0",
"@supabase/supabase-js": "^2.21.0",
"@sveltejs/repl": "^0.4.0",
"cookie": "^0.5.0",
"devalue": "^4.3.0",
@ -28,34 +28,31 @@
},
"devDependencies": {
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-vercel": "^2.4.2",
"@sveltejs/kit": "^1.15.7",
"@sveltejs/site-kit": "^5.0.3",
"@sveltejs/adapter-vercel": "^2.4.3",
"@sveltejs/kit": "^1.15.9",
"@sveltejs/site-kit": "^5.0.4",
"@types/marked": "^4.0.8",
"@types/node": "^18.15.11",
"@types/prismjs": "^1.26.0",
"@types/node": "^18.16.3",
"degit": "^2.8.4",
"dotenv": "^16.0.3",
"jimp": "^0.22.7",
"marked": "^4.3.0",
"node-fetch": "^3.3.1",
"prettier": "^2.8.7",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.0",
"prism-svelte": "^0.5.0",
"prismjs": "^1.29.0",
"rollup": "^3.20.2",
"rollup": "^3.21.2",
"rollup-plugin-dts": "^5.3.0",
"sass": "^1.62.0",
"satori": "^0.4.11",
"sass": "^1.62.1",
"satori": "^0.7.3",
"satori-html": "^0.3.2",
"shelljs": "^0.8.5",
"shiki": "^0.14.1",
"shiki-twoslash": "^3.1.1",
"shiki": "^0.14.2",
"shiki-twoslash": "^3.1.2",
"svelte": "^3.58.0",
"svelte-check": "^3.2.0",
"ts-morph": "^18.0.0",
"typescript": "^5.0.4",
"vite": "^4.2.1",
"vite": "^4.3.3",
"vite-imagetools": "^4.0.19"
}
}

@ -50,22 +50,6 @@ const config = {
fs: {
strict: false
}
},
// !HACK: Remove once vite 4.3 is out
worker: {
plugins: [
{
name: 'remove-manifest',
configResolved(c) {
const manifestPlugin = c.worker.plugins.findIndex((p) => p.name === 'vite:manifest');
c.worker.plugins.splice(manifestPlugin, 1);
const ssrManifestPlugin = c.worker.plugins.findIndex(
(p) => p.name === 'vite:ssr-manifest'
);
c.plugins.splice(ssrManifestPlugin, 1);
}
}
]
}
};

@ -9,6 +9,7 @@ import { TemplateNode } from '../../interfaces';
import Element from './Element';
import InlineComponent from './InlineComponent';
import Window from './Window';
import Document from './Document';
import { clone } from '../../utils/clone';
import compiler_errors from '../compiler_errors';
import compiler_warnings from '../compiler_warnings';
@ -36,7 +37,7 @@ export default class Binding extends Node {
is_contextual: boolean;
is_readonly: boolean;
constructor(component: Component, parent: Element | InlineComponent | Window, scope: TemplateScope, info: TemplateNode) {
constructor(component: Component, parent: Element | InlineComponent | Window | Document, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
if (info.expression.type !== 'Identifier' && info.expression.type !== 'MemberExpression') {

@ -1,14 +1,24 @@
import Node from './shared/Node';
import Binding from './Binding';
import EventHandler from './EventHandler';
import fuzzymatch from '../../utils/fuzzymatch';
import Action from './Action';
import Component from '../Component';
import list from '../../utils/list';
import TemplateScope from './shared/TemplateScope';
import { Element } from '../../interfaces';
import compiler_warnings from '../compiler_warnings';
import compiler_errors from '../compiler_errors';
const valid_bindings = [
'fullscreenElement',
'visibilityState'
];
export default class Document extends Node {
type: 'Document';
handlers: EventHandler[] = [];
bindings: Binding[] = [];
actions: Action[] = [];
constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) {
@ -17,6 +27,17 @@ export default class Document extends Node {
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (!~valid_bindings.indexOf(node.name)) {
const match = fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(node, compiler_errors.invalid_binding_on(node.name, '<svelte:document>', ` (did you mean '${match}'?)`));
} else {
return component.error(node, compiler_errors.invalid_binding_on(node.name, '<svelte:document>', ` — valid bindings are ${list(valid_bindings)}`));
}
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {

@ -121,6 +121,7 @@ const a11y_implicit_semantics = new Map([
['details', 'group'],
['dt', 'term'],
['fieldset', 'group'],
['figure', 'figure'],
['form', 'form'],
['h1', 'heading'],
['h2', 'heading'],
@ -132,6 +133,7 @@ const a11y_implicit_semantics = new Map([
['img', 'img'],
['li', 'listitem'],
['link', 'link'],
['main', 'main'],
['menu', 'list'],
['meter', 'progressbar'],
['nav', 'navigation'],
@ -142,6 +144,7 @@ const a11y_implicit_semantics = new Map([
['progress', 'progressbar'],
['section', 'region'],
['summary', 'button'],
['table', 'table'],
['tbody', 'rowgroup'],
['textarea', 'textbox'],
['tfoot', 'rowgroup'],
@ -631,9 +634,7 @@ export default class Element extends Node {
}
// no-redundant-roles
const has_redundant_role = current_role === get_implicit_role(this.name, attribute_map);
if (this.name === current_role || has_redundant_role) {
if (current_role === get_implicit_role(this.name, attribute_map)) {
component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(current_role));
}

@ -17,6 +17,7 @@ const valid_bindings = [
'outerHeight',
'scrollX',
'scrollY',
'devicePixelRatio',
'online'
];

@ -1,6 +1,6 @@
import Block from '../Block';
import Wrapper from './shared/Wrapper';
import { x } from 'code-red';
import { b, x } from 'code-red';
import Document from '../../nodes/Document';
import { Identifier } from 'estree';
import EventHandler from './Element/EventHandler';
@ -9,6 +9,16 @@ import { TemplateNode } from '../../../interfaces';
import Renderer from '../Renderer';
import add_actions from './shared/add_actions';
const associated_events = {
fullscreenElement: ['fullscreenchange'],
visibilityState: ['visibilitychange']
};
const readonly = new Set([
'fullscreenElement',
'visibilityState'
]);
export default class DocumentWrapper extends Wrapper {
node: Document;
handlers: EventHandler[];
@ -19,7 +29,66 @@ export default class DocumentWrapper extends Wrapper {
}
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
const { renderer } = this;
const { component } = renderer;
const events: Record<string, Array<{ name: string; value: string }>> = {};
const bindings: Record<string, string> = {};
add_event_handlers(block, x`@_document`, this.handlers);
add_actions(block, x`@_document`, this.node.actions);
this.node.bindings.forEach(binding => {
// TODO: what if it's a MemberExpression?
const binding_name = (binding.expression.node as Identifier).name;
// in dev mode, throw if read-only values are written to
if (readonly.has(binding.name)) {
renderer.readonly.add(binding_name);
}
bindings[binding.name] = binding_name;
const binding_events = associated_events[binding.name];
const property = binding.name;
binding_events.forEach(associated_event => {
if (!events[associated_event]) events[associated_event] = [];
events[associated_event].push({
name: binding_name,
value: property
});
});
});
Object.keys(events).forEach(event => {
const id = block.get_unique_name(`ondocument${event}`);
const props = events[event];
renderer.add_to_context(id.name);
const fn = renderer.reference(id.name);
props.forEach(prop => {
renderer.meta_bindings.push(
b`this._state.${prop.name} = @_document.${prop.value};`
);
});
block.event_listeners.push(x`
@listen(@_document, "${event}", ${fn})
`);
component.partly_hoisted.push(b`
function ${id}() {
${props.map(prop => renderer.invalidate(prop.name, x`${prop.name} = @_document.${prop.value}`))}
}
`);
block.chunks.init.push(b`
@add_render_callback(${fn});
`);
component.has_reactive_assignments = true;
});
}
}

@ -1240,20 +1240,28 @@ export default class ElementWrapper extends Wrapper {
block.chunks.hydrate.push(updater);
// Assume that style has changed through the spread attribute
if (has_spread) {
block.chunks.update.push(updater);
} else {
const self_deps = expression.dynamic_dependencies();
const all_deps = new Set([
...self_deps,
...this.dynamic_style_dependencies
]);
if (all_deps.size === 0) return;
let condition = block.renderer.dirty([...all_deps]);
// Assume that style has changed through the spread attribute
if (has_spread) {
if (should_cache && all_deps.size) {
// Update the cached value
block.chunks.update.push(b`
if (${condition}) {
${cached_snippet} = ${snippet};
}`
);
}
block.chunks.update.push(updater);
} else {
if (all_deps.size === 0) return;
if (should_cache) {
condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`;
}

@ -14,6 +14,7 @@ const associated_events = {
innerHeight: 'resize',
outerWidth: 'resize',
outerHeight: 'resize',
devicePixelRatio: 'resize',
scrollX: 'scroll',
scrollY: 'scroll'
@ -29,6 +30,7 @@ const readonly = new Set([
'innerHeight',
'outerWidth',
'outerHeight',
'devicePixelRatio',
'online'
]);

@ -3,5 +3,5 @@ export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
export const VERSION = '__VERSION__';
export const VERSION: string = '__VERSION__';
// additional exports added through generate-type-definitions.js

@ -641,7 +641,7 @@ export function set_input_type(input, type) {
}
export function set_style(node, key, value, important) {
if (value === null) {
if (value == null) {
node.style.removeProperty(key);
} else {
node.style.setProperty(key, value, important ? 'important' : '');

@ -12,8 +12,15 @@ export type Updater<T> = (value: T) => T;
/** Cleanup logic callback. */
type Invalidator<T> = (value?: T) => void;
/** Start and stop notification callbacks. */
export type StartStopNotifier<T> = (set: Subscriber<T>) => Unsubscriber | void;
/**
* Start and stop notification callbacks.
* This function is called when the first subscriber subscribes.
*
* @param {(value: T) => void} set Function that sets the value of the store.
* @returns {void | (() => void)} Optionally, a cleanup function that is called when the last remaining
* subscriber unsubscribes.
*/
export type StartStopNotifier<T> = (set: (value: T) => void) => void | (() => void);
/** Readable interface for subscribing. */
export interface Readable<T> {
@ -48,7 +55,7 @@ const subscriber_queue = [];
/**
* Creates a `Readable` store that allows reading by subscription.
* @param value initial value
* @param {StartStopNotifier}start start and stop notifications for subscriptions
* @param {StartStopNotifier} [start]
*/
export function readable<T>(value?: T, start?: StartStopNotifier<T>): Readable<T> {
return {
@ -59,7 +66,7 @@ export function readable<T>(value?: T, start?: StartStopNotifier<T>): Readable<T
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
* @param {StartStopNotifier=} start
*/
export function writable<T>(value?: T, start: StartStopNotifier<T> = noop): Writable<T> {
let stop: Unsubscriber;

@ -0,0 +1,18 @@
export default {
html: `
<div style="background-color: rgb(255, 0, 0);"></div>
`,
test({ assert, target, window, component }) {
const div = target.querySelector('div');
const styles = window.getComputedStyle(div);
assert.equal(styles.backgroundColor, 'rgb(255, 0, 0)');
{
component.backgroundColor = 128;
const div = target.querySelector('div');
const styles = window.getComputedStyle(div);
assert.equal(styles.backgroundColor, 'rgb(128, 0, 0)');
}
}
};

@ -0,0 +1,5 @@
<script lang="ts">
export let backgroundColor = 255;
</script>
<div style:background-color="rgb({backgroundColor}, 0, 0)" {...$$restProps} />

@ -0,0 +1,31 @@
export default {
before_test() {
Object.defineProperties(window.document, {
fullscreenElement: {
value: null,
configurable: true
}
});
},
// copied from window-binding
// there's some kind of weird bug with this test... it compiles with the wrong require.extensions hook for some bizarre reason
skip_if_ssr: true,
async test({ assert, target, window, component }) {
const event = new window.Event('fullscreenchange');
const div = target.querySelector('div');
Object.defineProperties(window.document, {
fullscreenElement: {
value: div,
configurable: true
}
});
window.document.dispatchEvent(event);
assert.equal(component.fullscreen, div);
}
};

@ -0,0 +1,7 @@
<script>
export let fullscreen;
</script>
<svelte:document bind:fullscreenElement={fullscreen}/>
<div />

@ -0,0 +1,11 @@
export default {
async test({ assert, target, window }) {
const div = target.querySelector('div');
const click = new window.MouseEvent('click');
assert.htmlEqual(target.innerHTML, '<div style="background: red;"></div>');
await div.dispatchEvent(click);
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '<div style=""></div>');
}
};

@ -0,0 +1,9 @@
<script>
let bg = "red";
const handle = () => {
bg = undefined;
};
</script>
<div style:background={bg} on:click={handle}></div>

@ -1,5 +1,5 @@
export default {
html: '<div>1024x768</div>',
html: '<div>1024x768</div><div>1</div>',
before_test() {
Object.defineProperties(window, {
@ -10,6 +10,10 @@ export default {
innerHeight: {
value: 768,
configurable: true
},
devicePixelRatio: {
value: 1,
configurable: true
}
});
},
@ -27,13 +31,17 @@ export default {
innerHeight: {
value: 456,
configurable: true
},
devicePixelRatio: {
value: 2,
configurable: true
}
});
await window.dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<div>567x456</div>
<div>567x456</div><div>2</div>
`);
}
};

@ -1,8 +1,10 @@
<script>
export let width;
export let height;
export let devicePixelRatio;
</script>
<svelte:window bind:innerWidth={width} bind:innerHeight={height}/>
<svelte:window bind:innerWidth={width} bind:innerHeight={height} bind:devicePixelRatio={devicePixelRatio}/>
<div>{width}x{height}</div>
<div>{devicePixelRatio}</div>

@ -42,3 +42,7 @@
<!-- Tested header/footer not nested in section/article -->
<header role="banner"></header>
<footer role="contentinfo"></footer>
<!-- Allowed -->
<!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
<menu role="menu" />

@ -1,6 +1,6 @@
[{
"code": "invalid-binding",
"message": "'potato' is not a valid binding on <svelte:window> — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY or online",
"message": "'potato' is not a valid binding on <svelte:window> — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY, devicePixelRatio or online",
"start": {
"line": 1,
"column": 15

Loading…
Cancel
Save