From c2e805f05cb652ed3e7fa29f4a7c3f2912a79678 Mon Sep 17 00:00:00 2001 From: Theodore Brown Date: Wed, 22 Jan 2025 13:54:12 -0600 Subject: [PATCH 1/5] fix: consistently set value to blank string when value attribute is undefined (#15057) Fixes #4467 --- .changeset/dirty-sloths-move.md | 5 ++ .../client/dom/elements/attributes.js | 4 +- .../value-attribute-null-undefined/_config.js | 49 +++++++++++++++++++ .../main.svelte | 9 ++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 .changeset/dirty-sloths-move.md create mode 100644 packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte diff --git a/.changeset/dirty-sloths-move.md b/.changeset/dirty-sloths-move.md new file mode 100644 index 0000000000..07436a0b2e --- /dev/null +++ b/.changeset/dirty-sloths-move.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: consistently set value to blank string when value attribute is undefined diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index a2fffe8696..eab27e6c02 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -68,14 +68,14 @@ export function set_value(element, value) { // treat null and undefined the same for the initial value value ?? undefined) || // @ts-expect-error - // `progress` elements always need their value set when its `0` + // `progress` elements always need their value set when it's `0` (element.value === value && (value !== 0 || element.nodeName !== 'PROGRESS')) ) { return; } // @ts-expect-error - element.value = value; + element.value = value ?? ''; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js new file mode 100644 index 0000000000..08f0c5aec7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js @@ -0,0 +1,49 @@ +import { test, ok } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + mode: ['client'], + + async test({ assert, target }) { + /** + * @type {HTMLInputElement | null} + */ + const input = target.querySelector('input[type=text]'); + /** + * @type {HTMLButtonElement | null} + */ + const setString = target.querySelector('#setString'); + /** + * @type {HTMLButtonElement | null} + */ + const setNull = target.querySelector('#setNull'); + /** + * @type {HTMLButtonElement | null} + */ + const setUndefined = target.querySelector('#setUndefined'); + + ok(input); + ok(setString); + ok(setNull); + ok(setUndefined); + + // value should always be blank string when value attribute is set to null or undefined + + assert.equal(input.value, ''); + setString.click(); + flushSync(); + assert.equal(input.value, 'foo'); + + setNull.click(); + flushSync(); + assert.equal(input.value, ''); + + setString.click(); + flushSync(); + assert.equal(input.value, 'foo'); + + setUndefined.click(); + flushSync(); + assert.equal(input.value, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte new file mode 100644 index 0000000000..9f944923c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte @@ -0,0 +1,9 @@ + + + + + + + From 7740d4576b95f2e4e4e95a9df2d5504cb12a9461 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 22 Jan 2025 15:48:41 -0500 Subject: [PATCH 2/5] fix: optimise || expressions in template (#15092) --- .changeset/large-islands-cover.md | 5 ++++ .../client/visitors/shared/utils.js | 26 +++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 .changeset/large-islands-cover.md diff --git a/.changeset/large-islands-cover.md b/.changeset/large-islands-cover.md new file mode 100644 index 0000000000..969d2fc254 --- /dev/null +++ b/.changeset/large-islands-cover.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: optimise || expressions in template diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 4c28616665..00634f229e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -118,22 +118,22 @@ export function build_template_chunk( // extra work in the template_effect (instead we do the work in set_text). return { value, has_state }; } else { - let expression = value; - // only add nullish coallescence if it hasn't been added already - if (value.type === 'LogicalExpression' && value.operator === '??') { - const { right } = value; - // `undefined` isn't a Literal (due to pre-ES5 shenanigans), so the only nullish literal is `null` - // however, you _can_ make a variable called `undefined` in a Svelte component, so we can't just treat it the same way - if (right.type !== 'Literal') { - expression = b.logical('??', value, b.literal('')); - } else if (right.value === null) { - // if they do something weird like `stuff ?? null`, replace `null` with empty string - value.right = b.literal(''); + // add `?? ''` where necessary (TODO optimise more cases) + if ( + value.type === 'LogicalExpression' && + value.right.type === 'Literal' && + (value.operator === '??' || value.operator === '||') + ) { + // `foo ?? null` -=> `foo ?? ''` + // otherwise leave the expression untouched + if (value.right.value === null) { + value = { ...value, right: b.literal('') }; } } else { - expression = b.logical('??', value, b.literal('')); + value = b.logical('??', value, b.literal('')); } - expressions.push(expression); + + expressions.push(value); } quasi = b.quasi('', i + 1 === values.length); From d17f5c748d4b842e63d0017461ccde49b00b78e0 Mon Sep 17 00:00:00 2001 From: Edoardo Cavazza Date: Wed, 22 Jan 2025 21:51:26 +0100 Subject: [PATCH 3/5] fix: add check for is attribute (#15086) Fixes #15085 Add a check for is attribute in is_custom_element_node function. --- .changeset/two-doors-exercise.md | 5 +++++ .../client/visitors/RegularElement.js | 2 +- .../client/visitors/shared/fragment.js | 3 ++- packages/svelte/src/compiler/phases/nodes.js | 8 ++++++-- .../custom-element-attributes/_config.js | 19 +++++++++++++++++++ .../custom-element-attributes/main.svelte | 2 ++ 6 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 .changeset/two-doors-exercise.md create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte diff --git a/.changeset/two-doors-exercise.md b/.changeset/two-doors-exercise.md new file mode 100644 index 0000000000..b41e582ae7 --- /dev/null +++ b/.changeset/two-doors-exercise.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: add check for `is` attribute to correctly detect custom elements 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 d0a148f216..c7e218d521 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 @@ -227,7 +227,7 @@ export function RegularElement(node, context) { node_id, attributes_id, (node.metadata.svg || node.metadata.mathml || is_custom_element_node(node)) && b.true, - node.name.includes('-') && b.true, + is_custom_element_node(node) && b.true, context.state ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 0b4ac87342..5bc3041ca4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -4,6 +4,7 @@ import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; +import { is_custom_element_node } from '../../../../nodes.js'; import { build_template_chunk } from './utils.js'; /** @@ -128,7 +129,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { function is_static_element(node, state) { if (node.type !== 'RegularElement') return false; if (node.fragment.metadata.dynamic) return false; - if (node.name.includes('-')) return false; // we're setting all attributes on custom elements through properties + if (is_custom_element_node(node)) return false; // we're setting all attributes on custom elements through properties for (const attribute of node.attributes) { if (attribute.type !== 'Attribute') { diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 5ca4ce3380..003c3a2c49 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -23,10 +23,14 @@ export function is_element_node(node) { /** * @param {AST.RegularElement | AST.SvelteElement} node - * @returns {node is AST.RegularElement} + * @returns {boolean} */ export function is_custom_element_node(node) { - return node.type === 'RegularElement' && node.name.includes('-'); + return ( + node.type === 'RegularElement' && + (node.name.includes('-') || + node.attributes.some((attr) => attr.type === 'Attribute' && attr.name === 'is')) + ); } /** diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js new file mode 100644 index 0000000000..118a51157e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client', 'server'], + async test({ assert, target }) { + const my_element = /** @type HTMLElement & { object: { test: true }; } */ ( + target.querySelector('my-element') + ); + const my_link = /** @type HTMLAnchorElement & { object: { test: true }; } */ ( + target.querySelector('a') + ); + assert.equal(my_element.getAttribute('string'), 'test'); + assert.equal(my_element.hasAttribute('object'), false); + assert.deepEqual(my_element.object, { test: true }); + assert.equal(my_link.getAttribute('string'), 'test'); + assert.equal(my_link.hasAttribute('object'), false); + assert.deepEqual(my_link.object, { test: true }); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte new file mode 100644 index 0000000000..ff94a9484c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte @@ -0,0 +1,2 @@ + + From d47f4f59084a26d8b47d864e961ddf76336a0ed8 Mon Sep 17 00:00:00 2001 From: "Trevor N. Suarez" Date: Thu, 23 Jan 2025 00:55:12 -0700 Subject: [PATCH 4/5] fix: expand boolean attribute support (#15095) * Adding test for expected boolean attribute support * Improving support for more boolean attributes: Added: - `defer` - `disablepictureinpicture` - `disableremoteplayback` Improved: - `allowfullscreen` - `novalidate` * Skipping JSDOM version of boolean attribute test JSDOM lacks support for some attributes, so we'll skip it for now. See: - `async`: https://github.com/jsdom/jsdom/issues/1564 - `nomodule`: https://github.com/jsdom/jsdom/issues/2475 - `autofocus`: https://github.com/jsdom/jsdom/issues/3041 - `inert`: https://github.com/jsdom/jsdom/issues/3605 - etc...: https://github.com/jestjs/jest/issues/139#issuecomment-592673550 * Adding changeset --- .changeset/strange-hairs-trade.md | 5 ++ packages/svelte/src/utils.js | 16 ++++- .../_config.js | 60 +++++++++++++++++++ .../main.svelte | 22 +++++++ 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 .changeset/strange-hairs-trade.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte diff --git a/.changeset/strange-hairs-trade.md b/.changeset/strange-hairs-trade.md new file mode 100644 index 0000000000..192e5ef3d1 --- /dev/null +++ b/.changeset/strange-hairs-trade.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: expand boolean attribute support diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index b16c0551f1..e8e1bc224c 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -170,7 +170,10 @@ const DOM_BOOLEAN_ATTRIBUTES = [ 'reversed', 'seamless', 'selected', - 'webkitdirectory' + 'webkitdirectory', + 'defer', + 'disablepictureinpicture', + 'disableremoteplayback' ]; /** @@ -197,7 +200,10 @@ const ATTRIBUTE_ALIASES = { defaultvalue: 'defaultValue', defaultchecked: 'defaultChecked', srcobject: 'srcObject', - novalidate: 'noValidate' + novalidate: 'noValidate', + allowfullscreen: 'allowFullscreen', + disablepictureinpicture: 'disablePictureInPicture', + disableremoteplayback: 'disableRemotePlayback' }; /** @@ -219,7 +225,11 @@ const DOM_PROPERTIES = [ 'volume', 'defaultValue', 'defaultChecked', - 'srcObject' + 'srcObject', + 'noValidate', + 'allowFullscreen', + 'disablePictureInPicture', + 'disableRemotePlayback' ]; /** diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js new file mode 100644 index 0000000000..d0b8a421b3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js @@ -0,0 +1,60 @@ +import { test } from '../../test'; + +export default test({ + // JSDOM lacks support for some of these attributes, so we'll skip it for now. + // + // See: + // - `async`: https://github.com/jsdom/jsdom/issues/1564 + // - `nomodule`: https://github.com/jsdom/jsdom/issues/2475 + // - `autofocus`: https://github.com/jsdom/jsdom/issues/3041 + // - `inert`: https://github.com/jsdom/jsdom/issues/3605 + // - etc...: https://github.com/jestjs/jest/issues/139#issuecomment-592673550 + skip_mode: ['client'], + + html: ` + +
+ + + + + + + + +
+
    +
    + + + +
    + + + + + + + + +
    +
      +
      + + + +
      + + + + + + + + +
      +
        +
        + +` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte new file mode 100644 index 0000000000..e9e5a16168 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte @@ -0,0 +1,22 @@ + + +{#each attributeValues as val} + +
        + + + + + + + + +
        +
          +
          + +{/each} From 3d828b46b24964163cbe184e683e1db9f4b0ddfd Mon Sep 17 00:00:00 2001 From: Elliott Johnson Date: Thu, 23 Jan 2025 16:45:14 -0700 Subject: [PATCH 5/5] feat: Remove repository-sync workflows in favor of github-docs-bot (#15097) * Revert "fix: ensure untrack correctly retains the active reaction (#15065)" This reverts commit 2ad519542dcba2d7802394068d412bb73d9cb803. * feat: Remove repository-sync workflows in favor of github-docs-bot * Revert "Revert "fix: ensure untrack correctly retains the active reaction (#15065)"" This reverts commit f4e98bf89f80cc332703c1da3c2bd3d1451d1dce. --- .../workflows/docs-preview-create-request.yml | 26 ------------------ .../workflows/docs-preview-delete-request.yml | 27 ------------------- .github/workflows/sync-request.yml | 22 --------------- 3 files changed, 75 deletions(-) delete mode 100644 .github/workflows/docs-preview-create-request.yml delete mode 100644 .github/workflows/docs-preview-delete-request.yml delete mode 100644 .github/workflows/sync-request.yml diff --git a/.github/workflows/docs-preview-create-request.yml b/.github/workflows/docs-preview-create-request.yml deleted file mode 100644 index f57766dc36..0000000000 --- a/.github/workflows/docs-preview-create-request.yml +++ /dev/null @@ -1,26 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Docs preview create request - -on: - pull_request_target: - branches: - - main - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: docs-preview-create - client-payload: |- - { - "package": "svelte", - "repo": "${{ github.repository }}", - "owner": "${{ github.event.pull_request.head.repo.owner.login }}", - "branch": "${{ github.event.pull_request.head.ref }}", - "pr": ${{ github.event.pull_request.number }} - } diff --git a/.github/workflows/docs-preview-delete-request.yml b/.github/workflows/docs-preview-delete-request.yml deleted file mode 100644 index 4eb0e996a6..0000000000 --- a/.github/workflows/docs-preview-delete-request.yml +++ /dev/null @@ -1,27 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Docs preview delete request - -on: - pull_request_target: - branches: - - main - types: [closed] - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: docs-preview-delete - client-payload: |- - { - "package": "svelte", - "repo": "${{ github.repository }}", - "owner": "${{ github.event.pull_request.head.repo.owner.login }}", - "branch": "${{ github.event.pull_request.head.ref }}", - "pr": ${{ github.event.pull_request.number }} - } diff --git a/.github/workflows/sync-request.yml b/.github/workflows/sync-request.yml deleted file mode 100644 index de2ce77692..0000000000 --- a/.github/workflows/sync-request.yml +++ /dev/null @@ -1,22 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Sync request - -on: - push: - branches: - - main - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: sync-request - client-payload: |- - { - "package": "svelte" - }