diff --git a/src/css/Selector.ts b/src/css/Selector.ts index 487b5d578e..4b21737271 100644 --- a/src/css/Selector.ts +++ b/src/css/Selector.ts @@ -102,7 +102,10 @@ export default class Selector { while (i-- > 1) { const selector = block.selectors[i]; 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) { 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` + }); } } } diff --git a/src/utils/error.ts b/src/utils/error.ts index 5ec337f05f..35daba2c80 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -2,6 +2,7 @@ import { locate } from 'locate-character'; import getCodeFrame from '../utils/getCodeFrame'; class CompileError extends Error { + code: string; loc: { line: number, column: number }; end: { line: number, column: number }; pos: number; @@ -15,6 +16,7 @@ class CompileError extends Error { export default function error(message: string, props: { name: string, + code: string, source: string, filename: string, start: number, @@ -26,6 +28,7 @@ export default function error(message: string, props: { const start = locate(props.source, 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.end = { line: end.line + 1, column: end.column }; error.pos = props.start; diff --git a/src/validate/html/index.ts b/src/validate/html/index.ts index 87291b7279..f543ebd27e 100644 --- a/src/validate/html/index.ts +++ b/src/validate/html/index.ts @@ -108,7 +108,10 @@ export default function validateHtml(validator: Validator, html: Node) { let message = `'refs.${ref}' does not exist`; if (match) message += ` (did you mean 'refs.${match}'?)`; - validator.error(message, callee); + validator.error(callee, { + code: `missing-ref`, + message + }); } }); } diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index e5fdd0e94b..820de23ea1 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -37,12 +37,18 @@ export default function validateElement( const nameAttribute = node.attributes.find((attribute: Node) => attribute.name === 'name'); if (nameAttribute) { if (nameAttribute.value.length !== 1 || nameAttribute.value[0].type !== 'Text') { - validator.error(` name cannot be dynamic`, nameAttribute); + validator.error(nameAttribute, { + code: `dynamic-slot-name`, + message: ` name cannot be dynamic` + }); } const slotName = nameAttribute.value[0].data; 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 @@ -64,18 +70,18 @@ export default function validateElement( if (node.name === 'title') { if (node.attributes.length > 0) { - validator.error( - ` cannot have attributes`, - node.attributes[0] - ); + validator.error(node.attributes[0], { + code: `illegal-attribute`, + message: `<title> cannot have attributes` + }); } node.children.forEach(child => { if (child.type !== 'Text' && child.type !== 'MustacheTag') { - validator.error( - `<title> can only contain text and {{tags}}`, - child - ); + validator.error(child, { + code: 'illegal-structure', + message: `<title> can only contain text and {{tags}}` + }); } }); } @@ -99,10 +105,10 @@ export default function validateElement( node.name !== 'textarea' && node.name !== 'select' ) { - validator.error( - `'value' is not a valid binding on <${node.name}> elements`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'value' is not a valid binding on <${node.name}> elements` + }); } if (node.name === 'select') { @@ -111,43 +117,43 @@ export default function validateElement( ); if (attribute && isDynamic(attribute)) { - validator.error( - `'multiple' attribute cannot be dynamic if select uses two-way binding`, - attribute - ); + validator.error(attribute, { + code: `dynamic-multiple-attribute`, + message: `'multiple' attribute cannot be dynamic if select uses two-way binding` + }); } } else { checkTypeAttribute(validator, node); } } else if (name === 'checked' || name === 'indeterminate') { if (node.name !== 'input') { - validator.error( - `'${name}' is not a valid binding on <${node.name}> elements`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'${name}' is not a valid binding on <${node.name}> elements` + }); } if (checkTypeAttribute(validator, node) !== 'checkbox') { - validator.error( - `'${name}' binding can only be used with <input type="checkbox">`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'${name}' binding can only be used with <input type="checkbox">` + }); } } else if (name === 'group') { if (node.name !== 'input') { - validator.error( - `'group' is not a valid binding on <${node.name}> elements`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'group' is not a valid binding on <${node.name}> elements` + }); } const type = checkTypeAttribute(validator, node); if (type !== 'checkbox' && type !== 'radio') { - validator.error( - `'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'checked' binding can only be used with <input type="checkbox"> or <input type="radio">` + }); } } else if ( name === 'currentTime' || @@ -159,23 +165,26 @@ export default function validateElement( name === 'volume' ) { if (node.name !== 'audio' && node.name !== 'video') { - validator.error( - `'${name}' binding can only be used with <audio> or <video>`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'${name}' binding can only be used with <audio> or <video>` + }); } } else { - validator.error( - `'${attribute.name}' is not a valid binding`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `'${attribute.name}' is not a valid binding` + }); } } else if (attribute.type === 'EventHandler') { validator.used.events.add(attribute.name); validateEventHandler(validator, attribute, refCallees); } else if (attribute.type === 'Transition') { 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); @@ -183,31 +192,31 @@ export default function validateElement( const bidi = attribute.intro && attribute.outro; if (hasTransition) { - if (bidi) - validator.error( - `An element can only have one 'transition' directive`, - attribute - ); - validator.error( - `An element cannot have both a 'transition' directive and an '${attribute.intro - ? 'in' - : 'out'}' directive`, - attribute - ); + if (bidi) { + validator.error(attribute, { + code: `duplicate-transition`, + message: `An element can only have one 'transition' directive` + }); + } + + validator.error(attribute, { + code: `duplicate-transition`, + message: `An element cannot have both a 'transition' directive and an '${attribute.intro ? 'in' : 'out'}' directive` + }); } if ((hasIntro && attribute.intro) || (hasOutro && attribute.outro)) { - if (bidi) - validator.error( - `An element cannot have both an '${hasIntro - ? 'in' - : 'out'}' directive and a 'transition' directive`, - attribute - ); - validator.error( - `An element can only have one '${hasIntro ? 'in' : 'out'}' directive`, - attribute - ); + if (bidi) { + validator.error(attribute, { + code: `duplicate-transition`, + message: `An element cannot have both an '${hasIntro ? 'in' : 'out'}' directive and a 'transition' directive` + }); + } + + validator.error(attribute, { + code: `duplicate-transition`, + message: `An element can only have one '${hasIntro ? 'in' : 'out'}' directive` + }); } if (attribute.intro) hasIntro = true; @@ -215,18 +224,18 @@ export default function validateElement( if (bidi) hasTransition = true; if (!validator.transitions.has(attribute.name)) { - validator.error( - `Missing transition '${attribute.name}'`, - attribute - ); + validator.error(attribute, { + code: `missing-transition`, + message: `Missing transition '${attribute.name}'` + }); } } else if (attribute.type === 'Attribute') { if (attribute.name === 'value' && node.name === 'textarea') { if (node.children.length) { - validator.error( - `A <textarea> can have either a value attribute or (equivalently) child content, but not both`, - attribute - ); + validator.error(attribute, { + code: `textarea-duplicate-value`, + 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') { 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); if (!validator.actions.has(attribute.name)) { - validator.error( - `Missing action '${attribute.name}'`, - attribute - ); + validator.error(attribute, { + code: `missing-action`, + message: `Missing action '${attribute.name}'` + }); } } }); @@ -257,14 +269,17 @@ function checkTypeAttribute(validator: Validator, node: Node) { if (!attribute) return null; 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)) { - validator.error( - `'type' attribute cannot be dynamic if input uses two-way binding`, - attribute - ); + validator.error(attribute, { + code: `invalid-type`, + message: `'type' attribute cannot be dynamic if input uses two-way binding` + }); } 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[]) { if (isDynamic(attribute)) { - validator.error( - `slot attribute cannot have a dynamic value`, - attribute - ); + validator.error(attribute, { + code: `invalid-slot-attribute`, + message: `slot attribute cannot have a dynamic value` + }); } let i = stack.length; @@ -289,11 +304,17 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s if (parent.type === 'IfBlock' || parent.type === 'EachBlock') { 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) { diff --git a/src/validate/html/validateEventHandler.ts b/src/validate/html/validateEventHandler.ts index 4cbe8db08c..086ae2f7b6 100644 --- a/src/validate/html/validateEventHandler.ts +++ b/src/validate/html/validateEventHandler.ts @@ -16,7 +16,10 @@ export default function validateEventHandlerCallee( const { callee, type } = attribute.expression; 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); diff --git a/src/validate/html/validateHead.ts b/src/validate/html/validateHead.ts index d57ffc426b..48756f9150 100644 --- a/src/validate/html/validateHead.ts +++ b/src/validate/html/validateHead.ts @@ -4,7 +4,10 @@ import { Node } from '../../interfaces'; export default function validateHead(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) { 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 diff --git a/src/validate/html/validateWindow.ts b/src/validate/html/validateWindow.ts index 5285457c80..29b5f2a7df 100644 --- a/src/validate/html/validateWindow.ts +++ b/src/validate/html/validateWindow.ts @@ -21,12 +21,10 @@ export default function validateWindow(validator: Validator, node: Node, refs: M if (attribute.value.type !== 'Identifier') { const { parts } = flattenReference(attribute.value); - validator.error( - `Bindings on <:Window/> must be to top-level properties, e.g. '${parts[ - parts.length - 1 - ]}' rather than '${parts.join('.')}'`, - attribute.value - ); + validator.error(attribute.value, { + code: `invalid-binding`, + message: `Bindings on <:Window/> must be to top-level properties, e.g. '${parts[parts.length - 1]}' rather than '${parts.join('.')}'` + }); } 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>`; if (match) { - validator.error( - `${message} (did you mean '${match}'?)`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `${message} (did you mean '${match}'?)` + }); } else { - validator.error( - `${message} — valid bindings are ${list(validBindings)}`, - attribute - ); + validator.error(attribute, { + code: `invalid-binding`, + message: `${message} — valid bindings are ${list(validBindings)}` + }); } } } else if (attribute.type === 'EventHandler') { diff --git a/src/validate/index.ts b/src/validate/index.ts index 796d2d28f9..96bc382375 100644 --- a/src/validate/index.ts +++ b/src/validate/index.ts @@ -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, { name: 'ValidationError', + code, source: this.source, start: pos.start, end: pos.end, diff --git a/src/validate/js/index.ts b/src/validate/js/index.ts index b6157578db..d4e01dee6a 100644 --- a/src/validate/js/index.ts +++ b/src/validate/js/index.ts @@ -14,15 +14,18 @@ export default function validateJs(validator: Validator, js: Node) { js.content.body.forEach((node: Node) => { // check there are no named exports 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.declaration.type !== 'ObjectExpression') { - return validator.error( - `Default export must be an object literal`, - node.declaration - ); + validator.error(node.declaration, { + code: `invalid-default-export`, + message: `Default export must be an object literal` + }); } checkForComputedKeys(validator, node.declaration.properties); @@ -36,17 +39,17 @@ export default function validateJs(validator: Validator, js: Node) { // Remove these checks in version 2 if (props.has('oncreate') && props.has('onrender')) { - validator.error( - 'Cannot have both oncreate and onrender', - props.get('onrender') - ); + validator.error(props.get('onrender'), { + code: `duplicate-oncreate`, + message: 'Cannot have both oncreate and onrender' + }); } if (props.has('ondestroy') && props.has('onteardown')) { - validator.error( - 'Cannot have both ondestroy and onteardown', - props.get('onteardown') - ); + validator.error(props.get('onteardown'), { + code: `duplicate-ondestroy`, + message: 'Cannot have both ondestroy and onteardown' + }); } // ensure all exported props are valid @@ -59,20 +62,20 @@ export default function validateJs(validator: Validator, js: Node) { } else { const match = fuzzymatch(name, validPropList); if (match) { - validator.error( - `Unexpected property '${name}' (did you mean '${match}'?)`, - prop - ); + validator.error(prop, { + code: `unexpected-property`, + message: `Unexpected property '${name}' (did you mean '${match}'?)` + }); } else if (/FunctionExpression/.test(prop.value.type)) { - validator.error( - `Unexpected property '${name}' (did you mean to include it in 'methods'?)`, - prop - ); + validator.error(prop, { + code: `unexpected-property`, + message: `Unexpected property '${name}' (did you mean to include it in 'methods'?)` + }); } else { - validator.error( - `Unexpected property '${name}'`, - prop - ); + validator.error(prop, { + code: `unexpected-property`, + message: `Unexpected property '${name}'` + }); } } }); diff --git a/src/validate/js/propValidators/actions.ts b/src/validate/js/propValidators/actions.ts index 8dc3ad9202..063a1b06b1 100644 --- a/src/validate/js/propValidators/actions.ts +++ b/src/validate/js/propValidators/actions.ts @@ -1,14 +1,14 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function actions(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'actions' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-actions`, + message: `The 'actions' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); diff --git a/src/validate/js/propValidators/components.ts b/src/validate/js/propValidators/components.ts index 4d695063f6..09f958c0ce 100644 --- a/src/validate/js/propValidators/components.ts +++ b/src/validate/js/propValidators/components.ts @@ -1,15 +1,15 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; import getName from '../../../utils/getName'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function components(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'components' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'components' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); @@ -19,10 +19,11 @@ export default function components(validator: Validator, prop: Node) { const name = getName(component.key); if (name === 'state') { - validator.error( - `Component constructors cannot be called 'state' due to technical limitations`, - component - ); + // TODO is this still true? + validator.error(component, { + code: `invalid-name`, + message: `Component constructors cannot be called 'state' due to technical limitations` + }); } if (!/^[A-Z]/.test(name)) { diff --git a/src/validate/js/propValidators/computed.ts b/src/validate/js/propValidators/computed.ts index 437b8d7f0d..2e86e5ca04 100644 --- a/src/validate/js/propValidators/computed.ts +++ b/src/validate/js/propValidators/computed.ts @@ -3,7 +3,7 @@ import checkForComputedKeys from '../utils/checkForComputedKeys'; import getName from '../../../utils/getName'; import isValidIdentifier from '../../../utils/isValidIdentifier'; import reservedNames from '../../../utils/reservedNames'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope'; import isThisGetCallExpression from '../../../utils/isThisGetCallExpression'; @@ -15,10 +15,10 @@ const isFunctionExpression = new Set([ export default function computed(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'computed' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'computed' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); @@ -29,49 +29,49 @@ export default function computed(validator: Validator, prop: Node) { if (!isValidIdentifier(name)) { const suggestion = name.replace(/[^_$a-z0-9]/ig, '_').replace(/^\d/, '_$&'); - validator.error( - `Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}`, - computation - ); + validator.error(computation, { + code: `invalid-computed-name`, + message: `Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}` + }); } if (reservedNames.has(name)) { - validator.error( - `Computed property name '${name}' is invalid — cannot be a JavaScript reserved word`, - computation - ); + validator.error(computation, { + code: `invalid-computed-name`, + message: `Computed property name '${name}' is invalid — cannot be a JavaScript reserved word` + }); } if (!isFunctionExpression.has(computation.value.type)) { - validator.error( - `Computed properties can be function expressions or arrow function expressions`, - computation.value - ); + validator.error(computation.value, { + code: `invalid-computed-value`, + message: `Computed properties can be function expressions or arrow function expressions` + }); } const { body, params } = computation.value; walkThroughTopFunctionScope(body, (node: Node) => { if (isThisGetCallExpression(node) && !node.callee.property.computed) { - validator.error( - `Cannot use this.get(...) — values must be passed into the function as arguments`, - node - ); + validator.error(node, { + code: `impure-computed`, + message: `Cannot use this.get(...) — values must be passed into the function as arguments` + }); } if (node.type === 'ThisExpression') { - validator.error( - `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'?`, - node - ); + validator.error(node, { + 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'?` + }); } }); if (params.length === 0) { - validator.error( - `A computed value must depend on at least one property`, - computation.value - ); + validator.error(computation.value, { + code: `impure-computed`, + message: `A computed value must depend on at least one property` + }); } params.forEach((param: Node) => { @@ -81,10 +81,11 @@ export default function computed(validator: Validator, prop: Node) { param.left.type === 'Identifier'); if (!valid) { - validator.error( - `Computed properties cannot use destructuring in function parameters`, - param - ); + // TODO change this for v2 + validator.error(param, { + code: `invalid-computed-arguments`, + message: `Computed properties cannot use destructuring in function parameters` + }); } }); }); diff --git a/src/validate/js/propValidators/data.ts b/src/validate/js/propValidators/data.ts index 36a88960da..064703cd85 100644 --- a/src/validate/js/propValidators/data.ts +++ b/src/validate/js/propValidators/data.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; 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; 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` + }); } } diff --git a/src/validate/js/propValidators/events.ts b/src/validate/js/propValidators/events.ts index 2660aac726..bf72fadac4 100644 --- a/src/validate/js/propValidators/events.ts +++ b/src/validate/js/propValidators/events.ts @@ -1,14 +1,14 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function events(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'events' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'events' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); diff --git a/src/validate/js/propValidators/helpers.ts b/src/validate/js/propValidators/helpers.ts index 38b9467e6e..77624ad49b 100644 --- a/src/validate/js/propValidators/helpers.ts +++ b/src/validate/js/propValidators/helpers.ts @@ -1,17 +1,17 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; import { walk } from 'estree-walker'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; import walkThroughTopFunctionScope from '../../../utils/walkThroughTopFunctionScope'; import isThisGetCallExpression from '../../../utils/isThisGetCallExpression'; export default function helpers(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'helpers' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'helpers' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); @@ -24,17 +24,17 @@ export default function helpers(validator: Validator, prop: Node) { walkThroughTopFunctionScope(prop.value.body, (node: Node) => { if (isThisGetCallExpression(node) && !node.callee.property.computed) { - validator.error( - `Cannot use this.get(...) — values must be passed into the helper function as arguments`, - node - ); + validator.error(node, { + code: `impure-helper`, + message: `Cannot use this.get(...) — values must be passed into the helper function as arguments` + }); } if (node.type === 'ThisExpression') { - validator.error( - `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'?`, - node - ); + validator.error(node, { + 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'?` + }); } else if (node.type === 'Identifier' && node.name === 'arguments') { usesArguments = true; } diff --git a/src/validate/js/propValidators/immutable.ts b/src/validate/js/propValidators/immutable.ts index 39e6f3c422..cf1dd19cb9 100644 --- a/src/validate/js/propValidators/immutable.ts +++ b/src/validate/js/propValidators/immutable.ts @@ -1,11 +1,11 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function immutable(validator: Validator, prop: Node) { if (prop.value.type !== 'Literal' || typeof prop.value.value !== 'boolean') { - validator.error( - `'immutable' must be a boolean literal`, - prop.value - ); + validator.error(prop.value, { + code: `invalid-property`, + message: `'immutable' must be a boolean literal` + }); } } diff --git a/src/validate/js/propValidators/methods.ts b/src/validate/js/propValidators/methods.ts index e09837a87e..84f83af7ea 100644 --- a/src/validate/js/propValidators/methods.ts +++ b/src/validate/js/propValidators/methods.ts @@ -3,17 +3,17 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; import usesThisOrArguments from '../utils/usesThisOrArguments'; import getName from '../../../utils/getName'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; const builtin = new Set(['set', 'get', 'on', 'fire', 'observe', 'destroy']); export default function methods(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'methods' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'methods' property must be an object literal` + }); } checkForAccessors(validator, prop.value.properties, 'Methods'); @@ -24,19 +24,18 @@ export default function methods(validator: Validator, prop: Node) { const name = getName(prop.key); if (builtin.has(name)) { - validator.error( - `Cannot overwrite built-in method '${name}'`, - prop - ); + validator.error(prop, { + code: `invalid-method-name`, + message: `Cannot overwrite built-in method '${name}'` + }); } if (prop.value.type === 'ArrowFunctionExpression') { if (usesThisOrArguments(prop.value.body)) { - validator.error( - `Method '${prop.key - .name}' should be a function expression, not an arrow function expression`, - prop - ); + validator.error(prop, { + code: `invalid-method-value`, + message: `Method '${prop.key.name}' should be a function expression, not an arrow function expression` + }); } } }); diff --git a/src/validate/js/propValidators/namespace.ts b/src/validate/js/propValidators/namespace.ts index e7436f3eb4..ffcbda5863 100644 --- a/src/validate/js/propValidators/namespace.ts +++ b/src/validate/js/propValidators/namespace.ts @@ -1,7 +1,7 @@ import * as namespaces from '../../../utils/namespaces'; import nodeToString from '../../../utils/nodeToString' import fuzzymatch from '../../utils/fuzzymatch'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; const valid = new Set(namespaces.validNamespaces); @@ -10,21 +10,24 @@ export default function namespace(validator: Validator, prop: Node) { const ns = nodeToString(prop.value); if (typeof ns !== 'string') { - validator.error( - `The 'namespace' property must be a string literal representing a valid namespace`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'namespace' property must be a string literal representing a valid namespace` + }); } if (!valid.has(ns)) { const match = fuzzymatch(ns, namespaces.validNamespaces); if (match) { - validator.error( - `Invalid namespace '${ns}' (did you mean '${match}'?)`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `Invalid namespace '${ns}' (did you mean '${match}'?)` + }); } else { - validator.error(`Invalid namespace '${ns}'`, prop); + validator.error(prop, { + code: `invalid-property`, + message: `Invalid namespace '${ns}'` + }); } } } diff --git a/src/validate/js/propValidators/oncreate.ts b/src/validate/js/propValidators/oncreate.ts index c1ed2b5743..db1e602812 100644 --- a/src/validate/js/propValidators/oncreate.ts +++ b/src/validate/js/propValidators/oncreate.ts @@ -1,14 +1,14 @@ import usesThisOrArguments from '../utils/usesThisOrArguments'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function oncreate(validator: Validator, prop: Node) { if (prop.value.type === 'ArrowFunctionExpression') { if (usesThisOrArguments(prop.value.body)) { - validator.error( - `'oncreate' should be a function expression, not an arrow function expression`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `'oncreate' should be a function expression, not an arrow function expression` + }); } } } diff --git a/src/validate/js/propValidators/ondestroy.ts b/src/validate/js/propValidators/ondestroy.ts index 988592a4f8..ff2565fffb 100644 --- a/src/validate/js/propValidators/ondestroy.ts +++ b/src/validate/js/propValidators/ondestroy.ts @@ -1,14 +1,14 @@ import usesThisOrArguments from '../utils/usesThisOrArguments'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function ondestroy(validator: Validator, prop: Node) { if (prop.value.type === 'ArrowFunctionExpression') { if (usesThisOrArguments(prop.value.body)) { - validator.error( - `'ondestroy' should be a function expression, not an arrow function expression`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `'ondestroy' should be a function expression, not an arrow function expression` + }); } } } diff --git a/src/validate/js/propValidators/preload.ts b/src/validate/js/propValidators/preload.ts index afcbe79bf1..8b93ce8ec8 100644 --- a/src/validate/js/propValidators/preload.ts +++ b/src/validate/js/propValidators/preload.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function preload(validator: Validator, prop: Node) { diff --git a/src/validate/js/propValidators/props.ts b/src/validate/js/propValidators/props.ts index 6731d6adcf..0030e0db40 100644 --- a/src/validate/js/propValidators/props.ts +++ b/src/validate/js/propValidators/props.ts @@ -1,21 +1,21 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; import nodeToString from '../../../utils/nodeToString'; export default function props(validator: Validator, prop: Node) { if (prop.value.type !== 'ArrayExpression') { - validator.error( - `'props' must be an array expression, if specified`, - prop.value - ); + validator.error(prop.value, { + code: `invalid-property`, + message: `'props' must be an array expression, if specified` + }); } prop.value.elements.forEach((element: Node) => { if (typeof nodeToString(element) !== 'string') { - validator.error( - `'props' must be an array of string literals`, - element - ); + validator.error(element, { + code: `invalid-property`, + message: `'props' must be an array of string literals` + }); } }); } diff --git a/src/validate/js/propValidators/setup.ts b/src/validate/js/propValidators/setup.ts index f98d78cb6b..709fee8c9a 100644 --- a/src/validate/js/propValidators/setup.ts +++ b/src/validate/js/propValidators/setup.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; 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; 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` + }); } } diff --git a/src/validate/js/propValidators/store.ts b/src/validate/js/propValidators/store.ts index 27da5551c6..48a1de6573 100644 --- a/src/validate/js/propValidators/store.ts +++ b/src/validate/js/propValidators/store.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function store(validator: Validator, prop: Node) { diff --git a/src/validate/js/propValidators/tag.ts b/src/validate/js/propValidators/tag.ts index 80a38baa80..3eb76ee5e8 100644 --- a/src/validate/js/propValidators/tag.ts +++ b/src/validate/js/propValidators/tag.ts @@ -1,20 +1,20 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; import nodeToString from '../../../utils/nodeToString'; export default function tag(validator: Validator, prop: Node) { const tag = nodeToString(prop.value); if (typeof tag !== 'string') { - validator.error( - `'tag' must be a string literal`, - prop.value - ); + validator.error(prop.value, { + code: `invalid-property`, + message: `'tag' must be a string literal` + }); } if (!/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { - validator.error( - `tag name must be two or more words joined by the '-' character`, - prop.value - ); + validator.error(prop.value, { + code: `invalid-property`, + message: `tag name must be two or more words joined by the '-' character` + }); } } diff --git a/src/validate/js/propValidators/transitions.ts b/src/validate/js/propValidators/transitions.ts index 4675f9fd92..5a814d244f 100644 --- a/src/validate/js/propValidators/transitions.ts +++ b/src/validate/js/propValidators/transitions.ts @@ -1,14 +1,14 @@ import checkForDupes from '../utils/checkForDupes'; import checkForComputedKeys from '../utils/checkForComputedKeys'; -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function transitions(validator: Validator, prop: Node) { if (prop.value.type !== 'ObjectExpression') { - validator.error( - `The 'transitions' property must be an object literal`, - prop - ); + validator.error(prop, { + code: `invalid-property`, + message: `The 'transitions' property must be an object literal` + }); } checkForDupes(validator, prop.value.properties); diff --git a/src/validate/js/utils/checkForAccessors.ts b/src/validate/js/utils/checkForAccessors.ts index 2445707a1c..10a88b8f36 100644 --- a/src/validate/js/utils/checkForAccessors.ts +++ b/src/validate/js/utils/checkForAccessors.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function checkForAccessors( @@ -8,7 +8,10 @@ export default function checkForAccessors( ) { properties.forEach(prop => { 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` + }); } }); } diff --git a/src/validate/js/utils/checkForComputedKeys.ts b/src/validate/js/utils/checkForComputedKeys.ts index 61e7efabbe..026f1a849d 100644 --- a/src/validate/js/utils/checkForComputedKeys.ts +++ b/src/validate/js/utils/checkForComputedKeys.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; export default function checkForComputedKeys( @@ -7,7 +7,10 @@ export default function checkForComputedKeys( ) { properties.forEach(prop => { if (prop.key.computed) { - validator.error(`Cannot use computed keys`, prop); + validator.error(prop, { + code: `computed-key`, + message: `Cannot use computed keys` + }); } }); } diff --git a/src/validate/js/utils/checkForDupes.ts b/src/validate/js/utils/checkForDupes.ts index 42fbcd137d..2629d7d10f 100644 --- a/src/validate/js/utils/checkForDupes.ts +++ b/src/validate/js/utils/checkForDupes.ts @@ -1,4 +1,4 @@ -import { Validator } from '../../'; +import { Validator } from '../../index'; import { Node } from '../../../interfaces'; import getName from '../../../utils/getName'; @@ -12,7 +12,10 @@ export default function checkForDupes( const name = getName(prop.key); if (seen.has(name)) { - validator.error(`Duplicate property '${name}'`, prop); + validator.error(prop, { + code: `duplicate-property`, + message: `Duplicate property '${name}'` + }); } seen.add(name); diff --git a/test/validator/index.js b/test/validator/index.js index 211c763f48..c79c5c07d8 100644 --- a/test/validator/index.js +++ b/test/validator/index.js @@ -2,7 +2,7 @@ import * as fs from "fs"; import assert from "assert"; import { svelte, loadConfig, tryToLoadJson } from "../helpers.js"; -describe("validate", () => { +describe.only("validate", () => { fs.readdirSync("test/validator/samples").forEach(dir => { if (dir[0] === ".") return; diff --git a/test/validator/samples/action-invalid/errors.json b/test/validator/samples/action-invalid/errors.json index 1b6c17d9fd..b4e7f1bac1 100644 --- a/test/validator/samples/action-invalid/errors.json +++ b/test/validator/samples/action-invalid/errors.json @@ -1,4 +1,5 @@ [{ + "code": "missing-action", "message": "Missing action 'whatever'", "pos": 5, "loc": { diff --git a/test/validator/samples/action-on-component/errors.json b/test/validator/samples/action-on-component/errors.json index f146d072b0..3b190ace21 100644 --- a/test/validator/samples/action-on-component/errors.json +++ b/test/validator/samples/action-on-component/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-action", "message": "Actions can only be applied to DOM elements, not components", "pos": 8, "loc": { diff --git a/test/validator/samples/binding-input-checked/errors.json b/test/validator/samples/binding-input-checked/errors.json index 477181e40c..b135d58aa2 100644 --- a/test/validator/samples/binding-input-checked/errors.json +++ b/test/validator/samples/binding-input-checked/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-binding", "message": "'checked' binding can only be used with <input type=\"checkbox\">", "loc": { "line": 1, diff --git a/test/validator/samples/binding-input-type-boolean/errors.json b/test/validator/samples/binding-input-type-boolean/errors.json index 7a2dbf2461..eaaf0266ab 100644 --- a/test/validator/samples/binding-input-type-boolean/errors.json +++ b/test/validator/samples/binding-input-type-boolean/errors.json @@ -1,4 +1,5 @@ [{ + "code": "missing-type", "message": "'type' attribute must be specified", "loc": { "line": 1, diff --git a/test/validator/samples/binding-input-type-dynamic/errors.json b/test/validator/samples/binding-input-type-dynamic/errors.json index 04ea638354..315c8aadb6 100644 --- a/test/validator/samples/binding-input-type-dynamic/errors.json +++ b/test/validator/samples/binding-input-type-dynamic/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-type", "message": "'type' attribute cannot be dynamic if input uses two-way binding", "loc": { "line": 1, diff --git a/test/validator/samples/binding-invalid-on-element/errors.json b/test/validator/samples/binding-invalid-on-element/errors.json index a0d783389b..5301203e86 100644 --- a/test/validator/samples/binding-invalid-on-element/errors.json +++ b/test/validator/samples/binding-invalid-on-element/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-binding", "message": "'value' is not a valid binding on <div> elements", "pos": 5, "loc": { diff --git a/test/validator/samples/binding-invalid/errors.json b/test/validator/samples/binding-invalid/errors.json index 7893961760..fca76fdf79 100644 --- a/test/validator/samples/binding-invalid/errors.json +++ b/test/validator/samples/binding-invalid/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-binding", "message": "'whatever' is not a valid binding", "pos": 5, "loc": { diff --git a/test/validator/samples/binding-select-multiple-dynamic/errors.json b/test/validator/samples/binding-select-multiple-dynamic/errors.json index f6c9f145af..b85ff5a3ac 100644 --- a/test/validator/samples/binding-select-multiple-dynamic/errors.json +++ b/test/validator/samples/binding-select-multiple-dynamic/errors.json @@ -1,4 +1,5 @@ [{ + "code": "dynamic-multiple-attribute", "message": "'multiple' attribute cannot be dynamic if select uses two-way binding", "loc": { "line": 1, diff --git a/test/validator/samples/component-cannot-be-called-state/errors.json b/test/validator/samples/component-cannot-be-called-state/errors.json index 55ffcc4749..68787e7e7c 100644 --- a/test/validator/samples/component-cannot-be-called-state/errors.json +++ b/test/validator/samples/component-cannot-be-called-state/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-name", "message": "Component constructors cannot be called 'state' due to technical limitations", "pos": 73, "loc": { diff --git a/test/validator/samples/component-slot-default-reserved/errors.json b/test/validator/samples/component-slot-default-reserved/errors.json index 769684407d..3605cd6d3b 100644 --- a/test/validator/samples/component-slot-default-reserved/errors.json +++ b/test/validator/samples/component-slot-default-reserved/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slot-name", "message": "default is a reserved word — it cannot be used as a slot name", "loc": { "line": 1, diff --git a/test/validator/samples/component-slot-dynamic-attribute/errors.json b/test/validator/samples/component-slot-dynamic-attribute/errors.json index 038e22ce32..8652396949 100644 --- a/test/validator/samples/component-slot-dynamic-attribute/errors.json +++ b/test/validator/samples/component-slot-dynamic-attribute/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slot-attribute", "message": "slot attribute cannot have a dynamic value", "loc": { "line": 2, diff --git a/test/validator/samples/component-slot-dynamic/errors.json b/test/validator/samples/component-slot-dynamic/errors.json index 7c3c96c37d..e09b1ab916 100644 --- a/test/validator/samples/component-slot-dynamic/errors.json +++ b/test/validator/samples/component-slot-dynamic/errors.json @@ -1,4 +1,5 @@ [{ + "code": "dynamic-slot-name", "message": "<slot> name cannot be dynamic", "loc": { "line": 1, diff --git a/test/validator/samples/component-slot-each-block/errors.json b/test/validator/samples/component-slot-each-block/errors.json index 71671fe1d7..0e67b707ab 100644 --- a/test/validator/samples/component-slot-each-block/errors.json +++ b/test/validator/samples/component-slot-each-block/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slot-placement", "message": "<slot> cannot be a child of an each-block", "loc": { "line": 2, diff --git a/test/validator/samples/component-slotted-each-block/errors.json b/test/validator/samples/component-slotted-each-block/errors.json index fb9a71d223..5d0102abf2 100644 --- a/test/validator/samples/component-slotted-each-block/errors.json +++ b/test/validator/samples/component-slotted-each-block/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slotted-content", "message": "Cannot place slotted elements inside an each-block", "loc": { "line": 3, diff --git a/test/validator/samples/component-slotted-if-block/errors.json b/test/validator/samples/component-slotted-if-block/errors.json index f1dfee1eb1..09518af674 100644 --- a/test/validator/samples/component-slotted-if-block/errors.json +++ b/test/validator/samples/component-slotted-if-block/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slotted-content", "message": "Cannot place slotted elements inside an if-block", "loc": { "line": 3, diff --git a/test/validator/samples/computed-purity-check-no-this/errors.json b/test/validator/samples/computed-purity-check-no-this/errors.json index f13a9ec5a0..f704b7a3ef 100644 --- a/test/validator/samples/computed-purity-check-no-this/errors.json +++ b/test/validator/samples/computed-purity-check-no-this/errors.json @@ -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'?", "pos": 83, "loc": { diff --git a/test/validator/samples/computed-purity-check-this-get/errors.json b/test/validator/samples/computed-purity-check-this-get/errors.json index a6f627e61d..035d5fee4f 100644 --- a/test/validator/samples/computed-purity-check-this-get/errors.json +++ b/test/validator/samples/computed-purity-check-this-get/errors.json @@ -1,4 +1,5 @@ [{ + "code": "impure-computed", "message": "Cannot use this.get(...) — values must be passed into the function as arguments", "pos": 73, "loc": { diff --git a/test/validator/samples/css-invalid-global-placement/errors.json b/test/validator/samples/css-invalid-global-placement/errors.json index e226a45cb7..e6026b7c95 100644 --- a/test/validator/samples/css-invalid-global-placement/errors.json +++ b/test/validator/samples/css-invalid-global-placement/errors.json @@ -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", "loc": { "line": 2, diff --git a/test/validator/samples/css-invalid-global/errors.json b/test/validator/samples/css-invalid-global/errors.json index 95e9e05f0d..6c6548dd80 100644 --- a/test/validator/samples/css-invalid-global/errors.json +++ b/test/validator/samples/css-invalid-global/errors.json @@ -1,4 +1,5 @@ [{ + "code": "css-invalid-global", "message": ":global(...) must be the first element in a compound selector", "loc": { "line": 2, diff --git a/test/validator/samples/event-handler-ref-invalid/errors.json b/test/validator/samples/event-handler-ref-invalid/errors.json index 2cd043f8ee..f2c80ca6a4 100644 --- a/test/validator/samples/event-handler-ref-invalid/errors.json +++ b/test/validator/samples/event-handler-ref-invalid/errors.json @@ -1,4 +1,5 @@ [{ + "code": "missing-ref", "message": "'refs.inputx' does not exist (did you mean 'refs.input'?)", "pos": 36, "loc": { diff --git a/test/validator/samples/export-default-must-be-object/errors.json b/test/validator/samples/export-default-must-be-object/errors.json index 90b8434ec7..253922a038 100644 --- a/test/validator/samples/export-default-must-be-object/errors.json +++ b/test/validator/samples/export-default-must-be-object/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-default-export", "message": "Default export must be an object literal", "pos": 25, "loc": { diff --git a/test/validator/samples/helper-purity-check-no-this/errors.json b/test/validator/samples/helper-purity-check-no-this/errors.json index 484fb8dde7..6a43b50404 100644 --- a/test/validator/samples/helper-purity-check-no-this/errors.json +++ b/test/validator/samples/helper-purity-check-no-this/errors.json @@ -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'?", "pos": 95, "loc": { diff --git a/test/validator/samples/helper-purity-check-this-get/errors.json b/test/validator/samples/helper-purity-check-this-get/errors.json index a18645579e..134cf9bb58 100644 --- a/test/validator/samples/helper-purity-check-this-get/errors.json +++ b/test/validator/samples/helper-purity-check-this-get/errors.json @@ -1,4 +1,5 @@ [{ + "code": "impure-helper", "message": "Cannot use this.get(...) — values must be passed into the helper function as arguments", "pos": 74, "loc": { diff --git a/test/validator/samples/method-arrow-this/errors.json b/test/validator/samples/method-arrow-this/errors.json index 82c82bcc88..62eccbe3fe 100644 --- a/test/validator/samples/method-arrow-this/errors.json +++ b/test/validator/samples/method-arrow-this/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-method-value", "message": "Method 'foo' should be a function expression, not an arrow function expression", "pos": 79, "loc": { diff --git a/test/validator/samples/named-export/errors.json b/test/validator/samples/named-export/errors.json index 331f66c8c9..0dad6200f9 100644 --- a/test/validator/samples/named-export/errors.json +++ b/test/validator/samples/named-export/errors.json @@ -1,4 +1,5 @@ [{ + "code": "named-export", "message": "A component can only have a default export", "pos": 10, "loc": { diff --git a/test/validator/samples/namespace-invalid-unguessable/errors.json b/test/validator/samples/namespace-invalid-unguessable/errors.json index bc4e72ba13..c734fbc9bb 100644 --- a/test/validator/samples/namespace-invalid-unguessable/errors.json +++ b/test/validator/samples/namespace-invalid-unguessable/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "Invalid namespace 'lol'", "pos": 29, "loc": { diff --git a/test/validator/samples/namespace-invalid/errors.json b/test/validator/samples/namespace-invalid/errors.json index 9bda600826..731363c90a 100644 --- a/test/validator/samples/namespace-invalid/errors.json +++ b/test/validator/samples/namespace-invalid/errors.json @@ -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'?)", "pos": 29, "loc": { diff --git a/test/validator/samples/namespace-non-literal/errors.json b/test/validator/samples/namespace-non-literal/errors.json index 87e6661ca2..dda09aabe2 100644 --- a/test/validator/samples/namespace-non-literal/errors.json +++ b/test/validator/samples/namespace-non-literal/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'namespace' property must be a string literal representing a valid namespace", "pos": 79, "loc": { diff --git a/test/validator/samples/non-object-literal-components/errors.json b/test/validator/samples/non-object-literal-components/errors.json index b24462633d..0e6aac6612 100644 --- a/test/validator/samples/non-object-literal-components/errors.json +++ b/test/validator/samples/non-object-literal-components/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'components' property must be an object literal", "loc": { "line": 3, diff --git a/test/validator/samples/non-object-literal-events/errors.json b/test/validator/samples/non-object-literal-events/errors.json index 9ff486382a..88bb9e8523 100644 --- a/test/validator/samples/non-object-literal-events/errors.json +++ b/test/validator/samples/non-object-literal-events/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'events' property must be an object literal", "loc": { "line": 3, diff --git a/test/validator/samples/non-object-literal-helpers/errors.json b/test/validator/samples/non-object-literal-helpers/errors.json index 89448096dd..56048de728 100644 --- a/test/validator/samples/non-object-literal-helpers/errors.json +++ b/test/validator/samples/non-object-literal-helpers/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'helpers' property must be an object literal", "loc": { "line": 3, diff --git a/test/validator/samples/non-object-literal-methods/errors.json b/test/validator/samples/non-object-literal-methods/errors.json index 94ae9a54ba..4f5767603a 100644 --- a/test/validator/samples/non-object-literal-methods/errors.json +++ b/test/validator/samples/non-object-literal-methods/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'methods' property must be an object literal", "loc": { "line": 3, diff --git a/test/validator/samples/non-object-literal-transitions/errors.json b/test/validator/samples/non-object-literal-transitions/errors.json index b897a49c58..eecdab50cf 100644 --- a/test/validator/samples/non-object-literal-transitions/errors.json +++ b/test/validator/samples/non-object-literal-transitions/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'transitions' property must be an object literal", "loc": { "line": 3, diff --git a/test/validator/samples/oncreate-arrow-this/errors.json b/test/validator/samples/oncreate-arrow-this/errors.json index 71efdb4b2c..61bcdc3749 100644 --- a/test/validator/samples/oncreate-arrow-this/errors.json +++ b/test/validator/samples/oncreate-arrow-this/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'oncreate' should be a function expression, not an arrow function expression", "pos": 29, "loc": { diff --git a/test/validator/samples/ondestroy-arrow-this/errors.json b/test/validator/samples/ondestroy-arrow-this/errors.json index c266c0754f..ab252e3289 100644 --- a/test/validator/samples/ondestroy-arrow-this/errors.json +++ b/test/validator/samples/ondestroy-arrow-this/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'ondestroy' should be a function expression, not an arrow function expression", "pos": 29, "loc": { diff --git a/test/validator/samples/properties-computed-cannot-be-reserved/errors.json b/test/validator/samples/properties-computed-cannot-be-reserved/errors.json index 5ba22a2406..d7e2702d40 100644 --- a/test/validator/samples/properties-computed-cannot-be-reserved/errors.json +++ b/test/validator/samples/properties-computed-cannot-be-reserved/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-computed-name", "message": "Computed property name 'new' is invalid — cannot be a JavaScript reserved word", "loc": { diff --git a/test/validator/samples/properties-computed-must-be-an-object/errors.json b/test/validator/samples/properties-computed-must-be-an-object/errors.json index 9928b933ff..eb547398ae 100644 --- a/test/validator/samples/properties-computed-must-be-an-object/errors.json +++ b/test/validator/samples/properties-computed-must-be-an-object/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "The 'computed' property must be an object literal", "loc": { "line": 5, diff --git a/test/validator/samples/properties-computed-must-be-functions/errors.json b/test/validator/samples/properties-computed-must-be-functions/errors.json index 91498f6f0c..5db219b222 100644 --- a/test/validator/samples/properties-computed-must-be-functions/errors.json +++ b/test/validator/samples/properties-computed-must-be-functions/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-computed-value", "message": "Computed properties can be function expressions or arrow function expressions", "loc": { "line": 6, diff --git a/test/validator/samples/properties-computed-must-be-valid-function-names/errors.json b/test/validator/samples/properties-computed-must-be-valid-function-names/errors.json index b29b60a5a8..7cdc2df451 100644 --- a/test/validator/samples/properties-computed-must-be-valid-function-names/errors.json +++ b/test/validator/samples/properties-computed-must-be-valid-function-names/errors.json @@ -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", "loc": { "line": 9, diff --git a/test/validator/samples/properties-computed-no-destructuring/errors.json b/test/validator/samples/properties-computed-no-destructuring/errors.json index 0426542fcb..7ac9e009d6 100644 --- a/test/validator/samples/properties-computed-no-destructuring/errors.json +++ b/test/validator/samples/properties-computed-no-destructuring/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-computed-arguments", "message": "Computed properties cannot use destructuring in function parameters", "loc": { "line": 6, diff --git a/test/validator/samples/properties-computed-values-needs-arguments/errors.json b/test/validator/samples/properties-computed-values-needs-arguments/errors.json index bc99ad0969..20d33b1fe9 100644 --- a/test/validator/samples/properties-computed-values-needs-arguments/errors.json +++ b/test/validator/samples/properties-computed-values-needs-arguments/errors.json @@ -1,4 +1,5 @@ [{ + "code": "impure-computed", "message": "A computed value must depend on at least one property", "pos": 49, "loc": { diff --git a/test/validator/samples/properties-data-must-be-function/errors.json b/test/validator/samples/properties-data-must-be-function/errors.json index ac4c70b4d2..2947c44455 100644 --- a/test/validator/samples/properties-data-must-be-function/errors.json +++ b/test/validator/samples/properties-data-must-be-function/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'data' must be a function", "loc": { "line": 5, diff --git a/test/validator/samples/properties-duplicated/errors.json b/test/validator/samples/properties-duplicated/errors.json index b0fb99dbdb..3b0acf6a12 100644 --- a/test/validator/samples/properties-duplicated/errors.json +++ b/test/validator/samples/properties-duplicated/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-property", "message": "Duplicate property 'methods'", "loc": { "line": 9, diff --git a/test/validator/samples/properties-methods-getters-setters/errors.json b/test/validator/samples/properties-methods-getters-setters/errors.json index baeb863631..5cde6051e4 100644 --- a/test/validator/samples/properties-methods-getters-setters/errors.json +++ b/test/validator/samples/properties-methods-getters-setters/errors.json @@ -1,4 +1,5 @@ [{ + "code": "illegal-accessor", "message": "Methods cannot use getters and setters", "loc": { "line": 4, diff --git a/test/validator/samples/properties-props-must-be-an-array/errors.json b/test/validator/samples/properties-props-must-be-an-array/errors.json index a1d3686c9a..1114860ea0 100644 --- a/test/validator/samples/properties-props-must-be-an-array/errors.json +++ b/test/validator/samples/properties-props-must-be-an-array/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'props' must be an array expression, if specified", "loc": { "line": 5, diff --git a/test/validator/samples/properties-props-must-be-array-of-strings/errors.json b/test/validator/samples/properties-props-must-be-array-of-strings/errors.json index 4d7e2ab20b..12f7eaf687 100644 --- a/test/validator/samples/properties-props-must-be-array-of-strings/errors.json +++ b/test/validator/samples/properties-props-must-be-array-of-strings/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'props' must be an array of string literals", "loc": { "line": 5, diff --git a/test/validator/samples/properties-unexpected-b/errors.json b/test/validator/samples/properties-unexpected-b/errors.json index ffa07eea39..45694450a8 100644 --- a/test/validator/samples/properties-unexpected-b/errors.json +++ b/test/validator/samples/properties-unexpected-b/errors.json @@ -1,4 +1,5 @@ [{ + "code": "unexpected-property", "message": "Unexpected property 'doSomething' (did you mean to include it in 'methods'?)", "loc": { "line": 5, diff --git a/test/validator/samples/properties-unexpected/errors.json b/test/validator/samples/properties-unexpected/errors.json index a9e17c465e..5bc2a4b416 100644 --- a/test/validator/samples/properties-unexpected/errors.json +++ b/test/validator/samples/properties-unexpected/errors.json @@ -1,4 +1,5 @@ [{ + "code": "unexpected-property", "message": "Unexpected property 'dada' (did you mean 'data'?)", "loc": { "line": 5, diff --git a/test/validator/samples/slot-attribute-invalid/errors.json b/test/validator/samples/slot-attribute-invalid/errors.json index 5243a6ebd9..5c56bed158 100644 --- a/test/validator/samples/slot-attribute-invalid/errors.json +++ b/test/validator/samples/slot-attribute-invalid/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-slotted-content", "message": "Element with a slot='...' attribute must be a descendant of a component or custom element", "loc": { "line": 1, diff --git a/test/validator/samples/tag-invalid/errors.json b/test/validator/samples/tag-invalid/errors.json index c57ea66f0b..5be5a54df1 100644 --- a/test/validator/samples/tag-invalid/errors.json +++ b/test/validator/samples/tag-invalid/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "tag name must be two or more words joined by the '-' character", "loc": { "line": 3, diff --git a/test/validator/samples/tag-non-string/errors.json b/test/validator/samples/tag-non-string/errors.json index 81a0dc51b3..5b24822156 100644 --- a/test/validator/samples/tag-non-string/errors.json +++ b/test/validator/samples/tag-non-string/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-property", "message": "'tag' must be a string literal", "loc": { "line": 3, diff --git a/test/validator/samples/textarea-value-children/errors.json b/test/validator/samples/textarea-value-children/errors.json index a35bc57136..24d94b9342 100644 --- a/test/validator/samples/textarea-value-children/errors.json +++ b/test/validator/samples/textarea-value-children/errors.json @@ -1,4 +1,5 @@ [{ + "code": "textarea-duplicate-value", "message": "A <textarea> can have either a value attribute or (equivalently) child content, but not both", "loc": { "line": 1, diff --git a/test/validator/samples/title-no-attributes/errors-v2.json b/test/validator/samples/title-no-attributes/errors-v2.json index 0491303d0a..84b421c093 100644 --- a/test/validator/samples/title-no-attributes/errors-v2.json +++ b/test/validator/samples/title-no-attributes/errors-v2.json @@ -1,4 +1,5 @@ [{ + "code": "illegal-attribute", "message": "<title> cannot have attributes", "loc": { "line": 2, diff --git a/test/validator/samples/title-no-attributes/errors.json b/test/validator/samples/title-no-attributes/errors.json index 613a7e2bc6..ebf39c3213 100644 --- a/test/validator/samples/title-no-attributes/errors.json +++ b/test/validator/samples/title-no-attributes/errors.json @@ -1,4 +1,5 @@ [{ + "code": "illegal-attribute", "message": "<title> cannot have attributes", "loc": { "line": 2, diff --git a/test/validator/samples/title-no-children/errors.json b/test/validator/samples/title-no-children/errors.json index f62fb4d0ba..12110831f7 100644 --- a/test/validator/samples/title-no-children/errors.json +++ b/test/validator/samples/title-no-children/errors.json @@ -1,4 +1,5 @@ [{ + "code": "illegal-structure", "message": "<title> can only contain text and {{tags}}", "loc": { "line": 2, diff --git a/test/validator/samples/transition-duplicate-in-transition/errors.json b/test/validator/samples/transition-duplicate-in-transition/errors.json index c9cad28cf7..b3e90c0fa1 100644 --- a/test/validator/samples/transition-duplicate-in-transition/errors.json +++ b/test/validator/samples/transition-duplicate-in-transition/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element cannot have both an 'in' directive and a 'transition' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-in/errors.json b/test/validator/samples/transition-duplicate-in/errors.json index 184a10ca80..3d48443486 100644 --- a/test/validator/samples/transition-duplicate-in/errors.json +++ b/test/validator/samples/transition-duplicate-in/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element can only have one 'in' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-out-transition/errors.json b/test/validator/samples/transition-duplicate-out-transition/errors.json index 6d9df9b0c7..5955783ac9 100644 --- a/test/validator/samples/transition-duplicate-out-transition/errors.json +++ b/test/validator/samples/transition-duplicate-out-transition/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element cannot have both an 'out' directive and a 'transition' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-out/errors.json b/test/validator/samples/transition-duplicate-out/errors.json index 524de7e4df..529256d965 100644 --- a/test/validator/samples/transition-duplicate-out/errors.json +++ b/test/validator/samples/transition-duplicate-out/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element can only have one 'out' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-transition-in/errors.json b/test/validator/samples/transition-duplicate-transition-in/errors.json index 38b0883aba..ce39d08e7e 100644 --- a/test/validator/samples/transition-duplicate-transition-in/errors.json +++ b/test/validator/samples/transition-duplicate-transition-in/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element cannot have both a 'transition' directive and an 'in' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-transition-out/errors.json b/test/validator/samples/transition-duplicate-transition-out/errors.json index 07db9792f0..59cde4fbd0 100644 --- a/test/validator/samples/transition-duplicate-transition-out/errors.json +++ b/test/validator/samples/transition-duplicate-transition-out/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element cannot have both a 'transition' directive and an 'out' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-duplicate-transition/errors.json b/test/validator/samples/transition-duplicate-transition/errors.json index b1deba96f0..7510d13b8d 100644 --- a/test/validator/samples/transition-duplicate-transition/errors.json +++ b/test/validator/samples/transition-duplicate-transition/errors.json @@ -1,4 +1,5 @@ [{ + "code": "duplicate-transition", "message": "An element can only have one 'transition' directive", "loc": { "line": 1, diff --git a/test/validator/samples/transition-missing/errors.json b/test/validator/samples/transition-missing/errors.json index 38ab5121a0..f647937245 100644 --- a/test/validator/samples/transition-missing/errors.json +++ b/test/validator/samples/transition-missing/errors.json @@ -1,4 +1,5 @@ [{ + "code": "missing-transition", "message": "Missing transition 'foo'", "loc": { "line": 1, diff --git a/test/validator/samples/transition-on-component/errors.json b/test/validator/samples/transition-on-component/errors.json index 99906238dc..8e2769055f 100644 --- a/test/validator/samples/transition-on-component/errors.json +++ b/test/validator/samples/transition-on-component/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-transition", "message": "Transitions can only be applied to DOM elements, not components", "loc": { "line": 1, diff --git a/test/validator/samples/window-binding-invalid-innerwidth/errors.json b/test/validator/samples/window-binding-invalid-innerwidth/errors.json index 891af56688..2f57874100 100644 --- a/test/validator/samples/window-binding-invalid-innerwidth/errors.json +++ b/test/validator/samples/window-binding-invalid-innerwidth/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-binding", "message": "'innerwidth' is not a valid binding on <:Window> (did you mean 'innerWidth'?)", "loc": { "line": 1, diff --git a/test/validator/samples/window-binding-invalid-value/errors.json b/test/validator/samples/window-binding-invalid-value/errors.json index 85672d4486..77256d371e 100644 --- a/test/validator/samples/window-binding-invalid-value/errors.json +++ b/test/validator/samples/window-binding-invalid-value/errors.json @@ -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'", "loc": { "line": 1, diff --git a/test/validator/samples/window-binding-invalid-width/errors.json b/test/validator/samples/window-binding-invalid-width/errors.json index 4b10ce8531..d065c442b4 100644 --- a/test/validator/samples/window-binding-invalid-width/errors.json +++ b/test/validator/samples/window-binding-invalid-width/errors.json @@ -1,4 +1,5 @@ [{ + "code": "invalid-binding", "message": "'width' is not a valid binding on <:Window> (did you mean 'innerWidth'?)", "loc": { "line": 1, diff --git a/test/validator/samples/window-binding-invalid/errors.json b/test/validator/samples/window-binding-invalid/errors.json index 5b9dd2589c..a7bbd525e5 100644 --- a/test/validator/samples/window-binding-invalid/errors.json +++ b/test/validator/samples/window-binding-invalid/errors.json @@ -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", "loc": { "line": 1,