Skip to content

Commit d2a5ac4

Browse files
committed
update warning, include mutation location
1 parent 8b9b6a9 commit d2a5ac4

File tree

8 files changed

+25
-14
lines changed

8 files changed

+25
-14
lines changed

documentation/docs/98-reference/.generated/client-warnings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ To fix it, `bind:` to the value instead of just passing a property (i.e. in this
171171
### ownership_invalid_mutation
172172
173173
```
174-
%component% mutated property `%name%` from parent component %owner%, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with `bind:` (e.g. `bind:%prop%={...}` instead of `%prop%={...}`), or use a callback instead
174+
Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead
175175
```
176176
177177
Consider the following code:

packages/svelte/messages/client-warnings/warnings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ To fix it, `bind:` to the value instead of just passing a property (i.e. in this
140140
141141
## ownership_invalid_mutation
142142
143-
> %component% mutated property `%name%` from parent component %owner%, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with `bind:` (e.g. `bind:%prop%={...}` instead of `%prop%={...}`), or use a callback instead
143+
> Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead
144144
145145
Consider the following code:
146146

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,14 @@ export function validate_mutation(node, context, expression) {
341341

342342
path.unshift(b.literal(name.name));
343343

344+
const loc = locator(/** @type {number} */ (left.start));
345+
344346
return b.call(
345347
'$$ownership_validator.mutation',
346348
b.literal(binding.prop_alias),
347349
b.array(path),
348-
expression
350+
expression,
351+
loc && b.literal(loc.line),
352+
loc && b.literal(loc.column)
349353
);
350354
}

packages/svelte/src/internal/client/dev/ownership.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { LEGACY_PROPS, STATE_SYMBOL } from '../constants.js';
55
import { FILENAME } from '../../../constants.js';
66
import { component_context } from '../context.js';
77
import * as w from '../warnings.js';
8+
import { sanitize_location } from '../../../utils.js';
89

910
/** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */
1011
const boundaries = {};
@@ -118,8 +119,10 @@ export function create_ownership_validator(props) {
118119
* @param {string} prop
119120
* @param {any[]} path
120121
* @param {any} result
122+
* @param {number} line
123+
* @param {number} column
121124
*/
122-
mutation: (prop, path, result) => {
125+
mutation: (prop, path, result, line, column) => {
123126
const name = path[0];
124127
if (is_bound(props, name) || !parent) {
125128
return result;
@@ -134,7 +137,9 @@ export function create_ownership_validator(props) {
134137
value = value[path[i]];
135138
}
136139

137-
w.ownership_invalid_mutation(component[FILENAME], name, parent[FILENAME], prop);
140+
const location = sanitize_location(`${component[FILENAME]}:${line}:${column}`);
141+
142+
w.ownership_invalid_mutation(name, location, prop, parent[FILENAME]);
138143

139144
return result;
140145
},

packages/svelte/src/internal/client/warnings.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,15 @@ export function ownership_invalid_binding(parent, prop, child, owner) {
144144
}
145145

146146
/**
147-
* %component% mutated property `%name%` from parent component %owner%, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with `bind:` (e.g. `bind:%prop%={...}` instead of `%prop%={...}`), or use a callback instead
148-
* @param {string} component
147+
* Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead
149148
* @param {string} name
150-
* @param {string} owner
149+
* @param {string} location
151150
* @param {string} prop
151+
* @param {string} parent
152152
*/
153-
export function ownership_invalid_mutation(component, name, owner, prop) {
153+
export function ownership_invalid_mutation(name, location, prop, parent) {
154154
if (DEV) {
155-
console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component} mutated property \`${name}\` from parent component ${owner}, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with \`bind:\` (e.g. \`bind:${prop}={...}\` instead of \`${prop}={...}\`), or use a callback instead\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal);
155+
console.warn(`%c[svelte] ownership_invalid_mutation\n%cMutating unbound props (\`${name}\`, at ${location}) is strongly discouraged. Consider using \`bind:${prop}={...}\` in ${parent} (or using a callback) instead\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal);
156156
} else {
157157
console.warn(`https://svelte.dev/e/ownership_invalid_mutation`);
158158
}

packages/svelte/src/utils.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,10 @@ export function is_raw_text_element(name) {
465465

466466
/**
467467
* Prevent devtools trying to make `location` a clickable link by inserting a zero-width space
468-
* @param {string | undefined} location
468+
* @template {string | undefined} T
469+
* @param {T} location
470+
* @returns {T};
469471
*/
470472
export function sanitize_location(location) {
471-
return location?.replace(/\//g, '/\u200b');
473+
return /** @type {T} */ (location?.replace(/\//g, '/\u200b'));
472474
}

packages/svelte/tests/runtime-runes/samples/non-local-mutation-discouraged/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default test({
1010

1111
test({ assert, target, warnings }) {
1212
const warning =
13-
'Counter.svelte mutated property `object` from parent component main.svelte, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with `bind:` (e.g. `bind:object={...}` instead of `object={...}`), or use a callback instead';
13+
'Mutating unbound props (`object`, at Counter.svelte:5:23) is strongly discouraged. Consider using `bind:object={...}` in main.svelte (or using a callback) instead';
1414
const [btn1, btn2] = target.querySelectorAll('button');
1515

1616
btn1.click();

packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default test({
3333
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button><button>clicks: 1</button>`);
3434

3535
assert.deepEqual(warnings, [
36-
'Counter.svelte mutated property `notshared` from parent component main.svelte, which did not declare it as a binding. This is strongly discouraged. Consider passing props to child components that mutate them with `bind:` (e.g. `bind:notshared={...}` instead of `notshared={...}`), or use a callback instead'
36+
'Mutating unbound props (`notshared`, at Counter.svelte:10:23) is strongly discouraged. Consider using `bind:notshared={...}` in main.svelte (or using a callback) instead'
3737
]);
3838
}
3939
});

0 commit comments

Comments
 (0)