chore: get more validator tests passing (#10714)

Get more validation tests passing:
- const tag cyclic validation (now runtime, based because of new reactivity system)
- illegal-variable-declaration
- illegal-attribute-character
- remove invalid-reactive-var validation as legacy reactive statements are transformed to functions under the hood, which never escape scope - arguably not completely correct, but will be what the user expects anyway
- invalid-rest-eachblock-binding
- remove edge-case redundant-event-modifier warning because event modifiers are deprecated anyway
- invalid-style-directive-modifier
- invalid-tag-property (now a different error)
- module-script-reactive-declaration
- take comment above script into account when silencing warnings
- invalid-css-declaration
- unused-export-let
- invalid-html-attribute
- illegal-store-subscription
- empty-block
pull/10718/head
Simon H 2 years ago committed by GitHub
parent 622195cc21
commit 881e84f988
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -110,7 +110,8 @@ const css = {
`:global(...) must not contain type or universal selectors when used in a compound selector`,
'invalid-css-selector': () => `Invalid selector`,
'invalid-css-identifier': () => 'Expected a valid CSS identifier',
'invalid-nesting-selector': () => `Nesting selectors can only be used inside a rule`
'invalid-nesting-selector': () => `Nesting selectors can only be used inside a rule`,
'invalid-css-declaration': () => 'Declaration cannot be empty'
};
/** @satisfies {Errors} */
@ -278,7 +279,9 @@ const attributes = {
directive2
)} directive`;
},
'invalid-let-directive-placement': () => 'let directive at invalid position'
'invalid-let-directive-placement': () => 'let directive at invalid position',
'invalid-style-directive-modifier': () =>
`Invalid 'style:' modifier. Valid modifiers are: 'important'`
};
/** @satisfies {Errors} */
@ -330,7 +333,11 @@ const variables = {
`${name} is an illegal variable name. To reference a global variable called ${name}, use globalThis.${name}`,
/** @param {string} name */
'duplicate-declaration': (name) => `'${name}' has already been declared`,
'default-export': () => `A component cannot have a default export`
'default-export': () => `A component cannot have a default export`,
'illegal-variable-declaration': () =>
'Cannot declare same variable name which is imported inside <script context="module">',
'illegal-store-subscription': () =>
'Cannot subscribe to stores that are not declared at the top level of the component'
};
/** @satisfies {Errors} */
@ -435,11 +442,6 @@ const errors = {
// message
// };
// },
// contextual_store: {
// code: 'contextual-store',
// message:
// 'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
// },
// default_export: {
// code: 'default-export',
// message: 'A component cannot have a default export'
@ -448,31 +450,11 @@ const errors = {
// code: 'illegal-declaration',
// message: 'The $ prefix is reserved, and cannot be used for variable and import names'
// },
// illegal_global: /** @param {string} name */ (name) => ({
// code: 'illegal-global',
// message: `${name} is an illegal variable name`
// }),
// illegal_variable_declaration: {
// code: 'illegal-variable-declaration',
// message: 'Cannot declare same variable name which is imported inside <script context="module">'
// },
// invalid_directive_value: {
// code: 'invalid-directive-value',
// message:
// 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)'
// },
// cyclical_const_tags: /** @param {string[]} cycle */ (cycle) => ({
// code: 'cyclical-const-tags',
// message: `Cyclical dependency detected: ${cycle.join(' → ')}`
// }),
// invalid_var_declaration: {
// code: 'invalid_var_declaration',
// message: '"var" scope should not extend outside the reactive block'
// },
// invalid_style_directive_modifier: /** @param {string} valid */ (valid) => ({
// code: 'invalid-style-directive-modifier',
// message: `Valid modifiers for style directives are: ${valid}`
// })
};
// interface is duplicated between here (used internally) and ./interfaces.js

@ -476,6 +476,10 @@ function read_declaration(parser) {
const value = read_value(parser);
if (!value && !property.startsWith('--')) {
error(parser.index, 'invalid-css-declaration');
}
const end = parser.index;
if (!parser.match('}')) {

@ -268,22 +268,41 @@ export default function tag(parser) {
if (name === 'script') {
const content = read_script(parser, start, element.attributes);
if (content) {
if (content.context === 'module') {
if (current.module) error(start, 'duplicate-script-element');
current.module = content;
} else {
if (current.instance) error(start, 'duplicate-script-element');
current.instance = content;
/** @type {import('#compiler').Comment | null} */
let prev_comment = null;
for (let i = current.fragment.nodes.length - 1; i >= 0; i--) {
const node = current.fragment.nodes[i];
if (i === current.fragment.nodes.length - 1 && node.end !== start) {
break;
}
if (node.type === 'Comment') {
prev_comment = node;
break;
} else if (node.type !== 'Text' || node.data.trim()) {
break;
}
}
if (prev_comment) {
// We take advantage of the fact that the root will never have leadingComments set,
// and set the previous comment to it so that the warning mechanism can later
// inspect the root and see if there was a html comment before it silencing specific warnings.
content.content.leadingComments = [{ type: 'Line', value: prev_comment.data }];
}
if (content.context === 'module') {
if (current.module) error(start, 'duplicate-script-element');
current.module = content;
} else {
if (current.instance) error(start, 'duplicate-script-element');
current.instance = content;
}
} else {
const content = read_style(parser, start, element.attributes);
if (content) {
if (current.css) error(start, 'duplicate-style-element');
current.css = content;
}
if (current.css) error(start, 'duplicate-style-element');
current.css = content;
}
return;
}

@ -300,6 +300,25 @@ export function analyze_component(root, options) {
declaration.initial.source.value === 'svelte/store'
))
) {
let is_nested_store_subscription = false;
for (const reference of references) {
for (let i = reference.path.length - 1; i >= 0; i--) {
const scope =
scopes.get(reference.path[i]) ||
module.scopes.get(reference.path[i]) ||
instance.scopes.get(reference.path[i]);
if (scope) {
const owner = scope?.owner(store_name);
is_nested_store_subscription =
!!owner && owner !== module.scope && owner !== instance.scope;
break;
}
}
}
if (is_nested_store_subscription) {
error(references[0].node, 'illegal-store-subscription');
}
if (options.runes !== false) {
if (declaration === null && /[a-z]/.test(store_name[0])) {
error(references[0].node, 'illegal-global', name);
@ -442,6 +461,17 @@ export function analyze_component(root, options) {
);
}
for (const [name, binding] of instance.scope.declarations) {
if (binding.kind === 'prop' && binding.node.name !== '$$props') {
const references = binding.references.filter(
(r) => r.node !== binding.node && r.path.at(-1)?.type !== 'ExportSpecifier'
);
if (!references.length && !instance.scope.declarations.has(`$${name}`)) {
warn(warnings, binding.node, [], 'unused-export-let', name);
}
}
}
analysis.reactive_statements = order_reactive_statements(analysis.reactive_statements);
}
@ -590,6 +620,17 @@ const legacy_scope_tweaker = {
state.reactive_statements.set(node, reactive_statement);
// Ideally this would be in the validation file, but that isn't possible because this visitor
// calls "next" before setting the reactive statements.
if (
reactive_statement.dependencies.size &&
[...reactive_statement.dependencies].every(
(d) => d.scope === state.analysis.module.scope && d.declaration_kind !== 'const'
)
) {
warn(state.analysis.warnings, node, path, 'module-script-reactive-declaration');
}
if (
node.body.type === 'ExpressionStatement' &&
node.body.expression.type === 'AssignmentExpression'
@ -710,7 +751,8 @@ const legacy_scope_tweaker = {
if (
binding.kind === 'state' ||
binding.kind === 'frozen_state' ||
(binding.kind === 'normal' && binding.declaration_kind === 'let')
(binding.kind === 'normal' &&
(binding.declaration_kind === 'let' || binding.declaration_kind === 'var'))
) {
binding.kind = 'prop';
if (specifier.exported.name !== specifier.local.name) {

@ -49,8 +49,12 @@ function validate_component(node, context) {
error(attribute, 'invalid-event-modifier');
}
if (attribute.type === 'Attribute' && attribute.name === 'slot') {
validate_slot_attribute(context, attribute);
if (attribute.type === 'Attribute') {
validate_attribute_name(attribute, context);
if (attribute.name === 'slot') {
validate_slot_attribute(context, attribute);
}
}
}
@ -61,6 +65,11 @@ function validate_component(node, context) {
});
}
const react_attributes = new Map([
['className', 'class'],
['htmlFor', 'for']
]);
/**
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
@ -105,6 +114,20 @@ function validate_element(node, context) {
if (attribute.name === 'is' && context.state.options.namespace !== 'foreign') {
warn(context.state.analysis.warnings, attribute, context.path, 'avoid-is');
}
const correct_name = react_attributes.get(attribute.name);
if (correct_name) {
warn(
context.state.analysis.warnings,
attribute,
context.path,
'invalid-html-attribute',
attribute.name,
correct_name
);
}
validate_attribute_name(attribute, context);
} else if (attribute.type === 'AnimateDirective') {
const parent = context.path.at(-2);
if (parent?.type !== 'EachBlock') {
@ -166,6 +189,21 @@ function validate_element(node, context) {
}
}
/**
* @param {import('#compiler').Attribute} attribute
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
*/
function validate_attribute_name(attribute, context) {
if (
attribute.name.includes(':') &&
!attribute.name.startsWith('xmlns:') &&
!attribute.name.startsWith('xlink:') &&
!attribute.name.startsWith('xml:')
) {
warn(context.state.analysis.warnings, attribute, context.path, 'illegal-attribute-character');
}
}
/**
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
* @param {import('#compiler').Attribute} attribute
@ -231,6 +269,19 @@ function validate_slot_attribute(context, attribute) {
}
}
/**
* @param {import('#compiler').Fragment | null | undefined} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
*/
function validate_block_not_empty(node, context) {
if (!node) return;
// Assumption: If the block has zero elements, someone's in the middle of typing it out,
// so don't warn in that case because it would be distracting.
if (node.nodes.length === 1 && node.nodes[0].type === 'Text' && !node.nodes[0].raw.trim()) {
warn(context.state.analysis.warnings, node.nodes[0], context.path, 'empty-block');
}
}
/**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>}
*/
@ -277,13 +328,24 @@ const validation = {
// TODO handle mutations of non-state/props in runes mode
}
const binding = context.state.scope.get(left.name);
if (node.name === 'group') {
const binding = context.state.scope.get(left.name);
if (!binding) {
error(node, 'INTERNAL', 'Cannot find declaration for bind:group');
}
}
if (binding?.kind === 'each' && binding.metadata?.inside_rest) {
warn(
context.state.analysis.warnings,
binding.node,
context.path,
'invalid-rest-eachblock-binding',
binding.node.name
);
}
const parent = context.path.at(-1);
if (
@ -521,8 +583,28 @@ const validation = {
);
}
},
SnippetBlock(node, { path }) {
IfBlock(node, context) {
validate_block_not_empty(node.consequent, context);
validate_block_not_empty(node.alternate, context);
},
EachBlock(node, context) {
validate_block_not_empty(node.body, context);
validate_block_not_empty(node.fallback, context);
},
AwaitBlock(node, context) {
validate_block_not_empty(node.pending, context);
validate_block_not_empty(node.then, context);
validate_block_not_empty(node.catch, context);
},
KeyBlock(node, context) {
validate_block_not_empty(node.fragment, context);
},
SnippetBlock(node, context) {
validate_block_not_empty(node.body, context);
if (node.expression.name !== 'children') return;
const { path } = context;
const parent = path.at(-2);
if (!parent) return;
if (
@ -539,6 +621,11 @@ const validation = {
}
}
},
StyleDirective(node) {
if (node.modifiers.length > 1 || (node.modifiers.length && node.modifiers[0] !== 'important')) {
error(node, 'invalid-style-directive-modifier');
}
},
SvelteHead(node) {
const attribute = node.attributes[0];
if (attribute) {
@ -622,6 +709,8 @@ const validation = {
export const validation_legacy = merge(validation, a11y_validators, {
VariableDeclarator(node, { state }) {
ensure_no_module_import_conflict(node, state);
if (node.init?.type !== 'CallExpression') return;
const callee = node.init.callee;
@ -732,6 +821,22 @@ function validate_call_expression(node, scope, path) {
}
}
/**
* @param {import('estree').VariableDeclarator} node
* @param {import('./types.js').AnalysisState} state
*/
function ensure_no_module_import_conflict(node, state) {
const ids = extract_identifiers(node.id);
for (const id of ids) {
if (
state.scope === state.analysis.instance.scope &&
state.analysis.module.scope.get(id.name)?.declaration_kind === 'import'
) {
error(node.id, 'illegal-variable-declaration');
}
}
}
/**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>}
*/
@ -912,6 +1017,8 @@ export const validation_runes = merge(validation, a11y_validators, {
next({ ...state });
},
VariableDeclarator(node, { state, path }) {
ensure_no_module_import_conflict(node, state);
const init = node.init;
const rune = get_rune(init, state.scope);

@ -1800,6 +1800,12 @@ export const template_visitors = {
)
)
);
// we need to eagerly evaluate the expression in order to hit any
// 'Cannot access x before initialization' errors
if (state.options.dev) {
state.init.push(b.stmt(b.call('$.get', declaration.id)));
}
} else {
const identifiers = extract_identifiers(declaration.id);
const tmp = b.id(state.scope.generate('computed_const'));
@ -1829,6 +1835,12 @@ export const template_visitors = {
b.const(tmp, b.call(state.options.runes ? '$.derived' : '$.derived_safe_equal', fn))
);
// we need to eagerly evaluate the expression in order to hit any
// 'Cannot access x before initialization' errors
if (state.options.dev) {
state.init.push(b.stmt(b.call('$.get', tmp)));
}
for (const node of identifiers) {
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.name));
binding.expression = b.member(b.call('$.get', tmp), node);

@ -14,7 +14,7 @@ export class Scope {
* The immediate parent scope
* @type {Scope | null}
*/
#parent;
parent;
/**
* Whether or not `var` declarations are contained by this scope
@ -56,7 +56,7 @@ export class Scope {
*/
constructor(root, parent, porous) {
this.root = root;
this.#parent = parent;
this.parent = parent;
this.#porous = porous;
this.function_depth = parent ? parent.function_depth + (porous ? 0 : 1) : 0;
}
@ -83,13 +83,13 @@ export class Scope {
error(node, 'invalid-dollar-prefix');
}
if (this.#parent) {
if (this.parent) {
if (declaration_kind === 'var' && this.#porous) {
return this.#parent.declare(node, kind, declaration_kind);
return this.parent.declare(node, kind, declaration_kind);
}
if (declaration_kind === 'import') {
return this.#parent.declare(node, kind, declaration_kind, initial);
return this.parent.declare(node, kind, declaration_kind, initial);
}
}
@ -112,7 +112,8 @@ export class Scope {
prop_alias: null,
expression: null,
mutation: null,
reassigned: false
reassigned: false,
metadata: null
};
this.declarations.set(node.name, binding);
this.root.conflicts.add(node.name);
@ -129,7 +130,7 @@ export class Scope {
*/
generate(preferred_name) {
if (this.#porous) {
return /** @type {Scope} */ (this.#parent).generate(preferred_name);
return /** @type {Scope} */ (this.parent).generate(preferred_name);
}
preferred_name = preferred_name.replace(/[^a-zA-Z0-9_$]/g, '_').replace(/^[0-9]/, '_');
@ -155,7 +156,7 @@ export class Scope {
* @returns {import('#compiler').Binding | null}
*/
get(name) {
return this.declarations.get(name) ?? this.#parent?.get(name) ?? null;
return this.declarations.get(name) ?? this.parent?.get(name) ?? null;
}
/**
@ -175,7 +176,7 @@ export class Scope {
* @returns {Scope | null}
*/
owner(name) {
return this.declarations.has(name) ? this : this.#parent && this.#parent.owner(name);
return this.declarations.has(name) ? this : this.parent && this.parent.owner(name);
}
/**
@ -193,8 +194,8 @@ export class Scope {
const binding = this.declarations.get(node.name);
if (binding) {
binding.references.push({ node, path });
} else if (this.#parent) {
this.#parent.reference(node, path);
} else if (this.parent) {
this.parent.reference(node, path);
} else {
// no binding was found, and this is the top level scope,
// which means this is a global
@ -534,11 +535,31 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
// declarations
for (const id of extract_identifiers(node.context)) {
scope.declare(id, 'each', 'const');
const binding = scope.declare(id, 'each', 'const');
let inside_rest = false;
let is_rest_id = false;
walk(node.context, null, {
Identifier(node) {
if (inside_rest && node === id) {
is_rest_id = true;
}
},
RestElement(_, { next }) {
const prev = inside_rest;
inside_rest = true;
next();
inside_rest = prev;
}
});
binding.metadata = { inside_rest: is_rest_id };
}
if (node.context.type !== 'Identifier') {
scope.declare(b.id('$$item'), 'derived', 'synthetic');
}
// Visit to pick up references from default initializers
visit(node.context, { scope });
if (node.index) {
const is_keyed =

@ -288,6 +288,11 @@ export interface Binding {
expression: Expression | ((id: Identifier) => Expression) | null;
/** If this is set, all mutations should use this expression */
mutation: ((assignment: AssignmentExpression, context: Context<any, any>) => Expression) | null;
/** Additional metadata, varies per binding type */
metadata: {
/** `true` if is (inside) a rest parameter */
inside_rest?: boolean;
} | null;
}
export * from './template.js';

@ -13,12 +13,9 @@ import type {
ObjectExpression,
Pattern,
Program,
SpreadElement,
CallExpression,
ChainExpression,
SimpleCallExpression
} from 'estree';
import type { Atrule, Rule } from './css';
export interface BaseNode {
type: string;

@ -15,7 +15,15 @@ const attributes = {
'avoid-is': () => 'The "is" attribute is not supported cross-browser and should be avoided',
/** @param {string} name */
'global-event-reference': (name) =>
`You are referencing globalThis.${name}. Did you forget to declare a variable with that name?`
`You are referencing globalThis.${name}. Did you forget to declare a variable with that name?`,
'illegal-attribute-character': () =>
"Attributes should not contain ':' characters to prevent ambiguity with Svelte directives",
/**
* @param {string} wrong
* @param {string} right
*/
'invalid-html-attribute': (wrong, right) =>
`'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?`
};
/** @satisfies {Warnings} */
@ -195,7 +203,10 @@ const a11y = {
/** @satisfies {Warnings} */
const state = {
'static-state-reference': () =>
`State referenced in its own scope will never update. Did you mean to reference it inside a closure?`
`State referenced in its own scope will never update. Did you mean to reference it inside a closure?`,
/** @param {string} name */
'invalid-rest-eachblock-binding': (name) =>
`The rest operator (...) will create a new object and binding '${name}' with the original object will not work`
};
/** @satisfies {Warnings} */
@ -214,7 +225,16 @@ const components = {
const legacy = {
'no-reactive-declaration': () =>
`Reactive declarations only exist at the top level of the instance script`
`Reactive declarations only exist at the top level of the instance script`,
'module-script-reactive-declaration': () =>
'All dependencies of the reactive declaration are declared in a module script and will not be reactive',
/** @param {string} name */
'unused-export-let': (name) =>
`Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``
};
const block = {
'empty-block': () => 'Empty block'
};
/** @satisfies {Warnings} */
@ -226,7 +246,8 @@ const warnings = {
...performance,
...state,
...components,
...legacy
...legacy,
...block
};
/** @typedef {typeof warnings} AllWarnings */
@ -234,7 +255,7 @@ const warnings = {
/**
* @template {keyof AllWarnings} T
* @param {import('./phases/types').RawWarning[]} array the array to push the warning to, if not ignored
* @param {{ start?: number, end?: number, parent?: import('#compiler').SvelteNode | null, leadingComments?: import('estree').Comment[] } | null} node the node related to the warning
* @param {{ start?: number, end?: number, type?: string, parent?: import('#compiler').SvelteNode | null, leadingComments?: import('estree').Comment[] } | null} node the node related to the warning
* @param {import('#compiler').SvelteNode[]} path the path to the node, so that we can traverse upwards to find svelte-ignore comments
* @param {T} code the warning code
* @param {Parameters<AllWarnings[T]>} args the arguments to pass to the warning function
@ -249,6 +270,13 @@ export function warn(array, node, path, code, ...args) {
/** @type {string[]} */
const ignores = [];
if (node) {
// comments inside JavaScript (estree)
if ('leadingComments' in node) {
ignores.push(...extract_svelte_ignore_from_comments(node));
}
}
for (let i = path.length - 1; i >= 0; i--) {
const current = path[i];

@ -1,10 +1,8 @@
import { test } from '../../test';
export default test({
skip: true,
error: {
code: '',
message:
'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
code: 'illegal-store-subscription',
message: 'Cannot subscribe to stores that are not declared at the top level of the component'
}
});

@ -1,9 +1,8 @@
import { test } from '../../test';
export default test({
skip: true,
error: {
code: '',
message: 'The $ prefix is reserved, and cannot be used for variable and import names'
code: 'invalid-dollar-prefix',
message: 'The $ prefix is reserved, and cannot be used for variables and imports'
}
});

@ -1,10 +1,8 @@
import { test } from '../../test';
export default test({
skip: true,
error: {
code: '',
message:
'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
code: 'illegal-store-subscription',
message: 'Cannot subscribe to stores that are not declared at the top level of the component'
}
});

@ -1,10 +1,8 @@
import { test } from '../../test';
export default test({
skip: true,
error: {
code: '',
message:
'Stores must be declared at the top level of the component (this may change in a future version of Svelte)'
code: 'illegal-store-subscription',
message: 'Cannot subscribe to stores that are not declared at the top level of the component'
}
});

@ -44,6 +44,7 @@
"column": 0
}
},
"leadingComments": [{ "type": "Line", "value": "should not error out" }],
"body": [
{
"type": "VariableDeclaration",

@ -0,0 +1,9 @@
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
error: "Cannot access 'c' before initialization"
});

@ -1,8 +1,8 @@
<script>
export let array;
export let array = [1];
</script>
{#each array as a}
{@const b = a + c}
{@const c = b + a}
{/each}
{/each}

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

@ -1,8 +0,0 @@
[
{
"code": "cyclical-const-tags",
"message": "Cyclical dependency detected: b → c → b",
"start": { "line": 6, "column": 2 },
"end": { "line": 6, "column": 20 }
}
]

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

@ -2,8 +2,17 @@
let things = [];
</script>
<!-- ok (zero elements very likely means someone's in the middle of typing) -->
{#each things as thing}{/each}
{#if true}{/if}
{#key things}x{/key}
{#await promise}{things}
{/await}
<!-- invalid -->
{#each things as thing}
{/each}
{#each things as thing}{/each}
{#if true} {/if}
{#key things} {/key}
{#await promise} {/await}

@ -2,25 +2,25 @@
{
"code": "empty-block",
"message": "Empty block",
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 7,
"column": 7
}
"start": { "line": 13, "column": 23 },
"end": { "line": 15, "column": 0 }
},
{
"code": "empty-block",
"message": "Empty block",
"start": {
"line": 9,
"column": 0
},
"end": {
"line": 9,
"column": 30
}
"start": { "line": 16, "column": 10 },
"end": { "line": 16, "column": 11 }
},
{
"code": "empty-block",
"message": "Empty block",
"start": { "line": 17, "column": 13 },
"end": { "line": 17, "column": 14 }
},
{
"code": "empty-block",
"message": "Empty block",
"start": { "line": 18, "column": 16 },
"end": { "line": 18, "column": 17 }
}
]

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

@ -1,12 +0,0 @@
<script>
function handleTouchstart() {
// ...
}
function handleClick() {
// ...
}
</script>
<button on:click|passive="{handleClick}"></button>
<div on:touchstart|passive="{handleTouchstart}"></div>

@ -1,26 +0,0 @@
[
{
"message": "The passive modifier only works with wheel and touch events",
"code": "redundant-event-modifier",
"start": {
"line": 11,
"column": 8
},
"end": {
"line": 11,
"column": 40
}
},
{
"message": "Touch event handlers that don't use the 'event' object are passive by default",
"code": "redundant-event-modifier",
"start": {
"line": 12,
"column": 5
},
"end": {
"line": 12,
"column": 47
}
}
]

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

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

@ -3,12 +3,12 @@
"code": "illegal-variable-declaration",
"message": "Cannot declare same variable name which is imported inside <script context=\"module\">",
"start": {
"line": 6,
"column": 1
"line": 12,
"column": 5
},
"end": {
"line": 6,
"column": 9
"line": 12,
"column": 8
}
}
]

@ -1,8 +1,14 @@
<script context="module">
import { FOO } from './dummy.svelte';
function ok() {
let FOO;
}
</script>
<script>
function ok() {
let FOO;
}
let FOO;
</script>

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

@ -1,14 +1,14 @@
[
{
"code": "invalid-declaration",
"code": "invalid-css-declaration",
"message": "Declaration cannot be empty",
"start": {
"line": 11,
"column": 0
"line": 5,
"column": 8
},
"end": {
"line": 11,
"column": 0
"line": 5,
"column": 8
}
}
]

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

@ -1,8 +0,0 @@
[
{
"code": "invalid_var_declaration",
"message": "\"var\" scope should not extend outside the reactive block",
"start": { "line": 14, "column": 7 },
"end": { "line": 14, "column": 16 }
}
]

@ -1,20 +0,0 @@
<script>
var a;
var {a, b: [d, f], c}= {a: 1, b: [1, 2], c: 2};
$: {
function one() {
var a = 'a';
function two() {
var a = 'b';
return a;
}
return two();
}
a = one();
for (var i = 0; i<5; i ++ ) {
// Todo
}
}
</script>
<h1>Hello {a}</h1>

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

@ -1,8 +0,0 @@
[
{
"code": "invalid_var_declaration",
"message": "\"var\" scope should not extend outside the reactive block",
"start": { "line": 4, "column": 2 },
"end": { "line": 4, "column": 50 }
}
]

@ -1,8 +0,0 @@
<script>
$: {
let f = 'f';
var {a, b: [c, d], e} = {a: 1, b: [2, 3], e: 4};
}
</script>
<h1>Hello</h1>

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

@ -1,9 +1,9 @@
[
{
"code": "module-script-reactive-declaration",
"message": "\"foo\" is declared in a module script and will not be reactive",
"message": "All dependencies of the reactive declaration are declared in a module script and will not be reactive",
"start": {
"column": 4,
"column": 1,
"line": 5
},
"end": {

@ -1,4 +0,0 @@
import { test } from '../../test';
// TODO this likely works in the new world - remove this warning?
export default test({ skip: true });

@ -1,4 +0,0 @@
import { test } from '../../test';
// TODO this likely works in the new world - remove this warning?
export default test({ skip: true });

@ -1,4 +0,0 @@
import { test } from '../../test';
// TODO this maybe works in the new world - remove this warning?
export default test({ skip: true });

@ -1,4 +0,0 @@
import { test } from '../../test';
// TODO this likely works in the new world - remove this warning?
export default test({ skip: true });

@ -2,8 +2,7 @@
let foo;
</script>
<!-- svelte-ignore unused-export-let module-script-reactive-declaration -->
<!-- svelte-ignore module-script-reactive-declaration -->
<script>
export let unused;
$: reactive = foo;
</script>

@ -3,8 +3,6 @@
</script>
<script>
// svelte-ignore unused-export-let
export let unused;
// svelte-ignore module-script-reactive-declaration
$: reactive = foo;
</script>

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

@ -1,6 +1,6 @@
[
{
"message": "Valid modifiers for style directives are: important",
"message": "Invalid 'style:' modifier. Valid modifiers are: 'important'",
"code": "invalid-style-directive-modifier",
"start": {
"line": 1,

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

@ -1,7 +1,7 @@
[
{
"code": "invalid-tag-property",
"message": "tag name must be two or more words joined by the '-' character",
"message": "tag name must be two or more words joined by the \"-\" character",
"start": {
"line": 1,
"column": 16

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

@ -1,14 +1,14 @@
[
{
"code": "invalid-tag-attribute",
"message": "'tag' must be a string literal",
"code": "invalid-svelte-option-customElement",
"message": "\"customElement\" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: \"open\" | \"none\"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }",
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 24
"column": 34
}
}
]

@ -1 +1 @@
<svelte:options tag={42}/>
<svelte:options customElement={42}/>

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

@ -1,7 +1,7 @@
[
{
"code": "unused-export-let",
"message": "Component_1 has unused export property 'default_value_5'. If it is for external reference only, please consider using `export const default_value_5`",
"message": "Component has unused export property 'default_value_5'. If it is for external reference only, please consider using `export const default_value_5`",
"start": {
"column": 12,
"line": 8
@ -13,7 +13,7 @@
},
{
"code": "unused-export-let",
"message": "Component_1 has unused export property 'default_value_6'. If it is for external reference only, please consider using `export const default_value_6`",
"message": "Component has unused export property 'default_value_6'. If it is for external reference only, please consider using `export const default_value_6`",
"start": {
"column": 12,
"line": 9

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

@ -2,31 +2,31 @@
{
"code": "unused-export-let",
"end": {
"column": 13,
"line": 31
"column": 8,
"line": 28
},
"message": "Component has unused export property 'd2'. If it is for external reference only, please consider using `export const d2`",
"start": {
"column": 11,
"line": 31
"column": 6,
"line": 28
}
},
{
"code": "unused-export-let",
"end": {
"column": 17,
"line": 31
"column": 8,
"line": 29
},
"message": "Component has unused export property 'e2'. If it is for external reference only, please consider using `export const e2`",
"start": {
"column": 15,
"line": 31
"column": 6,
"line": 29
}
},
{
"code": "unused-export-let",
"end": {
"column": 19,
"column": 15,
"line": 32
},
"message": "Component has unused export property 'g2'. If it is for external reference only, please consider using `export const g2`",
@ -38,7 +38,7 @@
{
"code": "unused-export-let",
"end": {
"column": 19,
"column": 15,
"line": 33
},
"message": "Component has unused export property 'h2'. If it is for external reference only, please consider using `export const h2`",
@ -50,7 +50,7 @@
{
"code": "unused-export-let",
"end": {
"column": 26,
"column": 15,
"line": 35
},
"message": "Component has unused export property 'j2'. If it is for external reference only, please consider using `export const j2`",

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

@ -756,6 +756,11 @@ declare module 'svelte/compiler' {
expression: Expression | ((id: Identifier) => Expression) | null;
/** If this is set, all mutations should use this expression */
mutation: ((assignment: AssignmentExpression, context: Context<any, any>) => Expression) | null;
/** Additional metadata, varies per binding type */
metadata: {
/** `true` if is (inside) a rest parameter */
inside_rest?: boolean;
} | null;
}
interface BaseNode_1 {
type: string;
@ -1020,6 +1025,10 @@ declare module 'svelte/compiler' {
constructor(root: ScopeRoot, parent: Scope | null, porous: boolean);
root: ScopeRoot;
/**
* The immediate parent scope
* */
parent: Scope | null;
/**
* A map of every identifier declared by this scope, and all the
* identifiers that reference it

Loading…
Cancel
Save