more tests, support svelte: elements

pull/1330/head
Rich Harris 7 years ago
parent 0edbac615c
commit cb514afde4

@ -791,7 +791,7 @@ export default class Generator {
node.generator = generator;
if (node.type === 'Element' && (node.name === ':Component' || node.name === ':Self' || generator.components.has(node.name))) {
if (node.type === 'Element' && (node.name === ':Component' || node.name === ':Self' || node.name === 'svelte:component' || node.name === 'svelte:self' || generator.components.has(node.name))) {
node.type = 'Component';
Object.setPrototypeOf(node, nodes.Component.prototype);
} else if (node.type === 'Element' && node.name === 'title' && parentIsHead(parent)) { // TODO do this in parse?

@ -45,8 +45,8 @@ export default class Component extends Node {
this.var = block.getUniqueName(
(
this.name === ':Self' ? this.generator.name :
this.name === ':Component' ? 'switch_instance' :
(this.name === ':Self' || this.name === 'svelte:self') ? this.generator.name :
(this.name === ':Component' || this.name === 'svelte:component') ? 'switch_instance' :
this.name
).toLowerCase()
);
@ -386,7 +386,7 @@ export default class Component extends Node {
block.builders.destroy.addLine(`if (${name}) ${name}.destroy(false);`);
} else {
const expression = this.name === ':Self'
const expression = (this.name === ':Self' || this.name === 'svelte:self')
? generator.name
: `%components-${this.name}`;

@ -91,7 +91,7 @@ export default function visitComponent(
if (isDynamicComponent) block.contextualise(node.expression);
const expression = (
node.name === ':Self' ? generator.name :
(node.name === ':Self' || node.name === 'svelte:self') ? generator.name :
isDynamicComponent ? `((${node.metadata.snippet}) || __missingComponent)` :
`%components-${node.name}`
);

@ -140,20 +140,25 @@ export function readDirective(
const expressionStart = parser.index;
if (parser.eat('{{')) {
let message = 'directive values should not be wrapped';
const expressionEnd = parser.template.indexOf('}}', expressionStart);
if (expressionEnd !== -1) {
const value = parser.template.slice(parser.index, expressionEnd);
message += ` — use '${value}', not '{{${value}}}'`;
try {
expression = readExpression(parser, expressionStart, quoteMark);
if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) {
parser.error(directive.error, expressionStart);
}
} catch (err) {
if (parser.template[expressionStart] === '{') {
// assume the mistake was wrapping the directive arguments.
// this could yield false positives! but hopefully not too many
let message = 'directive values should not be wrapped';
const expressionEnd = parser.template.indexOf((parser.v2 ? '}' : '}}'), expressionStart);
if (expressionEnd !== -1) {
const value = parser.template.slice(expressionStart + (parser.v2 ? 1 : 2), expressionEnd);
message += ` — use '${value}', not '${parser.v2 ? `{${value}}` : `{{${value}}}`}'`;
}
parser.error(message, expressionStart);
}
parser.error(message, expressionStart);
}
expression = readExpression(parser, expressionStart, quoteMark);
if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) {
parser.error(directive.error, expressionStart);
throw err;
}
}

@ -10,12 +10,11 @@ import { Node } from '../../interfaces';
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const SELF = ':Self';
const COMPONENT = ':Component';
const metaTags = new Set([
':Window',
':Head'
':Head',
'svelte:window',
'svelte:head'
]);
const specials = new Map([
@ -85,9 +84,9 @@ export default function tag(parser: Parser) {
if (metaTags.has(name)) {
if (isClosingTag) {
if (name === ':Window' && parser.current().children.length) {
if ((name === ':Window' || name === 'svelte:window') && parser.current().children.length) {
parser.error(
`<:Window> cannot have children`,
`<${name}> cannot have children`,
parser.current().children[0].start
);
}

@ -33,6 +33,8 @@ export default function validateHtml(validator: Validator, html: Node) {
const isComponent =
node.name === ':Self' ||
node.name === ':Component' ||
node.name === 'svelte:self' ||
node.name === 'svelte:component' ||
validator.components.has(node.name);
validateElement(

@ -280,7 +280,7 @@ function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, s
const parent = stack[i];
if (parent.type === 'Element') {
// if we're inside a component or a custom element, gravy
if (parent.name === ':Self' || parent.name === ':Component' || validator.components.has(parent.name)) return;
if (parent.name === ':Self' || parent.name === ':Component' || parent.name === 'svelte:self' || parent.name === 'svelte:component' || validator.components.has(parent.name)) return;
if (/-/.test(parent.name)) return;
}

@ -16,56 +16,49 @@ describe('parse', () => {
}
(solo ? it.only : it)(dir, () => {
const input = fs
.readFileSync(`test/parser/samples/${dir}/input.html`, 'utf-8')
.replace(/\s+$/, '');
const input_v2 = fs
.readFileSync(`test/parser/samples/${dir}/input-v2.html`, 'utf-8')
.replace(/\s+$/, '');
const options = tryToLoadJson(`test/parser/samples/${dir}/options.json`) || {};
try {
const actual = svelte.parse(input, options);
const expected = require(`./samples/${dir}/output.json`);
fs.writeFileSync(
`test/parser/samples/${dir}/_actual.json`,
JSON.stringify(actual, null, '\t')
);
assert.deepEqual(actual.html, expected.html);
assert.deepEqual(actual.css, expected.css);
assert.deepEqual(actual.js, expected.js);
// TODO remove v1 tests
const actual_v2 = svelte.parse(input_v2, Object.assign({ parser: 'v2' }, options));
const expected_v2 = require(`./samples/${dir}/output-v2.json`);
fs.writeFileSync(
`test/parser/samples/${dir}/_actual-v2.json`,
JSON.stringify(actual_v2, null, '\t')
);
assert.deepEqual(actual_v2.html, expected_v2.html);
assert.deepEqual(actual_v2.css, expected_v2.css);
assert.deepEqual(actual_v2.js, expected_v2.js);
} catch (err) {
if (err.name !== 'ParseError') throw err;
function test(options, input, expectedOutput, expectedError, outputFile) {
try {
const expected = require(`./samples/${dir}/error.json`);
assert.equal(err.message, expected.message);
assert.deepEqual(err.loc, expected.loc);
assert.equal(err.pos, expected.pos);
assert.equal(err.toString().split('\n')[0], `${expected.message} (${expected.loc.line}:${expected.loc.column})`);
} catch (err2) {
const e = err2.code === 'MODULE_NOT_FOUND' ? err : err2;
throw e;
const actual = svelte.parse(input, options);
fs.writeFileSync(outputFile, JSON.stringify(actual, null, '\t'));
assert.deepEqual(actual.html, expectedOutput.html);
assert.deepEqual(actual.css, expectedOutput.css);
assert.deepEqual(actual.js, expectedOutput.js);
} catch (err) {
if (err.name !== 'ParseError') throw err;
if (!expectedError) throw err;
try {
assert.equal(err.message, expectedError.message);
assert.deepEqual(err.loc, expectedError.loc);
assert.equal(err.pos, expectedError.pos);
assert.equal(err.toString().split('\n')[0], `${expectedError.message} (${expectedError.loc.line}:${expectedError.loc.column})`);
} catch (err2) {
const e = err2.code === 'MODULE_NOT_FOUND' ? err : err2;
throw e;
}
}
}
// TODO remove v1 tests
test(
options,
fs.readFileSync(`test/parser/samples/${dir}/input.html`, 'utf-8').replace(/\s+$/, ''),
tryToLoadJson(`test/parser/samples/${dir}/output.json`),
tryToLoadJson(`test/parser/samples/${dir}/error.json`),
`test/parser/samples/${dir}/_actual.json`
);
test(
Object.assign({ parser: 'v2' }, options),
fs.readFileSync(`test/parser/samples/${dir}/input-v2.html`, 'utf-8').replace(/\s+$/, ''),
tryToLoadJson(`test/parser/samples/${dir}/output-v2.json`),
tryToLoadJson(`test/parser/samples/${dir}/error-v2.json`),
`test/parser/samples/${dir}/_actual-v2.json`
);
});
});

@ -0,0 +1,8 @@
{
"message": "Attributes need to be unique",
"loc": {
"line": 1,
"column": 17
},
"pos": 17
}

@ -0,0 +1,8 @@
{
"message": "Two-way binding is disabled",
"loc": {
"line": 1,
"column": 7
},
"pos": 7
}

@ -0,0 +1,8 @@
{
"message": "directive values should not be wrapped — use 'foo', not '{foo}'",
"loc": {
"line": 1,
"column": 19
},
"pos": 19
}

@ -0,0 +1,8 @@
{
"message": "Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)",
"pos": 19,
"loc": {
"line": 1,
"column": 19
}
}

@ -0,0 +1,8 @@
{
"message": "comment was left open, expected -->",
"loc": {
"line": 1,
"column": 24
},
"pos": 24
}

@ -0,0 +1,8 @@
{
"message": "LeftCurlyBracket is expected",
"loc": {
"line": 2,
"column": 16
},
"pos": 24
}

@ -0,0 +1,8 @@
{
"message": "Expected a method call",
"loc": {
"line": 1,
"column": 15
},
"pos": 15
}

@ -0,0 +1,8 @@
{
"message": "Assigning to rvalue",
"loc": {
"line": 1,
"column": 1
},
"pos": 1
}

@ -0,0 +1,8 @@
{
"message": "You can only have one top-level <style> tag per component",
"loc": {
"line": 9,
"column": 0
},
"pos": 58
}

@ -0,0 +1,8 @@
{
"message": "ref directives cannot have a value",
"loc": {
"line": 1,
"column": 14
},
"pos": 14
}

@ -0,0 +1,8 @@
{
"message": "<script> must have a closing tag",
"loc": {
"line": 3,
"column": 8
},
"pos": 32
}

@ -0,0 +1,8 @@
{
"message": "<svelte:self> components can only exist inside if-blocks or each-blocks",
"loc": {
"line": 1,
"column": 1
},
"pos": 1
}

@ -0,0 +1,8 @@
{
"message": "Unexpected end of input",
"loc": {
"line": 1,
"column": 2
},
"pos": 2
}

@ -0,0 +1,8 @@
{
"message": "Unexpected end of input",
"loc": {
"line": 1,
"column": 1
},
"pos": 1
}

@ -0,0 +1,8 @@
{
"message": "Block was left open",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
}

@ -0,0 +1,8 @@
{
"message": "<div> was left open",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
}

@ -0,0 +1,8 @@
{
"message": "</div> attempted to close an element that was not open",
"loc": {
"line": 1,
"column": 0
},
"pos": 0
}

@ -0,0 +1,8 @@
{
"message": "<input> is a void element and cannot have children, or a closing tag",
"loc": {
"line": 1,
"column": 23
},
"pos": 23
}

@ -0,0 +1,8 @@
{
"message": "<svelte:window> cannot have children",
"loc": {
"line": 1,
"column": 15
},
"pos": 15
}

@ -0,0 +1,8 @@
{
"message": "A component can only have one <svelte:window> tag",
"loc": {
"line": 2,
"column": 0
},
"pos": 17
}

@ -0,0 +1,8 @@
{
"message": "<svelte:window> tags cannot be inside elements or blocks",
"loc": {
"line": 2,
"column": 1
},
"pos": 11
}

@ -0,0 +1,8 @@
{
"message": "<svelte:window> tags cannot be inside elements or blocks",
"loc": {
"line": 2,
"column": 1
},
"pos": 7
}
Loading…
Cancel
Save