From ef8bd6adeb238f2d8ccc8c04547e9e16cb932c25 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 12 Dec 2024 12:16:51 -0500 Subject: [PATCH 01/17] fix: better handle hydration of script/style elements (alternative) (#14683) * alternative approach to #14624 * changeset * fix * lint --- .changeset/rotten-yaks-nail.md | 5 +++++ .../src/internal/client/dom/blocks/svelte-element.js | 6 ++++++ packages/svelte/src/internal/server/index.js | 9 +++------ packages/svelte/src/utils.js | 8 ++++++++ .../svelte/tests/hydration/samples/script/_config.js | 11 +++++++++++ .../tests/hydration/samples/script/_expected.html | 1 + .../svelte/tests/hydration/samples/script/main.svelte | 1 + 7 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 .changeset/rotten-yaks-nail.md create mode 100644 packages/svelte/tests/hydration/samples/script/_config.js create mode 100644 packages/svelte/tests/hydration/samples/script/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/script/main.svelte diff --git a/.changeset/rotten-yaks-nail.md b/.changeset/rotten-yaks-nail.md new file mode 100644 index 0000000000..bbe9b777ae --- /dev/null +++ b/.changeset/rotten-yaks-nail.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better handle hydration of script/style elements diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index 823b9a4362..35d2f223ae 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -21,6 +21,7 @@ import { component_context, active_effect } from '../../runtime.js'; import { DEV } from 'esm-env'; import { EFFECT_TRANSPARENT } from '../../constants.js'; import { assign_nodes } from '../template.js'; +import { is_raw_text_element } from '../../../../utils.js'; /** * @param {Comment | Element} node @@ -116,6 +117,11 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio assign_nodes(element, element); if (render_fn) { + if (hydrating && is_raw_text_element(next_tag)) { + // prevent hydration glitches + element.append(document.createComment('')); + } + // If hydrating, use the existing ssr comment as the anchor so that the // inner open and close methods can pick up the existing nodes correctly var child_anchor = /** @type {TemplateNode} */ ( diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index b944c602b8..b8371b7e00 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -16,7 +16,7 @@ import { DEV } from 'esm-env'; import { current_component, pop, push } from './context.js'; import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js'; import { validate_store } from '../shared/validate.js'; -import { is_boolean_attribute, is_void } from '../../utils.js'; +import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { reset_elements } from './dev.js'; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 @@ -24,9 +24,6 @@ import { reset_elements } from './dev.js'; const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; -/** List of elements that require raw contents and should not have SSR comments put in them */ -const RAW_TEXT_ELEMENTS = ['textarea', 'script', 'style', 'title']; - /** * @param {Payload} to_copy * @returns {Payload} @@ -64,13 +61,13 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) payload.out += ''; if (tag) { - payload.out += `<${tag} `; + payload.out += `<${tag}`; attributes_fn(); payload.out += `>`; if (!is_void(tag)) { children_fn(); - if (!RAW_TEXT_ELEMENTS.includes(tag)) { + if (!is_raw_text_element(tag)) { payload.out += EMPTY_COMMENT; } payload.out += ``; diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 75171c1786..9324408007 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -441,3 +441,11 @@ const RUNES = /** @type {const} */ ([ export function is_rune(name) { return RUNES.includes(/** @type {RUNES[number]} */ (name)); } + +/** List of elements that require raw contents and should not have SSR comments put in them */ +const RAW_TEXT_ELEMENTS = /** @type {const} */ (['textarea', 'script', 'style', 'title']); + +/** @param {string} name */ +export function is_raw_text_element(name) { + return RAW_TEXT_ELEMENTS.includes(/** @type {RAW_TEXT_ELEMENTS[number]} */ (name)); +} diff --git a/packages/svelte/tests/hydration/samples/script/_config.js b/packages/svelte/tests/hydration/samples/script/_config.js new file mode 100644 index 0000000000..4723e4e454 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/script/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + snapshot(target) { + const script = target.querySelector('script'); + + return { + script + }; + } +}); diff --git a/packages/svelte/tests/hydration/samples/script/_expected.html b/packages/svelte/tests/hydration/samples/script/_expected.html new file mode 100644 index 0000000000..b3a4d92219 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/script/_expected.html @@ -0,0 +1 @@ + diff --git a/packages/svelte/tests/hydration/samples/script/main.svelte b/packages/svelte/tests/hydration/samples/script/main.svelte new file mode 100644 index 0000000000..3904d47f73 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/script/main.svelte @@ -0,0 +1 @@ +{"{}"} From 432db95358b3a8ad5a81e7958109b024ff2f4a8e Mon Sep 17 00:00:00 2001 From: James Glenn <47917431+JR-G@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:19:13 +0000 Subject: [PATCH 02/17] docs: Update the linked playgrounds in the snippet docs (#14676) * Update the linked playgrounds in the snippet docs * Apply suggestions from code review --------- Co-authored-by: Rich Harris --- documentation/docs/03-template-syntax/06-snippet.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/03-template-syntax/06-snippet.md b/documentation/docs/03-template-syntax/06-snippet.md index f8148f3dc3..c9951d3f34 100644 --- a/documentation/docs/03-template-syntax/06-snippet.md +++ b/documentation/docs/03-template-syntax/06-snippet.md @@ -112,7 +112,7 @@ Snippets can reference themselves and each other ([demo](/playground/untitled#H4 ## Passing snippets to components -Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE41SwY6bMBD9lRGplKQlYRMpF5ZF7T_0ttmDwSZYJbZrT9pGlv-9g4Fkk-xhxYV5vHlvhjc-aWQnXJK_-kSxo0jy5IcxSZrg2fSF-yM6FFQ7fbJ1jxSuttJguVd7lEejLcJPVnUCGquPMF9nsVoPjfNnohGx1sohMU4SHbzAa4_t0UNvmcOcGUNDzFP4jeccdikYK2v6sIWQ3lErpui5cDdPF_LmkVy3wlp5Vd5e2U_rHYSe_kYjFtl1KeVnTkljBEIrGBd2sYy8AtsyLlBk9DYhJHtTR_UbBDWybkR8NkqHWyOr_y74ZMNLz9f9AoG6ePkOJLMHLBp-xISvcPf11r0YUuMM2Ysfkgngh5XphUYKkJWU_FFz2UjBkxztSYT0cihR4LOn0tGaPrql439N-7Uh0Dl8MVYbt1jeJ1Fg7xDb_Uw2Y18YQqZ_S2U5FH1pS__dCkWMa3C0uR0pfQRTg89kE4bLLLDS_Dxy_Eywuo1TAnPAw4fqY1rvtH3W9w35ZZMgvU3jq8LhedwkguCHRhT_cMU6eVA5dKLB5wGutCWjlTOslupAxxrxceKoD2hzhe2qbmXHF1v1bbOcNCtW_zpYfVI8h5kQ4qY3mueHTlesW2C7TOEO4hcdwzgf3Nc7cZxUKKC4yuNhvIX_MlV_Xk0EAAA=)): +Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE3VS247aMBD9lZGpBGwDASRegonaPvQL2qdlH5zYEKvBNvbQLbL875VzAcKyj3PmzJnLGU8UOwqSkd8KJdaCk4TsZS0cyV49wYuJuQiQpGd-N2bu_ooaI1YwJ57hpVYoFDqSEepKKw3mO7VDeTTaIvxiRS1gb_URxvO0ibrS8WanIrHUyiHs7Vmigy28RmyHHmKvDMbMmFq4cQInvGSwTsBYWYoMVhCSB2rBFFPsyl0uruTlR3JZCWvlTXl1Yy_mawiR_rbZKZrellJ-5JQ0RiBUgnFhJ9OGR7HKmwVoilXeIye8DOJGfYCgRlZ3iE876TBsZPX7hPdteO75PC4QaIo8vwNPePmANQ2fMeEFHrLD7rR1jTNkW986E8C3KwfwVr8HSHOSEBT_kGRozyIkn_zQveXDL3rIfPJHtUDwzShJd_Qk3gQCbOGLsdq4yfTRJopRuin3I7nv6kL7ARRjmLdBDG3uv1mhuLA3V2mKtqNEf_oCn8p9aN-WYqH5peP4kWBl1UwJzAEPT9U7K--0fRrrWnPTXpCm1_EVdXjpNmlA8G1hPPyM1fKgMqjFHjctXGjLhZ05w0qpDhksGrybuNEHtJnCalZWsuaTlfq6nPaaBSv_HKw-K57BjzOiVj9ZKQYKzQjZodYFqydYTRN4gPhVzTDO2xnma3HsVWjaLjT8nbfwHy7Q5f2dBAAA)): ```svelte + +
+

Input/Textarea value

+ +
+ + + + + + + + +
+ + +
+ + + + + + + + +
+ + +
+ + + + + + + + +
+ +

Input checked

+ +
+ + + + +
+ + +
+ + + + +
+ + +
+ + + + +
+ + +
+ + +
+ + + +

Select (single)

+ + + + + + + + + + + + +

Select (multiple)

+ + + + + + + + +

Static values

+
+ + + +
+ + +
+ +

+ Bound values: + {value1} {value3} {value6} {value8} + {value9} {value12} {value14} {value16} + {value17} {value20} {value22} {value24} + {checked2} {checked4} + {checked6} {checked8} + {checked10} {checked12} + {checked14} + {selected1} + {selected2} + {selected3} + {selected4} + {selected5} + {selected6} +

diff --git a/packages/svelte/tests/runtime-runes/samples/form-default-value/_config.js b/packages/svelte/tests/runtime-runes/samples/form-default-value/_config.js index 5ef72aaa8e..35ab6e8ece 100644 --- a/packages/svelte/tests/runtime-runes/samples/form-default-value/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/form-default-value/_config.js @@ -68,7 +68,6 @@ export default test({ assert.htmlEqual(test1_span.innerHTML, 'foo foo foo foo'); after_reset.push(() => { - console.log('-------------'); check_inputs(inputs, 'value', 'x'); assert.htmlEqual(test1_span.innerHTML, 'x x x x'); }); @@ -88,7 +87,6 @@ export default test({ assert.htmlEqual(test2_span.innerHTML, 'foo foo foo foo'); after_reset.push(() => { - console.log('-------------'); check_inputs(inputs, 'value', 'x'); assert.htmlEqual(test2_span.innerHTML, 'x x x x'); }); From 65db40986035e0d20b8e1da8e4006e16d2c12971 Mon Sep 17 00:00:00 2001 From: waedi Date: Thu, 12 Dec 2024 20:22:06 +0100 Subject: [PATCH 04/17] docs: typo in ## script_context_deprecated (#14694) * Fix typo in ## script_context_deprecated Changed +++context+++ to +++module+++ * regenerate --------- Co-authored-by: Rich Harris --- documentation/docs/98-reference/.generated/compile-warnings.md | 2 +- packages/svelte/messages/compile-warnings/template.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 88b1f278a6..f6f2585df2 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -775,7 +775,7 @@ Reassignments of module-level declarations will not cause reactive statements to ``` ```svelte - ``` diff --git a/packages/svelte/messages/compile-warnings/template.md b/packages/svelte/messages/compile-warnings/template.md index b15b01241b..33e635bdb2 100644 --- a/packages/svelte/messages/compile-warnings/template.md +++ b/packages/svelte/messages/compile-warnings/template.md @@ -57,7 +57,7 @@ This code will work when the component is rendered on the client (which is why t > `context="module"` is deprecated, use the `module` attribute instead ```svelte - ``` From 88c2d6ea36f1b9bd6d1f52788c4e5a258c30868b Mon Sep 17 00:00:00 2001 From: Yang Pan <77009679+panyang05@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:43:01 -0500 Subject: [PATCH 05/17] fix: Allow unquoted slash in attributes (#14615) * test: add sample for unquoted attributes * fix: handle unquoted slash in attributes * docs: allow unquoted slash in attributes * test: add additional sample for unquoted href attributes * fix: improve handling of self-closing tags with unquoted attributes * Update .changeset/long-boxes-flow.md --------- Co-authored-by: Rich Harris --- .changeset/long-boxes-flow.md | 5 ++ .../compiler/phases/1-parse/state/element.js | 20 ++++- .../samples/attribute-unquoted/input.svelte | 4 +- .../samples/attribute-unquoted/output.json | 80 ++++++++++++++++++- 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 .changeset/long-boxes-flow.md diff --git a/.changeset/long-boxes-flow.md b/.changeset/long-boxes-flow.md new file mode 100644 index 0000000000..d249354b67 --- /dev/null +++ b/.changeset/long-boxes-flow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow unquoted slash in attributes diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 45350bb1ae..cd5cdd3e6e 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -504,8 +504,24 @@ function read_attribute(parser) { let value = true; if (parser.eat('=')) { parser.allow_whitespace(); - value = read_attribute_value(parser); - end = parser.index; + + if (parser.template[parser.index] === '/' && parser.template[parser.index + 1] === '>') { + const char_start = parser.index; + parser.index++; // consume '/' + value = [ + { + start: char_start, + end: char_start + 1, + type: 'Text', + raw: '/', + data: '/' + } + ]; + end = parser.index; + } else { + value = read_attribute_value(parser); + end = parser.index; + } } else if (parser.match_regex(regex_starts_with_quote_characters)) { e.expected_token(parser.index, '='); } diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte index 4bab0df72f..527d6eebf1 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/input.svelte @@ -1 +1,3 @@ -
\ No newline at end of file +
+home +home \ No newline at end of file diff --git a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json index 5df4d66ab6..ab2912a2c0 100644 --- a/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json +++ b/packages/svelte/tests/parser-legacy/samples/attribute-unquoted/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 21, + "end": 62, "children": [ { "type": "Element", @@ -27,6 +27,84 @@ } ], "children": [] + }, + { + "type": "Text", + "start": 21, + "end": 22, + "raw": "\n", + "data": "\n" + }, + { + "type": "Element", + "start": 22, + "end": 40, + "name": "a", + "attributes": [ + { + "type": "Attribute", + "start": 25, + "end": 31, + "name": "href", + "value": [ + { + "start": 30, + "end": 31, + "type": "Text", + "raw": "/", + "data": "/" + } + ] + } + ], + "children": [ + { + "type": "Text", + "start": 32, + "end": 36, + "raw": "home", + "data": "home" + } + ] + }, + { + "type": "Text", + "start": 40, + "end": 41, + "raw": "\n", + "data": "\n" + }, + { + "type": "Element", + "start": 41, + "end": 62, + "name": "a", + "attributes": [ + { + "type": "Attribute", + "start": 44, + "end": 53, + "name": "href", + "value": [ + { + "start": 49, + "end": 53, + "type": "Text", + "raw": "/foo", + "data": "/foo" + } + ] + } + ], + "children": [ + { + "type": "Text", + "start": 54, + "end": 58, + "raw": "home", + "data": "home" + } + ] } ] } From 780041a51e1425167c30875dd54d906028128eff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:55:51 -0500 Subject: [PATCH 06/17] Version Packages (#14689) Co-authored-by: github-actions[bot] --- .changeset/long-boxes-flow.md | 5 ----- .changeset/rotten-yaks-nail.md | 5 ----- .changeset/silent-tips-cover.md | 5 ----- .changeset/strong-pandas-provide.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/long-boxes-flow.md delete mode 100644 .changeset/rotten-yaks-nail.md delete mode 100644 .changeset/silent-tips-cover.md delete mode 100644 .changeset/strong-pandas-provide.md diff --git a/.changeset/long-boxes-flow.md b/.changeset/long-boxes-flow.md deleted file mode 100644 index d249354b67..0000000000 --- a/.changeset/long-boxes-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow unquoted slash in attributes diff --git a/.changeset/rotten-yaks-nail.md b/.changeset/rotten-yaks-nail.md deleted file mode 100644 index bbe9b777ae..0000000000 --- a/.changeset/rotten-yaks-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: better handle hydration of script/style elements diff --git a/.changeset/silent-tips-cover.md b/.changeset/silent-tips-cover.md deleted file mode 100644 index 1f51572cda..0000000000 --- a/.changeset/silent-tips-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: make `defaultValue` work with spread diff --git a/.changeset/strong-pandas-provide.md b/.changeset/strong-pandas-provide.md deleted file mode 100644 index 0fe7e70c6d..0000000000 --- a/.changeset/strong-pandas-provide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid mutation validation for invalidate_inner_signals diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 978e841bf8..6f3380ff5a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.11.3 + +### Patch Changes + +- fix: allow unquoted slash in attributes ([#14615](https://github.com/sveltejs/svelte/pull/14615)) + +- fix: better handle hydration of script/style elements ([#14683](https://github.com/sveltejs/svelte/pull/14683)) + +- fix: make `defaultValue` work with spread ([#14640](https://github.com/sveltejs/svelte/pull/14640)) + +- fix: avoid mutation validation for invalidate_inner_signals ([#14688](https://github.com/sveltejs/svelte/pull/14688)) + ## 5.11.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index e95341a0bd..abcda8613d 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.11.2", + "version": "5.11.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e264eace2c..39d89acd53 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.11.2'; +export const VERSION = '5.11.3'; export const PUBLIC_VERSION = '5'; From 2e0dcd78722d457f4c9e4b6db4ef4cb3ab26c037 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 12 Dec 2024 20:09:29 +0000 Subject: [PATCH 07/17] fix: ensure if block paths retain correct template namespacing (#14685) * fix: ensure if block paths retain correct template namespacing * add tests * address feedback * address feedback * simplify --------- Co-authored-by: Rich Harris --- .changeset/giant-moons-accept.md | 5 +++++ .../src/compiler/phases/3-transform/utils.js | 19 ++++++++++++++++++- .../svg-namespace-if-block/Child.svelte | 8 ++++++++ .../samples/svg-namespace-if-block/_config.js | 14 ++++++++++++++ .../svg-namespace-if-block/main.svelte | 7 +++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 .changeset/giant-moons-accept.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/main.svelte diff --git a/.changeset/giant-moons-accept.md b/.changeset/giant-moons-accept.md new file mode 100644 index 0000000000..7940371d6f --- /dev/null +++ b/.changeset/giant-moons-accept.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure if block paths retain correct template namespacing diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index 14fd3aa2e8..ffd07dd26a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -347,7 +347,24 @@ export function infer_namespace(namespace, parent, nodes) { } } - return namespace; + /** @type {Namespace | null} */ + let new_namespace = null; + + // Check the elements within the fragment and look for consistent namespaces. + // If we have no namespaces or they are mixed, then fallback to existing namespace + for (const node of nodes) { + if (node.type !== 'RegularElement') continue; + + if (node.metadata.mathml) { + new_namespace = new_namespace === null || new_namespace === 'mathml' ? 'mathml' : 'html'; + } else if (node.metadata.svg) { + new_namespace = new_namespace === null || new_namespace === 'svg' ? 'svg' : 'html'; + } else { + return 'html'; + } + } + + return new_namespace ?? namespace; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/Child.svelte b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/Child.svelte new file mode 100644 index 0000000000..53e6203dde --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/Child.svelte @@ -0,0 +1,8 @@ + +{#if true} + + + +{:else} +
lol
+{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/_config.js b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/_config.js new file mode 100644 index 0000000000..22a2469bfb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/_config.js @@ -0,0 +1,14 @@ +import { test, ok } from '../../test'; + +export default test({ + html: ``, + test({ assert, target }) { + const g = target.querySelector('g'); + const rect = target.querySelector('rect'); + ok(g); + ok(rect); + + assert.equal(g.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(rect.namespaceURI, 'http://www.w3.org/2000/svg'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/main.svelte b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/main.svelte new file mode 100644 index 0000000000..8f6154462f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block/main.svelte @@ -0,0 +1,7 @@ + + + + + From 61a0da8a5fdf5ac86431ceadfae0f54d38dc9a66 Mon Sep 17 00:00:00 2001 From: Mateusz Kadlubowski Date: Fri, 13 Dec 2024 04:15:40 +0800 Subject: [PATCH 08/17] feat: Expose more AST types from `"svelte/compiler"` (#14601) * add missing `SvelteBoundary` in `ElementLike` * make union of AST types public and exportable with `AST` namespace * apply AST types change to codebase * changeset * manually generate types * Add `AttributeLike` type * export namespace `Css` inside `AST` * manually generate types again * exported `Css` -> `CSS` * `Css` -> `AST.CSS` * fix Prettier issue * Apply suggestions from code review --------- Co-authored-by: Rich Harris --- .changeset/short-papayas-relate.md | 5 + packages/svelte/src/compiler/legacy.js | 6 +- packages/svelte/src/compiler/migrate/index.js | 10 +- .../src/compiler/phases/1-parse/index.js | 4 +- .../compiler/phases/1-parse/read/script.js | 4 +- .../src/compiler/phases/1-parse/read/style.js | 40 +++---- .../compiler/phases/1-parse/state/element.js | 18 +-- .../phases/2-analyze/css/css-analyze.js | 18 +-- .../phases/2-analyze/css/css-prune.js | 58 +++++----- .../compiler/phases/2-analyze/css/css-warn.js | 8 +- .../compiler/phases/2-analyze/css/utils.js | 14 +-- .../src/compiler/phases/2-analyze/index.js | 8 +- .../src/compiler/phases/2-analyze/types.d.ts | 8 +- .../visitors/AssignmentExpression.js | 1 - .../phases/2-analyze/visitors/Attribute.js | 4 +- .../2-analyze/visitors/CallExpression.js | 4 +- .../2-analyze/visitors/LabeledStatement.js | 4 +- .../phases/2-analyze/visitors/SnippetBlock.js | 4 +- .../phases/2-analyze/visitors/shared/a11y.js | 6 +- .../2-analyze/visitors/shared/attribute.js | 4 +- .../2-analyze/visitors/shared/fragment.js | 4 +- .../3-transform/client/transform-client.js | 10 +- .../phases/3-transform/client/types.d.ts | 10 +- .../phases/3-transform/client/utils.js | 4 +- .../client/visitors/BindDirective.js | 6 +- .../client/visitors/shared/component.js | 4 +- .../client/visitors/shared/events.js | 4 +- .../client/visitors/shared/fragment.js | 6 +- .../client/visitors/shared/utils.js | 6 +- .../compiler/phases/3-transform/css/index.js | 24 ++-- .../3-transform/server/transform-server.js | 10 +- .../phases/3-transform/server/types.d.ts | 10 +- .../server/visitors/AssignmentExpression.js | 4 +- .../server/visitors/shared/component.js | 4 +- .../server/visitors/shared/element.js | 4 +- .../server/visitors/shared/utils.js | 4 +- .../compiler/phases/3-transform/types.d.ts | 4 +- .../src/compiler/phases/3-transform/utils.js | 20 ++-- packages/svelte/src/compiler/phases/css.js | 4 +- packages/svelte/src/compiler/phases/nodes.js | 4 +- packages/svelte/src/compiler/phases/scope.js | 26 ++--- .../svelte/src/compiler/phases/types.d.ts | 8 +- packages/svelte/src/compiler/state.js | 8 +- packages/svelte/src/compiler/types/css.d.ts | 2 +- packages/svelte/src/compiler/types/index.d.ts | 6 +- .../src/compiler/types/legacy-nodes.d.ts | 4 +- .../svelte/src/compiler/types/template.d.ts | 101 ++++++++-------- packages/svelte/src/compiler/utils/ast.js | 4 +- packages/svelte/src/compiler/utils/slot.js | 4 +- packages/svelte/types/index.d.ts | 109 +++++++++++------- 50 files changed, 343 insertions(+), 303 deletions(-) create mode 100644 .changeset/short-papayas-relate.md diff --git a/.changeset/short-papayas-relate.md b/.changeset/short-papayas-relate.md new file mode 100644 index 0000000000..430c507a0c --- /dev/null +++ b/.changeset/short-papayas-relate.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: expose more AST types from `"svelte/compiler"` diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index 2d90988936..e3f88c8f1d 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -1,5 +1,5 @@ /** @import { Expression } from 'estree' */ -/** @import { AST, SvelteNode, TemplateNode } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import * as Legacy from './types/legacy-nodes.js' */ import { walk } from 'zimmerframe'; import { @@ -11,7 +11,7 @@ import { extract_svelte_ignore } from './utils/extract_svelte_ignore.js'; /** * Some of the legacy Svelte AST nodes remove whitespace from the start and end of their children. - * @param {TemplateNode[]} nodes + * @param {AST.TemplateNode[]} nodes */ function remove_surrounding_whitespace_nodes(nodes) { const first = nodes.at(0); @@ -40,7 +40,7 @@ function remove_surrounding_whitespace_nodes(nodes) { * @returns {Legacy.LegacyRoot} */ export function convert(source, ast) { - const root = /** @type {SvelteNode | Legacy.LegacySvelteNode} */ (ast); + const root = /** @type {AST.SvelteNode | Legacy.LegacySvelteNode} */ (ast); return /** @type {Legacy.LegacyRoot} */ ( walk(root, null, { diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 88f9bbf0ee..1bb7a69a20 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -2,7 +2,7 @@ /** @import { Visitors } from 'zimmerframe' */ /** @import { ComponentAnalysis } from '../phases/types.js' */ /** @import { Scope, ScopeRoot } from '../phases/scope.js' */ -/** @import { AST, Binding, SvelteNode, ValidatedCompileOptions } from '#compiler' */ +/** @import { AST, Binding, ValidatedCompileOptions } from '#compiler' */ import MagicString from 'magic-string'; import { walk } from 'zimmerframe'; import { parse } from '../phases/1-parse/index.js'; @@ -479,7 +479,7 @@ export function migrate(source, { filename, use_ts } = {}) { * }} State */ -/** @type {Visitors} */ +/** @type {Visitors} */ const instance_script = { _(node, { state, next }) { // @ts-expect-error @@ -1050,7 +1050,7 @@ function trim_block(state, start, end) { } } -/** @type {Visitors} */ +/** @type {Visitors} */ const template = { Identifier(node, { state, path }) { handle_identifier(node, state, path); @@ -1410,7 +1410,7 @@ const template = { /** * @param {AST.RegularElement | AST.SvelteElement | AST.SvelteComponent | AST.Component | AST.SlotElement | AST.SvelteFragment} node - * @param {SvelteNode[]} path + * @param {AST.SvelteNode[]} path * @param {State} state */ function migrate_slot_usage(node, path, state) { @@ -1580,7 +1580,7 @@ function migrate_slot_usage(node, path, state) { /** * @param {VariableDeclarator} declarator * @param {State} state - * @param {SvelteNode[]} path + * @param {AST.SvelteNode[]} path */ function extract_type_and_comment(declarator, state, path) { const str = state.str; diff --git a/packages/svelte/src/compiler/phases/1-parse/index.js b/packages/svelte/src/compiler/phases/1-parse/index.js index 7639c2f0ed..c3a8a098d3 100644 --- a/packages/svelte/src/compiler/phases/1-parse/index.js +++ b/packages/svelte/src/compiler/phases/1-parse/index.js @@ -1,4 +1,4 @@ -/** @import { AST, TemplateNode } from '#compiler' */ +/** @import { AST } from '#compiler' */ // @ts-expect-error acorn type definitions are borked in the release we use import { isIdentifierStart, isIdentifierChar } from 'acorn'; import fragment from './state/fragment.js'; @@ -28,7 +28,7 @@ export class Parser { /** Whether we're parsing in TypeScript mode */ ts = false; - /** @type {TemplateNode[]} */ + /** @type {AST.TemplateNode[]} */ stack = []; /** @type {AST.Fragment[]} */ 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 87367aff08..9d9ed3a1ef 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/script.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/script.js @@ -1,5 +1,5 @@ /** @import { Program } from 'estree' */ -/** @import { AST, Directive } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { Parser } from '../index.js' */ import * as acorn from '../acorn.js'; import { regex_not_newline_characters } from '../../patterns.js'; @@ -16,7 +16,7 @@ const ALLOWED_ATTRIBUTES = ['context', 'generics', 'lang', 'module']; /** * @param {Parser} parser * @param {number} start - * @param {Array} attributes + * @param {Array} attributes * @returns {AST.Script} */ export function read_script(parser, start, attributes) { diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index aa835a1d96..29e8a0e541 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -1,4 +1,4 @@ -/** @import { AST, Css, Directive } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { Parser } from '../index.js' */ import * as e from '../../../errors.js'; @@ -18,8 +18,8 @@ const REGEX_HTML_COMMENT_CLOSE = /-->/; /** * @param {Parser} parser * @param {number} start - * @param {Array} attributes - * @returns {Css.StyleSheet} + * @param {Array} attributes + * @returns {AST.CSS.StyleSheet} */ export default function read_style(parser, start, attributes) { const content_start = parser.index; @@ -49,7 +49,7 @@ export default function read_style(parser, start, attributes) { * @returns {any[]} */ function read_body(parser, close) { - /** @type {Array} */ + /** @type {Array} */ const children = []; while (parser.index < parser.template.length) { @@ -71,7 +71,7 @@ function read_body(parser, close) { /** * @param {Parser} parser - * @returns {Css.Atrule} + * @returns {AST.CSS.Atrule} */ function read_at_rule(parser) { const start = parser.index; @@ -81,7 +81,7 @@ function read_at_rule(parser) { const prelude = read_value(parser); - /** @type {Css.Block | null} */ + /** @type {AST.CSS.Block | null} */ let block = null; if (parser.match('{')) { @@ -104,7 +104,7 @@ function read_at_rule(parser) { /** * @param {Parser} parser - * @returns {Css.Rule} + * @returns {AST.CSS.Rule} */ function read_rule(parser) { const start = parser.index; @@ -126,10 +126,10 @@ function read_rule(parser) { /** * @param {Parser} parser * @param {boolean} [inside_pseudo_class] - * @returns {Css.SelectorList} + * @returns {AST.CSS.SelectorList} */ function read_selector_list(parser, inside_pseudo_class = false) { - /** @type {Css.ComplexSelector[]} */ + /** @type {AST.CSS.ComplexSelector[]} */ const children = []; allow_comment_or_whitespace(parser); @@ -162,18 +162,18 @@ function read_selector_list(parser, inside_pseudo_class = false) { /** * @param {Parser} parser * @param {boolean} [inside_pseudo_class] - * @returns {Css.ComplexSelector} + * @returns {AST.CSS.ComplexSelector} */ function read_selector(parser, inside_pseudo_class = false) { const list_start = parser.index; - /** @type {Css.RelativeSelector[]} */ + /** @type {AST.CSS.RelativeSelector[]} */ const children = []; /** - * @param {Css.Combinator | null} combinator + * @param {AST.CSS.Combinator | null} combinator * @param {number} start - * @returns {Css.RelativeSelector} + * @returns {AST.CSS.RelativeSelector} */ function create_selector(combinator, start) { return { @@ -190,7 +190,7 @@ function read_selector(parser, inside_pseudo_class = false) { }; } - /** @type {Css.RelativeSelector} */ + /** @type {AST.CSS.RelativeSelector} */ let relative_selector = create_selector(null, parser.index); while (parser.index < parser.template.length) { @@ -247,7 +247,7 @@ function read_selector(parser, inside_pseudo_class = false) { } else if (parser.eat(':')) { const name = read_identifier(parser); - /** @type {null | Css.SelectorList} */ + /** @type {null | AST.CSS.SelectorList} */ let args = null; if (parser.eat('(')) { @@ -372,7 +372,7 @@ function read_selector(parser, inside_pseudo_class = false) { /** * @param {Parser} parser - * @returns {Css.Combinator | null} + * @returns {AST.CSS.Combinator | null} */ function read_combinator(parser) { const start = parser.index; @@ -407,14 +407,14 @@ function read_combinator(parser) { /** * @param {Parser} parser - * @returns {Css.Block} + * @returns {AST.CSS.Block} */ function read_block(parser) { const start = parser.index; parser.eat('{', true); - /** @type {Array} */ + /** @type {Array} */ const children = []; while (parser.index < parser.template.length) { @@ -441,7 +441,7 @@ function read_block(parser) { * Reads a declaration, rule or at-rule * * @param {Parser} parser - * @returns {Css.Declaration | Css.Rule | Css.Atrule} + * @returns {AST.CSS.Declaration | AST.CSS.Rule | AST.CSS.Atrule} */ function read_block_item(parser) { if (parser.match('@')) { @@ -460,7 +460,7 @@ function read_block_item(parser) { /** * @param {Parser} parser - * @returns {Css.Declaration} + * @returns {AST.CSS.Declaration} */ function read_declaration(parser) { const start = parser.index; diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index cd5cdd3e6e..2b6a88f7bd 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -1,5 +1,5 @@ /** @import { Expression } from 'estree' */ -/** @import { AST, Directive, ElementLike, TemplateNode } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { Parser } from '../index.js' */ import { is_void } from '../../../../utils.js'; import read_expression from '../read/expression.js'; @@ -28,7 +28,7 @@ export const regex_valid_component_name = // (must start with uppercase letter if no dots, can contain dots) /^(?:\p{Lu}[$\u200c\u200d\p{ID_Continue}.]*|\p{ID_Start}[$\u200c\u200d\p{ID_Continue}]*(?:\.[$\u200c\u200d\p{ID_Continue}]+)+)$/u; -/** @type {Map} */ +/** @type {Map} */ const root_only_meta_tags = new Map([ ['svelte:head', 'SvelteHead'], ['svelte:options', 'SvelteOptions'], @@ -37,7 +37,7 @@ const root_only_meta_tags = new Map([ ['svelte:body', 'SvelteBody'] ]); -/** @type {Map} */ +/** @type {Map} */ const meta_tags = new Map([ ...root_only_meta_tags, ['svelte:element', 'SvelteElement'], @@ -137,7 +137,7 @@ export default function element(parser) { ? 'SlotElement' : 'RegularElement'; - /** @type {ElementLike} */ + /** @type {AST.ElementLike} */ const element = type === 'RegularElement' ? { @@ -155,7 +155,7 @@ export default function element(parser) { path: [] } } - : /** @type {ElementLike} */ ({ + : /** @type {AST.ElementLike} */ ({ type, start, end: -1, @@ -358,7 +358,7 @@ export default function element(parser) { } } -/** @param {TemplateNode[]} stack */ +/** @param {AST.TemplateNode[]} stack */ function parent_is_head(stack) { let i = stack.length; while (i--) { @@ -369,7 +369,7 @@ function parent_is_head(stack) { return false; } -/** @param {TemplateNode[]} stack */ +/** @param {AST.TemplateNode[]} stack */ function parent_is_shadowroot_template(stack) { // https://developer.chrome.com/docs/css-ui/declarative-shadow-dom#building_a_declarative_shadow_root let i = stack.length; @@ -433,7 +433,7 @@ function read_static_attribute(parser) { /** * @param {Parser} parser - * @returns {AST.Attribute | AST.SpreadAttribute | Directive | null} + * @returns {AST.Attribute | AST.SpreadAttribute | AST.Directive | null} */ function read_attribute(parser) { const start = parser.index; @@ -564,7 +564,7 @@ function read_attribute(parser) { } } - /** @type {Directive} */ + /** @type {AST.Directive} */ const directive = { start, end, diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 1dd2d9ae7c..b8c88a1023 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -1,5 +1,5 @@ /** @import { ComponentAnalysis } from '../../types.js' */ -/** @import { Css } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { Visitors } from 'zimmerframe' */ import { walk } from 'zimmerframe'; import * as e from '../../../errors.js'; @@ -8,17 +8,17 @@ import { is_global, is_unscoped_pseudo_class } from './utils.js'; /** * @typedef {Visitors< - * Css.Node, + * AST.CSS.Node, * { * keyframes: string[]; - * rule: Css.Rule | null; + * rule: AST.CSS.Rule | null; * } * >} CssVisitors */ /** * True if is `:global` - * @param {Css.SimpleSelector} simple_selector + * @param {AST.CSS.SimpleSelector} simple_selector */ function is_global_block_selector(simple_selector) { return ( @@ -112,7 +112,7 @@ const css_visitors = { } }, RelativeSelector(node, context) { - const parent = /** @type {Css.ComplexSelector} */ (context.path.at(-1)); + const parent = /** @type {AST.CSS.ComplexSelector} */ (context.path.at(-1)); if ( node.combinator != null && @@ -149,7 +149,7 @@ const css_visitors = { if (node.metadata.is_global_like || node.metadata.is_global) { // So that nested selectors like `:root:not(.x)` are not marked as unused for (const child of node.selectors) { - walk(/** @type {Css.Node} */ (child), null, { + walk(/** @type {AST.CSS.Node} */ (child), null, { ComplexSelector(node, context) { node.metadata.used = true; context.next(); @@ -177,7 +177,7 @@ const css_visitors = { if (idx !== -1) { is_global_block = true; for (let i = idx + 1; i < child.selectors.length; i++) { - walk(/** @type {Css.Node} */ (child.selectors[i]), null, { + walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { ComplexSelector(node) { node.metadata.used = true; } @@ -240,7 +240,7 @@ const css_visitors = { }); }, NestingSelector(node, context) { - const rule = /** @type {Css.Rule} */ (context.state.rule); + const rule = /** @type {AST.CSS.Rule} */ (context.state.rule); const parent_rule = rule.metadata.parent_rule; if (!parent_rule) { @@ -271,7 +271,7 @@ const css_visitors = { }; /** - * @param {Css.StyleSheet} stylesheet + * @param {AST.CSS.StyleSheet} stylesheet * @param {ComponentAnalysis} analysis */ export function analyze_css(stylesheet, analysis) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index d017b215f2..35bc675166 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -14,7 +14,7 @@ const whitelist_attribute_selector = new Map([ ['dialog', ['open']] ]); -/** @type {Compiler.Css.Combinator} */ +/** @type {Compiler.AST.CSS.Combinator} */ const descendant_combinator = { type: 'Combinator', name: ' ', @@ -22,7 +22,7 @@ const descendant_combinator = { end: -1 }; -/** @type {Compiler.Css.RelativeSelector} */ +/** @type {Compiler.AST.CSS.RelativeSelector} */ const nesting_selector = { type: 'RelativeSelector', start: -1, @@ -51,11 +51,11 @@ const seen = new Set(); /** * - * @param {Compiler.Css.StyleSheet} stylesheet + * @param {Compiler.AST.CSS.StyleSheet} stylesheet * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element */ export function prune(stylesheet, element) { - walk(/** @type {Compiler.Css.Node} */ (stylesheet), null, { + walk(/** @type {Compiler.AST.CSS.Node} */ (stylesheet), null, { Rule(node, context) { if (node.metadata.is_global_block) { context.visit(node.prelude); @@ -69,7 +69,11 @@ export function prune(stylesheet, element) { seen.clear(); if ( - apply_selector(selectors, /** @type {Compiler.Css.Rule} */ (node.metadata.rule), element) + apply_selector( + selectors, + /** @type {Compiler.AST.CSS.Rule} */ (node.metadata.rule), + element + ) ) { node.metadata.used = true; } @@ -86,7 +90,7 @@ export function prune(stylesheet, element) { * Also searches them for any existing `&` selectors and adds one if none are found. * This ensures we traverse up to the parent rule when the inner selectors match and we're * trying to see if the parent rule also matches. - * @param {Compiler.Css.ComplexSelector} node + * @param {Compiler.AST.CSS.ComplexSelector} node */ function get_relative_selectors(node) { const selectors = truncate(node); @@ -124,7 +128,7 @@ function get_relative_selectors(node) { /** * Discard trailing `:global(...)` selectors, these are unused for scoping purposes - * @param {Compiler.Css.ComplexSelector} node + * @param {Compiler.AST.CSS.ComplexSelector} node */ function truncate(node) { const i = node.children.findLastIndex(({ metadata, selectors }) => { @@ -152,8 +156,8 @@ function truncate(node) { } /** - * @param {Compiler.Css.RelativeSelector[]} relative_selectors - * @param {Compiler.Css.Rule} rule + * @param {Compiler.AST.CSS.RelativeSelector[]} relative_selectors + * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element * @returns {boolean} */ @@ -178,9 +182,9 @@ function apply_selector(relative_selectors, rule, element) { } /** - * @param {Compiler.Css.RelativeSelector} relative_selector - * @param {Compiler.Css.RelativeSelector[]} parent_selectors - * @param {Compiler.Css.Rule} rule + * @param {Compiler.AST.CSS.RelativeSelector} relative_selector + * @param {Compiler.AST.CSS.RelativeSelector[]} parent_selectors + * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node * @returns {boolean} */ @@ -263,8 +267,8 @@ function apply_combinator(relative_selector, parent_selectors, rule, node) { * it's a `:global(...)` or unscopeable selector, or * is an `:is(...)` or `:where(...)` selector that contains * a global selector - * @param {Compiler.Css.RelativeSelector} selector - * @param {Compiler.Css.Rule} rule + * @param {Compiler.AST.CSS.RelativeSelector} selector + * @param {Compiler.AST.CSS.Rule} rule */ function is_global(selector, rule) { if (selector.metadata.is_global || selector.metadata.is_global_like) { @@ -272,7 +276,7 @@ function is_global(selector, rule) { } for (const s of selector.selectors) { - /** @type {Compiler.Css.SelectorList | null} */ + /** @type {Compiler.AST.CSS.SelectorList | null} */ let selector_list = null; let owner = rule; @@ -283,7 +287,7 @@ function is_global(selector, rule) { } if (s.type === 'NestingSelector') { - owner = /** @type {Compiler.Css.Rule} */ (rule.metadata.parent_rule); + owner = /** @type {Compiler.AST.CSS.Rule} */ (rule.metadata.parent_rule); selector_list = owner.prelude; } @@ -306,8 +310,8 @@ const regex_backslash_and_following_character = /\\(.)/g; /** * Ensure that `element` satisfies each simple selector in `relative_selector` * - * @param {Compiler.Css.RelativeSelector} relative_selector - * @param {Compiler.Css.Rule} rule + * @param {Compiler.AST.CSS.RelativeSelector} relative_selector + * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element * @returns {boolean} */ @@ -352,7 +356,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) const seen = new Set(); /** - * @param {Compiler.SvelteNode} node + * @param {Compiler.AST.SvelteNode} node * @param {{ is_child: boolean }} state */ function walk_children(node, state) { @@ -389,7 +393,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) // upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the // selector in a way that is similar to ancestor matching. In a sense, we're treating `.x:has(.y)` as `.x .y`. for (const has_selector of has_selectors) { - const complex_selectors = /** @type {Compiler.Css.SelectorList} */ (has_selector.args) + const complex_selectors = /** @type {Compiler.AST.CSS.SelectorList} */ (has_selector.args) .children; let matched = false; @@ -578,7 +582,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) case 'NestingSelector': { let matched = false; - const parent = /** @type {Compiler.Css.Rule} */ (rule.metadata.parent_rule); + const parent = /** @type {Compiler.AST.CSS.Rule} */ (rule.metadata.parent_rule); for (const complex_selector of parent.prelude.children) { if ( @@ -611,9 +615,9 @@ function get_following_sibling_elements(element, include_self) { const path = element.metadata.path; let i = path.length; - /** @type {Compiler.SvelteNode} */ + /** @type {Compiler.AST.SvelteNode} */ let start = element; - let nodes = /** @type {Compiler.SvelteNode[]} */ ( + let nodes = /** @type {Compiler.AST.SvelteNode[]} */ ( /** @type {Compiler.AST.Fragment} */ (path[0]).nodes ); @@ -639,7 +643,7 @@ function get_following_sibling_elements(element, include_self) { const seen = new Set(); - /** @param {Compiler.SvelteNode} node */ + /** @param {Compiler.AST.SvelteNode} node */ function get_siblings(node) { walk(node, null, { RegularElement(node) { @@ -836,7 +840,7 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { const result = new Map(); const path = node.metadata.path; - /** @type {Compiler.SvelteNode} */ + /** @type {Compiler.AST.SvelteNode} */ let current = node; let i = path.length; @@ -1008,7 +1012,7 @@ function higher_existence(exist1, exist2) { } /** - * @param {Compiler.SvelteNode[]} children + * @param {Compiler.AST.SvelteNode[]} children * @param {boolean} adjacent_only */ function loop_child(children, adjacent_only) { @@ -1038,7 +1042,7 @@ function loop_child(children, adjacent_only) { } /** - * @param {Compiler.SvelteNode} node + * @param {Compiler.AST.SvelteNode} node * @returns {node is Compiler.AST.IfBlock | Compiler.AST.EachBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement} */ function is_block(node) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js index eab67327e2..238c83f00e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js @@ -1,17 +1,17 @@ /** @import { Visitors } from 'zimmerframe' */ -/** @import { Css } from '#compiler' */ +/** @import { AST } from '#compiler' */ import { walk } from 'zimmerframe'; import * as w from '../../../warnings.js'; import { is_keyframes_node } from '../../css.js'; /** - * @param {Css.StyleSheet} stylesheet + * @param {AST.CSS.StyleSheet} stylesheet */ export function warn_unused(stylesheet) { walk(stylesheet, { stylesheet }, visitors); } -/** @type {Visitors} */ +/** @type {Visitors} */ const visitors = { Atrule(node, context) { if (!is_keyframes_node(node)) { @@ -28,7 +28,7 @@ const visitors = { !node.metadata.used && // prevent double-marking of `.unused:is(.unused)` (context.path.at(-2)?.type !== 'PseudoClassSelector' || - /** @type {Css.ComplexSelector} */ (context.path.at(-4))?.metadata.used) + /** @type {AST.CSS.ComplexSelector} */ (context.path.at(-4))?.metadata.used) ) { const content = context.state.stylesheet.content; const text = content.styles.substring(node.start - content.start, node.end - content.start); diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/utils.js b/packages/svelte/src/compiler/phases/2-analyze/css/utils.js index 07171a23bb..d3fd71ec39 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/utils.js @@ -1,4 +1,4 @@ -/** @import { AST, Css } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { Node } from 'estree' */ const UNKNOWN = {}; @@ -36,7 +36,7 @@ export function get_possible_values(chunk) { /** * Returns all parent rules; root is last - * @param {Css.Rule | null} rule + * @param {AST.CSS.Rule | null} rule */ export function get_parent_rules(rule) { const rules = []; @@ -51,8 +51,8 @@ export function get_parent_rules(rule) { /** * True if is `:global(...)` or `:global` and no pseudo class that is scoped. - * @param {Css.RelativeSelector} relative_selector - * @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array] }} + * @param {AST.CSS.RelativeSelector} relative_selector + * @returns {relative_selector is AST.CSS.RelativeSelector & { selectors: [AST.CSS.PseudoClassSelector, ...Array] }} */ export function is_global(relative_selector) { const first = relative_selector.selectors[0]; @@ -72,7 +72,7 @@ export function is_global(relative_selector) { /** * `true` if is a pseudo class that cannot be or is not scoped - * @param {Css.SimpleSelector} selector + * @param {AST.CSS.SimpleSelector} selector */ export function is_unscoped_pseudo_class(selector) { return ( @@ -96,8 +96,8 @@ export function is_unscoped_pseudo_class(selector) { /** * True if is `:global(...)` or `:global`, irrespective of whether or not there are any pseudo classes that are scoped. * Difference to `is_global`: `:global(x):has(y)` is `true` for `is_outer_global` but `false` for `is_global`. - * @param {Css.RelativeSelector} relative_selector - * @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array] }} + * @param {AST.CSS.RelativeSelector} relative_selector + * @returns {relative_selector is AST.CSS.RelativeSelector & { selectors: [AST.CSS.PseudoClassSelector, ...Array] }} */ export function is_outer_global(relative_selector) { const first = relative_selector.selectors[0]; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 9e29813ee3..042e88fa2f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1,5 +1,5 @@ /** @import { Expression, Node, Program } from 'estree' */ -/** @import { Binding, AST, SvelteNode, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ +/** @import { Binding, AST, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ /** @import { AnalysisState, Visitors } from './types' */ /** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */ import { walk } from 'zimmerframe'; @@ -525,7 +525,7 @@ export function analyze_component(root, source, options) { // more legacy nonsense: if an `each` binding is reassigned/mutated, // treat the expression as being mutated as well - walk(/** @type {SvelteNode} */ (template.ast), null, { + walk(/** @type {AST.SvelteNode} */ (template.ast), null, { EachBlock(node) { const scope = /** @type {Scope} */ (template.scopes.get(node)); @@ -608,7 +608,7 @@ export function analyze_component(root, source, options) { reactive_statements: new Map() }; - walk(/** @type {SvelteNode} */ (ast), state, visitors); + walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); } // warn on any nonstate declarations that are a) reassigned and b) referenced in the template @@ -677,7 +677,7 @@ export function analyze_component(root, source, options) { function_depth: scope.function_depth }; - walk(/** @type {SvelteNode} */ (ast), state, visitors); + walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); } for (const [name, binding] of instance.scope.declarations) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index dedbe95ace..b4ca4dc262 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -1,11 +1,11 @@ import type { Scope } from '../scope.js'; import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; -import type { ExpressionMetadata, AST, ValidatedCompileOptions, SvelteNode } from '#compiler'; +import type { AST, ExpressionMetadata, ValidatedCompileOptions } from '#compiler'; import type { LabeledStatement } from 'estree'; export interface AnalysisState { scope: Scope; - scopes: Map; + scopes: Map; analysis: ComponentAnalysis; options: ValidatedCompileOptions; ast_type: 'instance' | 'template' | 'module'; @@ -31,11 +31,11 @@ export interface AnalysisState { } export type Context = import('zimmerframe').Context< - SvelteNode, + AST.SvelteNode, State >; export type Visitors = import('zimmerframe').Visitors< - SvelteNode, + AST.SvelteNode, State >; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js index 54e5b46486..a64c89cd88 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js @@ -1,5 +1,4 @@ /** @import { AssignmentExpression } from 'estree' */ -/** @import { SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ import { extract_identifiers, object } from '../../../utils/ast.js'; import { validate_assignment } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 6c050d966a..6eb9faca6d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -1,5 +1,5 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ -/** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */ +/** @import { AST, DelegatedEvent } from '#compiler' */ /** @import { Context } from '../types' */ import { cannot_be_set_statically, is_capture_event, is_delegated } from '../../../../utils.js'; import { @@ -16,7 +16,7 @@ import { mark_subtree_dynamic } from './shared/fragment.js'; export function Attribute(node, context) { context.next(); - const parent = /** @type {SvelteNode} */ (context.path.at(-1)); + const parent = /** @type {AST.SvelteNode} */ (context.path.at(-1)); if (parent.type === 'RegularElement') { // special case