From 313bcea6cc54c5b9f2e9c980f6bea17a8db25573 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:18:53 -0400 Subject: [PATCH 1/9] Version Packages (next) (#13330) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 2 ++ packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index c982eb6745..c8becba7ce 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -61,6 +61,7 @@ "brown-insects-float", "brown-months-flow", "brown-months-fry", + "brown-pens-repeat", "brown-radios-itch", "brown-spoons-boil", "brown-turkeys-tap", @@ -876,6 +877,7 @@ "two-keys-watch", "unlucky-boxes-obey", "unlucky-points-clap", + "unlucky-spies-flow", "unlucky-steaks-warn", "unlucky-trees-lick", "violet-bats-brake", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3bdb2d2576..6501efcc58 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.0.0-next.255 + +### Patch Changes + +- fix: keep bound inputs in sync in runes mode ([#13328](https://github.com/sveltejs/svelte/pull/13328)) + +- fix: silence snapshot warnings inside `$inspect` ([#13334](https://github.com/sveltejs/svelte/pull/13334)) + ## 5.0.0-next.254 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0739b4e437..73c12fee92 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.254", + "version": "5.0.0-next.255", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1897fd3ff8..ff0a85d649 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.254'; +export const VERSION = '5.0.0-next.255'; export const PUBLIC_VERSION = '5'; From d6ab12ae76a44cd20e94afa8058aac44e4fa24ab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 15:27:28 -0400 Subject: [PATCH 2/9] feat: deprecate `` in runes mode (#13333) * feat: deprecate `` in runes mode * fix --- .changeset/old-spoons-pull.md | 5 +++++ .../svelte/messages/compile-warnings/template.md | 4 ++++ .../phases/2-analyze/visitors/SvelteSelf.js | 12 ++++++++++++ packages/svelte/src/compiler/warnings.js | 13 ++++++++++++- .../samples/attribute-quoted/input.svelte | 1 + .../samples/attribute-quoted/warnings.json | 8 ++++---- .../samples/svelte-self-deprecated/input.svelte | 10 ++++++++++ .../samples/svelte-self-deprecated/warnings.json | 14 ++++++++++++++ 8 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 .changeset/old-spoons-pull.md create mode 100644 packages/svelte/tests/validator/samples/svelte-self-deprecated/input.svelte create mode 100644 packages/svelte/tests/validator/samples/svelte-self-deprecated/warnings.json diff --git a/.changeset/old-spoons-pull.md b/.changeset/old-spoons-pull.md new file mode 100644 index 0000000000..15de7e6e6c --- /dev/null +++ b/.changeset/old-spoons-pull.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: deprecate `` in runes mode diff --git a/packages/svelte/messages/compile-warnings/template.md b/packages/svelte/messages/compile-warnings/template.md index 194bd1087d..5e198c2d55 100644 --- a/packages/svelte/messages/compile-warnings/template.md +++ b/packages/svelte/messages/compile-warnings/template.md @@ -97,3 +97,7 @@ A derived value may be used in other contexts: ## svelte_element_invalid_this > `this` should be an `{expression}`. Using a string attribute value will cause an error in future versions of Svelte + +## svelte_self_deprecated + +> `` is deprecated — use self-imports (e.g. `import %name% from './%basename%'`) instead diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js index 18696e0c55..b87f082de0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js @@ -2,6 +2,8 @@ /** @import { Context } from '../types' */ import { visit_component } from './shared/component.js'; import * as e from '../../../errors.js'; +import * as w from '../../../warnings.js'; +import { filename } from '../../../state.js'; /** * @param {AST.SvelteSelf} node @@ -20,5 +22,15 @@ export function SvelteSelf(node, context) { e.svelte_self_invalid_placement(node); } + if (context.state.analysis.runes) { + const name = filename === '(unknown)' ? 'Self' : context.state.analysis.name; + const basename = + filename === '(unknown)' + ? 'Self.svelte' + : /** @type {string} */ (filename.split(/[/\\]/).pop()); + + w.svelte_self_deprecated(node, name, basename); + } + visit_component(node, context); } diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index 2d7d710281..bd28956230 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -121,7 +121,8 @@ export const codes = [ "script_unknown_attribute", "slot_element_deprecated", "svelte_component_deprecated", - "svelte_element_invalid_this" + "svelte_element_invalid_this", + "svelte_self_deprecated" ]; /** @@ -809,4 +810,14 @@ export function svelte_component_deprecated(node) { */ export function svelte_element_invalid_this(node) { w(node, "svelte_element_invalid_this", "`this` should be an `{expression}`. Using a string attribute value will cause an error in future versions of Svelte"); +} + +/** + * `` is deprecated — use self-imports (e.g. `import %name% from './%basename%'`) instead + * @param {null | NodeLike} node + * @param {string} name + * @param {string} basename + */ +export function svelte_self_deprecated(node, name, basename) { + w(node, "svelte_self_deprecated", `\`\` is deprecated — use self-imports (e.g. \`import ${name} from './${basename}'\`) instead`); } \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/attribute-quoted/input.svelte b/packages/svelte/tests/validator/samples/attribute-quoted/input.svelte index 9252d9b19a..626630bb16 100644 --- a/packages/svelte/tests/validator/samples/attribute-quoted/input.svelte +++ b/packages/svelte/tests/validator/samples/attribute-quoted/input.svelte @@ -15,6 +15,7 @@ {#if foo} + {/if} diff --git a/packages/svelte/tests/validator/samples/attribute-quoted/warnings.json b/packages/svelte/tests/validator/samples/attribute-quoted/warnings.json index d324e9f60f..e42af44dac 100644 --- a/packages/svelte/tests/validator/samples/attribute-quoted/warnings.json +++ b/packages/svelte/tests/validator/samples/attribute-quoted/warnings.json @@ -40,11 +40,11 @@ "message": "Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes", "start": { "column": 14, - "line": 18 + "line": 19 }, "end": { "column": 27, - "line": 18 + "line": 19 } }, { @@ -52,11 +52,11 @@ "message": "Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes", "start": { "column": 16, - "line": 21 + "line": 22 }, "end": { "column": 29, - "line": 21 + "line": 22 } } ] diff --git a/packages/svelte/tests/validator/samples/svelte-self-deprecated/input.svelte b/packages/svelte/tests/validator/samples/svelte-self-deprecated/input.svelte new file mode 100644 index 0000000000..764807819d --- /dev/null +++ b/packages/svelte/tests/validator/samples/svelte-self-deprecated/input.svelte @@ -0,0 +1,10 @@ + + +{#if n === 0} +

lift-off!

+{:else} +

{n}

+ +{/if} diff --git a/packages/svelte/tests/validator/samples/svelte-self-deprecated/warnings.json b/packages/svelte/tests/validator/samples/svelte-self-deprecated/warnings.json new file mode 100644 index 0000000000..4063f68666 --- /dev/null +++ b/packages/svelte/tests/validator/samples/svelte-self-deprecated/warnings.json @@ -0,0 +1,14 @@ +[ + { + "code": "svelte_self_deprecated", + "message": "`` is deprecated — use self-imports (e.g. `import Self from './Self.svelte'`) instead", + "start": { + "line": 9, + "column": 1 + }, + "end": { + "line": 9, + "column": 26 + } + } +] From 2553932c2cecf72bf360e065c62c3cbf547a0a11 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 15:31:31 -0400 Subject: [PATCH 3/9] fix: only warn on context="module" in runes mode (#13332) * fix: only warn on context="module" in runes mode * wow --- .changeset/old-planets-kiss.md | 5 +++++ packages/svelte/src/compiler/phases/1-parse/read/script.js | 2 -- packages/svelte/src/compiler/phases/2-analyze/index.js | 7 +++++++ .../input.svelte | 0 .../samples/script-context-module-legacy/warnings.json | 1 + .../script-context-module-runes-deprecated/input.svelte | 5 +++++ .../warnings.json | 4 ++-- 7 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .changeset/old-planets-kiss.md rename packages/svelte/tests/validator/samples/{script-context-module-deprecated => script-context-module-legacy}/input.svelte (100%) create mode 100644 packages/svelte/tests/validator/samples/script-context-module-legacy/warnings.json create mode 100644 packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/input.svelte rename packages/svelte/tests/validator/samples/{script-context-module-deprecated => script-context-module-runes-deprecated}/warnings.json (88%) diff --git a/.changeset/old-planets-kiss.md b/.changeset/old-planets-kiss.md new file mode 100644 index 0000000000..ad258f6b2f --- /dev/null +++ b/.changeset/old-planets-kiss.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only warn on context="module" in runes mode diff --git a/packages/svelte/src/compiler/phases/1-parse/read/script.js b/packages/svelte/src/compiler/phases/1-parse/read/script.js index f1bc4e5868..9675e5bbd8 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/script.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/script.js @@ -74,8 +74,6 @@ export function read_script(parser, start, attributes) { e.script_invalid_context(attribute); } - w.script_context_deprecated(attribute); - context = 'module'; } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 95a0989bde..e363ea9951 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -359,6 +359,13 @@ export function analyze_component(root, source, options) { const runes = options.runes ?? Array.from(module.scope.references.keys()).some(is_rune); + if (runes && root.module) { + const context = root.module.attributes.find((attribute) => attribute.name === 'context'); + if (context) { + w.script_context_deprecated(context); + } + } + // TODO remove all the ?? stuff, we don't need it now that we're validating the config /** @type {ComponentAnalysis} */ const analysis = { diff --git a/packages/svelte/tests/validator/samples/script-context-module-deprecated/input.svelte b/packages/svelte/tests/validator/samples/script-context-module-legacy/input.svelte similarity index 100% rename from packages/svelte/tests/validator/samples/script-context-module-deprecated/input.svelte rename to packages/svelte/tests/validator/samples/script-context-module-legacy/input.svelte diff --git a/packages/svelte/tests/validator/samples/script-context-module-legacy/warnings.json b/packages/svelte/tests/validator/samples/script-context-module-legacy/warnings.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/script-context-module-legacy/warnings.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/input.svelte b/packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/input.svelte new file mode 100644 index 0000000000..16f6431101 --- /dev/null +++ b/packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/input.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/validator/samples/script-context-module-deprecated/warnings.json b/packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/warnings.json similarity index 88% rename from packages/svelte/tests/validator/samples/script-context-module-deprecated/warnings.json rename to packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/warnings.json index 739e2b2295..1c059a4a02 100644 --- a/packages/svelte/tests/validator/samples/script-context-module-deprecated/warnings.json +++ b/packages/svelte/tests/validator/samples/script-context-module-runes-deprecated/warnings.json @@ -4,11 +4,11 @@ "message": "`context=\"module\"` is deprecated, use the `module` attribute instead", "start": { "column": 8, - "line": 1 + "line": 3 }, "end": { "column": 24, - "line": 1 + "line": 3 } } ] From b3f39151803797def93cdfec6bb544d07a0ac512 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 17:06:37 -0400 Subject: [PATCH 4/9] fix: set strings as attributes, non-strings as properties if property exists (#13327) * fix: set strings as attributes, non-strings as properties if property exists * simplify * remove draggable from list, no longer needed * in fact we dont need the lookup at all * lint * beef up test * correctly SSR translate attribute --- .changeset/perfect-ants-allow.md | 5 +++ .../client/dom/elements/attributes.js | 41 ++++++++----------- packages/svelte/src/internal/server/index.js | 16 +++++++- .../samples/attribute-if-string/_config.js | 24 +++++++++++ .../samples/attribute-if-string/main.svelte | 19 +++++++++ 5 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 .changeset/perfect-ants-allow.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-if-string/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-if-string/main.svelte diff --git a/.changeset/perfect-ants-allow.md b/.changeset/perfect-ants-allow.md new file mode 100644 index 0000000000..0d4e5a05bf --- /dev/null +++ b/.changeset/perfect-ants-allow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: set strings as attributes, non-strings as properties if property exists diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 38b584b40c..10e5c43b31 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -81,8 +81,6 @@ export function set_checked(element, checked) { * @param {boolean} [skip_warning] */ export function set_attribute(element, attribute, value, skip_warning) { - value = value == null ? null : value + ''; - // @ts-expect-error var attributes = (element.__attributes ??= {}); @@ -95,7 +93,7 @@ export function set_attribute(element, attribute, value, skip_warning) { (attribute === 'href' && element.nodeName === 'LINK') ) { if (!skip_warning) { - check_src_in_dev_hydration(element, attribute, value); + check_src_in_dev_hydration(element, attribute, value ?? ''); } // If we reset these attributes, they would result in another network request, which we want to avoid. @@ -113,8 +111,11 @@ export function set_attribute(element, attribute, value, skip_warning) { element[LOADING_ATTR_SYMBOL] = value; } - if (value === null) { + if (value == null) { element.removeAttribute(attribute); + } else if (attribute in element && typeof value !== 'string') { + // @ts-ignore + element[attribute] = value; } else { element.setAttribute(attribute, value); } @@ -287,15 +288,15 @@ export function set_attributes( name = normalize_attribute(name); } - if (setters.includes(name)) { + if (setters.includes(name) && typeof value !== 'string') { + // @ts-ignore + element[name] = value; + } else if (typeof value !== 'function') { if (hydrating && (name === 'src' || name === 'href' || name === 'srcset')) { - if (!skip_warning) check_src_in_dev_hydration(element, name, value); + if (!skip_warning) check_src_in_dev_hydration(element, name, value ?? ''); } else { - // @ts-ignore - element[name] = value; + set_attribute(element, name, value); } - } else if (typeof value !== 'function') { - set_attribute(element, name, value); } } } @@ -350,16 +351,6 @@ export function set_dynamic_element_attributes(node, prev, next, css_hash) { ); } -/** - * 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 - * accepts numeric values, but the attribute can also be set to a string like `50%`. - * In case of draggable trying to set `element.draggable='false'` will actually set - * draggable to `true`. If this list becomes too big, rethink this approach. - */ -var always_set_through_set_attribute = ['width', 'height', 'draggable']; - /** @type {Map} */ var setters_cache = new Map(); @@ -375,7 +366,7 @@ function get_setters(element) { descriptors = get_descriptors(proto); for (var key in descriptors) { - if (descriptors[key].set && !always_set_through_set_attribute.includes(key)) { + if (descriptors[key].set) { setters.push(key); } } @@ -389,12 +380,12 @@ function get_setters(element) { /** * @param {any} element * @param {string} attribute - * @param {string | null} value + * @param {string} value */ function check_src_in_dev_hydration(element, attribute, value) { if (!DEV) return; if (attribute === 'srcset' && srcset_url_equal(element, value)) return; - if (src_url_equal(element.getAttribute(attribute) ?? '', value ?? '')) return; + if (src_url_equal(element.getAttribute(attribute) ?? '', value)) return; w.hydration_attribute_changed( attribute, @@ -420,12 +411,12 @@ function split_srcset(srcset) { /** * @param {HTMLSourceElement | HTMLImageElement} element - * @param {string | undefined | null} srcset + * @param {string} srcset * @returns {boolean} */ function srcset_url_equal(element, srcset) { var element_urls = split_srcset(element.srcset); - var urls = split_srcset(srcset ?? ''); + var urls = split_srcset(srcset); return ( urls.length === element_urls.length && diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 2934c4c196..8889d52164 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -147,6 +147,19 @@ export function head(payload, fn) { head_payload.out += BLOCK_CLOSE; } +/** + * `
` should be rendered as `
` and _not_ + * `
`, which is equivalent to `
`. There + * may be other odd cases that need to be added to this list in future + * @type {Record>} + */ +const replacements = { + translate: new Map([ + [true, 'yes'], + [false, 'no'] + ]) +}; + /** * @template V * @param {string} name @@ -156,7 +169,8 @@ export function head(payload, fn) { */ export function attr(name, value, is_boolean = false) { if (value == null || (!value && is_boolean) || (value === '' && name === 'class')) return ''; - const assignment = is_boolean ? '' : `="${escape_html(value, true)}"`; + const normalized = (name in replacements && replacements[name].get(value)) || value; + const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; } diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-if-string/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-if-string/_config.js new file mode 100644 index 0000000000..e4c72306d8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-if-string/_config.js @@ -0,0 +1,24 @@ +import { test } from '../../test'; + +export default test({ + test({ assert, target, variant, hydrate }) { + function check(/** @type {boolean} */ condition) { + const divs = /** @type {NodeListOf} */ ( + target.querySelectorAll(`.translate-${condition} div`) + ); + + divs.forEach((div, i) => { + assert.equal(div.translate, condition, `${i + 1} of ${divs.length}: ${div.outerHTML}`); + }); + } + + check(false); + check(true); + + if (variant === 'hydrate') { + hydrate(); + check(false); + check(true); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-if-string/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-if-string/main.svelte new file mode 100644 index 0000000000..7696f4e78d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-if-string/main.svelte @@ -0,0 +1,19 @@ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
From 78b134dec165c1d14bec57d79f73222c1677821a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:26:45 -0400 Subject: [PATCH 5/9] Version Packages (next) (#13336) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 3 +++ packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index c8becba7ce..95283742aa 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -518,6 +518,8 @@ "old-mails-sneeze", "old-oranges-compete", "old-planets-bow", + "old-planets-kiss", + "old-spoons-pull", "olive-apples-lick", "olive-cobras-wonder", "olive-forks-grin", @@ -537,6 +539,7 @@ "orange-yaks-protect", "orange-zoos-heal", "perfect-actors-bake", + "perfect-ants-allow", "perfect-cooks-shop", "perfect-hairs-matter", "perfect-hats-dance", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6501efcc58..f0a81074cd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.0.0-next.256 + +### Patch Changes + +- fix: only warn on context="module" in runes mode ([#13332](https://github.com/sveltejs/svelte/pull/13332)) + +- feat: deprecate `` in runes mode ([#13333](https://github.com/sveltejs/svelte/pull/13333)) + +- fix: set strings as attributes, non-strings as properties if property exists ([#13327](https://github.com/sveltejs/svelte/pull/13327)) + ## 5.0.0-next.255 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 73c12fee92..2c49a142dc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.255", + "version": "5.0.0-next.256", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ff0a85d649..f91e4b5164 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.255'; +export const VERSION = '5.0.0-next.256'; export const PUBLIC_VERSION = '5'; From 3aa2ec081350f5f62725f9f918cee3b1efe9f180 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 20:17:37 -0400 Subject: [PATCH 6/9] fix: only set attribute as property if element has setter (#13341) --- .changeset/beige-clocks-notice.md | 5 +++++ .../svelte/src/internal/client/dom/elements/attributes.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/beige-clocks-notice.md diff --git a/.changeset/beige-clocks-notice.md b/.changeset/beige-clocks-notice.md new file mode 100644 index 0000000000..be133078d4 --- /dev/null +++ b/.changeset/beige-clocks-notice.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only set attribute as property if element has setter diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 10e5c43b31..9f92c59d15 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -113,7 +113,7 @@ export function set_attribute(element, attribute, value, skip_warning) { if (value == null) { element.removeAttribute(attribute); - } else if (attribute in element && typeof value !== 'string') { + } else if (typeof value !== 'string' && get_setters(element).includes(attribute)) { // @ts-ignore element[attribute] = value; } else { From de157ee1dc51e9ad3bfe8c571283fe76e0475f02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:23:06 -0400 Subject: [PATCH 7/9] Version Packages (next) (#13342) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 1 + packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 95283742aa..7a91f52887 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -19,6 +19,7 @@ "angry-plums-punch", "angry-wasps-help", "angry-weeks-design", + "beige-clocks-notice", "beige-cobras-smoke", "beige-flies-wash", "beige-gifts-appear", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f0a81074cd..e80de9ccfc 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.0.0-next.257 + +### Patch Changes + +- fix: only set attribute as property if element has setter ([#13341](https://github.com/sveltejs/svelte/pull/13341)) + ## 5.0.0-next.256 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 2c49a142dc..d96f04d3e9 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.256", + "version": "5.0.0-next.257", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index f91e4b5164..03dd320d22 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.256'; +export const VERSION = '5.0.0-next.257'; export const PUBLIC_VERSION = '5'; From 1a5cf6fef4de9152bfecf384d7c95b08967a2783 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 19 Sep 2024 21:02:26 -0400 Subject: [PATCH 8/9] perf: improve get_setters cache (#13343) --- .../svelte/src/internal/client/dom/elements/attributes.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 9f92c59d15..ee6525d8d5 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -184,8 +184,7 @@ export function set_attributes( next.class = next.class ? next.class + ' ' + css_hash : css_hash; } - var setters = setters_cache.get(element.nodeName); - if (!setters) setters_cache.set(element.nodeName, (setters = get_setters(element))); + var setters = get_setters(element); // @ts-expect-error var attributes = /** @type {Record} **/ (element.__attributes ??= {}); @@ -356,8 +355,9 @@ var setters_cache = new Map(); /** @param {Element} element */ function get_setters(element) { - /** @type {string[]} */ - var setters = []; + var setters = setters_cache.get(element.nodeName); + if (setters) return setters; + setters_cache.set(element.nodeName, (setters = [])); var descriptors; var proto = get_prototype_of(element); From f81f4feab851da65cb7830b8e1cc5576611db585 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 20 Sep 2024 07:24:08 -0400 Subject: [PATCH 9/9] chore: simplify attributes (#13337) * chore: simplify attributes * fix/optimise * unused --- .../client/visitors/RegularElement.js | 3 +- .../client/visitors/SvelteElement.js | 21 ++++-- .../client/dom/elements/attributes.js | 64 ++++--------------- packages/svelte/src/internal/client/index.js | 3 +- 4 files changed, 28 insertions(+), 63 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index d004da0411..3b3888cf5b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -511,7 +511,8 @@ function build_element_spread_attributes( b.object(values), context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), preserve_attribute_case && b.true, - is_ignored(element, 'hydration_attribute_changed') && b.true + is_ignored(element, 'hydration_attribute_changed') && b.true, + element.name.includes('-') && b.true ) ) ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 81314d67dd..01bd970035 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -1,7 +1,7 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, ObjectExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import { dev, locator } from '../../../../state.js'; +import { dev, is_ignored, locator } from '../../../../state.js'; import { get_attribute_expression, is_event_attribute, @@ -84,7 +84,7 @@ export function SvelteElement(node, context) { // Always use spread because we don't know whether the element is a custom element or not, // therefore we need to do the "how to set an attribute" logic at runtime. const is_attributes_reactive = - build_dynamic_element_attributes(attributes, inner_context, element_id) !== null; + build_dynamic_element_attributes(node, attributes, inner_context, element_id) !== null; // class/style directives must be applied last since they could override class/style attributes build_class_directives(class_directives, element_id, inner_context, is_attributes_reactive); @@ -137,12 +137,13 @@ export function SvelteElement(node, context) { /** * Serializes dynamic element attribute assignments. * Returns the `true` if spread is deemed reactive. + * @param {AST.SvelteElement} element * @param {Array} attributes * @param {ComponentContext} context * @param {Identifier} element_id * @returns {boolean} */ -function build_dynamic_element_attributes(attributes, context, element_id) { +function build_dynamic_element_attributes(element, attributes, context, element_id) { if (attributes.length === 0) { if (context.state.analysis.css.hash) { context.state.init.push( @@ -197,11 +198,14 @@ function build_dynamic_element_attributes(attributes, context, element_id) { '=', b.id(id), b.call( - '$.set_dynamic_element_attributes', + '$.set_attributes', element_id, b.id(id), b.object(values), - context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash) + context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), + b.binary('!==', b.member(element_id, 'namespaceURI'), b.id('$.NAMESPACE_SVG')), + is_ignored(element, 'hydration_attribute_changed') && b.true, + b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-')) ) ) ); @@ -218,11 +222,14 @@ function build_dynamic_element_attributes(attributes, context, element_id) { context.state.init.push( b.stmt( b.call( - '$.set_dynamic_element_attributes', + '$.set_attributes', element_id, b.literal(null), b.object(values), - context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash) + context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), + b.binary('!==', b.member(element_id, 'namespaceURI'), b.id('$.NAMESPACE_SVG')), + is_ignored(element, 'hydration_attribute_changed') && b.true, + b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-')) ) ) ); diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index ee6525d8d5..29e872dc24 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -136,18 +136,8 @@ export function set_xlink_attribute(dom, attribute, value) { * @param {any} value */ export function set_custom_element_data(node, prop, value) { - if (prop in node) { - // Reading the prop could cause an error, we don't want this to fail everything else - try { - var curr_val = node[prop]; - } catch { - set_attribute(node, prop, value); - return; - } - var next_val = typeof curr_val === 'boolean' && value === '' ? true : value; - if (typeof curr_val !== 'object' || curr_val !== next_val) { - node[prop] = next_val; - } + if (get_setters(node).includes(prop)) { + node[prop] = value; } else { set_attribute(node, prop, value); } @@ -161,6 +151,7 @@ export function set_custom_element_data(node, prop, value) { * @param {string} [css_hash] * @param {boolean} preserve_attribute_case * @param {boolean} [skip_warning] + * @param {boolean} [is_custom_element] * @returns {Record} */ export function set_attributes( @@ -169,7 +160,8 @@ export function set_attributes( next, css_hash, preserve_attribute_case = false, - skip_warning + skip_warning = false, + is_custom_element = false ) { var current = prev || {}; var is_option_element = element.tagName === 'OPTION'; @@ -271,14 +263,11 @@ export function set_attributes( delegate([event_name]); } } - } else if (value == null) { - attributes[key] = null; - element.removeAttribute(key); - } else if (key === 'style') { + } else if (key === 'style' && value != null) { element.style.cssText = value + ''; } else if (key === 'autofocus') { autofocus(/** @type {HTMLElement} */ (element), Boolean(value)); - } else if (key === '__value' || key === 'value') { + } else if (key === '__value' || (key === 'value' && value != null)) { // @ts-ignore element.value = element[key] = element.__value = value; } else { @@ -287,7 +276,10 @@ export function set_attributes( name = normalize_attribute(name); } - if (setters.includes(name) && typeof value !== 'string') { + if (value == null && !is_custom_element) { + attributes[key] = null; + element.removeAttribute(key); + } else if (setters.includes(name) && (is_custom_element || typeof value !== 'string')) { // @ts-ignore element[name] = value; } else if (typeof value !== 'function') { @@ -316,40 +308,6 @@ export function set_attributes( return current; } -/** - * @param {Element} node - * @param {Record | undefined} prev - * @param {Record} next The new attributes - this function mutates this object - * @param {string} [css_hash] - */ -export function set_dynamic_element_attributes(node, prev, next, css_hash) { - if (node.tagName.includes('-')) { - for (var key in prev) { - if (!(key in next)) { - next[key] = null; - } - } - - if (css_hash !== undefined) { - next.class = next.class ? next.class + ' ' + css_hash : css_hash; - } - - for (key in next) { - set_custom_element_data(node, key, next[key]); - } - - return next; - } - - return set_attributes( - /** @type {Element & ElementCSSInlineStyle} */ (node), - prev, - next, - css_hash, - node.namespaceURI !== NAMESPACE_SVG - ); -} - /** @type {Map} */ var setters_cache = new Map(); diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 0debfe9d19..766494e856 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -1,4 +1,4 @@ -export { FILENAME, HMR } from '../../constants.js'; +export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js'; export { cleanup_styles } from './dev/css.js'; export { add_locations } from './dev/elements.js'; export { hmr } from './dev/hmr.js'; @@ -30,7 +30,6 @@ export { set_attribute, set_attributes, set_custom_element_data, - set_dynamic_element_attributes, set_xlink_attribute, handle_lazy_img, set_value,