mirror of
https://github.com/sveltejs/ai-tools.git
synced 2026-07-03 11:12:28 +08:00
feat: add .$ property access autofixer
This commit is contained in:
@@ -109,7 +109,7 @@ describe('add_autofixers_issues', () => {
|
||||
});
|
||||
|
||||
describe.each([{ method: 'set' }, { method: 'update' }])(
|
||||
'set_or_update_state ($method)',
|
||||
'wrong_property_access_state ($method)',
|
||||
({ method }) => {
|
||||
it(`should add suggestions when using .${method}() on a stateful variable with a literal init`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
@@ -203,6 +203,87 @@ describe('add_autofixers_issues', () => {
|
||||
},
|
||||
);
|
||||
|
||||
describe.each([{ property: '$' }])(
|
||||
'wrong_property_access_state property ($property)',
|
||||
async ({ property }) => {
|
||||
it(`should add suggestions when reading .${property} on a stateful variable with a literal init`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
const count = $state(0);
|
||||
function read_count() {
|
||||
count.${property};
|
||||
}
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are trying to read the stateful variable "count" using "${property}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them.`,
|
||||
);
|
||||
});
|
||||
|
||||
it(`should add suggestions when reading .${property} on a stateful variable with an array init`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
const count = $state([1]);
|
||||
function read_count() {
|
||||
count.${property};
|
||||
}
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are trying to read the stateful variable "count" using "${property}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them.`,
|
||||
);
|
||||
});
|
||||
|
||||
it(`should add suggestions when reading .${property} on a stateful variable with conditional if it's not sure if the property could actually be present on the variable ($state({}))`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
const count = $state({ value: 0 });
|
||||
function read_count() {
|
||||
count.${property};
|
||||
}
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are trying to read the stateful variable "count" using "${property}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them. However I can't verify if "count" is a state variable of an object or a class with a "${property}" property on it. Please verify that before updating the code to use a normal access`,
|
||||
);
|
||||
});
|
||||
|
||||
it(`should add suggestions when reading .${property} on a stateful variable with conditional if it's not sure if the property could actually be present on the variable ($state(new Class()))`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
const count = $state(new Class());
|
||||
function read_count() {
|
||||
count.${property};
|
||||
}
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are trying to read the stateful variable "count" using "${property}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them. However I can't verify if "count" is a state variable of an object or a class with a "${property}" property on it. Please verify that before updating the code to use a normal access`,
|
||||
);
|
||||
});
|
||||
|
||||
it(`should add suggestions when reading .${property} on a stateful variable with conditional if it's not sure if the property could actually be present on the variable ($state(variable_name))`, () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
const { init } = $props();
|
||||
const count = $state(init);
|
||||
function read_count() {
|
||||
count.${property};
|
||||
}
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are trying to read the stateful variable "count" using "${property}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them. However I can't verify if "count" is a state variable of an object or a class with a "${property}" property on it. Please verify that before updating the code to use a normal access`,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
describe('imported_runes', () => {
|
||||
describe.each([{ source: 'svelte' }, { source: 'svelte/runes' }])(
|
||||
'from "$source"',
|
||||
|
||||
@@ -12,7 +12,7 @@ export type AutofixerState = {
|
||||
export type Autofixer = Visitors<Node | AST.SvelteNode, AutofixerState>;
|
||||
|
||||
export * from './assign-in-effect.js';
|
||||
export * from './set-or-update-state.js';
|
||||
export * from './wrong-property-access-state.js';
|
||||
export * from './imported-runes.js';
|
||||
export * from './derived-with-function.js';
|
||||
export * from './use-runes-instead-of-store.js';
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { Autofixer } from './index.js';
|
||||
import { left_most_id } from '../ast/utils.js';
|
||||
|
||||
const UPDATE_PROPERTIES = ['set', 'update'];
|
||||
const UPDATE_PROPERTIES = new Set(['set', 'update', '$']);
|
||||
const METHODS = new Set(['set', 'update']);
|
||||
|
||||
export const set_or_update_state: Autofixer = {
|
||||
export const wrong_property_access_state: Autofixer = {
|
||||
MemberExpression(node, { state, next, path }) {
|
||||
const parent = path[path.length - 1];
|
||||
let is_property = false;
|
||||
if (
|
||||
parent?.type === 'CallExpression' &&
|
||||
parent.callee === node &&
|
||||
node.property.type === 'Identifier' &&
|
||||
UPDATE_PROPERTIES.includes(node.property.name)
|
||||
((is_property = !METHODS.has(node.property.name)) ||
|
||||
(parent?.type === 'CallExpression' && parent.callee === node)) &&
|
||||
UPDATE_PROPERTIES.has(node.property.name)
|
||||
) {
|
||||
const id = left_most_id(node);
|
||||
if (id) {
|
||||
@@ -22,10 +24,12 @@ export const set_or_update_state: Autofixer = {
|
||||
init?.type === 'CallExpression' &&
|
||||
state.parsed.is_rune(init, ['$state', '$state.raw'])
|
||||
) {
|
||||
let suggestion = `You are trying to update the stateful variable "${id.name}" using "${node.property.name}". stateful variables should be updated with a normal assignment/mutation, do not use methods to update them.`;
|
||||
let suggestion = is_property
|
||||
? `You are trying to read the stateful variable "${id.name}" using "${node.property.name}". stateful variables should be read just by accessing them like normal variable, do not use properties to read them.`
|
||||
: `You are trying to update the stateful variable "${id.name}" using "${node.property.name}". stateful variables should be updated with a normal assignment/mutation, do not use methods to update them.`;
|
||||
const argument = init.arguments[0];
|
||||
if (!argument || (argument.type !== 'Literal' && argument.type !== 'ArrayExpression')) {
|
||||
suggestion += ` However I can't verify if "${id.name}" is a state variable of an object or a class with a "${node.property.name}" method on it. Please verify that before updating the code to use a normal assignment`;
|
||||
suggestion += ` However I can't verify if "${id.name}" is a state variable of an object or a class with a "${node.property.name}" ${is_property ? 'property' : 'method'} on it. Please verify that before updating the code to use a normal ${is_property ? 'access' : 'assignment'}`;
|
||||
}
|
||||
state.output.suggestions.push(suggestion);
|
||||
}
|
||||
Reference in New Issue
Block a user