Skip to content

implement syntax changes #1330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Apr 14, 2018
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ coverage.lcov
test/sourcemaps/samples/*/output.js
test/sourcemaps/samples/*/output.js.map
_actual.*
_actual-v2.*
_actual-bundle.*
src/generators/dom/shared.ts
package-lock.json
Expand Down
4 changes: 2 additions & 2 deletions src/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -875,7 +875,7 @@ export default class Generator {
this.skip();
}

if (node.type === 'Component' && node.name === ':Component') {
if (node.type === 'Component' && (node.name === ':Component' || node.name === 'svelte:component')) {
node.metadata = contextualise(node.expression, contextDependencies, indexes, false);
}

Expand Down
8 changes: 4 additions & 4 deletions src/generators/nodes/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
Expand Down Expand Up @@ -292,7 +292,7 @@ export default class Component extends Node {
`;
}

if (this.name === ':Component') {
if (this.name === ':Component' || this.name === 'svelte:component') {
const switch_value = block.getUniqueName('switch_value');
const switch_props = block.getUniqueName('switch_props');

Expand Down Expand Up @@ -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}`;

Expand Down
2 changes: 1 addition & 1 deletion src/generators/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export default class EachBlock extends Node {

// hack the sourcemap, so that if data is missing the bug
// is easy to find
let c = this.start + 3;
let c = this.start + 2;
while (generator.source[c] !== 'e') c += 1;
generator.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;
Expand Down
4 changes: 2 additions & 2 deletions src/generators/server-side-rendering/visitors/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ export default function visitComponent(
.concat(bindingProps)
.join(', ')} }`;

const isDynamicComponent = node.name === ':Component';
const isDynamicComponent = node.name === ':Component' || node.name === 'svelte:component';
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}`
);
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export interface CompileOptions {

onerror?: (error: Error) => void;
onwarn?: (warning: Warning) => void;

parser?: 'v2';
}

export interface GenerateOptions {
Expand Down
4 changes: 4 additions & 0 deletions src/parse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ 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;

Expand All @@ -32,6 +34,8 @@ 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');
}
Expand Down
29 changes: 17 additions & 12 deletions src/parse/read/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/parse/read/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(/\s*}}/);
const name = parser.readUntil(parser.v2 ? /\s*}/ : /\s*}}/);
if (name && /^[a-z]+$/.test(name)) {
const end = start + name.length;

Expand Down
2 changes: 1 addition & 1 deletion src/parse/state/fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function fragment(parser: Parser) {
return tag;
}

if (parser.match('{{')) {
if (parser.match(parser.v2 ? '{' : '{{')) {
return mustache;
}

Expand Down
86 changes: 62 additions & 24 deletions src/parse/state/mustache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) {

export default function mustache(parser: Parser) {
const start = parser.index;
parser.index += 2;
parser.index += parser.v2 ? 1 : 2;

parser.allowWhitespace();

Expand Down Expand Up @@ -61,7 +61,7 @@ export default function mustache(parser: Parser) {

parser.eat(expected, true);
parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

while (block.elseif) {
block.end = parser.index;
Expand All @@ -83,7 +83,7 @@ export default function mustache(parser: Parser) {

block.end = parser.index;
parser.stack.pop();
} else if (parser.eat('elseif')) {
} else if (parser.eat(parser.v2 ? ':elseif' : 'elseif')) {
const block = parser.current();
if (block.type !== 'IfBlock')
parser.error(
Expand All @@ -95,7 +95,7 @@ export default function mustache(parser: Parser) {
const expression = readExpression(parser);

parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

block.else = {
start: parser.index,
Expand All @@ -114,7 +114,7 @@ export default function mustache(parser: Parser) {
};

parser.stack.push(block.else.children[0]);
} else if (parser.eat('else')) {
} else if (parser.eat(parser.v2 ? ':else' : 'else')) {
const block = parser.current();
if (block.type !== 'IfBlock' && block.type !== 'EachBlock') {
parser.error(
Expand All @@ -123,7 +123,7 @@ export default function mustache(parser: Parser) {
}

parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

block.else = {
start: parser.index,
Expand All @@ -133,7 +133,7 @@ export default function mustache(parser: Parser) {
};

parser.stack.push(block.else);
} else if (parser.eat('then')) {
} else if (parser.eat(parser.v2 ? ':then' : 'then')) {
// TODO DRY out this and the next section
const pendingBlock = parser.current();
if (pendingBlock.type === 'PendingBlock') {
Expand All @@ -145,7 +145,7 @@ export default function mustache(parser: Parser) {
awaitBlock.value = parser.readIdentifier();

parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

const thenBlock: Node = {
start,
Expand All @@ -157,7 +157,7 @@ export default function mustache(parser: Parser) {
awaitBlock.then = thenBlock;
parser.stack.push(thenBlock);
}
} else if (parser.eat('catch')) {
} else if (parser.eat(parser.v2 ? ':catch' : 'catch')) {
const thenBlock = parser.current();
if (thenBlock.type === 'ThenBlock') {
thenBlock.end = start;
Expand All @@ -168,7 +168,7 @@ export default function mustache(parser: Parser) {
awaitBlock.error = parser.readIdentifier();

parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

const catchBlock: Node = {
start,
Expand Down Expand Up @@ -274,7 +274,27 @@ export default function mustache(parser: Parser) {
parser.allowWhitespace();
}

if (parser.eat('@')) {
if (parser.eat('(')) {
parser.allowWhitespace();

const expression = readExpression(parser);

// TODO eventually, we should accept any expression, and turn
// it into a function. For now, assume that every expression
// follows the `foo.id` pattern, and equates to `@id`
if (
expression.type !== 'MemberExpression' ||
expression.property.computed ||
expression.property.type !== 'Identifier'
) {
parser.error('invalid key', expression.start);
}

block.key = expression.property.name;
parser.allowWhitespace();
parser.eat(')', true);
parser.allowWhitespace();
} else if (parser.eat('@')) {
block.key = parser.readIdentifier();
if (!block.key) parser.error(`Expected name`);
parser.allowWhitespace();
Expand All @@ -288,7 +308,7 @@ export default function mustache(parser: Parser) {
parser.allowWhitespace();
}

parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

parser.current().children.push(block);
parser.stack.push(block);
Expand All @@ -302,22 +322,40 @@ export default function mustache(parser: Parser) {
// {{yield}}
// TODO deprecate
parser.allowWhitespace();
parser.eat('}}', true);

parser.current().children.push({
start,
end: parser.index,
type: 'Element',
name: 'slot',
attributes: [],
children: []
});
} else if (parser.eat('{')) {
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' : '{')) {
// {{{raw}}} mustache
const expression = readExpression(parser);

parser.allowWhitespace();
parser.eat('}}}', true);
parser.eat(parser.v2 ? '}' : '}}}', true);

parser.current().children.push({
start,
Expand All @@ -329,7 +367,7 @@ export default function mustache(parser: Parser) {
const expression = readExpression(parser);

parser.allowWhitespace();
parser.eat('}}', true);
parser.eat(parser.v2 ? '}' : '}}', true);

parser.current().children.push({
start,
Expand Down
Loading