pass node through where possible

pull/1250/head
James Birtles 8 years ago
parent da6a74016f
commit 4081a8a363

@ -102,7 +102,7 @@ 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`, { start: selector.start, end: selector.end }); validator.error(`:global(...) must be the first element in a compound selector`, selector);
} }
} }
}); });
@ -120,8 +120,7 @@ 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) {
const selector = this.blocks[i].selectors[0]; 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(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, { start: selector.start, end: selector.end });
} }
} }
} }

@ -29,6 +29,7 @@ export interface Parsed {
export interface Warning { export interface Warning {
loc?: { line: number; column: number; pos?: number }; loc?: { line: number; column: number; pos?: number };
end?: { line: number; column: number; };
pos?: number; pos?: number;
message: string; message: string;
filename?: string; filename?: string;

@ -33,7 +33,7 @@ export default function a11y(
if (name.startsWith('aria-')) { if (name.startsWith('aria-')) {
if (invisibleElements.has(node.name)) { if (invisibleElements.has(node.name)) {
// aria-unsupported-elements // aria-unsupported-elements
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute);
} }
const type = name.slice(5); const type = name.slice(5);
@ -42,7 +42,7 @@ export default function a11y(
let message = `A11y: Unknown aria attribute 'aria-${type}'`; let message = `A11y: Unknown aria attribute 'aria-${type}'`;
if (match) message += ` (did you mean '${match}'?)`; if (match) message += ` (did you mean '${match}'?)`;
validator.warn(message, { start: attribute.start, end: attribute.end }); validator.warn(message, attribute);
} }
} }
@ -50,7 +50,7 @@ export default function a11y(
if (name === 'role') { if (name === 'role') {
if (invisibleElements.has(node.name)) { if (invisibleElements.has(node.name)) {
// aria-unsupported-elements // aria-unsupported-elements
validator.warn(`A11y: <${node.name}> should not have role attribute`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute);
} }
const value = getStaticAttributeValue(node, 'role'); const value = getStaticAttributeValue(node, 'role');
@ -59,30 +59,30 @@ export default function a11y(
let message = `A11y: Unknown role '${value}'`; let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`; if (match) message += ` (did you mean '${match}'?)`;
validator.warn(message, { start: attribute.start, end: attribute.end }); validator.warn(message, attribute);
} }
} }
// no-access-key // no-access-key
if (name === 'accesskey') { if (name === 'accesskey') {
validator.warn(`A11y: Avoid using accesskey`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: Avoid using accesskey`, attribute);
} }
// no-autofocus // no-autofocus
if (name === 'autofocus') { if (name === 'autofocus') {
validator.warn(`A11y: Avoid using autofocus`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: Avoid using autofocus`, attribute);
} }
// scope // scope
if (name === 'scope' && node.name !== 'th') { if (name === 'scope' && node.name !== 'th') {
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute);
} }
// tabindex-no-positive // tabindex-no-positive
if (name === 'tabindex') { if (name === 'tabindex') {
const value = getStaticAttributeValue(node, 'tabindex'); const value = getStaticAttributeValue(node, 'tabindex');
if (!isNaN(value) && +value > 0) { if (!isNaN(value) && +value > 0) {
validator.warn(`A11y: avoid tabindex values above zero`, { start: attribute.start, end: attribute.end }); validator.warn(`A11y: avoid tabindex values above zero`, attribute);
} }
} }
@ -96,13 +96,13 @@ export default function a11y(
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` : attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
attributes[0]; attributes[0];
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, { start: node.start, end: node.end }); validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node);
} }
} }
function shouldHaveContent() { function shouldHaveContent() {
if (node.children.length === 0) { if (node.children.length === 0) {
validator.warn(`A11y: <${node.name}> element should have child content`, { start: node.start, end: node.end }); validator.warn(`A11y: <${node.name}> element should have child content`, node);
} }
} }
@ -110,7 +110,7 @@ export default function a11y(
const href = attributeMap.get(attribute); const href = attributeMap.get(attribute);
const value = getStaticAttributeValue(node, attribute); const value = getStaticAttributeValue(node, attribute);
if (value === '' || value === '#') { if (value === '' || value === '#') {
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, { start: href.start, end: href.end }); validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href);
} }
} }
@ -122,7 +122,7 @@ export default function a11y(
// anchor-in-svg-is-valid // anchor-in-svg-is-valid
shouldHaveValidHref('xlink:href') shouldHaveValidHref('xlink:href')
} else { } else {
validator.warn(`A11y: <a> element should have an href attribute`, { start: node.start, end: node.end }); validator.warn(`A11y: <a> element should have an href attribute`, node);
} }
// anchor-has-content // anchor-has-content
@ -141,8 +141,7 @@ export default function a11y(
shouldHaveContent(); shouldHaveContent();
if (attributeMap.has('aria-hidden')) { if (attributeMap.has('aria-hidden')) {
const attr = attributeMap.get('aria-hidden'); validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden'));
validator.warn(`A11y: <${node.name}> element should not be hidden`, { start: attr.start, end: attr.end });
} }
} }
@ -158,14 +157,14 @@ export default function a11y(
// no-distracting-elements // no-distracting-elements
if (node.name === 'marquee' || node.name === 'blink') { if (node.name === 'marquee' || node.name === 'blink') {
validator.warn(`A11y: Avoid <${node.name}> elements`, { start: node.start, end: node.end }); validator.warn(`A11y: Avoid <${node.name}> elements`, node);
} }
if (node.name === 'figcaption') { if (node.name === 'figcaption') {
const parent = elementStack[elementStack.length - 1]; const parent = elementStack[elementStack.length - 1];
if (parent) { if (parent) {
if (parent.name !== 'figure') { if (parent.name !== 'figure') {
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, { start: node.start, end: node.end }); validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node);
} else { } else {
const children = parent.children.filter(node => { const children = parent.children.filter(node => {
if (node.type === 'Comment') return false; if (node.type === 'Comment') return false;
@ -176,7 +175,7 @@ export default function a11y(
const index = children.indexOf(node); const index = children.indexOf(node);
if (index !== 0 && index !== children.length - 1) { if (index !== 0 && index !== children.length - 1) {
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, { start: node.start, end: node.end }); validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node);
} }
} }
} }

@ -67,7 +67,7 @@ export default function validateHtml(validator: Validator, html: Node) {
} }
if (validator.options.dev && isEmptyBlock(node)) { if (validator.options.dev && isEmptyBlock(node)) {
validator.warn('Empty block', { start: node.start, end: node.end }); validator.warn('Empty block', node);
} }
if (node.children) { if (node.children) {
@ -103,7 +103,7 @@ 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, { start: callee.start, end: callee.end }); validator.error(message, callee);
} }
}); });
} }

@ -20,13 +20,13 @@ export default function validateElement(
if (!isComponent && /^[A-Z]/.test(node.name[0])) { if (!isComponent && /^[A-Z]/.test(node.name[0])) {
// TODO upgrade to validator.error in v2 // TODO upgrade to validator.error in v2
validator.warn(`${node.name} component is not defined`, { start: node.start, end: node.end }); validator.warn(`${node.name} component is not defined`, node);
} }
if (elementStack.length === 0 && validator.namespace !== namespaces.svg && svg.test(node.name)) { if (elementStack.length === 0 && validator.namespace !== namespaces.svg && svg.test(node.name)) {
validator.warn( validator.warn(
`<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`, `<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`,
{ start: node.start, end: node.end } node
); );
} }
@ -34,12 +34,12 @@ 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`, { start: nameAttribute.start, end: nameAttribute.end }); validator.error(`<slot> name cannot be dynamic`, nameAttribute);
} }
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`, { start: nameAttribute.start, end: nameAttribute.end }); validator.error(`default is a reserved word — it cannot be used as a slot name`, nameAttribute);
} }
// 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
@ -61,10 +61,9 @@ export default function validateElement(
if (node.name === 'title') { if (node.name === 'title') {
if (node.attributes.length > 0) { if (node.attributes.length > 0) {
const attr = node.attributes[0];
validator.error( validator.error(
`<title> cannot have attributes`, `<title> cannot have attributes`,
{ start: attr.start, end: attr.end } node.attributes[0]
); );
} }
@ -72,7 +71,7 @@ export default function validateElement(
if (child.type !== 'Text' && child.type !== 'MustacheTag') { if (child.type !== 'Text' && child.type !== 'MustacheTag') {
validator.error( validator.error(
`<title> can only contain text and {{tags}}`, `<title> can only contain text and {{tags}}`,
{ start: child.start, end: child.end } child
); );
} }
}); });
@ -99,7 +98,7 @@ export default function validateElement(
) { ) {
validator.error( validator.error(
`'value' is not a valid binding on <${node.name}> elements`, `'value' is not a valid binding on <${node.name}> elements`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -108,21 +107,21 @@ export default function validateElement(
if (node.name !== 'input') { if (node.name !== 'input') {
validator.error( validator.error(
`'${name}' is not a valid binding on <${node.name}> elements`, `'${name}' is not a valid binding on <${node.name}> elements`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
if (checkTypeAttribute(validator, node) !== 'checkbox') { if (checkTypeAttribute(validator, node) !== 'checkbox') {
validator.error( validator.error(
`'${name}' binding can only be used with <input type="checkbox">`, `'${name}' binding can only be used with <input type="checkbox">`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} else if (name === 'group') { } else if (name === 'group') {
if (node.name !== 'input') { if (node.name !== 'input') {
validator.error( validator.error(
`'group' is not a valid binding on <${node.name}> elements`, `'group' is not a valid binding on <${node.name}> elements`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -131,7 +130,7 @@ export default function validateElement(
if (type !== 'checkbox' && type !== 'radio') { if (type !== 'checkbox' && type !== 'radio') {
validator.error( validator.error(
`'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`, `'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} else if ( } else if (
@ -146,13 +145,13 @@ export default function validateElement(
if (node.name !== 'audio' && node.name !== 'video') { if (node.name !== 'audio' && node.name !== 'video') {
validator.error( validator.error(
`'${name}' binding can only be used with <audio> or <video>`, `'${name}' binding can only be used with <audio> or <video>`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} else { } else {
validator.error( validator.error(
`'${attribute.name}' is not a valid binding`, `'${attribute.name}' is not a valid binding`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} else if (attribute.type === 'EventHandler') { } else if (attribute.type === 'EventHandler') {
@ -160,7 +159,7 @@ export default function validateElement(
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`, { start: attribute.start, end: attribute.end }); validator.error(`Transitions can only be applied to DOM elements, not components`, attribute);
} }
validator.used.transitions.add(attribute.name); validator.used.transitions.add(attribute.name);
@ -171,13 +170,13 @@ export default function validateElement(
if (bidi) if (bidi)
validator.error( validator.error(
`An element can only have one 'transition' directive`, `An element can only have one 'transition' directive`,
{ start: attribute.start, end: attribute.end } attribute
); );
validator.error( validator.error(
`An element cannot have both a 'transition' directive and an '${attribute.intro `An element cannot have both a 'transition' directive and an '${attribute.intro
? 'in' ? 'in'
: 'out'}' directive`, : 'out'}' directive`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -187,11 +186,11 @@ export default function validateElement(
`An element cannot have both an '${hasIntro `An element cannot have both an '${hasIntro
? 'in' ? 'in'
: 'out'}' directive and a 'transition' directive`, : 'out'}' directive and a 'transition' directive`,
{ start: attribute.start, end: attribute.end } attribute
); );
validator.error( validator.error(
`An element can only have one '${hasIntro ? 'in' : 'out'}' directive`, `An element can only have one '${hasIntro ? 'in' : 'out'}' directive`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -202,7 +201,7 @@ export default function validateElement(
if (!validator.transitions.has(attribute.name)) { if (!validator.transitions.has(attribute.name)) {
validator.error( validator.error(
`Missing transition '${attribute.name}'`, `Missing transition '${attribute.name}'`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} else if (attribute.type === 'Attribute') { } else if (attribute.type === 'Attribute') {
@ -210,7 +209,7 @@ export default function validateElement(
if (node.children.length) { if (node.children.length) {
validator.error( validator.error(
`A <textarea> can have either a value attribute or (equivalently) child content, but not both`, `A <textarea> can have either a value attribute or (equivalently) child content, but not both`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} }
@ -229,13 +228,13 @@ 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`, { start: attribute.start, end: attribute.end }); validator.error(`'type' attribute must be specified`, attribute);
} }
if (isDynamic(attribute)) { if (isDynamic(attribute)) {
validator.error( validator.error(
`'type' attribute cannot be dynamic if input uses two-way binding`, `'type' attribute cannot be dynamic if input uses two-way binding`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -246,7 +245,7 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s
if (isDynamic(attribute)) { if (isDynamic(attribute)) {
validator.error( validator.error(
`slot attribute cannot have a dynamic value`, `slot attribute cannot have a dynamic value`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
@ -261,11 +260,11 @@ 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, { start: attribute.start, end: attribute.end }); validator.error(message, attribute);
} }
} }
validator.error(`Element with a slot='...' attribute must be a descendant of a component or custom element`, { start: attribute.start, end: attribute.end }); validator.error(`Element with a slot='...' attribute must be a descendant of a component or custom element`, attribute);
} }
function isDynamic(attribute: Node) { function isDynamic(attribute: Node) {

@ -16,7 +16,7 @@ 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`, { start: attribute.expression.start, end: attribute.expression.end }); validator.error(`Expected a call expression`, attribute.expression);
} }
const { name } = flattenReference(callee); const { name } = flattenReference(callee);
@ -32,7 +32,7 @@ export default function validateEventHandlerCallee(
if (!validator.options.store) { if (!validator.options.store) {
validator.warn( validator.warn(
'compile with `store: true` in order to call store methods', 'compile with `store: true` in order to call store methods',
{ start: attribute.expression.start, end: attribute.expression.end } attribute.expression
); );
} }
return; return;
@ -59,5 +59,5 @@ export default function validateEventHandlerCallee(
message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`; message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;
} }
validator.warn(message, { start: attribute.expression.start, end: attribute.expression.end }); validator.warn(message, attribute.expression);
} }

@ -4,7 +4,7 @@ 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`, { start: node.start, end: node.end }); validator.error(`<:Head> should not have any attributes or directives`, node);
} }
// TODO ensure only valid elements are included here // TODO ensure only valid elements are included here

@ -25,7 +25,7 @@ export default function validateWindow(validator: Validator, node: Node, refs: M
`Bindings on <:Window/> must be to top-level properties, e.g. '${parts[ `Bindings on <:Window/> must be to top-level properties, e.g. '${parts[
parts.length - 1 parts.length - 1
]}' rather than '${parts.join('.')}'`, ]}' rather than '${parts.join('.')}'`,
{ start: attribute.value.start, end: attribute.value.end } attribute.value
); );
} }
@ -41,12 +41,12 @@ export default function validateWindow(validator: Validator, node: Node, refs: M
if (match) { if (match) {
validator.error( validator.error(
`${message} (did you mean '${match}'?)`, `${message} (did you mean '${match}'?)`,
{ start: attribute.start, end: attribute.end } attribute
); );
} else { } else {
validator.error( validator.error(
`${message} — valid bindings are ${list(validBindings)}`, `${message} — valid bindings are ${list(validBindings)}`,
{ start: attribute.start, end: attribute.end } attribute
); );
} }
} }

@ -150,7 +150,7 @@ export default function validate(
if (!validator.used[category].has(name)) { if (!validator.used[category].has(name)) {
validator.warn( validator.warn(
`The '${name}' ${categories[category]} is unused`, `The '${name}' ${categories[category]} is unused`,
{ start: prop.start, end: prop.end } prop
); );
} }
}); });

@ -13,14 +13,14 @@ 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`, { start: node.start, end: node.start }); validator.error(`A component can only have a default export`, node);
} }
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
if (node.declaration.type !== 'ObjectExpression') { if (node.declaration.type !== 'ObjectExpression') {
return validator.error( return validator.error(
`Default export must be an object literal`, `Default export must be an object literal`,
{ start: node.declaration.start, end: node.declaration.end } node.declaration
); );
} }
@ -35,18 +35,16 @@ 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')) {
const onrender = props.get('onrender');
validator.error( validator.error(
'Cannot have both oncreate and onrender', 'Cannot have both oncreate and onrender',
{ start: onrender.start, end: onrender.end } props.get('onrender')
); );
} }
if (props.has('ondestroy') && props.has('onteardown')) { if (props.has('ondestroy') && props.has('onteardown')) {
const onteardown = props.get('onteardown');
validator.error( validator.error(
'Cannot have both ondestroy and onteardown', 'Cannot have both ondestroy and onteardown',
{ start: onteardown.start, end: onteardown.end } props.get('onteardown')
); );
} }
@ -62,17 +60,17 @@ export default function validateJs(validator: Validator, js: Node) {
if (match) { if (match) {
validator.error( validator.error(
`Unexpected property '${name}' (did you mean '${match}'?)`, `Unexpected property '${name}' (did you mean '${match}'?)`,
{ start: prop.start, end: prop.end } prop
); );
} else if (/FunctionExpression/.test(prop.value.type)) { } else if (/FunctionExpression/.test(prop.value.type)) {
validator.error( validator.error(
`Unexpected property '${name}' (did you mean to include it in 'methods'?)`, `Unexpected property '${name}' (did you mean to include it in 'methods'?)`,
{ start: prop.start, end: prop.end } prop
); );
} else { } else {
validator.error( validator.error(
`Unexpected property '${name}'`, `Unexpected property '${name}'`,
{ start: prop.start, end: prop.end } prop
); );
} }
} }

@ -8,7 +8,7 @@ export default function components(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'components' property must be an object literal`, `The 'components' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -21,12 +21,12 @@ export default function components(validator: Validator, prop: Node) {
if (name === 'state') { if (name === 'state') {
validator.error( validator.error(
`Component constructors cannot be called 'state' due to technical limitations`, `Component constructors cannot be called 'state' due to technical limitations`,
{ start: component.start, end: component.end } component
); );
} }
if (!/^[A-Z]/.test(name)) { if (!/^[A-Z]/.test(name)) {
validator.warn(`Component names should be capitalised`, { start: component.start, end: component.end }); validator.warn(`Component names should be capitalised`, component);
} }
}); });
} }

@ -17,7 +17,7 @@ export default function computed(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'computed' property must be an object literal`, `The 'computed' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -31,21 +31,21 @@ export default function computed(validator: Validator, prop: Node) {
const suggestion = name.replace(/[^_$a-z0-9]/ig, '_').replace(/^\d/, '_$&'); const suggestion = name.replace(/[^_$a-z0-9]/ig, '_').replace(/^\d/, '_$&');
validator.error( validator.error(
`Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}`, `Computed property name '${name}' is invalid — must be a valid identifier such as ${suggestion}`,
{ start: computation.start, end: computation.end } computation
); );
} }
if (reservedNames.has(name)) { if (reservedNames.has(name)) {
validator.error( validator.error(
`Computed property name '${name}' is invalid — cannot be a JavaScript reserved word`, `Computed property name '${name}' is invalid — cannot be a JavaScript reserved word`,
{ start: computation.start, end: computation.end } computation
); );
} }
if (!isFunctionExpression.has(computation.value.type)) { if (!isFunctionExpression.has(computation.value.type)) {
validator.error( validator.error(
`Computed properties can be function expressions or arrow function expressions`, `Computed properties can be function expressions or arrow function expressions`,
{ start: computation.value.start, end: computation.value.end } computation.value
); );
} }
@ -55,14 +55,14 @@ export default function computed(validator: Validator, prop: Node) {
if (isThisGetCallExpression(node) && !node.callee.property.computed) { if (isThisGetCallExpression(node) && !node.callee.property.computed) {
validator.error( validator.error(
`Cannot use this.get(...) — values must be passed into the function as arguments`, `Cannot use this.get(...) — values must be passed into the function as arguments`,
{ start: node.start, end: node.end } node
); );
} }
if (node.type === 'ThisExpression') { if (node.type === 'ThisExpression') {
validator.error( 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'?`, `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'?`,
{ start: node.start, end: node.end } node
); );
} }
}); });
@ -70,7 +70,7 @@ export default function computed(validator: Validator, prop: Node) {
if (params.length === 0) { if (params.length === 0) {
validator.error( validator.error(
`A computed value must depend on at least one property`, `A computed value must depend on at least one property`,
{ start: computation.value.start, end: computation.value.end } computation.value
); );
} }
@ -83,7 +83,7 @@ export default function computed(validator: Validator, prop: Node) {
if (!valid) { if (!valid) {
validator.error( validator.error(
`Computed properties cannot use destructuring in function parameters`, `Computed properties cannot use destructuring in function parameters`,
{ start: param.start, end: param.end } param
); );
} }
}); });

@ -7,6 +7,6 @@ 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`, { start: prop.value.start, end: prop.value.end }); validator.error(`'data' must be a function`, prop.value);
} }
} }

@ -7,7 +7,7 @@ export default function events(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'events' property must be an object literal`, `The 'events' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }

@ -10,7 +10,7 @@ export default function helpers(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'helpers' property must be an object literal`, `The 'helpers' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -26,14 +26,14 @@ export default function helpers(validator: Validator, prop: Node) {
if (isThisGetCallExpression(node) && !node.callee.property.computed) { if (isThisGetCallExpression(node) && !node.callee.property.computed) {
validator.error( validator.error(
`Cannot use this.get(...) — values must be passed into the helper function as arguments`, `Cannot use this.get(...) — values must be passed into the helper function as arguments`,
{ start: node.start, end: node.end } node
); );
} }
if (node.type === 'ThisExpression') { if (node.type === 'ThisExpression') {
validator.error( 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'?`, `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'?`,
{ start: node.start, end: node.end } node
); );
} else if (node.type === 'Identifier' && node.name === 'arguments') { } else if (node.type === 'Identifier' && node.name === 'arguments') {
usesArguments = true; usesArguments = true;
@ -43,7 +43,7 @@ export default function helpers(validator: Validator, prop: Node) {
if (prop.value.params.length === 0 && !usesArguments) { if (prop.value.params.length === 0 && !usesArguments) {
validator.warn( validator.warn(
`Helpers should be pure functions, with at least one argument`, `Helpers should be pure functions, with at least one argument`,
{ start: prop.start, end: prop.end } prop
); );
} }
}); });

@ -5,7 +5,7 @@ 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(
`'immutable' must be a boolean literal`, `'immutable' must be a boolean literal`,
{ start: prop.value.start, end: prop.value.end } prop.value
); );
} }
} }

@ -12,7 +12,7 @@ export default function methods(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'methods' property must be an object literal`, `The 'methods' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -26,7 +26,7 @@ export default function methods(validator: Validator, prop: Node) {
if (builtin.has(name)) { if (builtin.has(name)) {
validator.error( validator.error(
`Cannot overwrite built-in method '${name}'`, `Cannot overwrite built-in method '${name}'`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -35,7 +35,7 @@ export default function methods(validator: Validator, prop: Node) {
validator.error( validator.error(
`Method '${prop.key `Method '${prop.key
.name}' should be a function expression, not an arrow function expression`, .name}' should be a function expression, not an arrow function expression`,
{ start: prop.start, end: prop.end } prop
); );
} }
} }

@ -11,7 +11,7 @@ export default function namespace(validator: Validator, prop: Node) {
if (prop.value.type !== 'Literal' || typeof ns !== 'string') { if (prop.value.type !== 'Literal' || typeof ns !== 'string') {
validator.error( validator.error(
`The 'namespace' property must be a string literal representing a valid namespace`, `The 'namespace' property must be a string literal representing a valid namespace`,
{ start: prop.start, end: prop.end } prop
); );
} }
@ -20,10 +20,10 @@ export default function namespace(validator: Validator, prop: Node) {
if (match) { if (match) {
validator.error( validator.error(
`Invalid namespace '${ns}' (did you mean '${match}'?)`, `Invalid namespace '${ns}' (did you mean '${match}'?)`,
{ start: prop.start, end: prop.end } prop
); );
} else { } else {
validator.error(`Invalid namespace '${ns}'`, { start: prop.start, end: prop.end }); validator.error(`Invalid namespace '${ns}'`, prop);
} }
} }
} }

@ -7,7 +7,7 @@ export default function oncreate(validator: Validator, prop: Node) {
if (usesThisOrArguments(prop.value.body)) { if (usesThisOrArguments(prop.value.body)) {
validator.error( validator.error(
`'oncreate' should be a function expression, not an arrow function expression`, `'oncreate' should be a function expression, not an arrow function expression`,
{ start: prop.start, end: prop.end } prop
); );
} }
} }

@ -7,7 +7,7 @@ export default function ondestroy(validator: Validator, prop: Node) {
if (usesThisOrArguments(prop.value.body)) { if (usesThisOrArguments(prop.value.body)) {
validator.error( validator.error(
`'ondestroy' should be a function expression, not an arrow function expression`, `'ondestroy' should be a function expression, not an arrow function expression`,
{ start: prop.start, end: prop.end } prop
); );
} }
} }

@ -5,7 +5,7 @@ import { Node } from '../../../interfaces';
export default function onrender(validator: Validator, prop: Node) { export default function onrender(validator: Validator, prop: Node) {
validator.warn( validator.warn(
`'onrender' has been deprecated in favour of 'oncreate', and will cause an error in Svelte 2.x`, `'onrender' has been deprecated in favour of 'oncreate', and will cause an error in Svelte 2.x`,
{ start: prop.start, end: prop.end } prop
); );
oncreate(validator, prop); oncreate(validator, prop);
} }

@ -5,7 +5,7 @@ import { Node } from '../../../interfaces';
export default function onteardown(validator: Validator, prop: Node) { export default function onteardown(validator: Validator, prop: Node) {
validator.warn( validator.warn(
`'onteardown' has been deprecated in favour of 'ondestroy', and will cause an error in Svelte 2.x`, `'onteardown' has been deprecated in favour of 'ondestroy', and will cause an error in Svelte 2.x`,
{ start: prop.start, end: prop.end } prop
); );
ondestroy(validator, prop); ondestroy(validator, prop);
} }

@ -5,7 +5,7 @@ export default function props(validator: Validator, prop: Node) {
if (prop.value.type !== 'ArrayExpression') { if (prop.value.type !== 'ArrayExpression') {
validator.error( validator.error(
`'props' must be an array expression, if specified`, `'props' must be an array expression, if specified`,
{ start: prop.value.start, end: prop.value.end } prop.value
); );
} }
@ -13,7 +13,7 @@ export default function props(validator: Validator, prop: Node) {
if (element.type !== 'Literal' || typeof element.value !== 'string') { if (element.type !== 'Literal' || typeof element.value !== 'string') {
validator.error( validator.error(
`'props' must be an array of string literals`, `'props' must be an array of string literals`,
{ start: element.start, end: element.end } element
); );
} }
}); });

@ -7,6 +7,6 @@ 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`, { start: prop.value.start, end: prop.value.end }); validator.error(`'setup' must be a function`, prop.value);
} }
} }

@ -5,7 +5,7 @@ export default function tag(validator: Validator, prop: Node) {
if (prop.value.type !== 'Literal' || typeof prop.value.value !== 'string') { if (prop.value.type !== 'Literal' || typeof prop.value.value !== 'string') {
validator.error( validator.error(
`'tag' must be a string literal`, `'tag' must be a string literal`,
{ start: prop.value.start, end: prop.value.end } prop.value
); );
} }
@ -13,7 +13,7 @@ export default function tag(validator: Validator, prop: Node) {
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(
`tag name must be two or more words joined by the '-' character`, `tag name must be two or more words joined by the '-' character`,
{ start: prop.value.start, end: prop.value.end } prop.value
); );
} }
} }

@ -7,7 +7,7 @@ export default function transitions(validator: Validator, prop: Node) {
if (prop.value.type !== 'ObjectExpression') { if (prop.value.type !== 'ObjectExpression') {
validator.error( validator.error(
`The 'transitions' property must be an object literal`, `The 'transitions' property must be an object literal`,
{ start: prop.start, end: prop.end } prop
); );
} }

@ -8,7 +8,7 @@ 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`, { start: prop.start, end: prop.end }); validator.error(`${label} cannot use getters and setters`, prop);
} }
}); });
} }

@ -7,7 +7,7 @@ export default function checkForComputedKeys(
) { ) {
properties.forEach(prop => { properties.forEach(prop => {
if (prop.key.computed) { if (prop.key.computed) {
validator.error(`Cannot use computed keys`, { start: prop.start, end: prop.end }); validator.error(`Cannot use computed keys`, prop);
} }
}); });
} }

@ -12,7 +12,7 @@ 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}'`, { start: prop.start, end: prop.end }); validator.error(`Duplicate property '${name}'`, prop);
} }
seen.add(name); seen.add(name);

Loading…
Cancel
Save