tighten up rune validation

pull/11327/head
Rich Harris 2 years ago
parent 2458a80522
commit 3f93f4fd26

@ -94,9 +94,21 @@
> `%rune%` must be called with %args%
## rune_invalid_computed_property
> Cannot access a computed property of a rune
## rune_invalid_name
> `%name%` is not a valid rune
## rune_invalid_usage
> Cannot use %rune% rune in non-runes mode
> Cannot use `%rune%` rune in non-runes mode
## rune_missing_parentheses
> Cannot use rune without parentheses
## runes_mode_invalid_import

@ -2,14 +2,6 @@
> Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`
## invalid_bindable_declaration
> Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?
## invalid_props_declaration
> Component properties are declared using `$props()` in runes mode. Did you forget to call the function?
## non_state_reference
> `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates

@ -311,13 +311,41 @@ export function rune_invalid_arguments_length(node, rune, args) {
}
/**
* Cannot use %rune% rune in non-runes mode
* Cannot access a computed property of a rune
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function rune_invalid_computed_property(node) {
e(node, "rune_invalid_computed_property", "Cannot access a computed property of a rune");
}
/**
* `%name%` is not a valid rune
* @param {null | number | NodeLike} node
* @param {string} name
* @returns {never}
*/
export function rune_invalid_name(node, name) {
e(node, "rune_invalid_name", `\`${name}\` is not a valid rune`);
}
/**
* Cannot use `%rune%` rune in non-runes mode
* @param {null | number | NodeLike} node
* @param {string} rune
* @returns {never}
*/
export function rune_invalid_usage(node, rune) {
e(node, "rune_invalid_usage", `Cannot use ${rune} rune in non-runes mode`);
e(node, "rune_invalid_usage", `Cannot use \`${rune}\` rune in non-runes mode`);
}
/**
* Cannot use rune without parentheses
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function rune_missing_parentheses(node) {
e(node, "rune_missing_parentheses", "Cannot use rune without parentheses");
}
/**

@ -1,3 +1,4 @@
import is_reference from 'is-reference';
import {
disallowed_paragraph_contents,
interactive_elements,
@ -18,6 +19,7 @@ import { binding_properties } from '../bindings.js';
import {
ContentEditableBindings,
EventModifiers,
Runes,
SVGElements,
VoidElements
} from '../constants.js';
@ -1054,6 +1056,36 @@ function validate_assignment(node, argument, state) {
}
export const validation_runes = merge(validation, a11y_validators, {
Identifier(node, { path, state }) {
let i = path.length;
let parent = /** @type {import('estree').Expression} */ (path[--i]);
if (
Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
is_reference(node, parent) &&
!state.scope.get(node.name.slice(1))
) {
/** @type {import('estree').Expression} */
let current = node;
let name = node.name;
while (parent.type === 'MemberExpression') {
if (parent.computed) e.rune_invalid_computed_property(parent);
name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`;
current = parent;
parent = /** @type {import('estree').Expression} */ (path[--i]);
if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
e.rune_invalid_name(parent, name);
}
}
if (parent.type !== 'CallExpression') {
e.rune_missing_parentheses(current, name);
}
}
},
LabeledStatement(node, { path }) {
if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return;
e.legacy_reactive_statement_invalid(node);
@ -1112,13 +1144,6 @@ export const validation_runes = merge(validation, a11y_validators, {
const init = node.init;
const rune = get_rune(init, state.scope);
if (rune === null) {
if (init?.type === 'Identifier' && init.name === '$props' && !state.scope.get('props')) {
w.invalid_props_declaration(node);
}
return;
}
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
// TODO some of this is duplicated with above, seems off
@ -1171,15 +1196,6 @@ export const validation_runes = merge(validation, a11y_validators, {
}
}
},
AssignmentPattern(node, { state }) {
if (
node.right.type === 'Identifier' &&
node.right.name === '$bindable' &&
!state.scope.get('bindable')
) {
w.invalid_bindable_declaration(node);
}
},
SlotElement(node, { state }) {
if (!state.analysis.custom_element) {
w.slot_element_deprecated(node);

@ -478,22 +478,6 @@ export function derived_iife(node) {
w(node, "derived_iife", "Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`");
}
/**
* Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?
* @param {null | NodeLike} node
*/
export function invalid_bindable_declaration(node) {
w(node, "invalid_bindable_declaration", "Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?");
}
/**
* Component properties are declared using `$props()` in runes mode. Did you forget to call the function?
* @param {null | NodeLike} node
*/
export function invalid_props_declaration(node) {
w(node, "invalid_props_declaration", "Component properties are declared using `$props()` in runes mode. Did you forget to call the function?");
}
/**
* `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
* @param {null | NodeLike} node

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
error: {
code: 'rune_missing_parentheses',
message: 'Cannot use rune without parentheses'
}
});

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
error: {
code: 'rune_missing_parentheses',
message: 'Cannot use rune without parentheses'
}
});

@ -1,3 +0,0 @@
import { test } from '../../test';
export default test({});

@ -1,14 +0,0 @@
[
{
"code": "invalid_bindable_declaration",
"message": "Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?",
"start": {
"column": 7,
"line": 2
},
"end": {
"column": 20,
"line": 2
}
}
]

@ -1,3 +0,0 @@
import { test } from '../../test';
export default test({});

@ -1,14 +0,0 @@
[
{
"code": "invalid_props_declaration",
"message": "Component properties are declared using `$props()` in runes mode. Did you forget to call the function?",
"start": {
"column": 5,
"line": 2
},
"end": {
"column": 19,
"line": 2
}
}
]
Loading…
Cancel
Save