From fd5cfd77d8a13f2edf723f288e288799f02966d7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 22 Jul 2024 14:49:24 -0400 Subject: [PATCH 1/4] fix: correctly set filename on HMR wrappers (#12543) * fix: correctly set filename on HMR wrappers * changeset --- .changeset/tender-suns-love.md | 5 +++++ .../phases/3-transform/client/transform-client.js | 10 ++++++++-- .../samples/hmr/_expected/client/index.svelte.js | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 .changeset/tender-suns-love.md diff --git a/.changeset/tender-suns-love.md b/.changeset/tender-suns-love.md new file mode 100644 index 0000000000..d4f4174da8 --- /dev/null +++ b/.changeset/tender-suns-love.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly set filename on HMR wrappers diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 0ef05eeac0..4422982d8d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -439,10 +439,16 @@ export function client_component(source, analysis, options) { const hmr = b.block([ b.const(b.id('s'), b.call('$.source', b.id(analysis.name))), - b.const(b.id('filename'), b.member(b.id(analysis.name), b.id('filename'))), + b.const(b.id('$$filename'), b.member(b.id(analysis.name), b.id('$.FILENAME'), true)), b.const(b.id('$$original'), b.id(analysis.name)), b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))), - b.stmt(b.assignment('=', b.member(b.id(analysis.name), b.id('filename')), b.id('filename'))), + b.stmt( + b.assignment( + '=', + b.member(b.id(analysis.name), b.id('$.FILENAME'), true), + b.id('$$filename') + ) + ), // Assign the original component to the wrapper so we can use it on hot reload patching, // else we would call the HMR function two times b.stmt( diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js index 44600907c1..696ad9b1fc 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js @@ -11,11 +11,11 @@ function Hmr($$anchor) { if (import.meta.hot) { const s = $.source(Hmr); - const filename = Hmr.filename; + const $$filename = Hmr[$.FILENAME]; const $$original = Hmr; Hmr = $.hmr(s); - Hmr.filename = filename; + Hmr[$.FILENAME] = $$filename; Hmr[$.ORIGINAL] = $$original; import.meta.hot.accept((module) => { From 73ac4fe4560d6d470af92adeb5b5277f0513f6ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 22 Jul 2024 16:30:29 -0400 Subject: [PATCH 2/4] fix: only emit binding_property_non_reactive warning in runes mode (#12544) --- .changeset/two-keys-watch.md | 5 ++++ .../3-transform/client/visitors/template.js | 12 ++++++++-- .../_config.js | 23 +++++++++++++++++++ .../main.svelte | 6 +++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 .changeset/two-keys-watch.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/main.svelte diff --git a/.changeset/two-keys-watch.md b/.changeset/two-keys-watch.md new file mode 100644 index 0000000000..92a455e07b --- /dev/null +++ b/.changeset/two-keys-watch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only emit binding_property_non_reactive warning in runes mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 4683a945bd..84c0ad0ed5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -780,7 +780,11 @@ function serialize_inline_component(node, component_name, context, anchor = cont } else if (attribute.type === 'BindDirective') { const expression = /** @type {Expression} */ (context.visit(attribute.expression)); - if (expression.type === 'MemberExpression' && context.state.options.dev) { + if ( + expression.type === 'MemberExpression' && + context.state.options.dev && + context.state.analysis.runes + ) { context.state.init.push(serialize_validate_binding(context.state, attribute, expression)); } @@ -2826,7 +2830,11 @@ export const template_visitors = { const { state, path, visit } = context; const expression = node.expression; - if (expression.type === 'MemberExpression' && context.state.options.dev) { + if ( + expression.type === 'MemberExpression' && + context.state.options.dev && + context.state.analysis.runes + ) { context.state.init.push( serialize_validate_binding( context.state, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/_config.js new file mode 100644 index 0000000000..e99efacba8 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target, window }) { + assert.htmlEqual(target.innerHTML, `

hello

`); + + const input = target.querySelector('input'); + ok(input); + + input.value = 'goodbye'; + input.dispatchEvent(new window.Event('input')); + + flushSync(); + assert.htmlEqual(target.innerHTML, `

goodbye

`); + }, + + warnings: [] +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/main.svelte b/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/main.svelte new file mode 100644 index 0000000000..5277309402 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/binding-member-expression-no-warning/main.svelte @@ -0,0 +1,6 @@ + + + +

{object.value}

From 53d32d4dfed5aac2cc9509d4fc4d330c0e7e9aaa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:40:35 -0400 Subject: [PATCH 3/4] Version Packages (next) (#12536) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 4 ++++ packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 5826fb7f66..746638d45f 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -385,10 +385,12 @@ "nasty-mayflies-smoke", "nasty-yaks-peel", "neat-boats-shake", + "neat-boxes-chew", "neat-dingos-clap", "neat-files-rescue", "neat-jokes-beam", "nervous-berries-boil", + "nervous-dolphins-allow", "nervous-ducks-repeat", "nervous-spoons-relax", "nervous-turkeys-end", @@ -645,6 +647,7 @@ "ten-worms-reflect", "tender-lemons-judge", "tender-rocks-walk", + "tender-suns-love", "thick-cycles-rule", "thick-pans-tell", "thick-shirts-deliver", @@ -695,6 +698,7 @@ "two-dogs-accept", "two-dragons-yell", "two-falcons-buy", + "two-keys-watch", "unlucky-boxes-obey", "unlucky-steaks-warn", "unlucky-trees-lick", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index b67d217c81..2e3f050a43 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.0.0-next.194 + +### Patch Changes + +- fix: bail-out of hydrating head if no anchor is found ([#12541](https://github.com/sveltejs/svelte/pull/12541)) + +- chore: add warning for invalid render function of createRawSnippet ([#12535](https://github.com/sveltejs/svelte/pull/12535)) + +- fix: correctly set filename on HMR wrappers ([#12543](https://github.com/sveltejs/svelte/pull/12543)) + +- fix: only emit binding_property_non_reactive warning in runes mode ([#12544](https://github.com/sveltejs/svelte/pull/12544)) + ## 5.0.0-next.193 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5067c4ad51..5efa601433 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.193", + "version": "5.0.0-next.194", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b947c0ce3f..fbd6debffa 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.193'; +export const VERSION = '5.0.0-next.194'; export const PUBLIC_VERSION = '5'; From 6fdfc537e4cd7fb912bd0b76805e82c09a2bd78b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 22 Jul 2024 16:44:11 -0400 Subject: [PATCH 4/4] fix: update original source in HMR update (#12547) * fix: update original source in HMR update * tidy up * comments * oops --- .changeset/small-planets-destroy.md | 5 +++ .../3-transform/client/transform-client.js | 33 ++++++++----------- packages/svelte/src/constants.js | 2 +- .../svelte/src/internal/client/dev/hmr.js | 25 ++++++++++++-- packages/svelte/src/internal/client/index.js | 2 +- packages/svelte/src/internal/server/index.js | 2 +- .../hmr/_expected/client/index.svelte.js | 11 ++----- 7 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 .changeset/small-planets-destroy.md diff --git a/.changeset/small-planets-destroy.md b/.changeset/small-planets-destroy.md new file mode 100644 index 0000000000..03a0b1bcea --- /dev/null +++ b/.changeset/small-planets-destroy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update original source in HMR update diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 4422982d8d..7733871ad4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -417,8 +417,19 @@ export function client_component(source, analysis, options) { ); if (options.hmr) { + const id = b.id(analysis.name); + const HMR = b.id('$.HMR'); + + const existing = b.member(id, HMR, true); + const incoming = b.member(b.id('module.default'), HMR, true); + const accept_fn_body = [ - b.stmt(b.call('$.set', b.id('s'), b.member(b.id('module.default'), b.id('$.ORIGINAL'), true))) + b.stmt( + b.assignment('=', b.member(incoming, b.id('source')), b.member(existing, b.id('source'))) + ), + b.stmt( + b.call('$.set', b.member(existing, b.id('source')), b.member(incoming, b.id('original'))) + ) ]; if (analysis.css.hash) { @@ -438,26 +449,10 @@ export function client_component(source, analysis, options) { } const hmr = b.block([ - b.const(b.id('s'), b.call('$.source', b.id(analysis.name))), - b.const(b.id('$$filename'), b.member(b.id(analysis.name), b.id('$.FILENAME'), true)), - b.const(b.id('$$original'), b.id(analysis.name)), - b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))), b.stmt( - b.assignment( - '=', - b.member(b.id(analysis.name), b.id('$.FILENAME'), true), - b.id('$$filename') - ) - ), - // Assign the original component to the wrapper so we can use it on hot reload patching, - // else we would call the HMR function two times - b.stmt( - b.assignment( - '=', - b.member(b.id(analysis.name), b.id('$.ORIGINAL'), true), - b.id('$$original') - ) + b.assignment('=', id, b.call('$.hmr', id, b.thunk(b.member(existing, b.id('source'))))) ), + b.stmt(b.call('import.meta.hot.accept', b.arrow([b.id('module')], b.block(accept_fn_body)))) ]); diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index f6adc94f50..061e6e2eb8 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -32,7 +32,7 @@ export const UNINITIALIZED = Symbol(); // Dev-time component properties export const FILENAME = Symbol('filename'); -export const ORIGINAL = Symbol('original'); +export const HMR = Symbol('hmr'); /** List of elements that require raw contents and should not have SSR comments put in them */ export const RawTextElements = ['textarea', 'script', 'style', 'title']; diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index 050901cd7c..0b511ee841 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -1,19 +1,22 @@ /** @import { Source, Effect } from '#client' */ +import { FILENAME, HMR } from '../../../constants.js'; import { EFFECT_TRANSPARENT } from '../constants.js'; import { block, branch, destroy_effect } from '../reactivity/effects.js'; +import { source } from '../reactivity/sources.js'; import { set_should_intro } from '../render.js'; import { get } from '../runtime.js'; /** * @template {(anchor: Comment, props: any) => any} Component - * @param {Source} source + * @param {Component} original + * @param {() => Source} get_source */ -export function hmr(source) { +export function hmr(original, get_source) { /** * @param {Comment} anchor * @param {any} props */ - return function (anchor, props) { + function wrapper(anchor, props) { let instance = {}; /** @type {Effect} */ @@ -22,6 +25,7 @@ export function hmr(source) { let ran = false; block(() => { + const source = get_source(); const component = get(source); if (effect) { @@ -50,5 +54,20 @@ export function hmr(source) { ran = true; return instance; + } + + // @ts-expect-error + wrapper[FILENAME] = original[FILENAME]; + + // @ts-expect-error + wrapper[HMR] = { + // When we accept an update, we set the original source to the new component + original, + // The `get_source` parameter reads `wrapper[HMR].source`, but in the `accept` + // function we always replace it with `previous[HMR].source`, which in practice + // means we only ever update the original + source: source(original) }; + + return wrapper; } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 859e85f22c..4349285c34 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -1,4 +1,4 @@ -export { FILENAME, ORIGINAL } from '../../constants.js'; +export { FILENAME, HMR } from '../../constants.js'; export { add_locations } from './dev/elements.js'; export { hmr } from './dev/hmr.js'; export { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 6300924ab2..593ddac548 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -1,6 +1,6 @@ /** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ -export { FILENAME, ORIGINAL } from '../../constants.js'; +export { FILENAME, HMR } from '../../constants.js'; import { is_promise, noop } from '../shared/utils.js'; import { subscribe_to_store } from '../../store/utils.js'; import { diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js index 696ad9b1fc..01daada7ac 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js @@ -10,16 +10,11 @@ function Hmr($$anchor) { } if (import.meta.hot) { - const s = $.source(Hmr); - const $$filename = Hmr[$.FILENAME]; - const $$original = Hmr; - - Hmr = $.hmr(s); - Hmr[$.FILENAME] = $$filename; - Hmr[$.ORIGINAL] = $$original; + Hmr = $.hmr(Hmr, () => Hmr[$.HMR].source); import.meta.hot.accept((module) => { - $.set(s, module.default[$.ORIGINAL]); + module.default[$.HMR].source = Hmr[$.HMR].source; + $.set(Hmr[$.HMR].source, module.default[$.HMR].original); }); }