Skip to content

Commit c199407

Browse files
committed
more validation
1 parent 0698525 commit c199407

File tree

14 files changed

+160
-27
lines changed

14 files changed

+160
-27
lines changed

compiler/index.js

+5-11
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,13 @@ import generate from './generate/index.js';
55
export function compile ( source, options = {} ) {
66
const parsed = parse( source, options );
77

8-
const { errors, warnings } = validate( parsed, source, options );
9-
10-
if ( errors.length ) {
11-
// TODO optionally show all errors?
12-
throw errors[0];
8+
if ( !options.onwarn ) {
9+
options.onwarn = warning => {
10+
console.warn( `(${warning.loc.line}:${warning.loc.column}) – ${warning.message}` ); // eslint-disable-line no-console
11+
};
1312
}
1413

15-
if ( warnings.length ) {
16-
console.warn( `Svelte: ${warnings.length} ${warnings.length === 1 ? 'error' : 'errors'} in ${options.filename || 'template'}:` );
17-
warnings.forEach( warning => {
18-
console.warn( `(${warning.loc.line}:${warning.loc.column}) – ${warning.message}` );
19-
});
20-
}
14+
validate( parsed, source, options );
2115

2216
return generate( parsed, source, options );
2317
}

compiler/parse/index.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ import hash from './utils/hash.js';
88
function ParseError ( message, template, index ) {
99
const { line, column } = locate( template, index );
1010

11-
const frame = getCodeFrame( template, line, column );
12-
1311
this.name = 'ParseError';
14-
this.message = `${message} (${line + 1}:${column})\n${frame}`;
12+
this.message = message;
13+
this.frame = getCodeFrame( template, line, column );
14+
1515
this.loc = { line: line + 1, column };
1616
this.pos = index;
17-
this.shortMessage = message;
1817
}
1918

19+
ParseError.prototype.toString = function () {
20+
return `${this.message} (${this.loc.line}:${this.loc.column})\n${this.frame}`;
21+
};
22+
2023
export default function parse ( template ) {
2124
if ( typeof template !== 'string' ) {
2225
throw new TypeError( 'Template must be a string' );

compiler/validate/index.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
11
import validateJs from './js/index.js';
22
import { getLocator } from 'locate-character';
3+
import getCodeFrame from '../utils/getCodeFrame.js';
34

4-
export default function validate ( parsed, source ) {
5+
export default function validate ( parsed, source, options ) {
56
const locator = getLocator( source );
67

78
const validator = {
89
error: ( message, pos ) => {
910
const { line, column } = locator( pos );
1011

11-
validator.errors.push({
12-
message,
13-
pos,
14-
loc: { line: line + 1, column }
15-
});
12+
const error = new Error( message );
13+
error.frame = getCodeFrame( source, line, column );
14+
error.loc = { line: line + 1, column };
15+
error.pos = pos;
16+
17+
error.toString = () => `${error.message} (${error.loc.line}:${error.loc.column})\n${error.frame}`;
18+
19+
if ( options.onerror ) {
20+
options.onerror( error );
21+
} else {
22+
throw error;
23+
}
1624
},
1725

1826
warn: ( message, pos ) => {
1927
const { line, column } = locator( pos );
2028

21-
validator.warnings.push({
29+
const frame = getCodeFrame( source, line, column );
30+
31+
options.onwarn({
2232
message,
33+
frame,
34+
loc: { line: line + 1, column },
2335
pos,
24-
loc: { line: line + 1, column }
36+
toString: () => `${message} (${line + 1}:${column})\n${frame}`
2537
});
2638
},
2739

@@ -32,7 +44,7 @@ export default function validate ( parsed, source ) {
3244
};
3345

3446
if ( parsed.js ) {
35-
validateJs( validator, parsed.js, source );
47+
validateJs( validator, parsed.js );
3648
}
3749

3850
return {

compiler/validate/js/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const validPropList = Object.keys( propValidators );
77

88
const fuzzySet = new FuzzySet( validPropList );
99

10-
export default function validateJs ( validator, js, source ) {
10+
export default function validateJs ( validator, js ) {
1111
js.content.body.forEach( node => {
1212
// check there are no named exports
1313
if ( node.type === 'ExportNamedDeclaration' ) {
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
import checkForDupes from '../utils/checkForDupes.js';
2+
import checkForComputedKeys from '../utils/checkForComputedKeys.js';
3+
14
export default function components ( validator, prop ) {
5+
if ( prop.value.type !== 'ObjectExpression' ) {
6+
validator.error( `The 'components' property must be an object literal`, prop.start );
7+
return;
8+
}
9+
10+
checkForDupes( validator, prop.value.properties );
11+
checkForComputedKeys( validator, prop.value.properties );
212

13+
prop.value.properties.forEach( component => {
14+
const char = component.key.name[0];
15+
if ( char === char.toLowerCase() ) {
16+
validator.warn( `Component names should be capitalised`, component.start );
17+
}
18+
});
319
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
const disallowed = {
2+
Literal: true,
3+
ObjectExpression: true,
4+
ArrayExpression: true
5+
};
6+
17
export default function data ( validator, prop ) {
8+
while ( prop.type === 'ParenthesizedExpression' ) prop = prop.expression;
9+
10+
// TODO should we disallow references and expressions as well?
211

12+
if ( disallowed[ prop.value.type ] ) {
13+
validator.error( `'data' must be a function`, prop.value.start );
14+
}
315
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import checkForDupes from '../utils/checkForDupes.js';
2+
import checkForComputedKeys from '../utils/checkForComputedKeys.js';
3+
14
export default function events ( validator, prop ) {
5+
if ( prop.value.type !== 'ObjectExpression' ) {
6+
validator.error( `The 'events' property must be an object literal`, prop.start );
7+
return;
8+
}
29

10+
checkForDupes( validator, prop.value.properties );
11+
checkForComputedKeys( validator, prop.value.properties );
312
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import checkForDupes from '../utils/checkForDupes.js';
2+
import checkForComputedKeys from '../utils/checkForComputedKeys.js';
3+
14
export default function helpers ( validator, prop ) {
5+
if ( prop.value.type !== 'ObjectExpression' ) {
6+
validator.error( `The 'helpers' property must be an object literal`, prop.start );
7+
return;
8+
}
29

10+
checkForDupes( validator, prop.value.properties );
11+
checkForComputedKeys( validator, prop.value.properties );
312
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
import checkForDupes from '../utils/checkForDupes.js';
2+
import checkForComputedKeys from '../utils/checkForComputedKeys.js';
3+
4+
const builtin = {
5+
set: true,
6+
get: true,
7+
on: true,
8+
fire: true,
9+
observe: true,
10+
teardown: true
11+
};
12+
113
export default function methods ( validator, prop ) {
14+
if ( prop.value.type !== 'ObjectExpression' ) {
15+
validator.error( `The 'methods' property must be an object literal`, prop.start );
16+
return;
17+
}
18+
19+
checkForDupes( validator, prop.value.properties );
20+
checkForComputedKeys( validator, prop.value.properties );
221

22+
prop.value.properties.forEach( prop => {
23+
if ( builtin[ prop.key.name ] ) {
24+
validator.error( `Cannot overwrite built-in method '${prop.key.name}'` );
25+
}
26+
});
327
}

test/test.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe( 'svelte', () => {
4949
try {
5050
const expected = require( `./parser/${dir}/error.json` );
5151

52-
assert.equal( err.shortMessage, expected.message );
52+
assert.equal( err.message, expected.message );
5353
assert.deepEqual( err.loc, expected.loc );
5454
assert.equal( err.pos, expected.pos );
5555
} catch ( err2 ) {
@@ -80,7 +80,27 @@ describe( 'svelte', () => {
8080

8181
try {
8282
const parsed = parse( input );
83-
const { errors, warnings } = validate( parsed, input );
83+
84+
const errors = [];
85+
const warnings = [];
86+
87+
validate( parsed, input, {
88+
onerror ( error ) {
89+
errors.push({
90+
message: error.message,
91+
pos: error.pos,
92+
loc: error.loc
93+
});
94+
},
95+
96+
onwarn ( warning ) {
97+
warnings.push({
98+
message: warning.message,
99+
pos: warning.pos,
100+
loc: warning.loc
101+
});
102+
}
103+
});
84104

85105
const expectedErrors = tryToLoadJson( `test/validator/${dir}/errors.json` ) || [];
86106
const expectedWarnings = tryToLoadJson( `test/validator/${dir}/warnings.json` ) || [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div></div>
2+
3+
<script>
4+
export default {
5+
components: {
6+
foo
7+
}
8+
};
9+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[{
2+
"message": "Component names should be capitalised",
3+
"loc": {
4+
"line": 6,
5+
"column": 3
6+
},
7+
"pos": 59
8+
}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[{
2+
"message": "'data' must be a function",
3+
"loc": {
4+
"line": 5,
5+
"column": 8
6+
},
7+
"pos": 48
8+
}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div></div>
2+
3+
<script>
4+
export default {
5+
data: {
6+
foo: 42
7+
}
8+
};
9+
</script>

0 commit comments

Comments
 (0)