From a752ef4709587851b783137997c7457fadd2ddb6 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 4 Oct 2022 00:34:10 +0300 Subject: [PATCH 01/45] GitHub Workflows security hardening (#7883) --- .github/workflows/ci.yml | 2 ++ .github/workflows/docs.yml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbfde04f67..4f5e161f89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,7 @@ name: CI on: [push, pull_request] +permissions: + contents: read # to fetch code (actions/checkout) jobs: Tests: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ea6e0f5a60..763fb72577 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,6 +7,8 @@ on: paths: - site/content/** +permissions: {} + jobs: release: name: Deploy docs @@ -25,4 +27,4 @@ jobs: repo: 'svelte' branch: 'master' docs_path: 'site/content' - token: ${{ steps.github-app.outputs.token }} \ No newline at end of file + token: ${{ steps.github-app.outputs.token }} From 7d20194d8ae9b600936e47826de53eb4f75e58e1 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 4 Oct 2022 21:38:58 -0700 Subject: [PATCH 02/45] [docs] create getting started guide outside blog (#7812) --- CONTRIBUTING.md | 2 +- site/content/docs/01-getting-started.md | 21 +++++++++++++++++++ ...onent-format.md => 02-component-format.md} | 0 ...mplate-syntax.md => 03-template-syntax.md} | 0 .../docs/{03-run-time.md => 04-run-time.md} | 0 ...{04-compile-time.md => 05-compile-time.md} | 0 ...rnings.md => 06-accessibility-warnings.md} | 0 .../01-introduction/01-basics/text.md | 2 +- .../19-next-steps/01-congratulations/text.md | 2 +- 9 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 site/content/docs/01-getting-started.md rename site/content/docs/{01-component-format.md => 02-component-format.md} (100%) rename site/content/docs/{02-template-syntax.md => 03-template-syntax.md} (100%) rename site/content/docs/{03-run-time.md => 04-run-time.md} (100%) rename site/content/docs/{04-compile-time.md => 05-compile-time.md} (100%) rename site/content/docs/{05-accessibility-warnings.md => 06-accessibility-warnings.md} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7989caeea..816faf31aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ The [Open Source Guides](https://opensource.guide/) website has a collection of There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here's a few ideas to get started: -- Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/blog/the-easiest-way-to-get-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). +- Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/docs#getting-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). - Look through the [open issues](https://github.com/sveltejs/svelte/issues). A good starting point would be issues tagged [good first issue](https://github.com/sveltejs/svelte/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](#triaging-issues-and-pull-requests). - If you find an issue you would like to fix, [open a pull request](#your-first-pull-request). - Read through our [tutorials](https://svelte.dev/tutorial/basics). If you find anything that is confusing or can be improved, you can make edits by clicking "Edit this chapter" at the bottom left of the tutorial page. diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md new file mode 100644 index 0000000000..b8119390f4 --- /dev/null +++ b/site/content/docs/01-getting-started.md @@ -0,0 +1,21 @@ +--- +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 +npm install +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). + +If you're having trouble, get help on [Discord](https://svelte.dev/blog/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte). diff --git a/site/content/docs/01-component-format.md b/site/content/docs/02-component-format.md similarity index 100% rename from site/content/docs/01-component-format.md rename to site/content/docs/02-component-format.md diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/03-template-syntax.md similarity index 100% rename from site/content/docs/02-template-syntax.md rename to site/content/docs/03-template-syntax.md diff --git a/site/content/docs/03-run-time.md b/site/content/docs/04-run-time.md similarity index 100% rename from site/content/docs/03-run-time.md rename to site/content/docs/04-run-time.md diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/05-compile-time.md similarity index 100% rename from site/content/docs/04-compile-time.md rename to site/content/docs/05-compile-time.md diff --git a/site/content/docs/05-accessibility-warnings.md b/site/content/docs/06-accessibility-warnings.md similarity index 100% rename from site/content/docs/05-accessibility-warnings.md rename to site/content/docs/06-accessibility-warnings.md diff --git a/site/content/tutorial/01-introduction/01-basics/text.md b/site/content/tutorial/01-introduction/01-basics/text.md index 08ba922b42..c0a2b9eba0 100644 --- a/site/content/tutorial/01-introduction/01-basics/text.md +++ b/site/content/tutorial/01-introduction/01-basics/text.md @@ -4,7 +4,7 @@ title: Basics Welcome to the Svelte tutorial. This will teach you everything you need to know to build fast, small web applications easily. -You can also consult the [API docs](/docs) and the [examples](/examples), or — if you're impatient to start hacking on your machine locally — the [60-second quickstart](/blog/the-easiest-way-to-get-started). +You can also consult the [API docs](/docs) and the [examples](/examples), or — if you're impatient to start hacking on your machine locally — the [60-second quickstart](/docs#getting-started). ## What is Svelte? diff --git a/site/content/tutorial/19-next-steps/01-congratulations/text.md b/site/content/tutorial/19-next-steps/01-congratulations/text.md index 5b8256463e..1b4b13d4ab 100644 --- a/site/content/tutorial/19-next-steps/01-congratulations/text.md +++ b/site/content/tutorial/19-next-steps/01-congratulations/text.md @@ -4,7 +4,7 @@ title: Congratulations! You've now finished the Svelte tutorial and are ready to start building apps. You can refer back to individual chapters at any time (click the title above to reveal a dropdown) or continue your learning via the [API reference](/docs), [Examples](/examples) and [Blog](/blog). If you're a Twitter user, you can get updates via [@sveltejs](https://twitter.com/sveltejs). -To get set up in your local development environment, check out [the quickstart guide](/blog/the-easiest-way-to-get-started). +To get set up in your local development environment, check out [the quickstart guide](/docs#getting-started). If you're looking for a more expansive framework that includes routing, server-side rendering and everything else, take a look at [SvelteKit](https://kit.svelte.dev). From be70a898f392149aa5910bc000a0a55b85ba7f9a Mon Sep 17 00:00:00 2001 From: Shawn <62589492+laazyCmd@users.noreply.github.com> Date: Wed, 5 Oct 2022 14:44:08 +0000 Subject: [PATCH 03/45] [docs] fix link redirect to discord (#7911) * fix link redirect to discord * fix link redirect to discord --- .../content/blog/2017-08-07-the-easiest-way-to-get-started.md | 2 +- .../2017-12-31-sapper-towards-the-ideal-web-app-framework.md | 2 +- site/content/blog/2018-04-18-version-2.md | 4 ++-- site/content/blog/2019-01-31-svelte-on-the-changelog.md | 2 +- site/content/docs/01-getting-started.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/content/blog/2017-08-07-the-easiest-way-to-get-started.md b/site/content/blog/2017-08-07-the-easiest-way-to-get-started.md index fe4a14eaeb..29e59270d0 100644 --- a/site/content/blog/2017-08-07-the-easiest-way-to-get-started.md +++ b/site/content/blog/2017-08-07-the-easiest-way-to-get-started.md @@ -63,4 +63,4 @@ npx degit your-name/template my-new-project And that's it! Do `npm run build` to create a production-ready version of your app, and check the project template's [README](https://github.com/sveltejs/template/blob/master/README.md) for instructions on how to easily deploy your app to the web with [Vercel](https://vercel.com) or [Surge](http://surge.sh/). -You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](chat), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter! +You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](https://svelte.dev/chat), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter! diff --git a/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md b/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md index 42ed7012b9..1001caa5f4 100644 --- a/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md +++ b/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md @@ -81,4 +81,4 @@ I believe the next frontier of web performance is 'whole-app optimisation'. Curr Speaking of Glimmer, the idea of compiling components to bytecode is one that we'll probably steal in 2018. A framework like Sapper could conceivably determine which compilation mode to use based on the characteristics of your app. It could even serve JavaScript for the initial route for the fastest possible startup time, then lazily serve a bytecode interpreter for subsequent routes, resulting in the optimal combination of startup size and total app size. -Mostly, though, we want the direction of Sapper to be determined by its users. If you're the kind of developer who enjoys life on the bleeding edge and would like to help shape the future of how we build web apps, please join us on [GitHub](https://github.com/sveltejs/svelte) and [Discord](chat). +Mostly, though, we want the direction of Sapper to be determined by its users. If you're the kind of developer who enjoys life on the bleeding edge and would like to help shape the future of how we build web apps, please join us on [GitHub](https://github.com/sveltejs/svelte) and [Discord](https://svelte.dev/chat). diff --git a/site/content/blog/2018-04-18-version-2.md b/site/content/blog/2018-04-18-version-2.md index cbfe98b282..48d4cb2c34 100644 --- a/site/content/blog/2018-04-18-version-2.md +++ b/site/content/blog/2018-04-18-version-2.md @@ -12,7 +12,7 @@ Almost a year after we first started talking about version 2 on the Svelte issue ## tl;dr -Each of these items is described in more depth below. If you get stuck, ask for help in our friendly [Discord chatroom](chat). +Each of these items is described in more depth below. If you get stuck, ask for help in our friendly [Discord chatroom](https://svelte.dev/chat). - Install Svelte v2 from npm - Upgrade your templates with [svelte-upgrade](https://github.com/sveltejs/svelte-upgrade) @@ -201,4 +201,4 @@ Before, there was a `svelte.validate` method which checked your component was va ## My app is broken! Help! -Hopefully this covers everything, and the update should be easier for you than it was for us. But if you find bugs, or discover things that aren't mentioned here, swing by [Discord chatroom](chat) or raise an issue on the [tracker](https://github.com/sveltejs/svelte/issues). +Hopefully this covers everything, and the update should be easier for you than it was for us. But if you find bugs, or discover things that aren't mentioned here, swing by [Discord chatroom](https://svelte.dev/chat) or raise an issue on the [tracker](https://github.com/sveltejs/svelte/issues). diff --git a/site/content/blog/2019-01-31-svelte-on-the-changelog.md b/site/content/blog/2019-01-31-svelte-on-the-changelog.md index 0b8e1894e4..c9f9c2897c 100644 --- a/site/content/blog/2019-01-31-svelte-on-the-changelog.md +++ b/site/content/blog/2019-01-31-svelte-on-the-changelog.md @@ -14,7 +14,7 @@ Earlier this month, I had the privilege of appearing on [The Changelog](https:// ...and, most importantly, Svelte 3. -Unless you hang out in our [Discord server](chat) or follow [@sveltejs](https://twitter.com/sveltejs) on Twitter, you might not know that Svelte 3 is just around the corner, and it's going to be a huge release. We've rethought the developer experience from the ground up, and while it *will* be a nuisance if you need to upgrade a Svelte 2 app (more on that soon) we think you're going to love it. +Unless you hang out in our [Discord server](https://svelte.dev/chat) or follow [@sveltejs](https://twitter.com/sveltejs) on Twitter, you might not know that Svelte 3 is just around the corner, and it's going to be a huge release. We've rethought the developer experience from the ground up, and while it *will* be a nuisance if you need to upgrade a Svelte 2 app (more on that soon) we think you're going to love it. On the podcast [Adam](https://twitter.com/adamstac), [Jerod](https://twitter.com/jerodsanto) and I talk about some of the changes and why we're making them. You can listen here or on the [podcast page](https://changelog.com/podcast/332). diff --git a/site/content/docs/01-getting-started.md b/site/content/docs/01-getting-started.md index b8119390f4..8ae5b1fb9c 100644 --- a/site/content/docs/01-getting-started.md +++ b/site/content/docs/01-getting-started.md @@ -18,4 +18,4 @@ Or use [SvelteKit](https://kit.svelte.dev/), the official application framework See the SvelteSociety website run by the Svelte community for a list of integrations with various [tooling and editots](https://sveltesociety.dev/tools). -If you're having trouble, get help on [Discord](https://svelte.dev/blog/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte). +If you're having trouble, get help on [Discord](https://svelte.dev/chat) or [StackOverflow](https://stackoverflow.com/questions/tagged/svelte). From 01a91163a9ffd6d18ea4699cef4c531b72fbfc00 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Thu, 6 Oct 2022 00:28:00 +0900 Subject: [PATCH 04/45] [fix] Improve error message if `this` attribute of `` is not valid (#7551) * add test * improve error message if this attribute of is not SvelteComponent * add more tests * improve validation * simplify test Co-authored-by: Tan Li Hau --- .../wrappers/InlineComponent/index.ts | 4 ++-- src/runtime/internal/dev.ts | 18 ++++++++++++++++++ src/runtime/internal/dom.ts | 4 ++++ src/runtime/internal/ssr.ts | 2 +- .../component-not-constructor-dev/_config.js | 8 ++++++++ .../component-not-constructor-dev/main.svelte | 5 +++++ .../component-not-constructor/Sub.svelte | 1 + .../component-not-constructor/_config.js | 8 ++++++++ .../component-not-constructor/main.svelte | 9 +++++++++ .../component-not-constructor2-dev/Sub.svelte | 1 + .../component-not-constructor2-dev/_config.js | 19 +++++++++++++++++++ .../main.svelte | 14 ++++++++++++++ .../component-not-constructor2/Sub.svelte | 1 + .../component-not-constructor2/_config.js | 16 ++++++++++++++++ .../component-not-constructor2/main.svelte | 14 ++++++++++++++ 15 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/component-not-constructor-dev/_config.js create mode 100644 test/runtime/samples/component-not-constructor-dev/main.svelte create mode 100644 test/runtime/samples/component-not-constructor/Sub.svelte create mode 100644 test/runtime/samples/component-not-constructor/_config.js create mode 100644 test/runtime/samples/component-not-constructor/main.svelte create mode 100644 test/runtime/samples/component-not-constructor2-dev/Sub.svelte create mode 100644 test/runtime/samples/component-not-constructor2-dev/_config.js create mode 100644 test/runtime/samples/component-not-constructor2-dev/main.svelte create mode 100644 test/runtime/samples/component-not-constructor2/Sub.svelte create mode 100644 test/runtime/samples/component-not-constructor2/_config.js create mode 100644 test/runtime/samples/component-not-constructor2/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index f44170cda1..5b2883850a 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -425,7 +425,7 @@ export default class InlineComponentWrapper extends Wrapper { } if (${switch_value}) { - ${name} = new ${switch_value}(${switch_props}(#ctx)); + ${name} = @construct_svelte_component(${switch_value}, ${switch_props}(#ctx)); ${munged_bindings} ${munged_handlers} @@ -473,7 +473,7 @@ export default class InlineComponentWrapper extends Wrapper { if (${switch_value}) { ${update_insert} - ${name} = new ${switch_value}(${switch_props}(#ctx)); + ${name} = @construct_svelte_component(${switch_value}, ${switch_props}(#ctx)); ${munged_bindings} ${munged_handlers} diff --git a/src/runtime/internal/dev.ts b/src/runtime/internal/dev.ts index 32d14571d9..9d24ad2a07 100644 --- a/src/runtime/internal/dev.ts +++ b/src/runtime/internal/dev.ts @@ -123,6 +123,24 @@ export function validate_void_dynamic_element(tag: undefined | string) { } } +export function construct_svelte_component_dev(component, props) { + const error_message = 'this={...} of should specify a Svelte component.'; + try { + const instance = new component(props); + if (!instance.$$ || !instance.$set || !instance.$on || !instance.$destroy) { + throw new Error(error_message); + } + return instance; + } catch (err) { + const { message } = err; + if (typeof message === 'string' && message.indexOf('is not a constructor') !== -1) { + throw new Error(error_message); + } else { + throw err; + } + } +} + type Props = Record; export interface SvelteComponentDev { $set(props?: Props): void; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index d359513eb4..a1c0e1c0aa 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -768,3 +768,7 @@ export function get_custom_elements_slots(element: HTMLElement) { }); return result; } + +export function construct_svelte_component(component, props) { + return new component(props); +} diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index dc4c16dffb..571a25f1ac 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -124,7 +124,7 @@ export const missing_component = { export function validate_component(component, name) { if (!component || !component.$$render) { if (name === 'svelte:component') name += ' this={...}'; - throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`); + throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`); } return component; diff --git a/test/runtime/samples/component-not-constructor-dev/_config.js b/test/runtime/samples/component-not-constructor-dev/_config.js new file mode 100644 index 0000000000..15ea469a0f --- /dev/null +++ b/test/runtime/samples/component-not-constructor-dev/_config.js @@ -0,0 +1,8 @@ +export default { + skip_if_ssr: true, + skip_if_hydrate_from_ssr: true, + compileOptions: { + dev: true + }, + error: 'this={...} of should specify a Svelte component.' +}; diff --git a/test/runtime/samples/component-not-constructor-dev/main.svelte b/test/runtime/samples/component-not-constructor-dev/main.svelte new file mode 100644 index 0000000000..80f9f969e6 --- /dev/null +++ b/test/runtime/samples/component-not-constructor-dev/main.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/runtime/samples/component-not-constructor/Sub.svelte b/test/runtime/samples/component-not-constructor/Sub.svelte new file mode 100644 index 0000000000..dd6d0bb1a9 --- /dev/null +++ b/test/runtime/samples/component-not-constructor/Sub.svelte @@ -0,0 +1 @@ +
Sub
\ No newline at end of file diff --git a/test/runtime/samples/component-not-constructor/_config.js b/test/runtime/samples/component-not-constructor/_config.js new file mode 100644 index 0000000000..8c0dcced41 --- /dev/null +++ b/test/runtime/samples/component-not-constructor/_config.js @@ -0,0 +1,8 @@ +export default { + skip_if_ssr: true, + skip_if_hydrate_from_ssr: true, + props: { + selected: false + }, + error: 'component is not a constructor' +}; diff --git a/test/runtime/samples/component-not-constructor/main.svelte b/test/runtime/samples/component-not-constructor/main.svelte new file mode 100644 index 0000000000..dec7e2af79 --- /dev/null +++ b/test/runtime/samples/component-not-constructor/main.svelte @@ -0,0 +1,9 @@ + + + diff --git a/test/runtime/samples/component-not-constructor2-dev/Sub.svelte b/test/runtime/samples/component-not-constructor2-dev/Sub.svelte new file mode 100644 index 0000000000..dd6d0bb1a9 --- /dev/null +++ b/test/runtime/samples/component-not-constructor2-dev/Sub.svelte @@ -0,0 +1 @@ +
Sub
\ No newline at end of file diff --git a/test/runtime/samples/component-not-constructor2-dev/_config.js b/test/runtime/samples/component-not-constructor2-dev/_config.js new file mode 100644 index 0000000000..98e3cc942b --- /dev/null +++ b/test/runtime/samples/component-not-constructor2-dev/_config.js @@ -0,0 +1,19 @@ +export default { + compileOptions: { + dev: true + }, + props: { + componentName: 'Sub' + }, + html: '
Sub
', + test({ assert, component, target }) { + component.componentName = 'Proxy'; + assert.htmlEqual(target.innerHTML, '
Sub
'); + try { + component.componentName = 'banana'; + throw new Error('Expected an error'); + } catch (err) { + assert.equal(err.message, 'this={...} of should specify a Svelte component.'); + } + } +}; diff --git a/test/runtime/samples/component-not-constructor2-dev/main.svelte b/test/runtime/samples/component-not-constructor2-dev/main.svelte new file mode 100644 index 0000000000..331fdfc385 --- /dev/null +++ b/test/runtime/samples/component-not-constructor2-dev/main.svelte @@ -0,0 +1,14 @@ + + + diff --git a/test/runtime/samples/component-not-constructor2/Sub.svelte b/test/runtime/samples/component-not-constructor2/Sub.svelte new file mode 100644 index 0000000000..dd6d0bb1a9 --- /dev/null +++ b/test/runtime/samples/component-not-constructor2/Sub.svelte @@ -0,0 +1 @@ +
Sub
\ No newline at end of file diff --git a/test/runtime/samples/component-not-constructor2/_config.js b/test/runtime/samples/component-not-constructor2/_config.js new file mode 100644 index 0000000000..8fae65afdd --- /dev/null +++ b/test/runtime/samples/component-not-constructor2/_config.js @@ -0,0 +1,16 @@ +export default { + props: { + componentName: 'Sub' + }, + html: '
Sub
', + test({ assert, component, target }) { + component.componentName = 'Proxy'; + assert.htmlEqual(target.innerHTML, '
Sub
'); + try { + component.componentName = 'banana'; + throw new Error('Expected an error'); + } catch (err) { + assert.equal(err.message, 'component is not a constructor'); + } + } +}; diff --git a/test/runtime/samples/component-not-constructor2/main.svelte b/test/runtime/samples/component-not-constructor2/main.svelte new file mode 100644 index 0000000000..331fdfc385 --- /dev/null +++ b/test/runtime/samples/component-not-constructor2/main.svelte @@ -0,0 +1,14 @@ + + + From ea2f83adebd0a656fae72200525f6e4a1259701f Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 5 Oct 2022 23:29:43 +0800 Subject: [PATCH 05/45] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0ee54c0d..afa432f613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * 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)) * 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)) ## 3.50.1 From 5c5bc27d9782039259ce8ffca3eb1673f22396c5 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 6 Oct 2022 00:18:37 +0800 Subject: [PATCH 06/45] [feat] Support style props for SVG components (#7859) --- site/content/docs/03-template-syntax.md | 16 ++++++ src/compiler/compile/nodes/InlineComponent.ts | 12 ++++ .../wrappers/InlineComponent/index.ts | 29 ++++++---- .../render_ssr/handlers/InlineComponent.ts | 18 ++++-- src/compiler/utils/namespaces.ts | 2 +- .../Svg.svelte | 17 ++++++ .../_config.js | 55 +++++++++++++++++++ .../main.svelte | 25 +++++++++ 8 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/Svg.svelte create mode 100644 test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/_config.js create mode 100644 test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/main.svelte diff --git a/site/content/docs/03-template-syntax.md b/site/content/docs/03-template-syntax.md index 1094e25184..24b6111fae 100644 --- a/site/content/docs/03-template-syntax.md +++ b/site/content/docs/03-template-syntax.md @@ -1374,6 +1374,22 @@ Desugars to this: --- +For SVG namespace, the example above desugars into using `` instead: + +```sv + + + +``` + +**Note**: Since this is an extra ``, beware that your CSS structure might accidentally target this. Be mindful of this added wrapper element when using this feature. + +--- + Svelte's CSS Variables support allows for easily themable components: ```sv diff --git a/src/compiler/compile/nodes/InlineComponent.ts b/src/compiler/compile/nodes/InlineComponent.ts index 13fb13c278..8871c5f306 100644 --- a/src/compiler/compile/nodes/InlineComponent.ts +++ b/src/compiler/compile/nodes/InlineComponent.ts @@ -22,6 +22,7 @@ export default class InlineComponent extends Node { css_custom_properties: Attribute[] = []; children: INode[]; scope: TemplateScope; + namespace: string; constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { super(component, parent, scope, info); @@ -33,6 +34,7 @@ export default class InlineComponent extends Node { } this.name = info.name; + this.namespace = get_namespace(parent, component.namespace); this.expression = this.name === 'svelte:component' ? new Expression(component, this, scope, info.expression) @@ -165,3 +167,13 @@ export default class InlineComponent extends Node { function not_whitespace_text(node) { return !(node.type === 'Text' && /^\s+$/.test(node.data)); } + +function get_namespace(parent: Node, explicit_namespace: string) { + const parent_element = parent.find_nearest(/^Element/); + + if (!parent_element) { + return explicit_namespace; + } + + return parent_element.namespace; +} diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index 5b2883850a..37aaf2e7b2 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -20,6 +20,7 @@ import { string_to_member_expression } from '../../../utils/string_to_member_exp import SlotTemplate from '../../../nodes/SlotTemplate'; import { is_head } from '../shared/is_head'; import compiler_warnings from '../../../compiler_warnings'; +import { namespaces } from '../../../../utils/namespaces'; type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node }; @@ -150,7 +151,9 @@ export default class InlineComponentWrapper extends Wrapper { } const has_css_custom_properties = this.node.css_custom_properties.length > 0; - const css_custom_properties_wrapper = has_css_custom_properties ? block.get_unique_name('div') : null; + const is_svg_namespace = this.node.namespace === namespaces.svg; + const css_custom_properties_wrapper_element = is_svg_namespace ? 'g' : 'div'; + const css_custom_properties_wrapper = has_css_custom_properties ? block.get_unique_name(css_custom_properties_wrapper_element) : null; if (has_css_custom_properties) { block.add_variable(css_custom_properties_wrapper); } @@ -411,7 +414,7 @@ export default class InlineComponentWrapper extends Wrapper { const snippet = this.node.expression.manipulate(block); if (has_css_custom_properties) { - this.set_css_custom_properties(block, css_custom_properties_wrapper); + this.set_css_custom_properties(block, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace); } block.chunks.init.push(b` @@ -440,7 +443,7 @@ export default class InlineComponentWrapper extends Wrapper { block.chunks.mount.push(b`if (${name}) @mount_component(${name}, ${mount_target}, ${mount_anchor});`); if (to_claim) { - if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper); + if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace); block.chunks.claim.push(b`if (${name}) @claim_component(${name}.$$.fragment, ${claim_nodes});`); } @@ -514,7 +517,7 @@ export default class InlineComponentWrapper extends Wrapper { `); if (has_css_custom_properties) { - this.set_css_custom_properties(block, css_custom_properties_wrapper); + this.set_css_custom_properties(block, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace); } block.chunks.create.push(b`@create_component(${name}.$$.fragment);`); @@ -522,7 +525,7 @@ export default class InlineComponentWrapper extends Wrapper { block.chunks.mount.push(b`@mount_component(${name}, ${mount_target}, ${mount_anchor});`); if (to_claim) { - if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper); + if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace); block.chunks.claim.push(b`@claim_component(${name}.$$.fragment, ${claim_nodes});`); } @@ -568,11 +571,14 @@ export default class InlineComponentWrapper extends Wrapper { private create_css_custom_properties_wrapper_claim_chunk( block: Block, parent_nodes: Identifier, - css_custom_properties_wrapper: Identifier | null + css_custom_properties_wrapper: Identifier | null, + css_custom_properties_wrapper_element: string, + is_svg_namespace: boolean ) { const nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`); + const claim_element = is_svg_namespace ? x`@claim_svg_element` : x`@claim_element`; block.chunks.claim.push(b` - ${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true }) + ${css_custom_properties_wrapper} = ${claim_element}(${parent_nodes}, "${css_custom_properties_wrapper_element.toUpperCase()}", { style: true }) var ${nodes} = @children(${css_custom_properties_wrapper}); `); return nodes; @@ -580,10 +586,13 @@ export default class InlineComponentWrapper extends Wrapper { private set_css_custom_properties( block: Block, - css_custom_properties_wrapper: Identifier + css_custom_properties_wrapper: Identifier, + css_custom_properties_wrapper_element: string, + is_svg_namespace: boolean ) { - block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`); - block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`); + const element = is_svg_namespace ? x`@svg_element` : x`@element`; + block.chunks.create.push(b`${css_custom_properties_wrapper} = ${element}("${css_custom_properties_wrapper_element}");`); + if (!is_svg_namespace) block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`); this.node.css_custom_properties.forEach((attr) => { const dependencies = attr.get_dependencies(); const should_cache = attr.should_cache(); diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index 626365e3e2..8287b46268 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -3,6 +3,7 @@ import { get_attribute_value } from './shared/get_attribute_value'; import Renderer, { RenderOptions } from '../Renderer'; import InlineComponent from '../../nodes/InlineComponent'; import { p, x } from 'code-red'; +import { namespaces } from '../../../utils/namespaces'; function get_prop_value(attribute) { if (attribute.is_true) return x`true`; @@ -88,11 +89,16 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend }`; if (node.css_custom_properties.length > 0) { - renderer.add_string('
'); } @@ -100,6 +106,10 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend renderer.add_expression(x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`); if (node.css_custom_properties.length > 0) { - renderer.add_string('
'); + if (node.namespace === namespaces.svg) { + renderer.add_string('
'); + } else { + renderer.add_string(''); + } } } diff --git a/src/compiler/utils/namespaces.ts b/src/compiler/utils/namespaces.ts index 7da64afc8c..d51b303fe1 100644 --- a/src/compiler/utils/namespaces.ts +++ b/src/compiler/utils/namespaces.ts @@ -25,4 +25,4 @@ export const valid_namespaces = [ xmlns ]; -export const namespaces: Record = { foreign, html, mathml, svg, xlink, xml, xmlns }; +export const namespaces = { foreign, html, mathml, svg, xlink, xml, xmlns } as const; diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/Svg.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/Svg.svelte new file mode 100644 index 0000000000..26e93bff29 --- /dev/null +++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/Svg.svelte @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/_config.js b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/_config.js new file mode 100644 index 0000000000..ea8b5584db --- /dev/null +++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/_config.js @@ -0,0 +1,55 @@ +export default { + props: { + rectColor1: 'green', + circleColor1: 'red', + rectColor2: 'black', + circleColor2: 'blue' + }, + html: ` + + + + + + + + + + + + + + + `, + test({ component, assert, target }) { + component.rectColor1 = 'yellow'; + component.circleColor2 = 'cyan'; + + assert.htmlEqual(target.innerHTML, ` + + + + + + + + + + + + + + + `); + + const circleColor1 = target.querySelector('#svg-1 circle'); + const rectColor1 = target.querySelector('#svg-1 rect'); + const circleColor2 = target.querySelector('#svg-2 circle'); + const rectColor2 = target.querySelector('#svg-2 rect'); + + assert.htmlEqual(window.getComputedStyle(circleColor1).fill, 'rgb(255, 0, 0)'); + assert.htmlEqual(window.getComputedStyle(rectColor1).fill, 'rgb(255, 255, 0)'); + assert.htmlEqual(window.getComputedStyle(circleColor2).fill, 'rgb(0, 255, 255)'); + assert.htmlEqual(window.getComputedStyle(rectColor2).fill, 'rgb(0, 0, 0)'); + } +}; diff --git a/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/main.svelte b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/main.svelte new file mode 100644 index 0000000000..b30be2dbac --- /dev/null +++ b/test/runtime-puppeteer/samples/component-css-custom-properties-dynamic-svg/main.svelte @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file From 57541e6abc3837c07019a468d32ef74552bf2677 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Thu, 6 Oct 2022 01:21:31 +0900 Subject: [PATCH 07/45] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa432f613..e26890d97b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * 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)) From 81d4dbad99f2c349ced55c65f7ac2db704a771f6 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 6 Oct 2022 09:21:36 +0800 Subject: [PATCH 08/45] [fix] call `on_destroy` if unmounted called immediately before `on_mount` (#7860) * call on_destroy if unmounted called immediately before on_mount * feat: review changes --- src/runtime/internal/Component.ts | 49 +++---------------- src/runtime/internal/await_block.ts | 29 ++++++++++- src/runtime/internal/transitions.ts | 2 +- src/runtime/internal/types.ts | 39 +++++++++++++++ .../Component.svelte | 13 +++++ .../_config.js | 9 ++++ .../main.svelte | 36 ++++++++++++++ 7 files changed, 133 insertions(+), 44 deletions(-) create mode 100644 src/runtime/internal/types.ts create mode 100644 test/runtime/samples/await-mount-and-unmount-immediately/Component.svelte create mode 100644 test/runtime/samples/await-mount-and-unmount-immediately/_config.js create mode 100644 test/runtime/samples/await-mount-and-unmount-immediately/main.svelte diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 624339e7fa..5aec24c651 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -3,43 +3,7 @@ import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_empty, is_function, run, run_all, noop } from './utils'; import { children, detach, start_hydrating, end_hydrating } from './dom'; import { transition_in } from './transitions'; - -/** - * INTERNAL, DO NOT USE. Code may change at any time. - */ -export interface Fragment { - key: string | null; - first: null; - /* create */ c: () => void; - /* claim */ l: (nodes: any) => void; - /* hydrate */ h: () => void; - /* mount */ m: (target: HTMLElement, anchor: any) => void; - /* update */ p: (ctx: T$$['ctx'], dirty: T$$['dirty']) => void; - /* measure */ r: () => void; - /* fix */ f: () => void; - /* animate */ a: () => void; - /* intro */ i: (local: any) => void; - /* outro */ o: (local: any) => void; - /* destroy */ d: (detaching: 0 | 1) => void; -} -interface T$$ { - dirty: number[]; - ctx: any[]; - bound: any; - update: () => void; - callbacks: any; - after_update: any[]; - props: Record; - fragment: null | false | Fragment; - not_equal: any; - before_update: any[]; - context: Map; - on_mount: any[]; - on_destroy: any[]; - skip_bound: boolean; - on_disconnect: any[]; - root:Element | ShadowRoot -} +import { T$$ } from './types'; export function bind(component, name, callback) { const index = component.$$.props[name]; @@ -58,7 +22,7 @@ export function claim_component(block, parent_nodes) { } export function mount_component(component, target, anchor, customElement) { - const { fragment, on_mount, on_destroy, after_update } = component.$$; + const { fragment, after_update } = component.$$; fragment && fragment.m(target, anchor); @@ -66,9 +30,12 @@ export function mount_component(component, target, anchor, customElement) { // onMount happens before the initial afterUpdate add_render_callback(() => { - const new_on_destroy = on_mount.map(run).filter(is_function); - if (on_destroy) { - on_destroy.push(...new_on_destroy); + const new_on_destroy = component.$$.on_mount.map(run).filter(is_function); + // if the component was destroyed immediately + // it will update the `$$.on_destroy` reference to `null`. + // the destructured on_destroy may still reference to the old array + if (component.$$.on_destroy) { + component.$$.on_destroy.push(...new_on_destroy); } else { // Edge case - component was destroyed immediately, // most likely as a result of a binding initialising diff --git a/src/runtime/internal/await_block.ts b/src/runtime/internal/await_block.ts index ea6e8a187f..1e09ada6cf 100644 --- a/src/runtime/internal/await_block.ts +++ b/src/runtime/internal/await_block.ts @@ -2,11 +2,36 @@ import { is_promise } from './utils'; import { check_outros, group_outros, transition_in, transition_out } from './transitions'; import { flush } from './scheduler'; import { get_current_component, set_current_component } from './lifecycle'; +import { Fragment, FragmentFactory } from './types'; + +interface PromiseInfo { + ctx: null | any; + // unique object instance as a key to compare different promises + token: {}, + hasCatch: boolean, + pending: FragmentFactory, + then: FragmentFactory, + catch: FragmentFactory, + // ctx index for resolved value and rejected error + value: number, + error: number, + // resolved value or rejected error + resolved?: T, + // the current factory function for creating the fragment + current: FragmentFactory | null, + // the current fragment + block: Fragment | null, + // tuple of the pending, then, catch fragment + blocks: [null | Fragment, null | Fragment, null | Fragment]; + // DOM elements to mount and anchor on for the {#await} block + mount: () => HTMLElement; + anchor: HTMLElement; +} -export function handle_promise(promise, info) { +export function handle_promise(promise: Promise, info: PromiseInfo) { const token = info.token = {}; - function update(type, index, key?, value?) { + function update(type: FragmentFactory, index: 0 | 1 | 2, key?: number, value?) { if (info.token !== token) return; info.resolved = value; diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts index 306a5e3793..9aa45dc4f1 100644 --- a/src/runtime/internal/transitions.ts +++ b/src/runtime/internal/transitions.ts @@ -5,7 +5,7 @@ import { create_rule, delete_rule } from './style_manager'; import { custom_event } from './dom'; import { add_render_callback } from './scheduler'; import { TransitionConfig } from '../transition'; -import { Fragment } from './Component'; +import { Fragment } from './types'; let promise: Promise | null; type INTRO = 1; diff --git a/src/runtime/internal/types.ts b/src/runtime/internal/types.ts new file mode 100644 index 0000000000..41f8f1ca43 --- /dev/null +++ b/src/runtime/internal/types.ts @@ -0,0 +1,39 @@ +/** + * INTERNAL, DO NOT USE. Code may change at any time. + */ +export interface Fragment { + key: string | null; + first: null; + /* create */ c: () => void; + /* claim */ l: (nodes: any) => void; + /* hydrate */ h: () => void; + /* mount */ m: (target: HTMLElement, anchor: any) => void; + /* update */ p: (ctx: T$$['ctx'], dirty: T$$['dirty']) => void; + /* measure */ r: () => void; + /* fix */ f: () => void; + /* animate */ a: () => void; + /* intro */ i: (local: any) => void; + /* outro */ o: (local: any) => void; + /* destroy */ d: (detaching: 0 | 1) => void; +} + +export type FragmentFactory = (ctx: any) => Fragment; + +export interface T$$ { + dirty: number[]; + ctx: any[]; + bound: any; + update: () => void; + callbacks: any; + after_update: any[]; + props: Record; + fragment: null | false | Fragment; + not_equal: any; + before_update: any[]; + context: Map; + on_mount: any[]; + on_destroy: any[]; + skip_bound: boolean; + on_disconnect: any[]; + root:Element | ShadowRoot +} diff --git a/test/runtime/samples/await-mount-and-unmount-immediately/Component.svelte b/test/runtime/samples/await-mount-and-unmount-immediately/Component.svelte new file mode 100644 index 0000000000..be0c7e36b0 --- /dev/null +++ b/test/runtime/samples/await-mount-and-unmount-immediately/Component.svelte @@ -0,0 +1,13 @@ + + +{state} diff --git a/test/runtime/samples/await-mount-and-unmount-immediately/_config.js b/test/runtime/samples/await-mount-and-unmount-immediately/_config.js new file mode 100644 index 0000000000..b944091319 --- /dev/null +++ b/test/runtime/samples/await-mount-and-unmount-immediately/_config.js @@ -0,0 +1,9 @@ +export default { + html: 'Loading...', + async test({ assert, component, target }) { + await component.test(); + + assert.htmlEqual(target.innerHTML, '1'); + assert.deepEqual(component.logs, ['mount 0', 'unmount 0', 'mount 1']); + } +}; diff --git a/test/runtime/samples/await-mount-and-unmount-immediately/main.svelte b/test/runtime/samples/await-mount-and-unmount-immediately/main.svelte new file mode 100644 index 0000000000..245304be83 --- /dev/null +++ b/test/runtime/samples/await-mount-and-unmount-immediately/main.svelte @@ -0,0 +1,36 @@ + + +{#await promise} + Loading... +{:then state} + +{/await} \ No newline at end of file From bfb7536c1a6718babc21c869070ff94de484c946 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Thu, 6 Oct 2022 10:28:41 +0900 Subject: [PATCH 09/45] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e26890d97b..b5ba64d0bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * Improve performance of custom element data setting in `` ([#7869](https://github.com/sveltejs/svelte/pull/7869)) * 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)) ## 3.50.1 From ce569f97ebd633d44c747cef8039725325c7d753 Mon Sep 17 00:00:00 2001 From: Marcos Mercuri Date: Thu, 6 Oct 2022 11:23:29 +0200 Subject: [PATCH 10/45] [docs] Add clarification on how reactivity works (#7819) * Add clarification on how reactivity works Based on the fact that there are multiple issues were opened related to a perceived bug on the reactive variables, I thought it would be good to add a clarification on the docs. Part of the text is taken from [this comment](https://github.com/sveltejs/svelte/issues/7818#issuecomment-1230374639) that I found super useful. * Reword based on PR comments --- site/content/docs/02-component-format.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/site/content/docs/02-component-format.md b/site/content/docs/02-component-format.md index 23508b5c7b..5212b001ac 100644 --- a/site/content/docs/02-component-format.md +++ b/site/content/docs/02-component-format.md @@ -192,6 +192,25 @@ Total: {total} ``` +--- +It is important to note that the reactive blocks are ordered via simple static analysis at compile time, and all the compiler looks at are the variables that are assigned to and used within the block itself, not in any functions called by them. This means that `yDependent` will not be updated when `x` is updated in the following example: + +```sv + +``` + +Moving the line `$: yDependent = y` bellow `$: setY(x)` will cause `yDependent` to be updated when `x` is updated. + --- If a statement consists entirely of an assignment to an undeclared variable, Svelte will inject a `let` declaration on your behalf. From 158ec43d99376ccb70c374e306552151b1dbf63b Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 6 Oct 2022 22:29:48 +0800 Subject: [PATCH 11/45] [fix] do not warn about missing props for bindings (#6583) --- src/compiler/compile/render_dom/index.ts | 21 +++++----- .../samples/capture-inject-state/expected.js | 22 +++++------ test/js/samples/debug-empty/expected.js | 14 +++---- .../debug-foo-bar-baz-things/expected.js | 38 +++++++++---------- test/js/samples/debug-foo/expected.js | 22 +++++------ .../expected.js | 14 +++---- .../Foo.svelte | 8 ++++ .../_config.js | 9 +++++ .../main.svelte | 8 ++++ 9 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/_config.js create mode 100644 test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 5fdac9bce4..173d93d5dd 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -121,7 +121,7 @@ export default function dom( const accessors = []; const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`; - let dev_props_check: Node[] | Node; + let missing_props_check: Node[] | Node; let inject_state: Expression; let capture_state: Expression; let props_inject: Node[] | Node; @@ -227,13 +227,13 @@ export default function dom( const expected = props.filter(prop => prop.writable && !prop.initialised); if (expected.length) { - dev_props_check = b` - const { ctx: #ctx } = this.$$; - const props = ${options.customElement ? x`this.attributes` : x`options.props || {}`}; - ${expected.map(prop => b` - if (${renderer.reference(prop.name)} === undefined && !('${prop.export_name}' in props)) { - @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); - }`)} + missing_props_check = b` + $$self.$$.on_mount.push(function () { + ${expected.map(prop => b` + if (${prop.name} === undefined && !(('${prop.export_name}' in $$props) || $$self.$$.bound[$$self.$$.props['${prop.export_name}']])) { + @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); + }`)} + }); `; } @@ -476,6 +476,7 @@ export default function dom( ${instance_javascript} + ${missing_props_check} ${unknown_props_check} ${renderer.binding_groups.size > 0 && b`const $$binding_groups = [${[...renderer.binding_groups.keys()].map(_ => x`[]`)}];`} @@ -533,8 +534,6 @@ export default function dom( @init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, null, ${dirty}); - ${dev_props_check} - if (options) { if (options.target) { @insert(options.target, this, options.anchor); @@ -594,8 +593,6 @@ export default function dom( super(${options.dev && 'options'}); @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${optional_parameters}); ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} - - ${dev_props_check} } } `[0] as ClassDeclaration; diff --git a/test/js/samples/capture-inject-state/expected.js b/test/js/samples/capture-inject-state/expected.js index 58e1879553..882b3e9cf6 100644 --- a/test/js/samples/capture-inject-state/expected.js +++ b/test/js/samples/capture-inject-state/expected.js @@ -113,6 +113,17 @@ function instance($$self, $$props, $$invalidate) { let { alias: realName } = $$props; let local; let shadowedByModule; + + $$self.$$.on_mount.push(function () { + if (prop === undefined && !('prop' in $$props || $$self.$$.bound[$$self.$$.props['prop']])) { + console.warn(" was created without expected prop 'prop'"); + } + + if (realName === undefined && !('alias' in $$props || $$self.$$.bound[$$self.$$.props['alias']])) { + console.warn(" was created without expected prop 'alias'"); + } + }); + const writable_props = ['prop', 'alias']; Object.keys($$props).forEach(key => { @@ -166,17 +177,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*prop*/ ctx[0] === undefined && !('prop' in props)) { - console.warn(" was created without expected prop 'prop'"); - } - - if (/*realName*/ ctx[1] === undefined && !('alias' in props)) { - console.warn(" was created without expected prop 'alias'"); - } } get prop() { diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 55303b41b7..ee304591cb 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -72,6 +72,13 @@ function instance($$self, $$props, $$invalidate) { let { $$slots: slots = {}, $$scope } = $$props; validate_slots('Component', slots, []); let { name } = $$props; + + $$self.$$.on_mount.push(function () { + if (name === undefined && !('name' in $$props || $$self.$$.bound[$$self.$$.props['name']])) { + console.warn(" was created without expected prop 'name'"); + } + }); + const writable_props = ['name']; Object.keys($$props).forEach(key => { @@ -106,13 +113,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*name*/ ctx[0] === undefined && !('name' in props)) { - console.warn(" was created without expected prop 'name'"); - } } get name() { diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index 3377d35018..02be04c26e 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -176,6 +176,25 @@ function instance($$self, $$props, $$invalidate) { let { foo } = $$props; let { bar } = $$props; let { baz } = $$props; + + $$self.$$.on_mount.push(function () { + if (things === undefined && !('things' in $$props || $$self.$$.bound[$$self.$$.props['things']])) { + console.warn(" was created without expected prop 'things'"); + } + + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + + if (bar === undefined && !('bar' in $$props || $$self.$$.bound[$$self.$$.props['bar']])) { + console.warn(" was created without expected prop 'bar'"); + } + + if (baz === undefined && !('baz' in $$props || $$self.$$.bound[$$self.$$.props['baz']])) { + console.warn(" was created without expected prop 'baz'"); + } + }); + const writable_props = ['things', 'foo', 'bar', 'baz']; Object.keys($$props).forEach(key => { @@ -216,25 +235,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*things*/ ctx[0] === undefined && !('things' in props)) { - console.warn(" was created without expected prop 'things'"); - } - - if (/*foo*/ ctx[1] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } - - if (/*bar*/ ctx[2] === undefined && !('bar' in props)) { - console.warn(" was created without expected prop 'bar'"); - } - - if (/*baz*/ ctx[3] === undefined && !('baz' in props)) { - console.warn(" was created without expected prop 'baz'"); - } } get things() { diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index 518897c237..84dedb0a91 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -168,6 +168,17 @@ function instance($$self, $$props, $$invalidate) { validate_slots('Component', slots, []); let { things } = $$props; let { foo } = $$props; + + $$self.$$.on_mount.push(function () { + if (things === undefined && !('things' in $$props || $$self.$$.bound[$$self.$$.props['things']])) { + console.warn(" was created without expected prop 'things'"); + } + + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + }); + const writable_props = ['things', 'foo']; Object.keys($$props).forEach(key => { @@ -204,17 +215,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*things*/ ctx[0] === undefined && !('things' in props)) { - console.warn(" was created without expected prop 'things'"); - } - - if (/*foo*/ ctx[1] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } } get things() { diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index 5e10ec55f8..a94cf7b5f2 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -69,6 +69,13 @@ function instance($$self, $$props, $$invalidate) { validate_slots('Component', slots, []); let { foo } = $$props; let bar; + + $$self.$$.on_mount.push(function () { + if (foo === undefined && !('foo' in $$props || $$self.$$.bound[$$self.$$.props['foo']])) { + console.warn(" was created without expected prop 'foo'"); + } + }); + const writable_props = ['foo']; Object.keys($$props).forEach(key => { @@ -110,13 +117,6 @@ class Component extends SvelteComponentDev { options, id: create_fragment.name }); - - const { ctx } = this.$$; - const props = options.props || {}; - - if (/*foo*/ ctx[0] === undefined && !('foo' in props)) { - console.warn(" was created without expected prop 'foo'"); - } } get foo() { diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte b/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte new file mode 100644 index 0000000000..60278c4845 --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/Foo.svelte @@ -0,0 +1,8 @@ + + +
{w} {x} {y}
\ No newline at end of file diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js b/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js new file mode 100644 index 0000000000..ececa00a8c --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/_config.js @@ -0,0 +1,9 @@ +export default { + compileOptions: { + dev: true + }, + + warnings: [ + " was created without expected prop 'y'" + ] +}; diff --git a/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte b/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte new file mode 100644 index 0000000000..eab3c8336f --- /dev/null +++ b/test/runtime/samples/dev-warning-missing-data-component-bind/main.svelte @@ -0,0 +1,8 @@ + + + \ No newline at end of file From d04b1cca24fdfb9f170d0a5f747d94eabe7ee22b Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Thu, 6 Oct 2022 23:30:42 +0900 Subject: [PATCH 12/45] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ba64d0bb..d12212be87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * 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)) ## 3.50.1 From 8de7931c64b46d22fc303adce14eb3f680c8ca66 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 7 Oct 2022 08:23:12 +0800 Subject: [PATCH 13/45] [feat] better error message for invalid logic block placement (#7862) * better error message for invalid logic block placement * include checking for {@html} tags in invalid location --- src/compiler/parse/errors.ts | 8 ++++++++ src/compiler/parse/state/tag.ts | 19 ++++++++++++++++--- .../html-block-in-attribute/errors.json | 9 +++++++++ .../html-block-in-attribute/input.svelte | 1 + .../html-block-in-textarea/errors.json | 9 +++++++++ .../html-block-in-textarea/input.svelte | 3 +++ .../logic-block-in-attribute/errors.json | 9 +++++++++ .../logic-block-in-attribute/input.svelte | 1 + .../logic-block-in-textarea/errors.json | 9 +++++++++ .../logic-block-in-textarea/input.svelte | 5 +++++ 10 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 test/validator/samples/html-block-in-attribute/errors.json create mode 100644 test/validator/samples/html-block-in-attribute/input.svelte create mode 100644 test/validator/samples/html-block-in-textarea/errors.json create mode 100644 test/validator/samples/html-block-in-textarea/input.svelte create mode 100644 test/validator/samples/logic-block-in-attribute/errors.json create mode 100644 test/validator/samples/logic-block-in-attribute/input.svelte create mode 100644 test/validator/samples/logic-block-in-textarea/errors.json create mode 100644 test/validator/samples/logic-block-in-textarea/input.svelte diff --git a/src/compiler/parse/errors.ts b/src/compiler/parse/errors.ts index 63bd5b0919..26f03c0cb1 100644 --- a/src/compiler/parse/errors.ts +++ b/src/compiler/parse/errors.ts @@ -107,6 +107,14 @@ export default { code: `invalid-${slug}-placement`, message: `<${name}> tags cannot be inside elements or blocks` }), + invalid_logic_block_placement: (location: string, name: string) => ({ + code: 'invalid-logic-block-placement', + message: `{#${name}} logic block cannot be ${location}` + }), + invalid_tag_placement: (location: string, name: string) => ({ + code: 'invalid-tag-placement', + message: `{@${name}} tag cannot be ${location}` + }), invalid_ref_directive: (name: string) => ({ code: 'invalid-ref-directive', message: `The ref directive is no longer supported — use \`bind:this={${name}}\` instead` diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index efce375b7e..4be47f25b0 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -219,7 +219,8 @@ export default function tag(parser: Parser) { element.children = read_sequence( parser, () => - /^<\/textarea(\s[^>]*)?>/i.test(parser.template.slice(parser.index)) + /^<\/textarea(\s[^>]*)?>/i.test(parser.template.slice(parser.index)), + 'inside \ 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 14/45] 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 15/45] [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 16/45] [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 17/45] [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 18/45] -> 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 19/45] [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 20/45] [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('