From 2cc299185f6e2d06871b29bc4beb63ce56de2147 Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:48:23 +0545 Subject: [PATCH 01/13] fix: ensure version is typed as string instead of the literal __VERSION__ (#8502) fixes #8498 --- src/compiler/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 76eb45d37f..57277ac756 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -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 From 6e1674e249b0ab714d6396323be21f70b32ea303 Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:48:39 +0545 Subject: [PATCH 02/13] docs: fix type signature for StartStopNotifier (#8509) It used the Subscriber type to represent the set callback and the Unsubscriber to represent the cleanup callback. But the names made it confusing what it was for. --- src/runtime/store/index.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 67c2bcb15c..e5184eb25b 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -12,8 +12,15 @@ export type Updater = (value: T) => T; /** Cleanup logic callback. */ type Invalidator = (value?: T) => void; -/** Start and stop notification callbacks. */ -export type StartStopNotifier = (set: Subscriber) => 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 = (set: (value: T) => void) => void | (() => void); /** Readable interface for subscribing. */ export interface Readable { @@ -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(value?: T, start?: StartStopNotifier): Readable { return { @@ -59,7 +66,7 @@ export function readable(value?: T, start?: StartStopNotifier): Readable(value?: T, start: StartStopNotifier = noop): Writable { let stop: Unsubscriber; From 1770fc140aea0ed326019c69e21646137cfbf742 Mon Sep 17 00:00:00 2001 From: James Scott-Brown Date: Tue, 18 Apr 2023 15:27:04 +0100 Subject: [PATCH 03/13] docs: clarify statement about initial values for props (#8477) --- site/content/docs/02-component-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/02-component-format.md b/site/content/docs/02-component-format.md index f1563a62c6..71468f3faf 100644 --- a/site/content/docs/02-component-format.md +++ b/site/content/docs/02-component-format.md @@ -42,7 +42,7 @@ Svelte uses the `export` keyword to mark a variable declaration as a *property* --- -You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value. +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#compile-time-svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`. From 1964535adf607edc4312a213b640d82dc14006e1 Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Tue, 18 Apr 2023 21:12:43 +0545 Subject: [PATCH 04/13] fix: interpolated style directive updates properly with spread (#8505) fixes #8438 --- .../render_dom/wrappers/Element/index.ts | 26 ++++++++++++------- .../_config.js | 18 +++++++++++++ .../main.svelte | 5 ++++ 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js create mode 100644 test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index cefebfcff3..e2c0ece733 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -1240,19 +1240,27 @@ export default class ElementWrapper extends Wrapper { block.chunks.hydrate.push(updater); + const self_deps = expression.dynamic_dependencies(); + const all_deps = new Set([ + ...self_deps, + ...this.dynamic_style_dependencies + ]); + + 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 { - 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]); + if (all_deps.size === 0) return; if (should_cache) { condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js new file mode 100644 index 0000000000..af1030c25a --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` +
+ `, + + 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)'); + } + } +}; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte new file mode 100644 index 0000000000..4f44ae2e67 --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte @@ -0,0 +1,5 @@ + + +
From 6bbae502f6d41ea37cc7c46585662bf6807561c0 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:29:59 +0200 Subject: [PATCH 05/13] chore: Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea9545aa65..d01782fa29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752)) * Add support for resize observer bindings (`
`) ([#8022](https://github.com/sveltejs/svelte/pull/8022)) +* Update interpolated style directive properly when using spread ([#8438](https://github.com/sveltejs/svelte/issues/8438)) +* Ensure version is typed as `string` instead of the literal `__VERSION__` ([#8498](https://github.com/sveltejs/svelte/issues/8498)) ## 3.58.0 From 32153e318d7b5be0dae9334801dba31881f077bc Mon Sep 17 00:00:00 2001 From: xxkl1 <84455605+xxkl1@users.noreply.github.com> Date: Wed, 19 Apr 2023 21:21:24 +0800 Subject: [PATCH 06/13] fix: inline style value become undefined (#8517) fixes #8462 --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- src/runtime/internal/dom.ts | 4 ++-- .../samples/inline-style-become-undefined/_config.js | 11 +++++++++++ .../samples/inline-style-become-undefined/main.svelte | 9 +++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/runtime/samples/inline-style-become-undefined/_config.js create mode 100644 test/runtime/samples/inline-style-become-undefined/main.svelte diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index c025313c15..066fa6eb1c 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -307,7 +307,7 @@ export function attr(node: Element, attribute: string, value?: string) { else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } -/** +/** * List of attributes that should always be set through the attr method, * because updating them through the property setter doesn't work reliably. * In the example of `width`/`height`, the problem is that the setter only @@ -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' : ''); diff --git a/test/runtime/samples/inline-style-become-undefined/_config.js b/test/runtime/samples/inline-style-become-undefined/_config.js new file mode 100644 index 0000000000..a2a0727efa --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/_config.js @@ -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, '
'); + await div.dispatchEvent(click); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, '
'); + } +}; diff --git a/test/runtime/samples/inline-style-become-undefined/main.svelte b/test/runtime/samples/inline-style-become-undefined/main.svelte new file mode 100644 index 0000000000..ee38934fc7 --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/main.svelte @@ -0,0 +1,9 @@ + + +
From 6ba2f722518b3fb6904d6d566c3c1a00d61fe70a Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:27:42 +0200 Subject: [PATCH 07/13] chore: Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d01782fa29..bc75b301a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752)) * Add support for resize observer bindings (`
`) ([#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 From 1e9e843ca5366699373002d32a96a7fd47336390 Mon Sep 17 00:00:00 2001 From: Puru Vijay Date: Thu, 20 Apr 2023 23:01:04 +0530 Subject: [PATCH 08/13] Merge --- CHANGELOG.md | 3 +++ .../01-svelte-components.md | 4 ++- .../render_dom/wrappers/Element/index.ts | 26 ++++++++++++------- src/compiler/index.ts | 2 +- src/runtime/internal/dom.ts | 4 +-- src/runtime/store/index.ts | 15 ++++++++--- .../_config.js | 18 +++++++++++++ .../main.svelte | 5 ++++ .../inline-style-become-undefined/_config.js | 11 ++++++++ .../inline-style-become-undefined/main.svelte | 9 +++++++ 10 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js create mode 100644 test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte create mode 100644 test/runtime/samples/inline-style-become-undefined/_config.js create mode 100644 test/runtime/samples/inline-style-become-undefined/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index f24bc543b7..7bcc280213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ * Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752)) * Add support for resize observer bindings (`
`) ([#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 diff --git a/site/content/docs/02-template-syntax/01-svelte-components.md b/site/content/docs/02-template-syntax/01-svelte-components.md index 460562fed8..d1f353629b 100644 --- a/site/content/docs/02-template-syntax/01-svelte-components.md +++ b/site/content/docs/02-template-syntax/01-svelte-components.md @@ -36,7 +36,9 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_ ``` -You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value. +--- + +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`. diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index cefebfcff3..e2c0ece733 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -1240,19 +1240,27 @@ export default class ElementWrapper extends Wrapper { block.chunks.hydrate.push(updater); + const self_deps = expression.dynamic_dependencies(); + const all_deps = new Set([ + ...self_deps, + ...this.dynamic_style_dependencies + ]); + + 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 { - 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]); + if (all_deps.size === 0) return; if (should_cache) { condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`; diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 76eb45d37f..57277ac756 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -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 diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index c025313c15..066fa6eb1c 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -307,7 +307,7 @@ export function attr(node: Element, attribute: string, value?: string) { else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } -/** +/** * List of attributes that should always be set through the attr method, * because updating them through the property setter doesn't work reliably. * In the example of `width`/`height`, the problem is that the setter only @@ -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' : ''); diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 67c2bcb15c..e5184eb25b 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -12,8 +12,15 @@ export type Updater = (value: T) => T; /** Cleanup logic callback. */ type Invalidator = (value?: T) => void; -/** Start and stop notification callbacks. */ -export type StartStopNotifier = (set: Subscriber) => 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 = (set: (value: T) => void) => void | (() => void); /** Readable interface for subscribing. */ export interface Readable { @@ -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(value?: T, start?: StartStopNotifier): Readable { return { @@ -59,7 +66,7 @@ export function readable(value?: T, start?: StartStopNotifier): Readable(value?: T, start: StartStopNotifier = noop): Writable { let stop: Unsubscriber; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js new file mode 100644 index 0000000000..af1030c25a --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` +
+ `, + + 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)'); + } + } +}; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte new file mode 100644 index 0000000000..4f44ae2e67 --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/test/runtime/samples/inline-style-become-undefined/_config.js b/test/runtime/samples/inline-style-become-undefined/_config.js new file mode 100644 index 0000000000..a2a0727efa --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/_config.js @@ -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, '
'); + await div.dispatchEvent(click); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, '
'); + } +}; diff --git a/test/runtime/samples/inline-style-become-undefined/main.svelte b/test/runtime/samples/inline-style-become-undefined/main.svelte new file mode 100644 index 0000000000..ee38934fc7 --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/main.svelte @@ -0,0 +1,9 @@ + + +
From ce686c95d1903688874c84e2e44c31fbea9cdf16 Mon Sep 17 00:00:00 2001 From: Puru Vijay <47742487+PuruVJ@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:21:33 +0530 Subject: [PATCH 09/13] chore: Upgrade to Vite 4.3 (#8524) --- sites/svelte.dev/package-lock.json | 102 ++++++++++++++++++++--------- sites/svelte.dev/package.json | 8 +-- sites/svelte.dev/vite.config.js | 16 ----- 3 files changed, 76 insertions(+), 50 deletions(-) diff --git a/sites/svelte.dev/package-lock.json b/sites/svelte.dev/package-lock.json index 7597fc98b9..bcde14e313 100644 --- a/sites/svelte.dev/package-lock.json +++ b/sites/svelte.dev/package-lock.json @@ -8,7 +8,7 @@ "name": "svelte.dev", "version": "1.0.0", "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", @@ -19,9 +19,9 @@ }, "devDependencies": { "@resvg/resvg-js": "^2.4.1", - "@sveltejs/adapter-vercel": "^2.4.1", - "@sveltejs/kit": "^1.15.6", - "@sveltejs/site-kit": "^5.0.1", + "@sveltejs/adapter-vercel": "^2.4.2", + "@sveltejs/kit": "^1.15.7", + "@sveltejs/site-kit": "^5.0.4", "@types/marked": "^4.0.8", "@types/prismjs": "^1.26.0", "degit": "^2.8.4", @@ -33,7 +33,7 @@ "prettier-plugin-svelte": "^2.10.0", "prism-svelte": "^0.5.0", "prismjs": "^1.29.0", - "satori": "^0.4.11", + "satori": "^0.4.14", "satori-html": "^0.3.2", "shelljs": "^0.8.5", "shiki": "^0.14.1", @@ -41,7 +41,7 @@ "svelte": "^3.58.0", "svelte-check": "^3.2.0", "typescript": "^5.0.4", - "vite": "^4.2.1", + "vite": "^4.3.0", "vite-imagetools": "^4.0.19" } }, @@ -1185,9 +1185,9 @@ } }, "node_modules/@supabase/gotrue-js": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.22.0.tgz", - "integrity": "sha512-YBxg1AoMgXSFbEuamMyuJhKwscmK/eCkL8K7CenCjJJRVKfNHDYkM9F7VX9+atDDY6ejufdYz1auVfsqRrt9pQ==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.23.0.tgz", + "integrity": "sha512-N6o+MMGsPDbdiz0R0Oy9GlgefYFjJJvoBduR16s8c1H3yG2jp6jq+pMEP18P1bg7uk2DljEjyBnVN7Wj7SJ2Zw==", "dependencies": { "cross-fetch": "^3.1.5" } @@ -1219,12 +1219,12 @@ } }, "node_modules/@supabase/supabase-js": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.20.0.tgz", - "integrity": "sha512-6sNX4WEaVBbmOS1K2U3PcpAhp0FcjVo1FRSEoj4N+NZ6DIokX+yut8IBhF176pg5kFCdFIL2wyNhf3SltREmWw==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.21.0.tgz", + "integrity": "sha512-FW3ZzBoc4orSgfX0dXrmJoXAcI/hiekmqXTkN64vjtUF2Urp3UjyAf71UTtV9Jl6ejHoe3K++e0+Rg9zKUJh5w==", "dependencies": { "@supabase/functions-js": "^2.1.0", - "@supabase/gotrue-js": "^2.22.0", + "@supabase/gotrue-js": "^2.23.0", "@supabase/postgrest-js": "^1.1.1", "@supabase/realtime-js": "^2.7.2", "@supabase/storage-js": "^2.5.1", @@ -1232,9 +1232,9 @@ } }, "node_modules/@sveltejs/adapter-vercel": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-2.4.1.tgz", - "integrity": "sha512-7YOHBq8onxqD5ZshQhveuSzJg9ZOicKoNZQtOnAj++0MJmPElxQ5Mjqp6aQfIhHAHl8tn5UbacCpvR39mPejgw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-2.4.2.tgz", + "integrity": "sha512-HxItPKrt9DmSbKw2X4XVRYa4J2ueTEfQr79ijDcI/ZUtQ64d4X3ilUnLpmm1nXMBb1DyxaWJF4o8avwiV4O2Hw==", "dev": true, "dependencies": { "@vercel/nft": "^0.22.1", @@ -1245,9 +1245,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.6.tgz", - "integrity": "sha512-nXAVKq4veZqztZbPRDMpZszerSuZk3dMZmIoYc/6YvctYYkZS0cdQy5qfeGQ404wXRonCpaCQb0lUrdJZ81f9A==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.7.tgz", + "integrity": "sha512-dgdKExsMJ16X3q8tEcuDlv+QIWAlJcf7IqCU2HWV13nmtTzwSA2n4VtEx9Gy5OGhH0SUAGNIupmlf0TdFSMXbw==", "hasInstallScript": true, "dependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.0", @@ -1294,7 +1294,7 @@ "svelte": "^3.50.0" } }, - "node_modules/@sveltejs/site-kit": { + "node_modules/@sveltejs/repl/node_modules/@sveltejs/site-kit": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-5.0.1.tgz", "integrity": "sha512-6LYCxTGzhC/LhObYc3Mazk4HSi/KjdZ+fURpbvi9tppMk4lfGJb3ajLvGoo0k8t0vBEwrN16Hf/JChgkFOorFg==", @@ -1307,6 +1307,20 @@ "svelte": "^3.54.0" } }, + "node_modules/@sveltejs/site-kit": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-5.0.4.tgz", + "integrity": "sha512-bn0Lk3hmIz/pDTMvVjiVX/US4OPoc1CzfceI0Y0ljxHs2fdxRTZz881xvItZRF7istLIueoFR1x3HAZPdI2F6g==", + "dev": true, + "dependencies": { + "esm-env": "^1.0.0", + "svelte-local-storage-store": "^0.4.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^1.0.0", + "svelte": "^3.54.0" + } + }, "node_modules/@sveltejs/vite-plugin-svelte": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.4.tgz", @@ -2297,7 +2311,8 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/gauge": { "version": "3.0.2", @@ -2397,6 +2412,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -2410,6 +2426,18 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/hex-rgb": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz", + "integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -2555,6 +2583,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -3142,6 +3171,16 @@ "xml2js": "^0.4.5" } }, + "node_modules/parse-css-color": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz", + "integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==", + "dev": true, + "dependencies": { + "color-name": "^1.1.4", + "hex-rgb": "^4.1.0" + } + }, "node_modules/parse-headers": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", @@ -3160,7 +3199,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/peek-readable": { "version": "4.1.0", @@ -3441,6 +3481,7 @@ "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -3619,9 +3660,9 @@ } }, "node_modules/satori": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/satori/-/satori-0.4.11.tgz", - "integrity": "sha512-Ev8xEkDkURnEz/qWeGLWss3BkbfeoSfB0tXTqijfLrdaOsUyTW4SQmEtvF5atoYw/8GgS1Nq2sJMXX4BXhyFPg==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/satori/-/satori-0.4.14.tgz", + "integrity": "sha512-zzkxey12UY6T1rQUhbgPAxeSBL5AJM7aefM4jX3TijN0I8LOHDUX4mh2MXEuqMWUgzV32aR/0nwPka7Eu42wYA==", "dev": true, "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", @@ -3630,6 +3671,7 @@ "css-to-react-native": "^3.0.0", "emoji-regex": "^10.2.1", "linebreak": "^1.1.0", + "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-wasm-web": "^0.3.3" }, @@ -3961,6 +4003,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -4330,14 +4373,13 @@ "dev": true }, "node_modules/vite": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", - "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-JTGFgDh3dVxeGBpuQX04Up+JZmuG6wu9414Ei36vQzaEruY/M4K0AgwtuB2b4HaBgB7R8l+LHxjB0jcgz4d2qQ==", "dependencies": { "esbuild": "^0.17.5", "postcss": "^8.4.21", - "resolve": "^1.22.1", - "rollup": "^3.18.0" + "rollup": "^3.20.2" }, "bin": { "vite": "bin/vite.js" diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json index a66e601c53..f87bc5ee0f 100644 --- a/sites/svelte.dev/package.json +++ b/sites/svelte.dev/package.json @@ -16,7 +16,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", @@ -29,7 +29,7 @@ "@resvg/resvg-js": "^2.4.1", "@sveltejs/adapter-vercel": "^2.4.2", "@sveltejs/kit": "^1.15.7", - "@sveltejs/site-kit": "^5.0.3", + "@sveltejs/site-kit": "^5.0.4", "@types/marked": "^4.0.8", "@types/prismjs": "^1.26.0", "degit": "^2.8.4", @@ -41,7 +41,7 @@ "prettier-plugin-svelte": "^2.10.0", "prism-svelte": "^0.5.0", "prismjs": "^1.29.0", - "satori": "^0.4.11", + "satori": "^0.4.14", "satori-html": "^0.3.2", "shelljs": "^0.8.5", "shiki": "^0.14.1", @@ -49,7 +49,7 @@ "svelte": "^3.58.0", "svelte-check": "^3.2.0", "typescript": "^5.0.4", - "vite": "^4.2.1", + "vite": "^4.3.0", "vite-imagetools": "^4.0.19" } } diff --git a/sites/svelte.dev/vite.config.js b/sites/svelte.dev/vite.config.js index f8d12e4baf..626bf8a8ec 100644 --- a/sites/svelte.dev/vite.config.js +++ b/sites/svelte.dev/vite.config.js @@ -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); - } - } - ] } }; From f064c39d5ff01a4e2aba71a3d3662c7300aba025 Mon Sep 17 00:00:00 2001 From: Nguyen Tran <88808276+ngtr6788@users.noreply.github.com> Date: Wed, 26 Apr 2023 03:18:22 -0400 Subject: [PATCH 10/13] fix: relax no-redundant-roles implementation (#8536) Deals with the no-redundant-roles part of #8529 There was an erroneous check which compares the element name with the current role. This fix brings no-redundant-roles closer to the original eslint-jsx implementation --- src/compiler/compile/nodes/Element.ts | 7 ++++--- .../validator/samples/a11y-no-redundant-roles/input.svelte | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2410904d63..44d84f7566 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -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)); } diff --git a/test/validator/samples/a11y-no-redundant-roles/input.svelte b/test/validator/samples/a11y-no-redundant-roles/input.svelte index 05525effb6..537d5c0fd3 100644 --- a/test/validator/samples/a11y-no-redundant-roles/input.svelte +++ b/test/validator/samples/a11y-no-redundant-roles/input.svelte @@ -41,4 +41,8 @@
-
\ No newline at end of file +
+ + + + From b7359c8361e476d1a5aba96b79c1749fec94cb3a Mon Sep 17 00:00:00 2001 From: xxkl1 <84455605+xxkl1@users.noreply.github.com> Date: Thu, 27 Apr 2023 22:17:42 +0800 Subject: [PATCH 11/13] feat: add window bind devicePixelRatio support (#8534) closes: #8285 add window bind devicePixelRatio support, change devicePixelRatio on window resize. --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- elements/index.d.ts | 1 + site/content/docs/03-template-syntax.md | 1 + src/compiler/compile/nodes/Window.ts | 1 + src/compiler/compile/render_dom/wrappers/Window.ts | 2 ++ .../runtime/samples/window-binding-resize/_config.js | 12 ++++++++++-- .../samples/window-binding-resize/main.svelte | 6 ++++-- .../samples/window-binding-invalid/errors.json | 2 +- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/elements/index.d.ts b/elements/index.d.ts index ac32ae94c3..dd91d14654 100644 --- a/elements/index.d.ts +++ b/elements/index.d.ts @@ -1083,6 +1083,7 @@ export interface SvelteWindowAttributes extends HTMLAttributes { 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; diff --git a/site/content/docs/03-template-syntax.md b/site/content/docs/03-template-syntax.md index 170c303b85..27d3ca9987 100644 --- a/site/content/docs/03-template-syntax.md +++ b/site/content/docs/03-template-syntax.md @@ -1742,6 +1742,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. diff --git a/src/compiler/compile/nodes/Window.ts b/src/compiler/compile/nodes/Window.ts index c5bec0acd3..2f8a015d8a 100644 --- a/src/compiler/compile/nodes/Window.ts +++ b/src/compiler/compile/nodes/Window.ts @@ -17,6 +17,7 @@ const valid_bindings = [ 'outerHeight', 'scrollX', 'scrollY', + 'devicePixelRatio', 'online' ]; diff --git a/src/compiler/compile/render_dom/wrappers/Window.ts b/src/compiler/compile/render_dom/wrappers/Window.ts index c98af18268..9e58bbcaad 100644 --- a/src/compiler/compile/render_dom/wrappers/Window.ts +++ b/src/compiler/compile/render_dom/wrappers/Window.ts @@ -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' ]); diff --git a/test/runtime/samples/window-binding-resize/_config.js b/test/runtime/samples/window-binding-resize/_config.js index c99e92a07b..d7f0282147 100644 --- a/test/runtime/samples/window-binding-resize/_config.js +++ b/test/runtime/samples/window-binding-resize/_config.js @@ -1,5 +1,5 @@ export default { - html: '
1024x768
', + html: '
1024x768
1
', 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, ` -
567x456
+
567x456
2
`); } }; diff --git a/test/runtime/samples/window-binding-resize/main.svelte b/test/runtime/samples/window-binding-resize/main.svelte index 405f4e6e23..8ece184416 100644 --- a/test/runtime/samples/window-binding-resize/main.svelte +++ b/test/runtime/samples/window-binding-resize/main.svelte @@ -1,8 +1,10 @@ - + -
{width}x{height}
\ No newline at end of file +
{width}x{height}
+
{devicePixelRatio}
diff --git a/test/validator/samples/window-binding-invalid/errors.json b/test/validator/samples/window-binding-invalid/errors.json index 1277984258..04ecbaafcd 100644 --- a/test/validator/samples/window-binding-invalid/errors.json +++ b/test/validator/samples/window-binding-invalid/errors.json @@ -1,6 +1,6 @@ [{ "code": "invalid-binding", - "message": "'potato' is not a valid binding on — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY or online", + "message": "'potato' is not a valid binding on — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY, devicePixelRatio or online", "start": { "line": 1, "column": 15 From a74caf1381f4007a33bdeac43c3562c366c1a3a8 Mon Sep 17 00:00:00 2001 From: abirtley Date: Fri, 28 Apr 2023 00:34:23 +1000 Subject: [PATCH 12/13] docs: Clarify when bind:group does not work (#8540) Clarify documentation around when bind:group does and does not work. See issue #2308 --- site/content/docs/03-template-syntax.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/content/docs/03-template-syntax.md b/site/content/docs/03-template-syntax.md index 27d3ca9987..fe06b0c0bf 100644 --- a/site/content/docs/03-template-syntax.md +++ b/site/content/docs/03-template-syntax.md @@ -823,6 +823,8 @@ Inputs that work together can use `bind:group`. ``` +> `bind:group` only works if the inputs are in the same Svelte component. + #### bind:this ```sv From c4261abfde96f10576b860ccab82951bf2cc35ef Mon Sep 17 00:00:00 2001 From: Cas <6506529+ThaUnknown@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:15:58 +0200 Subject: [PATCH 13/13] feat: document fullscreenElement and visibilityState bindings (#8507) --- elements/index.d.ts | 7 +- site/content/docs/03-template-syntax.md | 12 ++++ src/compiler/compile/nodes/Binding.ts | 3 +- src/compiler/compile/nodes/Document.ts | 21 ++++++ .../compile/render_dom/wrappers/Document.ts | 71 ++++++++++++++++++- .../document-binding-fullscreen/_config.js | 31 ++++++++ .../document-binding-fullscreen/main.svelte | 7 ++ 7 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/document-binding-fullscreen/_config.js create mode 100644 test/runtime/samples/document-binding-fullscreen/main.svelte diff --git a/elements/index.d.ts b/elements/index.d.ts index dd91d14654..e7ed8901af 100644 --- a/elements/index.d.ts +++ b/elements/index.d.ts @@ -1078,6 +1078,11 @@ export interface SvelteMediaTimeRange { end: number; } +export interface SvelteDocumentAttributes extends HTMLAttributes { + readonly 'bind:fullscreenElement'?: Document['fullscreenElement'] | undefined | null; + readonly 'bind:visibilityState'?: Document['visibilityState'] | undefined | null; +} + export interface SvelteWindowAttributes extends HTMLAttributes { readonly 'bind:innerWidth'?: Window['innerWidth'] | undefined | null; readonly 'bind:innerHeight'?: Window['innerHeight'] | undefined | null; @@ -1592,7 +1597,7 @@ export interface SvelteHTMLElements { // Svelte specific 'svelte:window': SvelteWindowAttributes; - 'svelte:document': HTMLAttributes; + 'svelte:document': SvelteDocumentAttributes; 'svelte:body': HTMLAttributes; 'svelte:fragment': { slot?: string }; 'svelte:options': { [name: string]: any }; diff --git a/site/content/docs/03-template-syntax.md b/site/content/docs/03-template-syntax.md index fe06b0c0bf..8038e972be 100644 --- a/site/content/docs/03-template-syntax.md +++ b/site/content/docs/03-template-syntax.md @@ -1759,6 +1759,9 @@ All except `scrollX` and `scrollY` are readonly. ```sv ``` +```sv + +``` --- @@ -1773,6 +1776,15 @@ As with ``, this element may only appear the top level of your co /> ``` +--- + +You can also bind to the following properties: + +* `fullscreenElement` +* `visibilityState` + +All are readonly. + ### `` ```sv diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 303506222f..f655554c81 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -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') { diff --git a/src/compiler/compile/nodes/Document.ts b/src/compiler/compile/nodes/Document.ts index 653ccb627b..60264aa40e 100644 --- a/src/compiler/compile/nodes/Document.ts +++ b/src/compiler/compile/nodes/Document.ts @@ -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, '', ` (did you mean '${match}'?)`)); + } else { + return component.error(node, compiler_errors.invalid_binding_on(node.name, '', ` — 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 { diff --git a/src/compiler/compile/render_dom/wrappers/Document.ts b/src/compiler/compile/render_dom/wrappers/Document.ts index 4f7c86c54f..0a9565e64c 100644 --- a/src/compiler/compile/render_dom/wrappers/Document.ts +++ b/src/compiler/compile/render_dom/wrappers/Document.ts @@ -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> = {}; + const bindings: Record = {}; + 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; + }); } } diff --git a/test/runtime/samples/document-binding-fullscreen/_config.js b/test/runtime/samples/document-binding-fullscreen/_config.js new file mode 100644 index 0000000000..154ec0445a --- /dev/null +++ b/test/runtime/samples/document-binding-fullscreen/_config.js @@ -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); + } +}; diff --git a/test/runtime/samples/document-binding-fullscreen/main.svelte b/test/runtime/samples/document-binding-fullscreen/main.svelte new file mode 100644 index 0000000000..5b00199821 --- /dev/null +++ b/test/runtime/samples/document-binding-fullscreen/main.svelte @@ -0,0 +1,7 @@ + + + + +
\ No newline at end of file