pull/1348/head
Rich Harris 7 years ago
parent 8eb4adc7e6
commit a2759604cb

@ -84,7 +84,6 @@ export default class Generator {
source: string;
name: string;
options: CompileOptions;
v2: boolean;
customElement: CustomElementOptions;
tag: string;
@ -133,8 +132,6 @@ export default class Generator {
stats.start('compile');
this.stats = stats;
this.v2 = options.parser === 'v2';
this.ast = clone(parsed);
this.parsed = parsed;
@ -560,9 +557,7 @@ export default class Generator {
const key = getName(prop.key);
const value = prop.value;
const deps = this.v2
? value.params[0].properties.map(prop => prop.key.name)
: value.params.map(param => param.type === 'AssignmentPattern' ? param.left.name : param.name);
const deps = value.params[0].properties.map(prop => prop.key.name);
deps.forEach(dep => {
this.expectedProperties.add(dep);
@ -621,12 +616,10 @@ export default class Generator {
this.namespace = namespaces[ns] || ns;
}
if (templateProperties.onrender) templateProperties.oncreate = templateProperties.onrender; // remove after v2
if (templateProperties.oncreate && dom) {
addDeclaration('oncreate', templateProperties.oncreate.value);
}
if (templateProperties.onteardown) templateProperties.ondestroy = templateProperties.onteardown; // remove after v2
if (templateProperties.ondestroy && dom) {
addDeclaration('ondestroy', templateProperties.ondestroy.value);
}

@ -99,11 +99,7 @@ export default function dom(
const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`;
const call = generator.v2
? `%computed-${key}(state)`
: `%computed-${key}(${deps.map(dep => `state.${dep}`).join(', ')})`;
const statement = `if (this._differs(state.${key}, (state.${key} = ${call}))) changed.${key} = true;`;
const statement = `if (this._differs(state.${key}, (state.${key} = %computed-${key}(state)))) changed.${key} = true;`;
computationBuilder.addConditional(condition, statement);
});

@ -137,7 +137,7 @@ export default function ssr(
${computations.map(
({ key, deps }) =>
`state.${key} = %computed-${key}(${deps.map(dep => `state.${dep}`).join(', ')});`
`state.${key} = %computed-${key}(state);`
)}
${generator.bindings.length &&

@ -65,8 +65,6 @@ export interface CompileOptions {
onerror?: (error: Error) => void;
onwarn?: (warning: Warning) => void;
parser?: 'v2';
}
export interface GenerateOptions {

@ -13,13 +13,11 @@ import error from '../utils/error';
interface ParserOptions {
filename?: string;
bind?: boolean;
parser?: 'v2';
}
type ParserState = (parser: Parser) => (ParserState | void);
export class Parser {
readonly v2: boolean;
readonly template: string;
readonly filename?: string;
@ -34,8 +32,6 @@ export class Parser {
allowBindings: boolean;
constructor(template: string, options: ParserOptions) {
this.v2 = options.parser === 'v2';
if (typeof template !== 'string') {
throw new TypeError('Template must be a string');
}

@ -165,10 +165,10 @@ export function readDirective(
// 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);
const expressionEnd = parser.template.indexOf('}', expressionStart);
if (expressionEnd !== -1) {
const value = parser.template.slice(expressionStart + (parser.v2 ? 1 : 2), expressionEnd);
message += ` — use '${value}', not '${parser.v2 ? `{${value}}` : `{{${value}}}`}'`;
const value = parser.template.slice(expressionStart + 1, expressionEnd);
message += ` — use '${value}', not '{${value}}'`;
}
parser.error({
code: `invalid-directive-value`,

@ -6,7 +6,7 @@ const literals = new Map([['true', true], ['false', false], ['null', null]]);
export default function readExpression(parser: Parser) {
const start = parser.index;
const name = parser.readUntil(parser.v2 ? /\s*}/ : /\s*}}/);
const name = parser.readUntil(/\s*}/);
if (name && /^[a-z]+$/.test(name)) {
const end = start + name.length;

@ -8,7 +8,7 @@ export default function fragment(parser: Parser) {
return tag;
}
if (parser.match(parser.v2 ? '{' : '{{')) {
if (parser.match('{')) {
return mustache;
}

@ -32,7 +32,7 @@ function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) {
export default function mustache(parser: Parser) {
const start = parser.index;
parser.index += parser.v2 ? 1 : 2;
parser.index += 1;
parser.allowWhitespace();
@ -64,7 +64,7 @@ export default function mustache(parser: Parser) {
parser.eat(expected, true);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
while (block.elseif) {
block.end = parser.index;
@ -86,7 +86,7 @@ export default function mustache(parser: Parser) {
block.end = parser.index;
parser.stack.pop();
} else if (parser.eat(parser.v2 ? ':elseif' : 'elseif')) {
} else if (parser.eat(':elseif')) {
const block = parser.current();
if (block.type !== 'IfBlock')
parser.error({
@ -99,7 +99,7 @@ export default function mustache(parser: Parser) {
const expression = readExpression(parser);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
block.else = {
start: parser.index,
@ -118,7 +118,7 @@ export default function mustache(parser: Parser) {
};
parser.stack.push(block.else.children[0]);
} else if (parser.eat(parser.v2 ? ':else' : 'else')) {
} else if (parser.eat(':else')) {
const block = parser.current();
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error({
@ -128,7 +128,7 @@ export default function mustache(parser: Parser) {
}
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
block.else = {
start: parser.index,
@ -138,7 +138,7 @@ export default function mustache(parser: Parser) {
};
parser.stack.push(block.else);
} else if (parser.eat(parser.v2 ? ':then' : 'then')) {
} else if (parser.eat(':then')) {
// TODO DRY out this and the next section
const pendingBlock = parser.current();
if (pendingBlock.type === 'PendingBlock') {
@ -150,7 +150,7 @@ export default function mustache(parser: Parser) {
awaitBlock.value = parser.readIdentifier();
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
const thenBlock: Node = {
start,
@ -162,7 +162,7 @@ export default function mustache(parser: Parser) {
awaitBlock.then = thenBlock;
parser.stack.push(thenBlock);
}
} else if (parser.eat(parser.v2 ? ':catch' : 'catch')) {
} else if (parser.eat(':catch')) {
const thenBlock = parser.current();
if (thenBlock.type === 'ThenBlock') {
thenBlock.end = start;
@ -173,7 +173,7 @@ export default function mustache(parser: Parser) {
awaitBlock.error = parser.readIdentifier();
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
const catchBlock: Node = {
start,
@ -336,7 +336,7 @@ export default function mustache(parser: Parser) {
parser.allowWhitespace();
}
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
parser.current().children.push(block);
parser.stack.push(block);
@ -346,44 +346,12 @@ export default function mustache(parser: Parser) {
childBlock.start = parser.index;
parser.stack.push(childBlock);
}
} else if (parser.eat('yield')) {
// {{yield}}
// TODO deprecate
parser.allowWhitespace();
if (parser.v2) {
const expressionEnd = parser.index;
parser.eat('}', true);
parser.current().children.push({
start,
end: parser.index,
type: 'MustacheTag',
expression: {
start: expressionEnd - 5,
end: expressionEnd,
type: 'Identifier',
name: 'yield'
}
});
} else {
parser.eat('}}', true);
parser.current().children.push({
start,
end: parser.index,
type: 'Element',
name: 'slot',
attributes: [],
children: []
});
}
} else if (parser.eat(parser.v2 ? '@html' : '{')) {
} else if (parser.eat('@html')) {
// {{{raw}}} mustache
const expression = readExpression(parser);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}}', true);
parser.eat('}', true);
parser.current().children.push({
start,
@ -395,7 +363,7 @@ export default function mustache(parser: Parser) {
const expression = readExpression(parser);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
parser.current().children.push({
start,

@ -11,8 +11,6 @@ import { Node } from '../../interfaces';
const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const metaTags = new Map([
[':Window', 'Window'],
[':Head', 'Head'],
['svelte:window', 'Window'],
['svelte:head', 'Head']
]);
@ -34,6 +32,9 @@ const specials = new Map([
],
]);
const SELF = 'svelte:self';
const COMPONENT = 'svelte:component';
// based on http://developers.whatwg.org/syntax.html#syntax-tag-omission
const disallowedContents = new Map([
['li', new Set(['li'])],
@ -198,7 +199,7 @@ export default function tag(parser: Parser) {
parser.allowWhitespace();
}
if (parser.v2 && name === 'svelte:component') {
if (name === 'svelte:component') {
// TODO post v2, treat this just as any other attribute
const index = element.attributes.findIndex(attr => attr.name === 'this');
if (!~index) {
@ -264,21 +265,11 @@ export default function tag(parser: Parser) {
element.end = parser.index;
} else if (name === 'style') {
// special case
if (parser.v2) {
const start = parser.index;
const data = parser.readUntil(/<\/style>/);
const end = parser.index;
element.children.push({ start, end, type: 'Text', data });
parser.eat('</style>', true);
} else {
element.children = readSequence(
parser,
() =>
parser.template.slice(parser.index, parser.index + 8) === '</style>'
);
parser.read(/<\/style>/);
element.end = parser.index;
}
const start = parser.index;
const data = parser.readUntil(/<\/style>/);
const end = parser.index;
element.children.push({ start, end, type: 'Text', data });
parser.eat('</style>', true);
} else {
parser.stack.push(element);
}
@ -287,10 +278,6 @@ export default function tag(parser: Parser) {
function readTagName(parser: Parser) {
const start = parser.index;
// TODO hoist these back to the top, post-v2
const SELF = parser.v2 ? 'svelte:self' : ':Self';
const COMPONENT = parser.v2 ? 'svelte:component' : ':Component';
if (parser.eat(SELF)) {
// check we're inside a block, otherwise this
// will cause infinite recursion
@ -334,14 +321,14 @@ function readTagName(parser: Parser) {
function readAttribute(parser: Parser, uniqueNames: Set<string>) {
const start = parser.index;
if (parser.eat(parser.v2 ? '{' : '{{')) {
if (parser.eat('{')) {
parser.allowWhitespace();
if (parser.eat('...')) {
const expression = readExpression(parser);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
return {
start,
@ -350,13 +337,6 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
expression
};
} else {
if (!parser.v2) {
parser.error({
code: `expected-spread`,
message: 'Expected spread operator (...)'
});
}
const valueStart = parser.index;
const name = parser.readIdentifier();
@ -449,7 +429,7 @@ function readSequence(parser: Parser, done: () => boolean) {
});
return chunks;
} else if (parser.eat(parser.v2 ? '{' : '{{')) {
} else if (parser.eat('{')) {
if (currentChunk.data) {
currentChunk.end = index;
chunks.push(currentChunk);
@ -457,7 +437,7 @@ function readSequence(parser: Parser, done: () => boolean) {
const expression = readExpression(parser);
parser.allowWhitespace();
parser.eat(parser.v2 ? '}' : '}}', true);
parser.eat('}', true);
chunks.push({
start: index,

@ -9,7 +9,7 @@ export default function text(parser: Parser) {
while (
parser.index < parser.template.length &&
!parser.match('<') &&
!parser.match(parser.v2 ? '{' : '{{')
!parser.match('{')
) {
data += parser.template[parser.index++];
}

@ -2,29 +2,28 @@ import * as fs from 'fs';
import * as path from 'path';
import { compile } from '../index.ts';
let compileOptions = {};
let compileOptions = {
extensions: ['.html']
};
function capitalise(name) {
return name[0].toUpperCase() + name.slice(1);
}
export default function register(options) {
const { extensions } = options;
if (extensions) {
_deregister('.html');
extensions.forEach(_register);
if (options.extensions) {
compileOptions.extensions.forEach(deregisterExtension);
options.extensions.forEach(registerExtension);
}
// TODO make this the default and remove in v2
if (options) compileOptions = options;
compileOptions = options;
}
function _deregister(extension) {
function deregisterExtension(extension) {
delete require.extensions[extension];
}
function _register(extension) {
function registerExtension(extension) {
require.extensions[extension] = function(module, filename) {
const name = path.basename(filename)
.slice(0, -path.extname(filename).length)
@ -43,4 +42,4 @@ function _register(extension) {
};
}
_register('.html');
registerExtension('.html');

@ -19,8 +19,7 @@ export default function validateElement(
}
if (!isComponent && /^[A-Z]/.test(node.name[0])) {
// TODO upgrade to validator.error in v2
validator.warn(node, {
validator.error(node, {
code: `missing-component`,
message: `${node.name} component is not defined`
});

@ -11,7 +11,6 @@ import { Node, Parsed, CompileOptions, Warning } from '../interfaces';
export class Validator {
readonly source: string;
readonly filename: string;
readonly v2: boolean;
readonly stats: Stats;
options: CompileOptions;
@ -41,7 +40,6 @@ export class Validator {
this.filename = options.filename;
this.options = options;
this.v2 = options.parser === 'v2';
this.namespace = null;
this.defaultExport = null;

@ -75,36 +75,19 @@ export default function computed(validator: Validator, prop: Node) {
});
}
if (validator.v2) {
if (params.length > 1) {
validator.error(computation.value, {
code: `invalid-computed-arguments`,
message: `Computed properties must take a single argument`
});
}
const param = params[0];
if (param.type !== 'ObjectPattern') {
// TODO in v2, allow the entire object to be passed in
validator.error(computation.value, {
code: `invalid-computed-argument`,
message: `Computed property argument must be a destructured object pattern`
});
}
} else {
params.forEach((param: Node) => {
const valid =
param.type === 'Identifier' ||
(param.type === 'AssignmentPattern' &&
param.left.type === 'Identifier');
if (params.length > 1) {
validator.error(computation.value, {
code: `invalid-computed-arguments`,
message: `Computed properties must take a single argument`
});
}
if (!valid) {
// TODO change this for v2
validator.error(param, {
code: `invalid-computed-arguments`,
message: `Computed properties cannot use destructuring in function parameters`
});
}
const param = params[0];
if (param.type !== 'ObjectPattern') {
// TODO post-v2, allow the entire object to be passed in
validator.error(computation.value, {
code: `invalid-computed-argument`,
message: `Computed property argument must be a destructured object pattern`
});
}
});

@ -110,9 +110,7 @@ describe("ssr", () => {
delete require.cache[resolved];
});
const compileOptions = Object.assign(config.compileOptions || {}, {
store: !!config.store
});
const compileOptions = config.compileOptions || {};
require("../../ssr/register")(compileOptions);

@ -1 +1 @@
<div style='font-family: {{font}}; color: {{color}};'>{{color}} {{font}}</div>
<div style='font-family: {font}; color: {color};'>{color} {font}</div>

@ -1,4 +1,4 @@
{{y}}<Foo bind:y='x'/>{{y}}
{y}<Foo bind:y='x'/>{y}
<script>
import Foo from './Foo.html';

@ -1,4 +1,4 @@
{{x}}<Foo bind:x/>{{x}}
{x}<Foo bind:x/>{x}
<script>
import Foo from './Foo.html';

@ -1,4 +1,4 @@
<p>foo: {{foo}}</p>
<p>baz: {{baz}} ({{typeof baz}})</p>
<p>qux: {{qux}}</p>
<p>quux: {{quux}}</p>
<p>foo: {foo}</p>
<p>baz: {baz} ({typeof baz})</p>
<p>qux: {qux}</p>
<p>quux: {quux}</p>

@ -1,5 +1,5 @@
<div>
<Widget foo='{{bar}}' baz='{{40 + x}}' qux='this is a {{compound}} string' quux='{{go.deeper}}'/>
<Widget foo='{bar}' baz='{40 + x}' qux='this is a {compound} string' quux='{go.deeper}'/>
</div>
<script>

@ -1,2 +1,2 @@
<p>foo: {{foo}}</p>
<p>baz: {{baz}} ({{typeof baz}})</p>
<p>foo: {foo}</p>
<p>baz: {baz} ({typeof baz})</p>

@ -1,4 +1,4 @@
<div><Widget ref:widget foo='{{foo}}'/></div>
<div><Widget ref:widget foo='{foo}'/></div>
<script>
import Widget from './Widget.html';

@ -1,4 +1,4 @@
<p>{{yield}}</p>
<p><slot></slot></p>
<script>
export default {

@ -1,5 +1,5 @@
<div>
<Widget>{{data}}</Widget>
<Widget>{data}</Widget>
</div>
<script>

@ -1,11 +1,11 @@
<p>{{a}} + {{b}} = {{c}}</p>
<p>{{c}} * {{c}} = {{cSquared}}</p>
<p>{a} + {b} = {c}</p>
<p>{c} * {c} = {cSquared}</p>
<script>
export default {
computed: {
c: ( a, b ) => a + b,
cSquared: c => c * c
c: ({ a, b }) => a + b,
cSquared: ({ c }) => c * c
}
};
</script>

@ -1,4 +1,4 @@
<p>{{foo}}</p>
<p>{foo}</p>
<script>
export default {

@ -1,4 +1,4 @@
<p>{{foo}}</p>
<p>{foo}</p>
<script>
export default {

@ -1,3 +1,3 @@
{{#each animals as animal, i}}
<p>{{i + 1}}: {{animal}}</p>
{{/each}}
{#each animals as animal, i}
<p>{i + 1}: {animal}</p>
{/each}

@ -1,3 +1,3 @@
<:Head>
<title>a {{adjective}} title</title>
</:Head>
<svelte:head>
<title>a {adjective} title</title>
</svelte:head>

@ -1,4 +1,4 @@
<p>{{thrice(foo)}}</p>
<p>{thrice(foo)}</p>
<script>
export default {

@ -1,3 +1,3 @@
{{#if foo}}
{#if foo}
<p>foo is true</p>
{{/if}}
{/if}

@ -1,3 +1,3 @@
{{#if foo}}
{#if foo}
<p>foo is true</p>
{{/if}}
{/if}

@ -1,5 +1,5 @@
<div>i got {{problems}} problems</div>
<div>the answer is {{answer}}</div>
<div>i got {problems} problems</div>
<div>the answer is {answer}</div>
<script>
import answer from './answer.js';

@ -1 +1 @@
before{{{raw}}}after
before{@html raw}after

@ -1,4 +1,4 @@
<div>green: {{message}}</div>
<div>green: {message}</div>
<!-- Two styles should *not* be included -->
<!-- <Two message='{{message}}'/> -->

@ -1,4 +1,4 @@
<div>blue: {{message}}</div>
<div>blue: {message}</div>
<style>
div {

@ -1,5 +1,5 @@
<textarea>
<p>not actually an element. {{foo}}</p>
<p>not actually an element. {foo}</p>
</textarea>
<script>

@ -1,4 +1,4 @@
<textarea value='{{foo}}'/>
<textarea value='{foo}'/>
<script>
export default {

@ -1,4 +1,4 @@
<div>{{{triple}}}</div>
<div>{@html triple}</div>
<script>
export default {

Loading…
Cancel
Save