validate <:Window>

pull/565/head
Rich-Harris 7 years ago
parent a85b6002a7
commit 37749bd3e4

@ -80,7 +80,8 @@
},
"nyc": {
"include": [
"src/**/*.js"
"src/**/*.js",
"shared.js"
],
"exclude": [
"src/**/__test__.js",

@ -60,11 +60,6 @@ export default function visitWindow ( generator, block, node ) {
}
if ( attribute.type === 'Binding' ) {
if ( attribute.value.type !== 'Identifier' ) {
const { parts, keypath } = flattenReference( attribute.value );
throw new Error( `Bindings on <:Window/> must be to top-level properties, e.g. '${parts.pop()}' rather than '${keypath}'` );
}
// in dev mode, throw if read-only values are written to
if ( readonly.has( attribute.name ) ) {
generator.readonly.add( attribute.value.name );

@ -1,10 +1,4 @@
import flattenReference from '../../utils/flattenReference.js';
const validBuiltins = new Set([
'set',
'fire',
'destroy'
]);
import validateEventHandler from './validateEventHandler.js';
export default function validateElement ( validator, node ) {
const isComponent = node.name === ':Self' || validator.components.has( node.name );
@ -53,30 +47,7 @@ export default function validateElement ( validator, node ) {
}
if ( attribute.type === 'EventHandler' ) {
const { callee, start, type } = attribute.expression;
if ( type !== 'CallExpression' ) {
validator.error( `Expected a call expression`, start );
}
const { name } = flattenReference( callee );
if ( name === 'this' || name === 'event' ) return;
if ( callee.type === 'Identifier' && validBuiltins.has( callee.name ) || validator.methods.has( callee.name ) ) return;
const validCallees = [ 'this.*', 'event.*' ]
.concat(
Array.from( validBuiltins ),
Array.from( validator.methods.keys() )
);
let message = `'${validator.source.slice( callee.start, callee.end )}' is an invalid callee (should be one of ${list( validCallees )})`;
if ( callee.type === 'Identifier' && validator.helpers.has( callee.name ) ) {
message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;
}
validator.error( message, start );
validateEventHandler( validator, attribute );
}
});
}
@ -95,8 +66,3 @@ function getType ( validator, node ) {
return attribute.value[0].data;
}
function list ( items, conjunction = 'or' ) {
if ( items.length === 1 ) return items[0];
return `${items.slice( 0, -1 ).join( ', ' )} ${conjunction} ${items[ items.length - 1 ]}`;
}

@ -0,0 +1,35 @@
import flattenReference from '../../utils/flattenReference.js';
import list from '../utils/list.js';
const validBuiltins = new Set([
'set',
'fire',
'destroy'
]);
export default function validateEventHandlerCallee ( validator, attribute ) {
const { callee, start, type } = attribute.expression;
if ( type !== 'CallExpression' ) {
validator.error( `Expected a call expression`, start );
}
const { name } = flattenReference( callee );
if ( name === 'this' || name === 'event' ) return;
if ( callee.type === 'Identifier' && validBuiltins.has( callee.name ) || validator.methods.has( callee.name ) ) return;
const validCallees = [ 'this.*', 'event.*' ]
.concat(
Array.from( validBuiltins ),
Array.from( validator.methods.keys() )
);
let message = `'${validator.source.slice( callee.start, callee.end )}' is an invalid callee (should be one of ${list( validCallees )})`;
if ( callee.type === 'Identifier' && validator.helpers.has( callee.name ) ) {
message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;
}
validator.error( message, start );
}

@ -1,3 +1,48 @@
export default function validateWindow () {
// TODO
import flattenReference from '../../utils/flattenReference.js';
import fuzzymatch from '../utils/fuzzymatch.js';
import list from '../utils/list.js';
import validateEventHandler from './validateEventHandler.js';
const validBindings = [
'innerWidth',
'innerHeight',
'outerWidth',
'outerHeight',
'scrollX',
'scrollY'
];
export default function validateWindow ( validator, node ) {
node.attributes.forEach( attribute => {
if ( attribute.type === 'Binding' ) {
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.start
);
}
if ( !~validBindings.indexOf( attribute.name ) ) {
const match = (
attribute.name === 'width' ? 'innerWidth' :
attribute.name === 'height' ? 'innerHeight' :
fuzzymatch( attribute.name, validBindings )
);
const message = `'${attribute.name}' is not a valid binding on <:Window>`;
if ( match ) {
validator.error( `${message} (did you mean '${match}'?)`, attribute.start );
} else {
validator.error( `${message} — valid bindings are ${list( validBindings )}`, attribute.start );
}
}
}
else if ( attribute.type === 'EventHandler' ) {
validateEventHandler( validator, attribute );
}
});
}

@ -1,13 +1,11 @@
import propValidators from './propValidators/index.js';
import FuzzySet from './utils/FuzzySet.js';
import fuzzymatch from '../utils/fuzzymatch.js';
import checkForDupes from './utils/checkForDupes.js';
import checkForComputedKeys from './utils/checkForComputedKeys.js';
import namespaces from '../../utils/namespaces.js';
const validPropList = Object.keys( propValidators );
const fuzzySet = new FuzzySet( validPropList );
export default function validateJs ( validator, js ) {
js.content.body.forEach( node => {
// check there are no named exports
@ -45,9 +43,9 @@ export default function validateJs ( validator, js ) {
if ( propValidator ) {
propValidator( validator, prop );
} else {
const matches = fuzzySet.get( prop.key.name );
if ( matches && matches[0] && matches[0][0] > 0.7 ) {
validator.error( `Unexpected property '${prop.key.name}' (did you mean '${matches[0][1]}'?)`, prop.start );
const match = fuzzymatch( prop.key.name, validPropList );
if ( match ) {
validator.error( `Unexpected property '${prop.key.name}' (did you mean '${match}'?)`, prop.start );
} else if ( /FunctionExpression/.test( prop.value.type ) ) {
validator.error( `Unexpected property '${prop.key.name}' (did you mean to include it in 'methods'?)`, prop.start );
} else {

@ -1,7 +1,6 @@
import * as namespaces from '../../../utils/namespaces.js';
import FuzzySet from '../utils/FuzzySet.js';
import fuzzymatch from '../../utils/fuzzymatch.js';
const fuzzySet = new FuzzySet( namespaces.validNamespaces );
const valid = new Set( namespaces.validNamespaces );
export default function namespace ( validator, prop ) {
@ -12,9 +11,9 @@ export default function namespace ( validator, prop ) {
}
if ( !valid.has( ns ) ) {
const matches = fuzzySet.get( ns );
if ( matches && matches[0] && matches[0][0] > 0.7 ) {
validator.error( `Invalid namespace '${ns}' (did you mean '${matches[0][1]}'?)`, prop.start );
const match = fuzzymatch( ns, namespaces.validNamespaces );
if ( match ) {
validator.error( `Invalid namespace '${ns}' (did you mean '${match}'?)`, prop.start );
} else {
validator.error( `Invalid namespace '${ns}'`, prop.start );
}

@ -0,0 +1,10 @@
import FuzzySet from './FuzzySet.js';
export default function fuzzymatch ( name, names ) {
const set = new FuzzySet( names );
const matches = set.get( name );
return matches && matches[0] && matches[0][0] > 0.7 ?
matches[0][1] :
null;
}

@ -0,0 +1,4 @@
export default function list ( items, conjunction = 'or' ) {
if ( items.length === 1 ) return items[0];
return `${items.slice( 0, -1 ).join( ', ' )} ${conjunction} ${items[ items.length - 1 ]}`;
}

@ -0,0 +1,8 @@
[{
"message": "'innerwidth' is not a valid binding on <:Window> (did you mean 'innerWidth'?)",
"loc": {
"line": 1,
"column": 9
},
"pos": 9
}]

@ -0,0 +1,8 @@
[{
"message": "Bindings on <:Window/> must be to top-level properties, e.g. 'baz' rather than 'foo.bar.baz'",
"loc": {
"line": 1,
"column": 26
},
"pos": 26
}]

@ -0,0 +1 @@
<:Window bind:innerWidth='foo.bar.baz'/>

@ -0,0 +1,8 @@
[{
"message": "'width' is not a valid binding on <:Window> (did you mean 'innerWidth'?)",
"loc": {
"line": 1,
"column": 9
},
"pos": 9
}]

@ -0,0 +1,8 @@
[{
"message": "'potato' is not a valid binding on <:Window> — valid bindings are innerWidth, innerHeight, outerWidth, outerHeight, scrollX or scrollY",
"loc": {
"line": 1,
"column": 9
},
"pos": 9
}]

@ -0,0 +1,8 @@
[{
"message": "'resize' is an invalid callee (should be one of this.*, event.*, set, fire or destroy)",
"loc": {
"line": 1,
"column": 20
},
"pos": 20
}]
Loading…
Cancel
Save