diff --git a/.changeset/proud-cycles-hunt.md b/.changeset/proud-cycles-hunt.md deleted file mode 100644 index 5f0f88cb69..0000000000 --- a/.changeset/proud-cycles-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: align `disclose-version` exports specification diff --git a/.changeset/six-teachers-divide.md b/.changeset/six-teachers-divide.md deleted file mode 100644 index a11f257fef..0000000000 --- a/.changeset/six-teachers-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: check srcset when hydrating to prevent needless requests diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c92e8a5355..b8e948b3a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ permissions: contents: read # to fetch code (actions/checkout) env: - # We only install Chromium manually + # We only install Chromium manually PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1' jobs: diff --git a/documentation/blog/2020-07-17-svelte-and-typescript.md b/documentation/blog/2020-07-17-svelte-and-typescript.md index bcca836d4e..a6e07242ec 100644 --- a/documentation/blog/2020-07-17-svelte-and-typescript.md +++ b/documentation/blog/2020-07-17-svelte-and-typescript.md @@ -16,13 +16,9 @@ We think it'll give you a much nicer development experience — one that also sc ## Try it now -You can start a new Svelte TypeScript project using the [normal template](https://github.com/sveltejs/template) and by running `node scripts/setupTypeScript.js` before you do anything else: +You can start a new Svelte TypeScript project using Svelte's official scaffolding CLI by running `npm create svelte@latest` and following the prompts. This sets up a new SvelteKit project for you. -```bash -npx degit sveltejs/template svelte-typescript-app -cd svelte-typescript-app -node scripts/setupTypeScript.js -``` +Alternatively you can run `npm create vite@latest myapp -- --template svelte-ts` to scaffold a Vite project using Svelte and TypeScript. If you're a VS Code user, make sure you're using the (new) [official extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), which replaces the popular extension by James Birtles. Later in this blog post, we'll detail the individual steps involved in using TypeScript in an existing Svelte project. diff --git a/documentation/blog/2023-07-01-whats-new-in-svelte-july-2023.md b/documentation/blog/2023-07-01-whats-new-in-svelte-july-2023.md new file mode 100644 index 0000000000..afb39a5539 --- /dev/null +++ b/documentation/blog/2023-07-01-whats-new-in-svelte-july-2023.md @@ -0,0 +1,95 @@ +--- +title: "What's new in Svelte: July 2023" +description: "Svelte 4.0, new website and a tour around the community" +author: Dani Sandoval +authorURL: https://dreamindani.com +--- + +Svelte 4 is out and folks have been building! There's a bunch of new showcases, libraries and tutorials to share. So let's get right into it... + +## What's new in Svelte +The big news this month was the release of Svelte 4.0! You can read all about it in the [Announcing Svelte 4 post](https://svelte.dev/blog/svelte-4). From performance fixes and developer experience improvements to [a brand new site, docs and tutorial](https://svelte.dev/blog/svelte-dev-overhaul)... this new release sets the stage for Svelte 5 with minimal breaking changes. + +If you're already on Node.js 16, it's possible you won't see any breaking changes in your project. But be sure to read the [migration guide](https://svelte.dev/docs/v4-migration-guide) for all the details. + +For a full list of all the changes to the Svelte compiler, including unreleased changes, check out the [CHANGELOG](https://github.com/sveltejs/svelte/blob/master/packages/svelte/CHANGELOG.md). + +## What's new in SvelteKit +This month there were lots of awesome [bug fixes](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md), so be sure to upgrade to the latest version! There are also a few new features to mention: +- The new `event.isSubRequest` boolean indicates whether this is a same-origin fetch request to one of the app's own APIs during a server request (**1.21.0**, [Docs](https://kit.svelte.dev/docs/types#public-types-requestevent), [#10170](https://github.com/sveltejs/kit/pull/10170)) +- A new config option, `config.kit.env.privatePrefix` will set a private prefix on environment variables. This defaults to `''` (**1.21.0**, [Docs](https://kit.svelte.dev/docs/configuration), [#9996](https://github.com/sveltejs/kit/pull/9996)) +- `VERSION` is now exported and accessible via `@sveltejs/kit`. This can be used for feature detection or anything else that requires knowledge of the current version of SvelteKit (**1.21.0**, [Docs](https://kit.svelte.dev/docs/modules#sveltejs-kit-version), [#9969](https://github.com/sveltejs/kit/pull/9969)) + +For adapter-specific changes, check out the CHANGELOGs in each of [the `adapter` directories](https://github.com/sveltejs/kit/tree/master/packages). + +--- + +## Community Showcase + +**Apps & Sites built with Svelte** +- [Heerdle](https://github.com/DreaminDani/heerdle) is a remake of Spotify's now-defunct Heardle - the daily music guessing game +- [Meoweler](https://meoweler.com/) is a travel site filled with cats and helpful facts about popular destinations +- [A tech lead from IKEA](https://www.reddit.com/r/sveltejs/comments/13w4zg3/comment/jmaxial/?utm_source=share&utm_medium=web2x&context=3) gave a few more details on the way they build pages (and page template) using Svelte +- [The Quest to Replace Passwords](https://notes.ekzhang.com/papers/passwords) features an interactive comparison visualization for all the popular password management tools +- [audiogest](https://audiogest.app/en) lets you turn speech to text & summarize any audio in one click +- [heroify](https://www.heroify.lol/) generates 3D graphics for your website with AI +- [Diesel Legacy: The Brazen Age](https://store.steampowered.com/app/1959140/Diesel_Legacy_The_Brazen_Age/) is a fighting game whose leaderboard and profile pages were all built in Svelte +- [markmyimages](https://www.markmyimages.com/) is a watermarking tool with bulk image resize, rename, effects, and more +- [md.robino.dev](https://github.com/rossrobino/md) is a web based markdown editor +- [YABin](https://github.com/Yureien/YABin) is Yet Another Pastebin with some very specific features + +**Learning Resources** +- [Announcing Svelte 4 post](https://svelte.dev/blog/svelte-4) +- [svelte.dev: A complete overhaul](https://svelte.dev/blog/svelte-dev-overhaul) + +_Featuring Svelte Contributors and Ambassadors_ +- [Dev Vlog: June 2023](https://www.youtube.com/watch?v=AOXq89h8saI) - Svelte 4.0 with Rich Harris +- [PodRocket: Svelte 4](https://podrocket.logrocket.com/svelte-4) with Geoff +- [This Dot Media: Svelte 4 Launch Party](https://www.youtube.com/watch?v=-9gy_leMmcQ) with Simon, Ben, Geoff, and Puru +- [Exposing Svelte: Between Two Nerds](https://www.youtube.com/watch?v=kAfotLrebhY) is a comedic conversation between Rich Harris and Dax Raad +- [Community Tutorial: Self-hosting SvelteKit with a VPS, Docker, CapRover and GitHub Actions](https://www.youtube.com/watch?v=KbIFRVvdgA8) with Stanislav Khromov +- [SvelteKit and Storybook](https://www.youtube.com/watch?v=1wH7rR7hZlg) with Jeppe Reinhold +- This Week in Svelte: + - [2023 June 2](https://www.youtube.com/watch?v=B2AOYWs6eko) - SvelteKit 1.20.1, Svelte 4 pre-release, Headless UI libraries + - [2023 June 9](https://www.youtube.com/watch?v=OG70PKD0hEU) - Updates, Self-hosting SvelteKit, Passing styles to children + - [2023 June 16](https://www.youtube.com/watch?v=GNEbC5K34Po) - Svelte 4 next.1, how to create a hamburger menu, group layouts + - [2023 June 23](https://www.youtube.com/watch?v=o-qnnbMbmE4) - Svelte 4, Popovers and hover, Real Time requests with SvelteKit +- Svelte Radio + - [SvelteLab - a Svelte REPL for SvelteKit](https://www.svelteradio.com/episodes/sveltelab-a-svelte-repl-for-sveltekit-with-antonio-and-paolo) with Antonio and Paolo + - [Svelte Radio Live - Svelte 4 Summer Special](https://www.youtube.com/watch?v=72TIVhRtyWE) with Simon and Puru +- [Svelte Society - London June 2023](https://www.youtube.com/watch?v=EkH0aMgeIKw) +- [Using The Svelte Context API With Stores](https://www.youtube.com/watch?v=dp-7NvLDrK4), [Impossible FLIP Layout Animations With Svelte And GSAP](https://www.youtube.com/watch?v=ecP8RwpkiQw) and [Create Beautiful Presentations With Svelte](https://www.youtube.com/watch?v=67lqa5kTQkA) by Joy of Code + + +_To Watch_ +- [Server-side filtered, paginated and sorted Table in SvelteKit](https://www.youtube.com/watch?v=VgCU0cVWgJE) by hartenfellerdev +- [Best Icon Library for Svelte and SvelteKit in 2023](https://www.youtube.com/watch?v=qJP6hC4YIhk) by SvelteRust + +_To Read_ +- [From Zero to Production with SvelteKit](https://www.okupter.com/events/from-zero-to-production-with-sveltekit) by Justin Ahinon +- [Thoughts on Svelte(Kit), one year and 3 billion requests later](https://claudioholanda.ch/en/blog/svelte-kit-after-3-billion-requests/) by Claudio Holanda +- [How I published a gratitude journaling app for iOS and Android using SvelteKit and Capacitor](https://khromov.se/how-i-published-a-gratitude-journaling-app-for-ios-and-android-using-sveltekit-and-capacitor/) by Stanislav Khromov +- [Learning by doing - Vue devs build a Svelte Single Page App](https://www.blackspike.com/blog/learning-svelte-by-building-a-single-page-application/) by Black Spike +- [Generate Breadcrumb and Navigation in SvelteKit](https://blog.aakashgoplani.in/generate-breadcrumb-and-navigation-in-sveltekit), [SvelteKit Authentication using SvelteKitAuth and OAuth providers: A Comprehensive Guide](https://blog.aakashgoplani.in/sveltekit-authentication-using-sveltekitauth-and-oauth-providers-a-comprehensive-guide) and [SvelteKitAuth with Salesforce OAuth provider](https://blog.aakashgoplani.in/sveltekitauth-with-salesforce-oauth-provider) by Aakash Goplani +- [Instantly find and remove Svelte component orphans](https://node-jz.medium.com/instantly-find-and-remove-svelte-component-orphans-9b2838ea2d99) by Jeremy Zaborowski +- [Migration Guide from Routify to SvelteKit Router](https://blog.aakashgoplani.in/migration-guide-from-routify-to-sveltekit-router) by Aakash Goplani +- [Creating 3D data visualization using Threlte and D3](https://www.datavizcubed.com/) by DataViz Cubed +- [Svelte Real‑time Multiplayer Game: User Presence](https://rodneylab.com/svelte-realtime-multiplayer-game/) and [SvelteKit PostCSS Tutorial: use Future CSS Today](https://rodneylab.com/sveltekit-postcss-tutorial/) by Rodney Lab +- [SvelteKit’s World of Routing: Unleash power of your app using Dynamic Routes and Parameters](https://www.inow.dev/sveltekits-world-of-routing-unleash-power-of-your-app-using-dynamic-routes-and-parameters/) by Igor Nowosad + + +**Libraries, Tools & Components** +- [The Vercel AI SDK](https://vercel.com/blog/introducing-the-vercel-ai-sdk) is an interoperable, streaming-enabled, edge-ready software development kit for AI apps built with React and Svelte +- [Superforms 1.0](https://superforms.rocks/) has been released. Check out the [migration guide](https://superforms.rocks/migration) and [new feature list](https://superforms.rocks/whats-new-v1) for more details +- [Panda CSS](https://panda-css.com/docs/getting-started/svelte) is CSS-in-JS with build time generated styles, RSC compatibility and multi-variant support +- [svelte-section-list](https://github.com/TIKramer/svelte-section-list) is a headless Svelte npm package that provides drag-and-drop functionality for managing items and sections +- [WebStorm](https://twitter.com/tomblachut/status/1669759906579185681?t=6WzLPUi65wsLtbVvYky7UQ&s=19) is starting to use the Svelte Language Server in its IDE tooling +- [shadcn-svelte](https://www.shadcn-svelte.com/) is an unofficial port of [shadcn/ui](https://github.com/shadcn/ui) to Svelte that makes it easy to build your component library from common base components +- [sveltekit-multibuild](https://github.com/MrNNP/sveltekit-multibuild) is a starter repo to create Android apps, web sites, desktop apps, and Chrome extensions automatically +- [SvelteKit AI Chatbot](https://github.com/jianyuan/sveltekit-ai-chatbot) is an open-source AI chatbot app template built with SvelteKit, the Vercel AI SDK, OpenAI, and Vercel KV. +- [KitAI](https://kit-ai.vercel.app/) provides batteries-included AI templates for SvelteKit and Next.js +- [Svelte Form Builder](https://github.com/pragmatic-engineering/svelte-form-builder-community) is a no-code drag&drop form builder for Svelte + +Thanks for reading! As always, feel free to let us know if we missed anything on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.gg/svelte). + +Until next time 👋 diff --git a/documentation/docs/05-misc/04-v4-migration-guide.md b/documentation/docs/05-misc/04-v4-migration-guide.md index 7da5f3e118..cbde43c6f6 100644 --- a/documentation/docs/05-misc/04-v4-migration-guide.md +++ b/documentation/docs/05-misc/04-v4-migration-guide.md @@ -121,7 +121,18 @@ The migration script will do both automatically for you. ([#8512](https://github ## Transitions are local by default -Transitions are now local by default to prevent confusion around page navigations. To make them global, add the `|global` modifier. The migration script will do this automatically for you. ([#6686](https://github.com/sveltejs/svelte/issues/6686)) +Transitions are now local by default to prevent confusion around page navigations. "local" means that a transition will not play if it's within a nested control flow block (`each/if/await/key`) and not the direct parent block but a block above it is created/destroyed. In the following example, the `slide` intro animation will only play when `success` goes from `false` to `true`, but it will _not_ play when `show` goes from `false` to `true`: + +```svelte +{#if show} + ... + {#if success} +

Success

+ {/each} +{/if} +``` + +To make transitions global, add the `|global` modifier - then they will play when _any_ control flow block above is created/destroyed. The migration script will do this automatically for you. ([#6686](https://github.com/sveltejs/svelte/issues/6686)) ## Default slot bindings @@ -152,7 +163,7 @@ The order in which preprocessors are applied has changed. Now, preprocessors are - the `inert` attribute is now applied to outroing elements to make them invisible to assistive technology and prevent interaction. ([#8628](https://github.com/sveltejs/svelte/pull/8628)) - the runtime now uses `classList.toggle(name, boolean)` which may not work in very old browsers. Consider using a [polyfill](https://github.com/eligrey/classList.js) if you need to support these browsers. ([#8629](https://github.com/sveltejs/svelte/issues/8629)) -- the runtime now uses the `CustomElement` constructor which may not work in very old browsers. Consider using a [polyfill](https://github.com/theftprevention/event-constructor-polyfill/tree/master) if you need to support these browsers. ([#8775](https://github.com/sveltejs/svelte/pull/8775)) +- the runtime now uses the `CustomEvent` constructor which may not work in very old browsers. Consider using a [polyfill](https://github.com/theftprevention/event-constructor-polyfill/tree/master) if you need to support these browsers. ([#8775](https://github.com/sveltejs/svelte/pull/8775)) - people implementing their own stores from scratch using the `StartStopNotifier` interface (which is passed to the create function of `writable` etc) from `svelte/store` now need to pass an update function in addition to the set function. This has no effect on people using stores or creating stores using the existing Svelte stores. ([#6750](https://github.com/sveltejs/svelte/issues/6750)) - `derived` will now throw an error on falsy values instead of stores passed to it. ([#7947](https://github.com/sveltejs/svelte/issues/7947)) - type definitions for `svelte/internal` were removed to further discourage usage of those internal methods which are not public API. Most of these will likely change for Svelte 5 diff --git a/documentation/tutorial/02-reactivity/01-reactive-assignments/app-a/App.svelte b/documentation/tutorial/02-reactivity/01-reactive-assignments/app-a/App.svelte index 836e573dc6..8b1569bffb 100644 --- a/documentation/tutorial/02-reactivity/01-reactive-assignments/app-a/App.svelte +++ b/documentation/tutorial/02-reactivity/01-reactive-assignments/app-a/App.svelte @@ -10,3 +10,9 @@ Clicked {count} {count === 1 ? 'time' : 'times'} + + diff --git a/documentation/tutorial/02-reactivity/01-reactive-assignments/app-b/App.svelte b/documentation/tutorial/02-reactivity/01-reactive-assignments/app-b/App.svelte index 382b2ee9e0..d51ca2e068 100644 --- a/documentation/tutorial/02-reactivity/01-reactive-assignments/app-b/App.svelte +++ b/documentation/tutorial/02-reactivity/01-reactive-assignments/app-b/App.svelte @@ -10,3 +10,9 @@ Clicked {count} {count === 1 ? 'time' : 'times'} + + diff --git a/documentation/tutorial/05-events/02-inline-handlers/text.md b/documentation/tutorial/05-events/02-inline-handlers/text.md index 2fe1c48e2c..99ae43c429 100644 --- a/documentation/tutorial/05-events/02-inline-handlers/text.md +++ b/documentation/tutorial/05-events/02-inline-handlers/text.md @@ -10,6 +10,4 @@ You can also declare event handlers inline: ``` -The quote marks are optional, but they're helpful for syntax highlighting in some environments. - > In some frameworks you may see recommendations to avoid inline event handlers for performance reasons, particularly inside loops. That advice doesn't apply to Svelte — the compiler will always do the right thing, whichever form you choose. diff --git a/package.json b/package.json index 47afc17e58..e0f1bdcfd9 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "pnpm -r --filter=./packages/* build", "build:sites": "pnpm -r --filter=./sites/* build", "check": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r check", - "lint": "pnpm -r lint", + "lint": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r lint", "format": "pnpm -r format", "changeset:version": "changeset version && pnpm -r generate:version && git add --all", "changeset:publish": "changeset publish" diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ffa517208d..e0881bb865 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,25 @@ # svelte +## 4.0.3 + +### Patch Changes + +- fix: handle falsy srcset values ([#8901](https://github.com/sveltejs/svelte/pull/8901)) + +## 4.0.2 + +### Patch Changes + +- fix: reflect all custom element prop updates back to attribute ([#8898](https://github.com/sveltejs/svelte/pull/8898)) + +- fix: shrink custom element baseline a bit ([#8858](https://github.com/sveltejs/svelte/pull/8858)) + +- fix: use non-destructive hydration for all `@html` tags ([#8880](https://github.com/sveltejs/svelte/pull/8880)) + +- fix: align `disclose-version` exports specification ([#8874](https://github.com/sveltejs/svelte/pull/8874)) + +- fix: check srcset when hydrating to prevent needless requests ([#8868](https://github.com/sveltejs/svelte/pull/8868)) + ## 4.0.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0b554d0efa..cc4060a9a8 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "4.0.1", + "version": "4.0.3", "description": "Cybernetically enhanced web apps", "type": "module", "module": "src/runtime/index.js", @@ -74,7 +74,7 @@ "types": "types/index.d.ts", "scripts": { "format": "prettier . --cache --plugin-search-dir=. --write", - "check": "prettier . --cache --plugin-search-dir=. --check", + "check": "tsc --noEmit", "test": "vitest run && echo \"manually check that there are no type errors in test/types by opening the files in there\"", "build": "rollup -c && pnpm types", "generate:version": "node ./scripts/generate-version.js", @@ -82,7 +82,7 @@ "posttest": "agadoo src/internal/index.js", "prepublishOnly": "pnpm build", "types": "node ./scripts/generate-dts.js", - "lint": "eslint \"{src,test}/**/*.{ts,js}\" --cache" + "lint": "prettier . --cache --plugin-search-dir=. --check && eslint \"{src,test}/**/*.{ts,js}\" --cache" }, "repository": { "type": "git", diff --git a/packages/svelte/src/compiler/compile/render_dom/wrappers/RawMustacheTag.js b/packages/svelte/src/compiler/compile/render_dom/wrappers/RawMustacheTag.js index 11a838ab8e..cc382a9b0b 100644 --- a/packages/svelte/src/compiler/compile/render_dom/wrappers/RawMustacheTag.js +++ b/packages/svelte/src/compiler/compile/render_dom/wrappers/RawMustacheTag.js @@ -26,7 +26,7 @@ export default class RawMustacheTagWrapper extends Tag { render(block, parent_node, _parent_nodes) { const in_head = is_head(parent_node); const can_use_innerhtml = !in_head && parent_node && !this.prev && !this.next; - if (can_use_innerhtml) { + if (can_use_innerhtml && !this.renderer.options.hydratable) { /** @param {import('estree').Node} content */ const insert = (content) => b`${parent_node}.innerHTML = ${content};`[0]; const { init } = this.rename_this_method(block, (content) => insert(content)); diff --git a/packages/svelte/src/runtime/internal/Component.js b/packages/svelte/src/runtime/internal/Component.js index a1da213da3..4733b1d745 100644 --- a/packages/svelte/src/runtime/internal/Component.js +++ b/packages/svelte/src/runtime/internal/Component.js @@ -13,7 +13,9 @@ import { start_hydrating, end_hydrating, get_custom_elements_slots, - insert + insert, + element, + attr } from './dom.js'; import { transition_in } from './transitions.js'; @@ -157,23 +159,29 @@ export let SvelteElement; if (typeof HTMLElement === 'function') { SvelteElement = class extends HTMLElement { - $$componentCtor; - $$slots; - $$component; - $$connected = false; - $$data = {}; - $$reflecting = false; - /** @type {Record} */ - $$props_definition = {}; - /** @type {Record} */ - $$listeners = {}; - /** @type {Map} */ - $$listener_unsubscribe_fns = new Map(); + /** The Svelte component constructor */ + $$ctor; + /** Slots */ + $$s; + /** The Svelte component instance */ + $$c; + /** Whether or not the custom element is connected */ + $$cn = false; + /** Component props data */ + $$d = {}; + /** `true` if currently in the process of reflecting component props back to attributes */ + $$r = false; + /** @type {Record} Props definition (name, reflected, type etc) */ + $$p_d = {}; + /** @type {Record} Event listeners */ + $$l = {}; + /** @type {Map} Event listener unsubscribe functions */ + $$l_u = new Map(); constructor($$componentCtor, $$slots, use_shadow_dom) { super(); - this.$$componentCtor = $$componentCtor; - this.$$slots = $$slots; + this.$$ctor = $$componentCtor; + this.$$s = $$slots; if (use_shadow_dom) { this.attachShadow({ mode: 'open' }); } @@ -183,32 +191,32 @@ if (typeof HTMLElement === 'function') { // We can't determine upfront if the event is a custom event or not, so we have to // listen to both. If someone uses a custom event with the same name as a regular // browser event, this fires twice - we can't avoid that. - this.$$listeners[type] = this.$$listeners[type] || []; - this.$$listeners[type].push(listener); - if (this.$$component) { - const unsub = this.$$component.$on(type, listener); - this.$$listener_unsubscribe_fns.set(listener, unsub); + this.$$l[type] = this.$$l[type] || []; + this.$$l[type].push(listener); + if (this.$$c) { + const unsub = this.$$c.$on(type, listener); + this.$$l_u.set(listener, unsub); } super.addEventListener(type, listener, options); } removeEventListener(type, listener, options) { super.removeEventListener(type, listener, options); - if (this.$$component) { - const unsub = this.$$listener_unsubscribe_fns.get(listener); + if (this.$$c) { + const unsub = this.$$l_u.get(listener); if (unsub) { unsub(); - this.$$listener_unsubscribe_fns.delete(listener); + this.$$l_u.delete(listener); } } } async connectedCallback() { - this.$$connected = true; - if (!this.$$component) { + this.$$cn = true; + if (!this.$$c) { // We wait one tick to let possible child slot elements be created/mounted await Promise.resolve(); - if (!this.$$connected) { + if (!this.$$cn) { return; } function create_slot(name) { @@ -216,9 +224,9 @@ if (typeof HTMLElement === 'function') { let node; const obj = { c: function create() { - node = document.createElement('slot'); + node = element('slot'); if (name !== 'default') { - node.setAttribute('name', name); + attr(node, 'name', name); } }, /** @@ -239,74 +247,89 @@ if (typeof HTMLElement === 'function') { } const $$slots = {}; const existing_slots = get_custom_elements_slots(this); - for (const name of this.$$slots) { + for (const name of this.$$s) { if (name in existing_slots) { $$slots[name] = [create_slot(name)]; } } for (const attribute of this.attributes) { // this.$$data takes precedence over this.attributes - const name = this.$$get_prop_name(attribute.name); - if (!(name in this.$$data)) { - this.$$data[name] = get_custom_element_value( - name, - attribute.value, - this.$$props_definition, - 'toProp' - ); + const name = this.$$g_p(attribute.name); + if (!(name in this.$$d)) { + this.$$d[name] = get_custom_element_value(name, attribute.value, this.$$p_d, 'toProp'); } } - this.$$component = new this.$$componentCtor({ + this.$$c = new this.$$ctor({ target: this.shadowRoot || this, props: { - ...this.$$data, + ...this.$$d, $$slots, $$scope: { ctx: [] } } }); - for (const type in this.$$listeners) { - for (const listener of this.$$listeners[type]) { - const unsub = this.$$component.$on(type, listener); - this.$$listener_unsubscribe_fns.set(listener, unsub); + + // Reflect component props as attributes + const reflect_attributes = () => { + this.$$r = true; + for (const key in this.$$p_d) { + this.$$d[key] = this.$$c.$$.ctx[this.$$c.$$.props[key]]; + if (this.$$p_d[key].reflect) { + const attribute_value = get_custom_element_value( + key, + this.$$d[key], + this.$$p_d, + 'toAttribute' + ); + if (attribute_value == null) { + this.removeAttribute(key); + } else { + this.setAttribute(this.$$p_d[key].attribute || key, attribute_value); + } + } + } + this.$$r = false; + }; + this.$$c.$$.after_update.push(reflect_attributes); + reflect_attributes(); // once initially because after_update is added too late for first render + + for (const type in this.$$l) { + for (const listener of this.$$l[type]) { + const unsub = this.$$c.$on(type, listener); + this.$$l_u.set(listener, unsub); } } - this.$$listeners = {}; + this.$$l = {}; } } // We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte // and setting attributes through setAttribute etc, this is helpful attributeChangedCallback(attr, _oldValue, newValue) { - if (this.$$reflecting) return; - attr = this.$$get_prop_name(attr); - this.$$data[attr] = get_custom_element_value( - attr, - newValue, - this.$$props_definition, - 'toProp' - ); - this.$$component?.$set({ [attr]: this.$$data[attr] }); + if (this.$$r) return; + attr = this.$$g_p(attr); + this.$$d[attr] = get_custom_element_value(attr, newValue, this.$$p_d, 'toProp'); + this.$$c?.$set({ [attr]: this.$$d[attr] }); } disconnectedCallback() { - this.$$connected = false; + this.$$cn = false; // In a microtask, because this could be a move within the DOM Promise.resolve().then(() => { - if (!this.$$connected) { - this.$$component.$destroy(); - this.$$component = undefined; + if (!this.$$cn) { + this.$$c.$destroy(); + this.$$c = undefined; } }); } - $$get_prop_name(attribute_name) { + $$g_p(attribute_name) { return ( - Object.keys(this.$$props_definition).find( + Object.keys(this.$$p_d).find( (key) => - this.$$props_definition[key].attribute === attribute_name || - (!this.$$props_definition[key].attribute && key.toLowerCase() === attribute_name) + this.$$p_d[key].attribute === attribute_name || + (!this.$$p_d[key].attribute && key.toLowerCase() === attribute_name) ) || attribute_name ); } @@ -371,7 +394,7 @@ export function create_custom_element( const Class = class extends SvelteElement { constructor() { super(Component, slots, use_shadow_dom); - this.$$props_definition = props_definition; + this.$$p_d = props_definition; } static get observedAttributes() { return Object.keys(props_definition).map((key) => @@ -382,36 +405,19 @@ export function create_custom_element( Object.keys(props_definition).forEach((prop) => { Object.defineProperty(Class.prototype, prop, { get() { - return this.$$component && prop in this.$$component - ? this.$$component[prop] - : this.$$data[prop]; + return this.$$c && prop in this.$$c ? this.$$c[prop] : this.$$d[prop]; }, set(value) { value = get_custom_element_value(prop, value, props_definition); - this.$$data[prop] = value; - this.$$component?.$set({ [prop]: value }); - if (props_definition[prop].reflect) { - this.$$reflecting = true; - const attribute_value = get_custom_element_value( - prop, - value, - props_definition, - 'toAttribute' - ); - if (attribute_value == null) { - this.removeAttribute(prop); - } else { - this.setAttribute(props_definition[prop].attribute || prop, attribute_value); - } - this.$$reflecting = false; - } + this.$$d[prop] = value; + this.$$c?.$set({ [prop]: value }); } }); }); accessors.forEach((accessor) => { Object.defineProperty(Class.prototype, accessor, { get() { - return this.$$component?.[accessor]; + return this.$$c?.[accessor]; } }); }); diff --git a/packages/svelte/src/runtime/internal/utils.js b/packages/svelte/src/runtime/internal/utils.js index 190c4534c6..ec39c0d9e4 100644 --- a/packages/svelte/src/runtime/internal/utils.js +++ b/packages/svelte/src/runtime/internal/utils.js @@ -90,12 +90,12 @@ function split_srcset(srcset) { /** * @param {HTMLSourceElement | HTMLImageElement} element_srcset - * @param {string} srcset + * @param {string | undefined | null} srcset * @returns {boolean} */ export function srcset_url_equal(element_srcset, srcset) { const element_urls = split_srcset(element_srcset.srcset); - const urls = split_srcset(srcset); + const urls = split_srcset(srcset || ''); return ( urls.length === element_urls.length && diff --git a/packages/svelte/src/shared/version.js b/packages/svelte/src/shared/version.js index 835c2fda5b..7279358a14 100644 --- a/packages/svelte/src/shared/version.js +++ b/packages/svelte/src/shared/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '4.0.1'; +export const VERSION = '4.0.3'; export const PUBLIC_VERSION = '4'; diff --git a/packages/svelte/svelte-4.0.0.tgz b/packages/svelte/svelte-4.0.0.tgz deleted file mode 100644 index 8c527a3fd8..0000000000 Binary files a/packages/svelte/svelte-4.0.0.tgz and /dev/null differ diff --git a/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes/main.svelte b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes/main.svelte index 0e36f0143c..a662227a13 100644 --- a/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes/main.svelte +++ b/packages/svelte/test/runtime-browser/custom-elements-samples/reflect-attributes/main.svelte @@ -1,19 +1,20 @@
hi

hi

- + +