From 1803290864df3dffa772af5adad987f4192183da Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Wed, 20 Apr 2022 01:20:16 +0900 Subject: [PATCH 001/303] [fix] Specify svg namespace if {@html} is used in svg (#7464) * add test * create svg element if {@html} tag is inside of svg * always use claim_html_tag --- .../render_dom/wrappers/RawMustacheTag.ts | 9 ++++- src/runtime/internal/dom.ts | 30 ++++++++------ .../each-block-changed-check/expected.js | 4 +- test/runtime/samples/svg-html-tag/_config.js | 35 +++++++++++++++++ test/runtime/samples/svg-html-tag/main.svelte | 10 +++++ test/runtime/samples/svg-html-tag2/_config.js | 39 +++++++++++++++++++ .../runtime/samples/svg-html-tag2/main.svelte | 12 ++++++ test/runtime/samples/svg-html-tag3/_config.js | 33 ++++++++++++++++ .../runtime/samples/svg-html-tag3/main.svelte | 11 ++++++ 9 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 test/runtime/samples/svg-html-tag/_config.js create mode 100644 test/runtime/samples/svg-html-tag/main.svelte create mode 100644 test/runtime/samples/svg-html-tag2/_config.js create mode 100644 test/runtime/samples/svg-html-tag2/main.svelte create mode 100644 test/runtime/samples/svg-html-tag3/_config.js create mode 100644 test/runtime/samples/svg-html-tag3/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts index 1315f1e144..7810d8f099 100644 --- a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts +++ b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts @@ -1,8 +1,10 @@ +import { namespaces } from './../../../utils/namespaces'; import { b, x } from 'code-red'; import Renderer from '../Renderer'; import Block from '../Block'; import Tag from './shared/Tag'; import Wrapper from './shared/Wrapper'; +import Element from '../../nodes/Element'; import MustacheTag from '../../nodes/MustacheTag'; import RawMustacheTag from '../../nodes/RawMustacheTag'; import { is_head } from './shared/is_head'; @@ -51,9 +53,12 @@ export default class RawMustacheTagWrapper extends Tag { const update_anchor = needs_anchor ? html_anchor : this.next ? this.next.var : 'null'; - block.chunks.create.push(b`${html_tag} = new @HtmlTag();`); + const parent_element = this.node.find_nearest(/^Element/) as Element; + const is_svg = parent_element && parent_element.namespace === namespaces.svg; + block.chunks.create.push(b`${html_tag} = new @HtmlTag(${is_svg ? 'true' : 'false'});`); + if (this.renderer.options.hydratable) { - block.chunks.claim.push(b`${html_tag} = @claim_html_tag(${_parent_nodes});`); + block.chunks.claim.push(b`${html_tag} = @claim_html_tag(${_parent_nodes}, ${is_svg ? 'true' : 'false'});`); } block.chunks.hydrate.push(b`${html_tag}.a = ${update_anchor};`); block.chunks.mount.push(b`${html_tag}.m(${init}, ${parent_node || '#target'}, ${parent_node ? null : '#anchor'});`); diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index e2a7420236..cadc1abbaa 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -492,12 +492,13 @@ function find_comment(nodes, text, start) { return nodes.length; } -export function claim_html_tag(nodes) { + +export function claim_html_tag(nodes, is_svg: boolean) { // find html opening tag const start_index = find_comment(nodes, 'HTML_TAG_START', 0); const end_index = find_comment(nodes, 'HTML_TAG_END', start_index); if (start_index === end_index) { - return new HtmlTagHydration(); + return new HtmlTagHydration(undefined, is_svg); } init_claim_info(nodes); @@ -509,7 +510,7 @@ export function claim_html_tag(nodes) { n.claim_order = nodes.claim_info.total_claimed; nodes.claim_info.total_claimed += 1; } - return new HtmlTagHydration(claimed_nodes); + return new HtmlTagHydration(claimed_nodes, is_svg); } export function set_data(text, data) { @@ -645,16 +646,18 @@ export function query_selector_all(selector: string, parent: HTMLElement = docum } export class HtmlTag { + private is_svg = false; // parent for creating node - e: HTMLElement; + e: HTMLElement | SVGElement; // html tag nodes n: ChildNode[]; // target - t: HTMLElement; + t: HTMLElement | SVGElement; // anchor - a: HTMLElement; + a: HTMLElement | SVGElement; - constructor() { + constructor(is_svg: boolean = false) { + this.is_svg = is_svg; this.e = this.n = null; } @@ -662,9 +665,14 @@ export class HtmlTag { this.h(html); } - m(html: string, target: HTMLElement, anchor: HTMLElement = null) { + m( + html: string, + target: HTMLElement | SVGElement, + anchor: HTMLElement | SVGElement = null + ) { if (!this.e) { - this.e = element(target.nodeName as keyof HTMLElementTagNameMap); + if (this.is_svg) this.e = svg_element(target.nodeName as keyof SVGElementTagNameMap); + else this.e = element(target.nodeName as keyof HTMLElementTagNameMap); this.t = target; this.c(html); } @@ -698,8 +706,8 @@ export class HtmlTagHydration extends HtmlTag { // hydration claimed nodes l: ChildNode[] | void; - constructor(claimed_nodes?: ChildNode[]) { - super(); + constructor(claimed_nodes?: ChildNode[], is_svg: boolean = false) { + super(is_svg); this.e = this.n = null; this.l = claimed_nodes; } diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index 19947edbbb..e91f77ecd8 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -52,7 +52,7 @@ function create_each_block(ctx) { t4 = text(t4_value); t5 = text(" ago:"); t6 = space(); - html_tag = new HtmlTag(); + html_tag = new HtmlTag(false); attr(span, "class", "meta"); html_tag.a = null; attr(div, "class", "comment"); @@ -170,4 +170,4 @@ class Component extends SvelteComponent { } } -export default Component; \ No newline at end of file +export default Component; diff --git a/test/runtime/samples/svg-html-tag/_config.js b/test/runtime/samples/svg-html-tag/_config.js new file mode 100644 index 0000000000..f35bfc21d5 --- /dev/null +++ b/test/runtime/samples/svg-html-tag/_config.js @@ -0,0 +1,35 @@ +export default { + html: ` + + + + + `, + test({ assert, target, component }) { + + let svg = target.querySelector('svg'); + let circles = target.querySelectorAll('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(2, circles.length); + assert.equal(circles[0].namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circles[1].namespaceURI, 'http://www.w3.org/2000/svg'); + + component.width = 200; + component.height = 120; + assert.htmlEqual( + target.innerHTML, + ` + + + + + ` + ); + svg = target.querySelector('svg'); + circles = target.querySelectorAll('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(2, circles.length); + assert.equal(circles[0].namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circles[1].namespaceURI, 'http://www.w3.org/2000/svg'); + } +}; diff --git a/test/runtime/samples/svg-html-tag/main.svelte b/test/runtime/samples/svg-html-tag/main.svelte new file mode 100644 index 0000000000..dfeb6b04d3 --- /dev/null +++ b/test/runtime/samples/svg-html-tag/main.svelte @@ -0,0 +1,10 @@ + + + + {@html circle} + + diff --git a/test/runtime/samples/svg-html-tag2/_config.js b/test/runtime/samples/svg-html-tag2/_config.js new file mode 100644 index 0000000000..3c07cd7499 --- /dev/null +++ b/test/runtime/samples/svg-html-tag2/_config.js @@ -0,0 +1,39 @@ +export default { + html: ` + + + + + + + `, + test({ assert, target, component }) { + + let svg = target.querySelector('svg'); + let circles = target.querySelectorAll('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(2, circles.length); + assert.equal(circles[0].namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circles[1].namespaceURI, 'http://www.w3.org/2000/svg'); + + component.width = 200; + component.height = 120; + assert.htmlEqual( + target.innerHTML, + ` + + + + + + + ` + ); + svg = target.querySelector('svg'); + circles = target.querySelectorAll('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(2, circles.length); + assert.equal(circles[0].namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circles[1].namespaceURI, 'http://www.w3.org/2000/svg'); + } +}; diff --git a/test/runtime/samples/svg-html-tag2/main.svelte b/test/runtime/samples/svg-html-tag2/main.svelte new file mode 100644 index 0000000000..d8192955d9 --- /dev/null +++ b/test/runtime/samples/svg-html-tag2/main.svelte @@ -0,0 +1,12 @@ + + + + + {@html circle} + + + diff --git a/test/runtime/samples/svg-html-tag3/_config.js b/test/runtime/samples/svg-html-tag3/_config.js new file mode 100644 index 0000000000..1209d298fe --- /dev/null +++ b/test/runtime/samples/svg-html-tag3/_config.js @@ -0,0 +1,33 @@ +export default { + html: ` + + + + + + `, + test({ assert, target, component }) { + + let svg = target.querySelector('svg'); + let circle = target.querySelector('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circle.namespaceURI, 'http://www.w3.org/1999/xhtml'); + + component.width = 200; + component.height = 120; + assert.htmlEqual( + target.innerHTML, + ` + + + + + + ` + ); + svg = target.querySelector('svg'); + circle = target.querySelector('circle'); + assert.equal(svg.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(circle.namespaceURI, 'http://www.w3.org/1999/xhtml'); + } +}; diff --git a/test/runtime/samples/svg-html-tag3/main.svelte b/test/runtime/samples/svg-html-tag3/main.svelte new file mode 100644 index 0000000000..86dbb5aa7a --- /dev/null +++ b/test/runtime/samples/svg-html-tag3/main.svelte @@ -0,0 +1,11 @@ + + + + + {@html circle} + + From 9e8592ef2cd8e3feaadeff58c10a237234a43e26 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 20 Apr 2022 00:25:15 +0800 Subject: [PATCH 002/303] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa27bd1ee0..914a837954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Strip leading newline after `
` and `
\ No newline at end of file
diff --git a/test/validator/samples/logic-block-in-attribute/errors.json b/test/validator/samples/logic-block-in-attribute/errors.json
new file mode 100644
index 0000000000..ebf68938e7
--- /dev/null
+++ b/test/validator/samples/logic-block-in-attribute/errors.json
@@ -0,0 +1,9 @@
+[
+	{
+		"code": "invalid-logic-block-placement",
+		"message": "{#if} logic block cannot be in attribute value",
+		"start": { "line": 1, "column": 12, "character": 12 },
+		"end": { "line": 1, "column": 12, "character": 12 },
+		"pos": 12
+	}
+]
diff --git a/test/validator/samples/logic-block-in-attribute/input.svelte b/test/validator/samples/logic-block-in-attribute/input.svelte
new file mode 100644
index 0000000000..241529527c
--- /dev/null
+++ b/test/validator/samples/logic-block-in-attribute/input.svelte
@@ -0,0 +1 @@
+
\ No newline at end of file diff --git a/test/validator/samples/logic-block-in-textarea/errors.json b/test/validator/samples/logic-block-in-textarea/errors.json new file mode 100644 index 0000000000..b1d73d4903 --- /dev/null +++ b/test/validator/samples/logic-block-in-textarea/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-logic-block-placement", + "message": "{#each} logic block cannot be inside \ No newline at end of file From 2b7393885724ed158dd3ce1b9e53b9737a5f3c3e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 7 Oct 2022 08:25:04 +0800 Subject: [PATCH 142/303] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d12212be87..6ca984af7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Better error message when specifying an invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) * Fix to call component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) * Fix false positive warnings about props binding in dev mode ([#4457](https://github.com/sveltejs/svelte/issues/4457)) +* Better error message when using logic blocks or tags in invalid place ([#7552](https://github.com/sveltejs/svelte/issues/7552)) ## 3.50.1 From 220325cd9feea12f9a9bc5eb8df5120f856b1bc9 Mon Sep 17 00:00:00 2001 From: Samuel Stroschein <35429197+samuelstroschein@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:11:46 +0200 Subject: [PATCH 143/303] [docs] add inline documentation to svelte runtime functions (#7846) * add documentation * add links do docs --- src/runtime/internal/lifecycle.ts | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index fbbeca9a67..e75bbdc501 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -11,18 +11,47 @@ export function get_current_component() { return current_component; } +/** + * Schedules a callback to run immediately before the component is updated after any state change. + * + * The first time the callback runs will be before the initial `onMount` + * + * https://svelte.dev/docs#run-time-svelte-beforeupdate + */ export function beforeUpdate(fn: () => any) { get_current_component().$$.before_update.push(fn); } +/** + * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. + * It must be called during the component's initialisation (but doesn't need to live *inside* the component; + * it can be called from an external module). + * + * `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). + * + * https://svelte.dev/docs#run-time-svelte-onmount + */ export function onMount(fn: () => any) { get_current_component().$$.on_mount.push(fn); } +/** + * Schedules a callback to run immediately after the component has been updated. + * + * The first time the callback runs will be after the initial `onMount` + */ export function afterUpdate(fn: () => any) { get_current_component().$$.after_update.push(fn); } +/** + * Schedules a callback to run immediately before the component is unmounted. + * + * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the + * only one that runs inside a server-side component. + * + * https://svelte.dev/docs#run-time-svelte-ondestroy + */ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } @@ -31,6 +60,18 @@ export interface DispatchOptions { cancelable?: boolean; } +/** + * Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). + * Event dispatchers are functions that can take two arguments: `name` and `detail`. + * + * Component events created with `createEventDispatcher` create a + * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). + * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). + * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) + * property and can contain any type of data. + * + * https://svelte.dev/docs#run-time-svelte-createeventdispatcher + */ export function createEventDispatcher(): < EventKey extends Extract >( @@ -57,19 +98,47 @@ export function createEventDispatcher(): < }; } +/** + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component + * (including slotted content) with `getContext`. + * + * Like lifecycle functions, this must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-setcontext + */ export function setContext(key, context: T): T { get_current_component().$$.context.set(key, context); return context; } +/** + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-getcontext + */ export function getContext(key): T { return get_current_component().$$.context.get(key); } +/** + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you + * programmatically create a component and want to pass the existing context to it. + * + * https://svelte.dev/docs#run-time-svelte-getallcontexts + */ export function getAllContexts = Map>(): T { return get_current_component().$$.context; } +/** + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * + * https://svelte.dev/docs#run-time-svelte-hascontext + */ export function hasContext(key): boolean { return get_current_component().$$.context.has(key); } From ff2759e1437559669bd032abaff6920c2fc46570 Mon Sep 17 00:00:00 2001 From: Geoff Rich <4992896+geoffrich@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:25:19 -0700 Subject: [PATCH 144/303] [docs] fix typo in getting started (#7918) --- site/content/docs/01-getting-started.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md index 8ae5b1fb9c..e8aae80f23 100644 --- a/site/content/docs/01-getting-started.md +++ b/site/content/docs/01-getting-started.md @@ -7,6 +7,7 @@ title: Getting started To try Svelte in an interactive online environment you can try [the REPL](https://svelte.dev/repl) or [StackBlitz](https://node.new/svelte). To create a project locally, run: + ``` npm create vite@latest myapp -- --template svelte cd myapp @@ -16,6 +17,6 @@ npm run dev Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in beta). -See the SvelteSociety website run by the Svelte community for a list of integrations with various [tooling and editots](https://sveltesociety.dev/tools). +The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and the Svelte community has created a list of integrations with various other [tooling and editors](https://sveltesociety.dev/tools). If you're having trouble, get help on [Discord](https://svelte.dev/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte). From 1c659193a0b1fdf6e472be182fb600a430784d20 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:34:31 -0700 Subject: [PATCH 145/303] [docs] update SvelteKit status (#7919) --- site/content/docs/01-getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md index e8aae80f23..48cdbcc231 100644 --- a/site/content/docs/01-getting-started.md +++ b/site/content/docs/01-getting-started.md @@ -15,7 +15,7 @@ npm install npm run dev ``` -Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in beta). +Or use [SvelteKit](https://kit.svelte.dev/), the official application framework from the Svelte team (currently in release candidate status). The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and the Svelte community has created a list of integrations with various other [tooling and editors](https://sveltesociety.dev/tools). From 6ac7038e47c38221f16f0f73af93c9024ff2a18c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 10 Oct 2022 13:15:45 -0400 Subject: [PATCH 146/303] -> v3.51.0 --- CHANGELOG.md | 34 +++++++++++++++++----------------- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca984af7d..cbf22201f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,26 +1,26 @@ # Svelte changelog -## Unreleased +## 3.51.0 -* Fix hydration issue with using `{@html}` and components in `svelte:head` ([#4533](https://github.com/sveltejs/svelte/issues/4533), [#6463](https://github.com/sveltejs/svelte/issues/6463), [#7444](https://github.com/sveltejs/svelte/issues/7444)) -* Warn instead of throwing error if `` is void tag ([#7566](https://github.com/sveltejs/svelte/issues/7566)) -* Treat `inert` as boolean attribute ([#7785](https://github.com/sveltejs/svelte/pull/7785)) -* Supporting scoped style for `` ([#7443](https://github.com/sveltejs/svelte/issues/7443)) -* Supports SVG elements with ``([#7613](https://github.com/sveltejs/svelte/issues/7613)) -* Warn user when binding on a `{...rest}` object in `{#each}` block ([#6860](https://github.com/sveltejs/svelte/issues/6860)) -* Supports `--style-props` for `` ([#7461](https://github.com/sveltejs/svelte/issues/7461)) -* Supports `--style-props` for SVG components ([#7808](https://github.com/sveltejs/svelte/issues/7808)) * Add a11y warnings: - * `a11y-no-noninteractive-tabindex`: check for tabindex on non-interactive elements ([#6693](https://github.com/sveltejs/svelte/pull/6693)) * `a11y-click-events-have-key-events`: check if click event is accompanied by key events ([#5073](https://github.com/sveltejs/svelte/pull/5073)) -* `a11y-role-has-required-aria-props` do not warn when elements matched their semantic role ([#7838](https://github.com/sveltejs/svelte/pull/7838)) -* Supports custom element in `` ([#7766](https://github.com/sveltejs/svelte/pull/7766)) -* Improve performance of custom element data setting in `` ([#7869](https://github.com/sveltejs/svelte/pull/7869)) + * `a11y-no-noninteractive-tabindex`: check for tabindex on non-interactive elements ([#6693](https://github.com/sveltejs/svelte/pull/6693)) +* Warn when two-way binding to `{...rest}` object in `{#each}` block ([#6860](https://github.com/sveltejs/svelte/issues/6860)) +* Support `--style-props` on `` ([#7461](https://github.com/sveltejs/svelte/issues/7461)) * Supports nullish values for component event handlers ([#7568](https://github.com/sveltejs/svelte/issues/7568)) -* Better error message when specifying an invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) -* Fix to call component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) -* Fix false positive warnings about props binding in dev mode ([#4457](https://github.com/sveltejs/svelte/issues/4457)) -* Better error message when using logic blocks or tags in invalid place ([#7552](https://github.com/sveltejs/svelte/issues/7552)) +* Supports SVG elements with ``([#7613](https://github.com/sveltejs/svelte/issues/7613)) +* Treat `inert` as boolean attribute ([#7785](https://github.com/sveltejs/svelte/pull/7785)) +* Support `--style-props` for SVG components ([#7808](https://github.com/sveltejs/svelte/issues/7808)) +* Fix false positive dev warnings about unset props when they are bound ([#4457](https://github.com/sveltejs/svelte/issues/4457)) +* Fix hydration with `{@html}` and components in `` ([#4533](https://github.com/sveltejs/svelte/issues/4533), [#6463](https://github.com/sveltejs/svelte/issues/6463), [#7444](https://github.com/sveltejs/svelte/issues/7444)) +* Support scoped style for `` ([#7443](https://github.com/sveltejs/svelte/issues/7443)) +* Improve error message for invalid value for `` ([#7550](https://github.com/sveltejs/svelte/issues/7550)) +* Improve error message when using logic blocks or tags at invalid location ([#7552](https://github.com/sveltejs/svelte/issues/7552)) +* Warn instead of throwing error if `` is a void tag ([#7566](https://github.com/sveltejs/svelte/issues/7566)) +* Supports custom elements in `` ([#7733](https://github.com/sveltejs/svelte/issues/7733)) +* Fix calling component unmount if a component is mounted and then immediately unmounted ([#7817](https://github.com/sveltejs/svelte/issues/7817)) +* Do not generate `a11y-role-has-required-aria-props` warning when elements match their semantic role ([#7837](https://github.com/sveltejs/svelte/issues/7837)) +* Improve performance of custom element data setting in `` ([#7869](https://github.com/sveltejs/svelte/pull/7869)) ## 3.50.1 diff --git a/package-lock.json b/package-lock.json index 23ccc6b66f..dec2c8ef0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.50.1", + "version": "3.51.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 836fa33267..bf6b9e7754 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.50.1", + "version": "3.51.0", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 26a428972b7865d3871e05368df5e7b47bbf7e5b Mon Sep 17 00:00:00 2001 From: metonym Date: Thu, 13 Oct 2022 05:49:00 -0700 Subject: [PATCH 147/303] [fix] "not interactive" -> "noninteractive" (#7930) --- site/content/docs/06-accessibility-warnings.md | 2 +- src/compiler/compile/compiler_warnings.ts | 2 +- .../samples/a11y-no-nointeractive-tabindex/warnings.json | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/content/docs/06-accessibility-warnings.md b/site/content/docs/06-accessibility-warnings.md index c82ef21fcb..8be8eb888c 100644 --- a/site/content/docs/06-accessibility-warnings.md +++ b/site/content/docs/06-accessibility-warnings.md @@ -268,7 +268,7 @@ Some HTML elements have default ARIA roles. Giving these elements an ARIA role t Tab key navigation should be limited to elements on the page that can be interacted with. ```sv - +
``` diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index f194090870..3a11e04bd1 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -185,7 +185,7 @@ export default { }), a11y_no_noninteractive_tabindex: { code: 'a11y-no-noninteractive-tabindex', - message: 'A11y: not interactive element cannot have positive tabIndex value' + message: 'A11y: noninteractive element cannot have positive tabIndex value' }, redundant_event_modifier_for_touch: { code: 'redundant-event-modifier', diff --git a/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json b/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json index 97b8ccd9b3..740d9b346c 100644 --- a/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json +++ b/test/validator/samples/a11y-no-nointeractive-tabindex/warnings.json @@ -6,7 +6,7 @@ "column": 20, "line": 11 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 221, "start": { "character": 221, @@ -21,7 +21,7 @@ "column": 35, "line": 12 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 242, "start": { "character": 242, @@ -36,7 +36,7 @@ "column": 24, "line": 13 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 278, "start": { "character": 278, @@ -51,7 +51,7 @@ "column": 26, "line": 14 }, - "message": "A11y: not interactive element cannot have positive tabIndex value", + "message": "A11y: noninteractive element cannot have positive tabIndex value", "pos": 303, "start": { "character": 303, From 0eba57113be4185836a76ba894cbc29f366da252 Mon Sep 17 00:00:00 2001 From: Hofer Ivan Date: Thu, 13 Oct 2022 14:54:30 +0200 Subject: [PATCH 148/303] [chore]: store regexp as variable instead of defining it inline (#7716) * store regexp as variable instead of defining it inline * fix naming of `regex_quoted_value` * some more variables * optimize `.replace() calls * restore formatting changes * optimize `parser.*` calls * small refactor * optimize `.test() calls * rename some variables * fix tests * rename pattern variables * extract common regexes into `patters.ts` * rename variables to use snake_case * fix trim --- src/compiler/compile/Component.ts | 22 +++++++----- src/compiler/compile/css/Selector.ts | 15 +++++--- src/compiler/compile/css/Stylesheet.ts | 9 +++-- src/compiler/compile/index.ts | 7 ++-- src/compiler/compile/nodes/Binding.ts | 4 +-- src/compiler/compile/nodes/Element.ts | 26 +++++++++----- src/compiler/compile/nodes/EventHandler.ts | 6 ++-- src/compiler/compile/nodes/Head.ts | 3 +- src/compiler/compile/nodes/InlineComponent.ts | 7 ++-- src/compiler/compile/nodes/Text.ts | 7 ++-- .../compile/nodes/shared/AbstractBlock.ts | 4 ++- .../compile/nodes/shared/Expression.ts | 4 ++- src/compiler/compile/render_dom/Block.ts | 3 +- src/compiler/compile/render_dom/index.ts | 3 +- .../render_dom/wrappers/Element/Attribute.ts | 17 +++++---- .../wrappers/Element/StyleAttribute.ts | 13 ++++--- .../render_dom/wrappers/Element/index.ts | 36 ++++++++++++------- .../compile/render_dom/wrappers/Fragment.ts | 3 +- .../wrappers/InlineComponent/index.ts | 4 ++- .../compile/render_dom/wrappers/Text.ts | 4 ++- .../render_dom/wrappers/shared/add_actions.ts | 4 ++- .../shared/create_debugging_comment.ts | 4 ++- .../compile/render_ssr/handlers/Element.ts | 6 ++-- .../handlers/shared/get_attribute_value.ts | 3 +- .../utils/remove_whitespace_children.ts | 3 +- .../compile/utils/get_name_from_filename.ts | 19 ++++++---- src/compiler/compile/utils/hash.ts | 5 ++- src/compiler/compile/utils/stringify.ts | 8 +++-- src/compiler/parse/index.ts | 14 ++++---- src/compiler/parse/read/context.ts | 3 +- src/compiler/parse/read/expression.ts | 4 +-- src/compiler/parse/read/script.ts | 9 +++-- src/compiler/parse/read/style.ts | 6 ++-- src/compiler/parse/state/mustache.ts | 30 ++++++++-------- src/compiler/parse/state/tag.ts | 26 +++++++++----- src/compiler/preprocess/index.ts | 22 +++++++----- src/compiler/utils/extract_svelte_ignore.ts | 7 ++-- src/compiler/utils/get_code_frame.ts | 4 ++- src/compiler/utils/mapped_code.ts | 10 ++++-- src/compiler/utils/names.ts | 12 ++++--- src/compiler/utils/patterns.ts | 28 ++++++++++++--- src/compiler/utils/trim.ts | 6 ++-- 42 files changed, 283 insertions(+), 147 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 2f8874de7a..1bbaac3ff4 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -47,6 +47,10 @@ interface ComponentOptions { preserveWhitespace?: boolean; } +const regex_leading_directory_separator = /^[/\\]/; +const regex_starts_with_term_export = /^Export/; +const regex_contains_term_function = /Function/; + export default class Component { stats: Stats; warnings: Warning[]; @@ -136,7 +140,7 @@ export default class Component { (typeof process !== 'undefined' ? compile_options.filename .replace(process.cwd(), '') - .replace(/^[/\\]/, '') + .replace(regex_leading_directory_separator, '') : compile_options.filename); this.locate = getLocator(this.source, { offsetLine: 1 }); @@ -638,7 +642,7 @@ export default class Component { body.splice(i, 1); } - if (/^Export/.test(node.type)) { + if (regex_starts_with_term_export.test(node.type)) { const replacement = this.extract_exports(node, true); if (replacement) { body[i] = replacement; @@ -795,7 +799,7 @@ export default class Component { return this.skip(); } - if (/^Export/.test(node.type)) { + if (regex_starts_with_term_export.test(node.type)) { const replacement = component.extract_exports(node); if (replacement) { this.replace(replacement); @@ -918,7 +922,7 @@ export default class Component { } if (name[1] !== '$' && scope.has(name.slice(1)) && scope.find_owner(name.slice(1)) !== this.instance_scope) { - if (!((/Function/.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) { + if (!((regex_contains_term_function.test(parent.type) && prop === 'params') || (parent.type === 'VariableDeclarator' && prop === 'id'))) { return this.error(node as any, compiler_errors.contextual_store); } } @@ -965,7 +969,7 @@ export default class Component { walk(this.ast.instance.content, { enter(node: Node) { - if (/Function/.test(node.type)) { + if (regex_contains_term_function.test(node.type)) { return this.skip(); } @@ -1089,7 +1093,7 @@ export default class Component { this.replace(b` ${node.declarations.length ? node : null} - ${ props.length > 0 && b`let { ${ props } } = $$props;`} + ${ props.length > 0 && b`let { ${props} } = $$props;`} ${inserts} ` as any); return this.skip(); @@ -1460,6 +1464,8 @@ export default class Component { } } +const regex_valid_tag_name = /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/; + function process_component_options(component: Component, nodes) { const component_options: ComponentOptions = { immutable: component.compile_options.immutable || false, @@ -1473,7 +1479,7 @@ function process_component_options(component: Component, nodes) { const node = nodes.find(node => node.name === 'svelte:options'); - function get_value(attribute, {code, message}) { + function get_value(attribute, { code, message }) { const { value } = attribute; const chunk = value[0]; @@ -1505,7 +1511,7 @@ function process_component_options(component: Component, nodes) { return component.error(attribute, compiler_errors.invalid_tag_attribute); } - if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { + if (tag && !regex_valid_tag_name.test(tag)) { return component.error(attribute, compiler_errors.invalid_tag_property); } diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index 17302c4abd..28cf5ba6af 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -9,6 +9,7 @@ import EachBlock from '../nodes/EachBlock'; import IfBlock from '../nodes/IfBlock'; import AwaitBlock from '../nodes/AwaitBlock'; import compiler_errors from '../compiler_errors'; +import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns'; enum BlockAppliesToNode { NotPossible, @@ -25,6 +26,8 @@ const whitelist_attribute_selector = new Map([ ['dialog', new Set(['open'])] ]); +const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/; + export default class Selector { node: CssNode; stylesheet: Stylesheet; @@ -157,7 +160,7 @@ export default class Selector { for (const block of this.blocks) { for (const selector of block.selectors) { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - if (/[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/.test(selector.children[0].value)) { + if (regex_is_single_css_selector.test(selector.children[0].value)) { component.error(selector, compiler_errors.css_invalid_global_selector); } } @@ -281,12 +284,14 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{ return true; } +const regex_backslash_and_following_character = /\\(.)/g; + function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToNode { let i = block.selectors.length; while (i--) { const selector = block.selectors[i]; - const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1'); + const name = typeof selector.name === 'string' && selector.name.replace(regex_backslash_and_following_character, '$1'); if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) { return BlockAppliesToNode.NotPossible; @@ -371,7 +376,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, const start_with_space = []; const remaining = []; current_possible_values.forEach((current_possible_value: string) => { - if (/^\s/.test(current_possible_value)) { + if (regex_starts_with_whitespace.test(current_possible_value)) { start_with_space.push(current_possible_value); } else { remaining.push(current_possible_value); @@ -392,7 +397,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, prev_values = combined; start_with_space.forEach((value: string) => { - if (/\s$/.test(value)) { + if (regex_ends_with_whitespace.test(value)) { possible_values.add(value); } else { prev_values.push(value); @@ -406,7 +411,7 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, } current_possible_values.forEach((current_possible_value: string) => { - if (/\s$/.test(current_possible_value)) { + if (regex_ends_with_whitespace.test(current_possible_value)) { possible_values.add(current_possible_value); } else { prev_values.push(current_possible_value); diff --git a/src/compiler/compile/css/Stylesheet.ts b/src/compiler/compile/css/Stylesheet.ts index 1a9ea7feeb..9a3cbe9d13 100644 --- a/src/compiler/compile/css/Stylesheet.ts +++ b/src/compiler/compile/css/Stylesheet.ts @@ -9,9 +9,12 @@ import hash from '../utils/hash'; import compiler_warnings from '../compiler_warnings'; import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore'; import { push_array } from '../../utils/push_array'; +import { regex_only_whitespaces, regex_whitespace } from '../../utils/patterns'; + +const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/; function remove_css_prefix(name: string): string { - return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, ''); + return name.replace(regex_css_browser_prefix, ''); } const is_keyframes_node = (node: CssNode) => @@ -147,10 +150,10 @@ class Declaration { // Don't minify whitespace in custom properties, since some browsers (Chromium < 99) // treat --foo: ; and --foo:; differently - if (first.type === 'Raw' && /^\s+$/.test(first.value)) return; + if (first.type === 'Raw' && regex_only_whitespaces.test(first.value)) return; let start = first.start; - while (/\s/.test(code.original[start])) start += 1; + while (regex_whitespace.test(code.original[start])) start += 1; if (start - c > 1) { code.overwrite(c, start, ':'); diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index afe9c56cf4..a5edc3a6f7 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -35,6 +35,9 @@ const valid_options = [ 'cssHash' ]; +const regex_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/; +const regex_starts_with_lowercase_character = /^[a-z]/; + function validate_options(options: CompileOptions, warnings: Warning[]) { const { name, filename, loopGuardTimeout, dev, namespace } = options; @@ -48,11 +51,11 @@ function validate_options(options: CompileOptions, warnings: Warning[]) { } }); - if (name && !/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name)) { + if (name && !regex_valid_identifier.test(name)) { throw new Error(`options.name must be a valid identifier (got '${name}')`); } - if (name && /^[a-z]/.test(name)) { + if (name && regex_starts_with_lowercase_character.test(name)) { const message = 'options.name should be capitalised'; warnings.push({ code: 'options-lowercase-name', diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 594490a5fb..f826df4828 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -3,7 +3,7 @@ import get_object from '../utils/get_object'; import Expression from './shared/Expression'; import Component from '../Component'; import TemplateScope from './shared/TemplateScope'; -import {dimensions} from '../../utils/patterns'; +import { regex_dimensions } from '../../utils/patterns'; import { Node as ESTreeNode } from 'estree'; import { TemplateNode } from '../../interfaces'; import Element from './Element'; @@ -88,7 +88,7 @@ export default class Binding extends Node { const type = parent.get_static_attribute_value('type'); this.is_readonly = - dimensions.test(this.name) || + regex_dimensions.test(this.name) || (isElement(parent) && ((parent.is_media_node() && read_only_media_attributes.has(this.name)) || (parent.name === 'input' && type === 'file')) /* TODO others? */); diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 79f2800437..d5fad18f54 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -11,7 +11,7 @@ import StyleDirective from './StyleDirective'; import Text from './Text'; import { namespaces } from '../../utils/namespaces'; import map_children from './shared/map_children'; -import { dimensions, start_newline } from '../../utils/patterns'; +import { regex_dimensions, regex_starts_with_newline, regex_non_whitespace_character } from '../../utils/patterns'; import fuzzymatch from '../../utils/fuzzymatch'; import list from '../../utils/list'; import Let from './Let'; @@ -203,6 +203,10 @@ function is_valid_aria_attribute_value(schema: ARIAPropertyDefinition, value: st } } +const regex_any_repeated_whitespaces = /[\s]+/g; +const regex_heading_tags = /^h[1-6]$/; +const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/; + export default class Element extends Node { type: 'Element'; name: string; @@ -253,7 +257,7 @@ export default class Element extends Node { // places if there's another newline afterwards. // see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions // see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element - first.data = first.data.replace(start_newline, ''); + first.data = first.data.replace(regex_starts_with_newline, ''); } } @@ -398,7 +402,7 @@ export default class Element extends Node { // Errors - if (/(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/.test(name)) { + if (regex_illegal_attribute_character.test(name)) { return component.error(attribute, compiler_errors.illegal_attribute(name)); } @@ -464,7 +468,7 @@ export default class Element extends Node { component.warn(attribute, compiler_warnings.a11y_unknown_aria_attribute(type, match)); } - if (name === 'aria-hidden' && /^h[1-6]$/.test(this.name)) { + if (name === 'aria-hidden' && regex_heading_tags.test(this.name)) { component.warn(attribute, compiler_warnings.a11y_hidden(this.name)); } @@ -729,7 +733,7 @@ export default class Element extends Node { if (this.name === 'figure') { const children = this.children.filter(node => { if (node.type === 'Comment') return false; - if (node.type === 'Text') return /\S/.test(node.data); + if (node.type === 'Text') return regex_non_whitespace_character.test(node.data); return true; }); @@ -861,7 +865,7 @@ export default class Element extends Node { if (this.name !== 'video') { return component.error(binding, compiler_errors.invalid_binding_element_with('