add codes to validation errors

pull/1340/head
Rich Harris 8 years ago
parent 5a245b2871
commit b86a1edb52

@ -102,7 +102,10 @@ export default class Selector {
while (i-- > 1) { while (i-- > 1) {
const selector = block.selectors[i]; const selector = block.selectors[i];
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
validator.error(`:global(...) must be the first element in a compound selector`, selector); validator.error(selector, {
code: `css-invalid-global`,
message: `:global(...) must be the first element in a compound selector`
});
} }
} }
}); });
@ -120,7 +123,10 @@ export default class Selector {
for (let i = start; i < end; i += 1) { for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) { if (this.blocks[i].global) {
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, this.blocks[i].selectors[0]); validator.error(this.blocks[i].selectors[0], {
code: `css-invalid-global`,
message: `:global(...) can be at the start or end of a selector sequence, but not in the middle`
});
} }
} }
} }

@ -2,6 +2,7 @@ import { locate } from 'locate-character';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
class CompileError extends Error { class CompileError extends Error {
code: string;
loc: { line: number, column: number }; loc: { line: number, column: number };
end: { line: number, column: number }; end: { line: number, column: number };
pos: number; pos: number;
@ -15,6 +16,7 @@ class CompileError extends Error {
export default function error(message: string, props: { export default function error(message: string, props: {
name: string, name: string,
code: string,
source: string, source: string,
filename: string, filename: string,
start: number, start: number,
@ -26,6 +28,7 @@ export default function error(message: string, props: {
const start = locate(props.source, props.start); const start = locate(props.source, props.start);
const end = locate(props.source, props.end || props.start); const end = locate(props.source, props.end || props.start);
error.code = props.code;
error.loc = { line: start.line + 1, column: start.column }; error.loc = { line: start.line + 1, column: start.column };
error.end = { line: end.line + 1, column: end.column }; error.end = { line: end.line + 1, column: end.column };
error.pos = props.start; error.pos = props.start;

@ -108,7 +108,10 @@ export default function validateHtml(validator: Validator, html: Node) {
let message = `'refs.${ref}' does not exist`; let message = `'refs.${ref}' does not exist`;
if (match) message += ` (did you mean 'refs.${match}'?)`; if (match) message += ` (did you mean 'refs.${match}'?)`;
validator.error(message, callee); validator.error(callee, {
code: `missing-ref`,
message
});
} }
}); });
} }

@ -37,12 +37,18 @@ export default function validateElement(
const nameAttribute = node.attributes.find((attribute: Node) => attribute.name === 'name'); const nameAttribute = node.attributes.find((attribute: Node) => attribute.name === 'name');
if (nameAttribute) { if (nameAttribute) {
if (nameAttribute.value.length !== 1 || nameAttribute.value[0].type !== 'Text') { if (nameAttribute.value.length !== 1 || nameAttribute.value[0].type !== 'Text') {
validator.error(`<slot> name cannot be dynamic`, nameAttribute); validator.error(nameAttribute, {
code: `dynamic-slot-name`,
message: `<slot> name cannot be dynamic`
});
} }
const slotName = nameAttribute.value[0].data; const slotName = nameAttribute.value[0].data;
if (slotName === 'default') { if (slotName === 'default') {
validator.error(`default is a reserved word — it cannot be used as a slot name`, nameAttribute); validator.error(nameAttribute, {
code: `invalid-slot-name`,
message: `default is a reserved word — it cannot be used as a slot name`
});
} }
// TODO should duplicate slots be disallowed? Feels like it's more likely to be a // TODO should duplicate slots be disallowed? Feels like it's more likely to be a
@ -64,18 +70,18 @@ export default function validateElement(
if (node.name === 'title') { if (node.name === 'title') {
if (node.attributes.length > 0) { if (node.attributes.length > 0) {
validator.error( validator.error(node.attributes[0], {
`<title> cannot have attributes`, code: `illegal-attribute`,
node.attributes[0] message: `<title> cannot have attributes`
); });
} }
node.children.forEach(child => { node.children.forEach(child => {
if (child.type !== 'Text' && child.type !== 'MustacheTag') { if (child.type !== 'Text' && child.type !== 'MustacheTag') {
validator.error( validator.error(child, {
`<title> can only contain text and {{tags}}`, code: 'illegal-structure',
child message: `<title> can only contain text and {{tags}}`
); });
} }
}); });
} }
@ -99,10 +105,10 @@ export default function validateElement(
node.name !== 'textarea' && node.name !== 'textarea' &&
node.name !== 'select' node.name !== 'select'
) { ) {
validator.error( validator.error(attribute, {
`'value' is not a valid binding on <${node.name}> elements`, code: `invalid-binding`,
attribute message: `'value' is not a valid binding on <${node.name}> elements`
); });
} }
if (node.name === 'select') { if (node.name === 'select') {
@ -111,43 +117,43 @@ export default function validateElement(
); );
if (attribute && isDynamic(attribute)) { if (attribute && isDynamic(attribute)) {
validator.error( validator.error(attribute, {
`'multiple' attribute cannot be dynamic if select uses two-way binding`, code: `dynamic-multiple-attribute`,
attribute message: `'multiple' attribute cannot be dynamic if select uses two-way binding`
); });
} }
} else { } else {
checkTypeAttribute(validator, node); checkTypeAttribute(validator, node);
} }
} else if (name === 'checked' || name === 'indeterminate') { } else if (name === 'checked' || name === 'indeterminate') {
if (node.name !== 'input') { if (node.name !== 'input') {
validator.error( validator.error(attribute, {
`'${name}' is not a valid binding on <${node.name}> elements`, code: `invalid-binding`,
attribute message: `'${name}' is not a valid binding on <${node.name}> elements`
); });
} }
if (checkTypeAttribute(validator, node) !== 'checkbox') { if (checkTypeAttribute(validator, node) !== 'checkbox') {
validator.error( validator.error(attribute, {
`'${name}' binding can only be used with <input type="checkbox">`, code: `invalid-binding`,
attribute message: `'${name}' binding can only be used with <input type="checkbox">`
); });
} }
} else if (name === 'group') { } else if (name === 'group') {
if (node.name !== 'input') { if (node.name !== 'input') {
validator.error( validator.error(attribute, {
`'group' is not a valid binding on <${node.name}> elements`, code: `invalid-binding`,
attribute message: `'group' is not a valid binding on <${node.name}> elements`
); });
} }
const type = checkTypeAttribute(validator, node); const type = checkTypeAttribute(validator, node);
if (type !== 'checkbox' && type !== 'radio') { if (type !== 'checkbox' && type !== 'radio') {
validator.error( validator.error(attribute, {
`'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`, code: `invalid-binding`,
attribute message: `'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`
); });
} }
} else if ( } else if (
name === 'currentTime' || name === 'currentTime' ||
@ -159,23 +165,26 @@ export default function validateElement(
name === 'volume' name === 'volume'
) { ) {
if (node.name !== 'audio' && node.name !== 'video') { if (node.name !== 'audio' && node.name !== 'video') {
validator.error( validator.error(attribute, {
`'${name}' binding can only be used with <audio> or <video>`, code: `invalid-binding`,
attribute message: `'${name}' binding can only be used with <audio> or <video>`
); });
} }
} else { } else {
validator.error( validator.error(attribute, {
`'${attribute.name}' is not a valid binding`, code: `invalid-binding`,
attribute message: `'${attribute.name}' is not a valid binding`
); });
} }
} else if (attribute.type === 'EventHandler') { } else if (attribute.type === 'EventHandler') {
validator.used.events.add(attribute.name); validator.used.events.add(attribute.name);
validateEventHandler(validator, attribute, refCallees); validateEventHandler(validator, attribute, refCallees);
} else if (attribute.type === 'Transition') { } else if (attribute.type === 'Transition') {
if (isComponent) { if (isComponent) {
validator.error(`Transitions can only be applied to DOM elements, not components`, attribute); validator.error(attribute, {
code: `invalid-transition`,
message: `Transitions can only be applied to DOM elements, not components`
});
} }
validator.used.transitions.add(attribute.name); validator.used.transitions.add(attribute.name);
@ -183,31 +192,31 @@ export default function validateElement(
const bidi = attribute.intro && attribute.outro; const bidi = attribute.intro && attribute.outro;
if (hasTransition) { if (hasTransition) {
if (bidi) if (bidi) {
validator.error( validator.error(attribute, {
`An element can only have one 'transition' directive`, code: `duplicate-transition`,
attribute message: `An element can only have one 'transition' directive`
); });
validator.error( }
`An element cannot have both a 'transition' directive and an '${attribute.intro
? 'in' validator.error(attribute, {
: 'out'}' directive`, code: `duplicate-transition`,
attribute message: `An element cannot have both a 'transition' directive and an '${attribute.intro ? 'in' : 'out'}' directive`
); });
} }
if ((hasIntro && attribute.intro) || (hasOutro && attribute.outro)) { if ((hasIntro && attribute.intro) || (hasOutro && attribute.outro)) {
if (bidi) if (bidi) {
validator.error( validator.error(attribute, {
`An element cannot have both an '${hasIntro code: `duplicate-transition`,
? 'in' message: `An element cannot have both an '${hasIntro ? 'in' : 'out'}' directive and a 'transition' directive`
: 'out'}' directive and a 'transition' directive`, });
attribute }
);
validator.error( validator.error(attribute, {
`An element can only have one '${hasIntro ? 'in' : 'out'}' directive`, code: `duplicate-transition`,
attribute message: `An element can only have one '${hasIntro ? 'in' : 'out'}' directive`
); });
} }
if (attribute.intro) hasIntro = true; if (attribute.intro) hasIntro = true;
@ -215,18 +224,18 @@ export default function validateElement(
if (bidi) hasTransition = true; if (bidi) hasTransition = true;
if (!validator.transitions.has(attribute.name)) { if (!validator.transitions.has(attribute.name)) {
validator.error( validator.error(attribute, {
`Missing transition '${attribute.name}'`, code: `missing-transition`,
attribute message: `Missing transition '${attribute.name}'`
); });
} }
} else if (attribute.type === 'Attribute') { } else if (attribute.type === 'Attribute') {
if (attribute.name === 'value' && node.name === 'textarea') { if (attribute.name === 'value' && node.name === 'textarea') {
if (node.children.length) { if (node.children.length) {
validator.error( validator.error(attribute, {
`A <textarea> can have either a value attribute or (equivalently) child content, but not both`, code: `textarea-duplicate-value`,
attribute message: `A <textarea> can have either a value attribute or (equivalently) child content, but not both`
); });
} }
} }
@ -235,16 +244,19 @@ export default function validateElement(
} }
} else if (attribute.type === 'Action') { } else if (attribute.type === 'Action') {
if (isComponent) { if (isComponent) {
validator.error(`Actions can only be applied to DOM elements, not components`, attribute); validator.error(attribute, {
code: `invalid-action`,
message: `Actions can only be applied to DOM elements, not components`
});
} }
validator.used.actions.add(attribute.name); validator.used.actions.add(attribute.name);
if (!validator.actions.has(attribute.name)) { if (!validator.actions.has(attribute.name)) {
validator.error( validator.error(attribute, {
`Missing action '${attribute.name}'`, code: `missing-action`,
attribute message: `Missing action '${attribute.name}'`
); });
} }
} }
}); });
@ -257,14 +269,17 @@ function checkTypeAttribute(validator: Validator, node: Node) {
if (!attribute) return null; if (!attribute) return null;
if (attribute.value === true) { if (attribute.value === true) {
validator.error(`'type' attribute must be specified`, attribute); validator.error(attribute, {
code: `missing-type`,
message: `'type' attribute must be specified`
});
} }
if (isDynamic(attribute)) { if (isDynamic(attribute)) {
validator.error( validator.error(attribute, {
`'type' attribute cannot be dynamic if input uses two-way binding`, code: `invalid-type`,
attribute message: `'type' attribute cannot be dynamic if input uses two-way binding`
); });
} }
return attribute.value[0].data; return attribute.value[0].data;
@ -272,10 +287,10 @@ function checkTypeAttribute(validator: Validator, node: Node) {
function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, stack: Node[]) { function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, stack: Node[]) {
if (isDynamic(attribute)) { if (isDynamic(attribute)) {
validator.error( validator.error(attribute, {
`slot attribute cannot have a dynamic value`, code: `invalid-slot-attribute`,
attribute message: `slot attribute cannot have a dynamic value`
); });
} }
let i = stack.length; let i = stack.length;
@ -289,11 +304,17 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s
if (parent.type === 'IfBlock' || parent.type === 'EachBlock') { if (parent.type === 'IfBlock' || parent.type === 'EachBlock') {
const message = `Cannot place slotted elements inside an ${parent.type === 'IfBlock' ? 'if' : 'each'}-block`; const message = `Cannot place slotted elements inside an ${parent.type === 'IfBlock' ? 'if' : 'each'}-block`;
validator.error(message, attribute); validator.error(attribute, {
code: `invalid-slotted-content`,
message
});
} }
} }
validator.error(`Element with a slot='...' attribute must be a descendant of a component or custom element`, attribute); validator.error(attribute, {
code: `invalid-slotted-content`,
message: `Element with a slot='...' attribute must be a descendant of a component or custom element`
});
} }
function isDynamic(attribute: Node) { function isDynamic(attribute: Node) {

@ -16,7 +16,10 @@ export default function validateEventHandlerCallee(
const { callee, type } = attribute.expression; const { callee, type } = attribute.expression;
if (type !== 'CallExpression') { if (type !== 'CallExpression') {
validator.error(`Expected a call expression`, attribute.expression); validator.error(attribute.expression, {
code: `invalid-event-handler`,
message: `Expected a call expression`
});
} }
const { name } = flattenReference(callee); const { name } = flattenReference(callee);

@ -4,7 +4,10 @@ import { Node } from '../../interfaces';
export default function validateHead(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) { export default function validateHead(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) {
if (node.attributes.length) { if (node.attributes.length) {
validator.error(`<:Head> should not have any attributes or directives`, node); validator.error(node.attributes[0], {
code: `invalid-attribute`,
message: `<:Head> should not have any attributes or directives`
});
} }
// TODO ensure only valid elements are included here // TODO ensure only valid elements are included here

@ -21,12 +21,10 @@ export default function validateWindow(validator: Validator, node: Node, refs: M
if (attribute.value.type !== 'Identifier') { if (attribute.value.type !== 'Identifier') {
const { parts } = flattenReference(attribute.value); const { parts } = flattenReference(attribute.value);
validator.error( validator.error(attribute.value, {
`Bindings on <:Window/> must be to top-level properties, e.g. '${parts[ code: `invalid-binding`,
parts.length - 1 message: `Bindings on <:Window/> must be to top-level properties, e.g. '${parts[parts.length - 1]}' rather than '${parts.join('.')}'`
]}' rather than '${parts.join('.')}'`, });
attribute.value
);
} }
if (!~validBindings.indexOf(attribute.name)) { if (!~validBindings.indexOf(attribute.name)) {
@ -39,15 +37,15 @@ export default function validateWindow(validator: Validator, node: Node, refs: M
const message = `'${attribute.name}' is not a valid binding on <:Window>`; const message = `'${attribute.name}' is not a valid binding on <:Window>`;
if (match) { if (match) {
validator.error( validator.error(attribute, {
`${message} (did you mean '${match}'?)`, code: `invalid-binding`,
attribute message: `${message} (did you mean '${match}'?)`
); });
} else { } else {
validator.error( validator.error(attribute, {
`${message} — valid bindings are ${list(validBindings)}`, code: `invalid-binding`,
attribute message: `${message} — valid bindings are ${list(validBindings)}`
); });
} }
} }
} else if (attribute.type === 'EventHandler') { } else if (attribute.type === 'EventHandler') {

@ -59,9 +59,10 @@ export class Validator {
}; };
} }
error(message: string, pos: { start: number, end: number }) { error(pos: { start: number, end: number }, { code, message } : { code: string, message: string }) {
error(message, { error(message, {
name: 'ValidationError', name: 'ValidationError',
code,
source: this.source, source: this.source,
start: pos.start, start: pos.start,
end: pos.end, end: pos.end,

@ -14,15 +14,18 @@ export default function validateJs(validator: Validator, js: Node) {
js.content.body.forEach((node: Node) => { js.content.body.forEach((node: Node) => {
// check there are no named exports // check there are no named exports
if (node.type === 'ExportNamedDeclaration') { if (node.type === 'ExportNamedDeclaration') {
validator.error(`A component can only have a default export`, node); validator.error(node, {
code: `named-export`,
message: `A component can only have a default export`
});
} }
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
if (node.declaration.type !== 'ObjectExpression') { if (node.declaration.type !== 'ObjectExpression') {
return validator.error( validator.error(node.declaration, {
`Default export must be an object literal`, code: `invalid-default-export`,
node.declaration message: `Default export must be an object literal`
); });
} }
checkForComputedKeys(validator, node.declaration.properties); checkForComputedKeys(validator, node.declaration.properties);
@ -36,17 +39,17 @@ export default function validateJs(validator: Validator, js: Node) {
// Remove these checks in version 2 // Remove these checks in version 2
if (props.has('oncreate') && props.has('onrender')) { if (props.has('oncreate') && props.has('onrender')) {
validator.error( validator.error(props.get('onrender'), {
'Cannot have both oncreate and onrender', code: `duplicate-oncreate`,
props.get('onrender') message: 'Cannot have both oncreate and onrender'
); });
} }
if (props.has('ondestroy') && props.has('onteardown')) { if (props.has('ondestroy') && props.has('onteardown')) {
validator.error( validator.error(props.get('onteardown'), {
'Cannot have both ondestroy and onteardown', code: `duplicate-ondestroy`,
props.get('onteardown') message: 'Cannot have both ondestroy and onteardown'
); });
} }
// ensure all exported props are valid // ensure all exported props are valid
@ -59,20 +62,20 @@ export default function validateJs(validator: Validator, js: Node) {
} else { } else {
const match = fuzzymatch(name, validPropList); const match = fuzzymatch(name, validPropList);
if (match) { if (match) {
validator.error( validator.error(prop, {
`Unexpected property '${name}' (did you mean '${match}'?)`, code: `unexpected-property`,
prop message: `Unexpected property '${name}' (did you mean '${match}'?)`
); });
} else if (/FunctionExpression/.test(prop.value.type)) { } else if (/FunctionExpression/.test(prop.value.type)) {
validator.error( validator.error(prop, {
`Unexpected property '${name}' (did you mean to include it in 'methods'?)`, code: `unexpected-property`,
prop message: `Unexpected property '${name}' (did you mean to include it in 'methods'?)`
); });
} else { } else {
validator.error( validator.error(prop, {
`Unexpected property '${name}'`, code: `unexpected-property`,
prop message: `Unexpected property '${name}'`
); });
} }
} }
}); });

@ -1,14 +1,14 @@
import checkForDupes from '../utils/checkForDupes'; import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function actions(validator: Validator, prop: Node) { export default function actions(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'actions' property must be an object literal`, code: `invalid-actions`,
prop message: `The 'actions' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);

@ -1,15 +1,15 @@
import checkForDupes from '../utils/checkForDupes'; import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import getName from '../../../utils/getName'; import getName from '../../../utils/getName';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function components(validator: Validator, prop: Node) { export default function components(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'components' property must be an object literal`, code: `invalid-property`,
prop message: `The 'components' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);
@ -19,10 +19,11 @@ export default function components(validator: Validator, prop: Node) {
const name = getName(component.key); const name = getName(component.key);
if (name === 'state') { if (name === 'state') {
validator.error( // TODO is this still true?
`Component constructors cannot be called 'state' due to technical limitations`, validator.error(component, {
component code: `invalid-name`,
); message: `Component constructors cannot be called 'state' due to technical limitations`
});
} }
if (!/^[A-Z]/.test(name)) { if (!/^[A-Z]/.test(name)) {

@ -3,7 +3,7 @@ import checkForComputedKeys from '../utils/checkForComputedKeys';
import getName from '../../../utils/getName'; import getName from '../../../utils/getName';
import isValidIdentifier from '../../../utils/isValidIdentifier'; import isValidIdentifier from '../../../utils/isValidIdentifier';
import reservedNames from '../../../utils/reservedNames'; import reservedNames from '../../../utils/reservedNames';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope'; import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope';
import isThisGetCallExpression from '../../../utils/isThisGetCallExpression'; import isThisGetCallExpression from '../../../utils/isThisGetCallExpression';
@ -15,10 +15,10 @@ const isFunctionExpression = new Set([
export default function computed(validator: Validator, prop: Node) { export default function computed(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'computed' property must be an object literal`, code: `invalid-property`,
prop message: `The 'computed' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);
@ -29,49 +29,49 @@ export default function computed(validator: Validator, prop: Node) {
if (!isValidIdentifier(name)) { if (!isValidIdentifier(name)) {
const suggestion = name.replace(/[^_$a-z0-9]/ig, '_').replace(/^\d/, '_$&'); const suggestion = name.replace(/[^_$a-z0-9]/ig, '_').replace(/^\d/, '_$&');
validator.error( validator.error(computation, {
`Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}`, code: `invalid-computed-name`,
computation message: `Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}`
); });
} }
if (reservedNames.has(name)) { if (reservedNames.has(name)) {
validator.error( validator.error(computation, {
`Computed property name '${name}' is invalid — cannot be a JavaScript reserved word`, code: `invalid-computed-name`,
computation message: `Computed property name '${name}' is invalid — cannot be a JavaScript reserved word`
); });
} }
if (!isFunctionExpression.has(computation.value.type)) { if (!isFunctionExpression.has(computation.value.type)) {
validator.error( validator.error(computation.value, {
`Computed properties can be function expressions or arrow function expressions`, code: `invalid-computed-value`,
computation.value message: `Computed properties can be function expressions or arrow function expressions`
); });
} }
const { body, params } = computation.value; const { body, params } = computation.value;
walkThroughTopFunctionScope(body, (node: Node) => { walkThroughTopFunctionScope(body, (node: Node) => {
if (isThisGetCallExpression(node) && !node.callee.property.computed) { if (isThisGetCallExpression(node) && !node.callee.property.computed) {
validator.error( validator.error(node, {
`Cannot use this.get(...) — values must be passed into the function as arguments`, code: `impure-computed`,
node message: `Cannot use this.get(...) — values must be passed into the function as arguments`
); });
} }
if (node.type === 'ThisExpression') { if (node.type === 'ThisExpression') {
validator.error( validator.error(node, {
`Computed properties should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?`, code: `impure-computed`,
node message: `Computed properties should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?`
); });
} }
}); });
if (params.length === 0) { if (params.length === 0) {
validator.error( validator.error(computation.value, {
`A computed value must depend on at least one property`, code: `impure-computed`,
computation.value message: `A computed value must depend on at least one property`
); });
} }
params.forEach((param: Node) => { params.forEach((param: Node) => {
@ -81,10 +81,11 @@ export default function computed(validator: Validator, prop: Node) {
param.left.type === 'Identifier'); param.left.type === 'Identifier');
if (!valid) { if (!valid) {
validator.error( // TODO change this for v2
`Computed properties cannot use destructuring in function parameters`, validator.error(param, {
param code: `invalid-computed-arguments`,
); message: `Computed properties cannot use destructuring in function parameters`
});
} }
}); });
}); });

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
const disallowed = new Set(['Literal', 'ObjectExpression', 'ArrayExpression']); const disallowed = new Set(['Literal', 'ObjectExpression', 'ArrayExpression']);
@ -7,6 +7,9 @@ export default function data(validator: Validator, prop: Node) {
while (prop.type === 'ParenthesizedExpression') prop = prop.expression; while (prop.type === 'ParenthesizedExpression') prop = prop.expression;
if (disallowed.has(prop.value.type)) { if (disallowed.has(prop.value.type)) {
validator.error(`'data' must be a function`, prop.value); validator.error(prop.value, {
code: `invalid-property`,
message: `'data' must be a function`
});
} }
} }

@ -1,14 +1,14 @@
import checkForDupes from '../utils/checkForDupes'; import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function events(validator: Validator, prop: Node) { export default function events(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'events' property must be an object literal`, code: `invalid-property`,
prop message: `The 'events' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);

@ -1,17 +1,17 @@
import checkForDupes from '../utils/checkForDupes'; import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope'; import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope';
import isThisGetCallExpression from '../../../utils/isThisGetCallExpression'; import isThisGetCallExpression from '../../../utils/isThisGetCallExpression';
export default function helpers(validator: Validator, prop: Node) { export default function helpers(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'helpers' property must be an object literal`, code: `invalid-property`,
prop message: `The 'helpers' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);
@ -24,17 +24,17 @@ export default function helpers(validator: Validator, prop: Node) {
walkThroughTopFunctionScope(prop.value.body, (node: Node) => { walkThroughTopFunctionScope(prop.value.body, (node: Node) => {
if (isThisGetCallExpression(node) && !node.callee.property.computed) { if (isThisGetCallExpression(node) && !node.callee.property.computed) {
validator.error( validator.error(node, {
`Cannot use this.get(...) — values must be passed into the helper function as arguments`, code: `impure-helper`,
node message: `Cannot use this.get(...) — values must be passed into the helper function as arguments`
); });
} }
if (node.type === 'ThisExpression') { if (node.type === 'ThisExpression') {
validator.error( validator.error(node, {
`Helpers should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?`, code: `impure-helper`,
node message: `Helpers should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?`
); });
} else if (node.type === 'Identifier' && node.name === 'arguments') { } else if (node.type === 'Identifier' && node.name === 'arguments') {
usesArguments = true; usesArguments = true;
} }

@ -1,11 +1,11 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function immutable(validator: Validator, prop: Node) { export default function immutable(validator: Validator, prop: Node) {
if (prop.value.type !== 'Literal' || typeof prop.value.value !== 'boolean') { if (prop.value.type !== 'Literal' || typeof prop.value.value !== 'boolean') {
validator.error( validator.error(prop.value, {
`'immutable' must be a boolean literal`, code: `invalid-property`,
prop.value message: `'immutable' must be a boolean literal`
); });
} }
} }

@ -3,17 +3,17 @@ import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import usesThisOrArguments from '../utils/usesThisOrArguments'; import usesThisOrArguments from '../utils/usesThisOrArguments';
import getName from '../../../utils/getName'; import getName from '../../../utils/getName';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
const builtin = new Set(['set', 'get', 'on', 'fire', 'observe', 'destroy']); const builtin = new Set(['set', 'get', 'on', 'fire', 'observe', 'destroy']);
export default function methods(validator: Validator, prop: Node) { export default function methods(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'methods' property must be an object literal`, code: `invalid-property`,
prop message: `The 'methods' property must be an object literal`
); });
} }
checkForAccessors(validator, prop.value.properties, 'Methods'); checkForAccessors(validator, prop.value.properties, 'Methods');
@ -24,19 +24,18 @@ export default function methods(validator: Validator, prop: Node) {
const name = getName(prop.key); const name = getName(prop.key);
if (builtin.has(name)) { if (builtin.has(name)) {
validator.error( validator.error(prop, {
`Cannot overwrite built-in method '${name}'`, code: `invalid-method-name`,
prop message: `Cannot overwrite built-in method '${name}'`
); });
} }
if (prop.value.type === 'ArrowFunctionExpression') { if (prop.value.type === 'ArrowFunctionExpression') {
if (usesThisOrArguments(prop.value.body)) { if (usesThisOrArguments(prop.value.body)) {
validator.error( validator.error(prop, {
`Method '${prop.key code: `invalid-method-value`,
.name}' should be a function expression, not an arrow function expression`, message: `Method '${prop.key.name}' should be a function expression, not an arrow function expression`
prop });
);
} }
} }
}); });

@ -1,7 +1,7 @@
import * as namespaces from '../../../utils/namespaces'; import * as namespaces from '../../../utils/namespaces';
import nodeToString from '../../../utils/nodeToString' import nodeToString from '../../../utils/nodeToString'
import fuzzymatch from '../../utils/fuzzymatch'; import fuzzymatch from '../../utils/fuzzymatch';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
const valid = new Set(namespaces.validNamespaces); const valid = new Set(namespaces.validNamespaces);
@ -10,21 +10,24 @@ export default function namespace(validator: Validator, prop: Node) {
const ns = nodeToString(prop.value); const ns = nodeToString(prop.value);
if (typeof ns !== 'string') { if (typeof ns !== 'string') {
validator.error( validator.error(prop, {
`The 'namespace' property must be a string literal representing a valid namespace`, code: `invalid-property`,
prop message: `The 'namespace' property must be a string literal representing a valid namespace`
); });
} }
if (!valid.has(ns)) { if (!valid.has(ns)) {
const match = fuzzymatch(ns, namespaces.validNamespaces); const match = fuzzymatch(ns, namespaces.validNamespaces);
if (match) { if (match) {
validator.error( validator.error(prop, {
`Invalid namespace '${ns}' (did you mean '${match}'?)`, code: `invalid-property`,
prop message: `Invalid namespace '${ns}' (did you mean '${match}'?)`
); });
} else { } else {
validator.error(`Invalid namespace '${ns}'`, prop); validator.error(prop, {
code: `invalid-property`,
message: `Invalid namespace '${ns}'`
});
} }
} }
} }

@ -1,14 +1,14 @@
import usesThisOrArguments from '../utils/usesThisOrArguments'; import usesThisOrArguments from '../utils/usesThisOrArguments';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function oncreate(validator: Validator, prop: Node) { export default function oncreate(validator: Validator, prop: Node) {
if (prop.value.type === 'ArrowFunctionExpression') { if (prop.value.type === 'ArrowFunctionExpression') {
if (usesThisOrArguments(prop.value.body)) { if (usesThisOrArguments(prop.value.body)) {
validator.error( validator.error(prop, {
`'oncreate' should be a function expression, not an arrow function expression`, code: `invalid-property`,
prop message: `'oncreate' should be a function expression, not an arrow function expression`
); });
} }
} }
} }

@ -1,14 +1,14 @@
import usesThisOrArguments from '../utils/usesThisOrArguments'; import usesThisOrArguments from '../utils/usesThisOrArguments';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function ondestroy(validator: Validator, prop: Node) { export default function ondestroy(validator: Validator, prop: Node) {
if (prop.value.type === 'ArrowFunctionExpression') { if (prop.value.type === 'ArrowFunctionExpression') {
if (usesThisOrArguments(prop.value.body)) { if (usesThisOrArguments(prop.value.body)) {
validator.error( validator.error(prop, {
`'ondestroy' should be a function expression, not an arrow function expression`, code: `invalid-property`,
prop message: `'ondestroy' should be a function expression, not an arrow function expression`
); });
} }
} }
} }

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function preload(validator: Validator, prop: Node) { export default function preload(validator: Validator, prop: Node) {

@ -1,21 +1,21 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import nodeToString from '../../../utils/nodeToString'; import nodeToString from '../../../utils/nodeToString';
export default function props(validator: Validator, prop: Node) { export default function props(validator: Validator, prop: Node) {
if (prop.value.type !== 'ArrayExpression') { if (prop.value.type !== 'ArrayExpression') {
validator.error( validator.error(prop.value, {
`'props' must be an array expression, if specified`, code: `invalid-property`,
prop.value message: `'props' must be an array expression, if specified`
); });
} }
prop.value.elements.forEach((element: Node) => { prop.value.elements.forEach((element: Node) => {
if (typeof nodeToString(element) !== 'string') { if (typeof nodeToString(element) !== 'string') {
validator.error( validator.error(element, {
`'props' must be an array of string literals`, code: `invalid-property`,
element message: `'props' must be an array of string literals`
); });
} }
}); });
} }

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
const disallowed = new Set(['Literal', 'ObjectExpression', 'ArrayExpression']); const disallowed = new Set(['Literal', 'ObjectExpression', 'ArrayExpression']);
@ -7,6 +7,9 @@ export default function setup(validator: Validator, prop: Node) {
while (prop.type === 'ParenthesizedExpression') prop = prop.expression; while (prop.type === 'ParenthesizedExpression') prop = prop.expression;
if (disallowed.has(prop.value.type)) { if (disallowed.has(prop.value.type)) {
validator.error(`'setup' must be a function`, prop.value); validator.error(prop.value, {
code: `invalid-property`,
message: `'setup' must be a function`
});
} }
} }

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function store(validator: Validator, prop: Node) { export default function store(validator: Validator, prop: Node) {

@ -1,20 +1,20 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import nodeToString from '../../../utils/nodeToString'; import nodeToString from '../../../utils/nodeToString';
export default function tag(validator: Validator, prop: Node) { export default function tag(validator: Validator, prop: Node) {
const tag = nodeToString(prop.value); const tag = nodeToString(prop.value);
if (typeof tag !== 'string') { if (typeof tag !== 'string') {
validator.error( validator.error(prop.value, {
`'tag' must be a string literal`, code: `invalid-property`,
prop.value message: `'tag' must be a string literal`
); });
} }
if (!/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { if (!/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
validator.error( validator.error(prop.value, {
`tag name must be two or more words joined by the '-' character`, code: `invalid-property`,
prop.value message: `tag name must be two or more words joined by the '-' character`
); });
} }
} }

@ -1,14 +1,14 @@
import checkForDupes from '../utils/checkForDupes'; import checkForDupes from '../utils/checkForDupes';
import checkForComputedKeys from '../utils/checkForComputedKeys'; import checkForComputedKeys from '../utils/checkForComputedKeys';
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function transitions(validator: Validator, prop: Node) { export default function transitions(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(prop, {
`The 'transitions' property must be an object literal`, code: `invalid-property`,
prop message: `The 'transitions' property must be an object literal`
); });
} }
checkForDupes(validator, prop.value.properties); checkForDupes(validator, prop.value.properties);

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function checkForAccessors( export default function checkForAccessors(
@ -8,7 +8,10 @@ export default function checkForAccessors(
) { ) {
properties.forEach(prop => { properties.forEach(prop => {
if (prop.kind !== 'init') { if (prop.kind !== 'init') {
validator.error(`${label} cannot use getters and setters`, prop); validator.error(prop, {
code: `illegal-accessor`,
message: `${label} cannot use getters and setters`
});
} }
}); });
} }

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
export default function checkForComputedKeys( export default function checkForComputedKeys(
@ -7,7 +7,10 @@ export default function checkForComputedKeys(
) { ) {
properties.forEach(prop => { properties.forEach(prop => {
if (prop.key.computed) { if (prop.key.computed) {
validator.error(`Cannot use computed keys`, prop); validator.error(prop, {
code: `computed-key`,
message: `Cannot use computed keys`
});
} }
}); });
} }

@ -1,4 +1,4 @@
import { Validator } from '../../'; import { Validator } from '../../index';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import getName from '../../../utils/getName'; import getName from '../../../utils/getName';
@ -12,7 +12,10 @@ export default function checkForDupes(
const name = getName(prop.key); const name = getName(prop.key);
if (seen.has(name)) { if (seen.has(name)) {
validator.error(`Duplicate property '${name}'`, prop); validator.error(prop, {
code: `duplicate-property`,
message: `Duplicate property '${name}'`
});
} }
seen.add(name); seen.add(name);

@ -2,7 +2,7 @@ import * as fs from "fs";
import assert from "assert"; import assert from "assert";
import { svelte, loadConfig, tryToLoadJson } from "../helpers.js"; import { svelte, loadConfig, tryToLoadJson } from "../helpers.js";
describe("validate", () => { describe.only("validate", () => {
fs.readdirSync("test/validator/samples").forEach(dir => { fs.readdirSync("test/validator/samples").forEach(dir => {
if (dir[0] === ".") return; if (dir[0] === ".") return;

@ -1,4 +1,5 @@
[{ [{
"code": "missing-action",
"message": "Missing action 'whatever'", "message": "Missing action 'whatever'",
"pos": 5, "pos": 5,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-action",
"message": "Actions can only be applied to DOM elements, not components", "message": "Actions can only be applied to DOM elements, not components",
"pos": 8, "pos": 8,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'checked' binding can only be used with <input type=\"checkbox\">", "message": "'checked' binding can only be used with <input type=\"checkbox\">",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "missing-type",
"message": "'type' attribute must be specified", "message": "'type' attribute must be specified",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-type",
"message": "'type' attribute cannot be dynamic if input uses two-way binding", "message": "'type' attribute cannot be dynamic if input uses two-way binding",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'value' is not a valid binding on <div> elements", "message": "'value' is not a valid binding on <div> elements",
"pos": 5, "pos": 5,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'whatever' is not a valid binding", "message": "'whatever' is not a valid binding",
"pos": 5, "pos": 5,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "dynamic-multiple-attribute",
"message": "'multiple' attribute cannot be dynamic if select uses two-way binding", "message": "'multiple' attribute cannot be dynamic if select uses two-way binding",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-name",
"message": "Component constructors cannot be called 'state' due to technical limitations", "message": "Component constructors cannot be called 'state' due to technical limitations",
"pos": 73, "pos": 73,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slot-name",
"message": "default is a reserved word — it cannot be used as a slot name", "message": "default is a reserved word — it cannot be used as a slot name",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slot-attribute",
"message": "slot attribute cannot have a dynamic value", "message": "slot attribute cannot have a dynamic value",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "dynamic-slot-name",
"message": "<slot> name cannot be dynamic", "message": "<slot> name cannot be dynamic",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slot-placement",
"message": "<slot> cannot be a child of an each-block", "message": "<slot> cannot be a child of an each-block",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slotted-content",
"message": "Cannot place slotted elements inside an each-block", "message": "Cannot place slotted elements inside an each-block",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slotted-content",
"message": "Cannot place slotted elements inside an if-block", "message": "Cannot place slotted elements inside an if-block",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "impure-computed",
"message": "Computed properties should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?", "message": "Computed properties should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?",
"pos": 83, "pos": 83,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "impure-computed",
"message": "Cannot use this.get(...) — values must be passed into the function as arguments", "message": "Cannot use this.get(...) — values must be passed into the function as arguments",
"pos": 73, "pos": 73,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "css-invalid-global",
"message": ":global(...) can be at the start or end of a selector sequence, but not in the middle", "message": ":global(...) can be at the start or end of a selector sequence, but not in the middle",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "css-invalid-global",
"message": ":global(...) must be the first element in a compound selector", "message": ":global(...) must be the first element in a compound selector",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "missing-ref",
"message": "'refs.inputx' does not exist (did you mean 'refs.input'?)", "message": "'refs.inputx' does not exist (did you mean 'refs.input'?)",
"pos": 36, "pos": 36,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-default-export",
"message": "Default export must be an object literal", "message": "Default export must be an object literal",
"pos": 25, "pos": 25,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "impure-helper",
"message": "Helpers should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?", "message": "Helpers should be pure functions — they do not have access to the component instance and cannot use 'this'. Did you mean to put this in 'methods'?",
"pos": 95, "pos": 95,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "impure-helper",
"message": "Cannot use this.get(...) — values must be passed into the helper function as arguments", "message": "Cannot use this.get(...) — values must be passed into the helper function as arguments",
"pos": 74, "pos": 74,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-method-value",
"message": "Method 'foo' should be a function expression, not an arrow function expression", "message": "Method 'foo' should be a function expression, not an arrow function expression",
"pos": 79, "pos": 79,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "named-export",
"message": "A component can only have a default export", "message": "A component can only have a default export",
"pos": 10, "pos": 10,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "Invalid namespace 'lol'", "message": "Invalid namespace 'lol'",
"pos": 29, "pos": 29,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "Invalid namespace 'http://www.w3.org/1999/svg' (did you mean 'http://www.w3.org/2000/svg'?)", "message": "Invalid namespace 'http://www.w3.org/1999/svg' (did you mean 'http://www.w3.org/2000/svg'?)",
"pos": 29, "pos": 29,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'namespace' property must be a string literal representing a valid namespace", "message": "The 'namespace' property must be a string literal representing a valid namespace",
"pos": 79, "pos": 79,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'components' property must be an object literal", "message": "The 'components' property must be an object literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'events' property must be an object literal", "message": "The 'events' property must be an object literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'helpers' property must be an object literal", "message": "The 'helpers' property must be an object literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'methods' property must be an object literal", "message": "The 'methods' property must be an object literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'transitions' property must be an object literal", "message": "The 'transitions' property must be an object literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'oncreate' should be a function expression, not an arrow function expression", "message": "'oncreate' should be a function expression, not an arrow function expression",
"pos": 29, "pos": 29,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'ondestroy' should be a function expression, not an arrow function expression", "message": "'ondestroy' should be a function expression, not an arrow function expression",
"pos": 29, "pos": 29,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-computed-name",
"message": "message":
"Computed property name 'new' is invalid — cannot be a JavaScript reserved word", "Computed property name 'new' is invalid — cannot be a JavaScript reserved word",
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "The 'computed' property must be an object literal", "message": "The 'computed' property must be an object literal",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-computed-value",
"message": "Computed properties can be function expressions or arrow function expressions", "message": "Computed properties can be function expressions or arrow function expressions",
"loc": { "loc": {
"line": 6, "line": 6,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-computed-name",
"message": "Computed property name 'with-hyphen' is invalid — must be a valid identifier such as with_hyphen", "message": "Computed property name 'with-hyphen' is invalid — must be a valid identifier such as with_hyphen",
"loc": { "loc": {
"line": 9, "line": 9,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-computed-arguments",
"message": "Computed properties cannot use destructuring in function parameters", "message": "Computed properties cannot use destructuring in function parameters",
"loc": { "loc": {
"line": 6, "line": 6,

@ -1,4 +1,5 @@
[{ [{
"code": "impure-computed",
"message": "A computed value must depend on at least one property", "message": "A computed value must depend on at least one property",
"pos": 49, "pos": 49,
"loc": { "loc": {

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'data' must be a function", "message": "'data' must be a function",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-property",
"message": "Duplicate property 'methods'", "message": "Duplicate property 'methods'",
"loc": { "loc": {
"line": 9, "line": 9,

@ -1,4 +1,5 @@
[{ [{
"code": "illegal-accessor",
"message": "Methods cannot use getters and setters", "message": "Methods cannot use getters and setters",
"loc": { "loc": {
"line": 4, "line": 4,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'props' must be an array expression, if specified", "message": "'props' must be an array expression, if specified",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'props' must be an array of string literals", "message": "'props' must be an array of string literals",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "unexpected-property",
"message": "Unexpected property 'doSomething' (did you mean to include it in 'methods'?)", "message": "Unexpected property 'doSomething' (did you mean to include it in 'methods'?)",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "unexpected-property",
"message": "Unexpected property 'dada' (did you mean 'data'?)", "message": "Unexpected property 'dada' (did you mean 'data'?)",
"loc": { "loc": {
"line": 5, "line": 5,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-slotted-content",
"message": "Element with a slot='...' attribute must be a descendant of a component or custom element", "message": "Element with a slot='...' attribute must be a descendant of a component or custom element",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-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",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-property",
"message": "'tag' must be a string literal", "message": "'tag' must be a string literal",
"loc": { "loc": {
"line": 3, "line": 3,

@ -1,4 +1,5 @@
[{ [{
"code": "textarea-duplicate-value",
"message": "A <textarea> can have either a value attribute or (equivalently) child content, but not both", "message": "A <textarea> can have either a value attribute or (equivalently) child content, but not both",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "illegal-attribute",
"message": "<title> cannot have attributes", "message": "<title> cannot have attributes",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "illegal-attribute",
"message": "<title> cannot have attributes", "message": "<title> cannot have attributes",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "illegal-structure",
"message": "<title> can only contain text and {{tags}}", "message": "<title> can only contain text and {{tags}}",
"loc": { "loc": {
"line": 2, "line": 2,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element cannot have both an 'in' directive and a 'transition' directive", "message": "An element cannot have both an 'in' directive and a 'transition' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element can only have one 'in' directive", "message": "An element can only have one 'in' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element cannot have both an 'out' directive and a 'transition' directive", "message": "An element cannot have both an 'out' directive and a 'transition' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element can only have one 'out' directive", "message": "An element can only have one 'out' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element cannot have both a 'transition' directive and an 'in' directive", "message": "An element cannot have both a 'transition' directive and an 'in' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element cannot have both a 'transition' directive and an 'out' directive", "message": "An element cannot have both a 'transition' directive and an 'out' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "duplicate-transition",
"message": "An element can only have one 'transition' directive", "message": "An element can only have one 'transition' directive",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "missing-transition",
"message": "Missing transition 'foo'", "message": "Missing transition 'foo'",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-transition",
"message": "Transitions can only be applied to DOM elements, not components", "message": "Transitions can only be applied to DOM elements, not components",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'innerwidth' is not a valid binding on <:Window> (did you mean 'innerWidth'?)", "message": "'innerwidth' is not a valid binding on <:Window> (did you mean 'innerWidth'?)",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "Bindings on <:Window/> must be to top-level properties, e.g. 'baz' rather than 'foo.bar.baz'", "message": "Bindings on <:Window/> must be to top-level properties, e.g. 'baz' rather than 'foo.bar.baz'",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'width' is not a valid binding on <:Window> (did you mean 'innerWidth'?)", "message": "'width' is not a valid binding on <:Window> (did you mean 'innerWidth'?)",
"loc": { "loc": {
"line": 1, "line": 1,

@ -1,4 +1,5 @@
[{ [{
"code": "invalid-binding",
"message": "'potato' is not a valid binding on <:Window> — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY or online", "message": "'potato' is not a valid binding on <:Window> — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY or online",
"loc": { "loc": {
"line": 1, "line": 1,

Loading…
Cancel
Save