Skip to content

Commit 813e077

Browse files
authored
Merge pull request #1330 from sveltejs/gh-1318
implement syntax changes
2 parents eed32f7 + 2b3fe0e commit 813e077

File tree

271 files changed

+14612
-208
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

271 files changed

+14612
-208
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ coverage.lcov
1212
test/sourcemaps/samples/*/output.js
1313
test/sourcemaps/samples/*/output.js.map
1414
_actual.*
15+
_actual-v2.*
1516
_actual-bundle.*
1617
src/generators/dom/shared.ts
1718
package-lock.json

src/generators/Generator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ export default class Generator {
791791

792792
node.generator = generator;
793793

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

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

src/generators/nodes/Component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export default class Component extends Node {
4545

4646
this.var = block.getUniqueName(
4747
(
48-
this.name === ':Self' ? this.generator.name :
49-
this.name === ':Component' ? 'switch_instance' :
48+
(this.name === ':Self' || this.name === 'svelte:self') ? this.generator.name :
49+
(this.name === ':Component' || this.name === 'svelte:component') ? 'switch_instance' :
5050
this.name
5151
).toLowerCase()
5252
);
@@ -292,7 +292,7 @@ export default class Component extends Node {
292292
`;
293293
}
294294

295-
if (this.name === ':Component') {
295+
if (this.name === ':Component' || this.name === 'svelte:component') {
296296
const switch_value = block.getUniqueName('switch_value');
297297
const switch_props = block.getUniqueName('switch_props');
298298

@@ -386,7 +386,7 @@ export default class Component extends Node {
386386

387387
block.builders.destroy.addLine(`if (${name}) ${name}.destroy(false);`);
388388
} else {
389-
const expression = this.name === ':Self'
389+
const expression = (this.name === ':Self' || this.name === 'svelte:self')
390390
? generator.name
391391
: `%components-${this.name}`;
392392

src/generators/nodes/EachBlock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export default class EachBlock extends Node {
126126

127127
// hack the sourcemap, so that if data is missing the bug
128128
// is easy to find
129-
let c = this.start + 3;
129+
let c = this.start + 2;
130130
while (generator.source[c] !== 'e') c += 1;
131131
generator.code.overwrite(c, c + 4, 'length');
132132
const length = `[✂${c}-${c+4}✂]`;

src/generators/server-side-rendering/visitors/Component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ export default function visitComponent(
8787
.concat(bindingProps)
8888
.join(', ')} }`;
8989

90-
const isDynamicComponent = node.name === ':Component';
90+
const isDynamicComponent = node.name === ':Component' || node.name === 'svelte:component';
9191
if (isDynamicComponent) block.contextualise(node.expression);
9292

9393
const expression = (
94-
node.name === ':Self' ? generator.name :
94+
(node.name === ':Self' || node.name === 'svelte:self') ? generator.name :
9595
isDynamicComponent ? `((${node.metadata.snippet}) || __missingComponent)` :
9696
`%components-${node.name}`
9797
);

src/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export interface CompileOptions {
6666

6767
onerror?: (error: Error) => void;
6868
onwarn?: (warning: Warning) => void;
69+
70+
parser?: 'v2';
6971
}
7072

7173
export interface GenerateOptions {

src/parse/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import error from '../utils/error';
1313
interface ParserOptions {
1414
filename?: string;
1515
bind?: boolean;
16+
parser?: 'v2';
1617
}
1718

1819
type ParserState = (parser: Parser) => (ParserState | void);
1920

2021
export class Parser {
22+
readonly v2: boolean;
2123
readonly template: string;
2224
readonly filename?: string;
2325

@@ -32,6 +34,8 @@ export class Parser {
3234
allowBindings: boolean;
3335

3436
constructor(template: string, options: ParserOptions) {
37+
this.v2 = options.parser === 'v2';
38+
3539
if (typeof template !== 'string') {
3640
throw new TypeError('Template must be a string');
3741
}

src/parse/read/directives.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,20 +140,25 @@ export function readDirective(
140140

141141
const expressionStart = parser.index;
142142

143-
if (parser.eat('{{')) {
144-
let message = 'directive values should not be wrapped';
145-
const expressionEnd = parser.template.indexOf('}}', expressionStart);
146-
if (expressionEnd !== -1) {
147-
const value = parser.template.slice(parser.index, expressionEnd);
148-
message += ` — use '${value}', not '{{${value}}}'`;
143+
try {
144+
expression = readExpression(parser, expressionStart, quoteMark);
145+
if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) {
146+
parser.error(directive.error, expressionStart);
147+
}
148+
} catch (err) {
149+
if (parser.template[expressionStart] === '{') {
150+
// assume the mistake was wrapping the directive arguments.
151+
// this could yield false positives! but hopefully not too many
152+
let message = 'directive values should not be wrapped';
153+
const expressionEnd = parser.template.indexOf((parser.v2 ? '}' : '}}'), expressionStart);
154+
if (expressionEnd !== -1) {
155+
const value = parser.template.slice(expressionStart + (parser.v2 ? 1 : 2), expressionEnd);
156+
message += ` — use '${value}', not '${parser.v2 ? `{${value}}` : `{{${value}}}`}'`;
157+
}
158+
parser.error(message, expressionStart);
149159
}
150160

151-
parser.error(message, expressionStart);
152-
}
153-
154-
expression = readExpression(parser, expressionStart, quoteMark);
155-
if (directive.allowedExpressionTypes.indexOf(expression.type) === -1) {
156-
parser.error(directive.error, expressionStart);
161+
throw err;
157162
}
158163
}
159164

src/parse/read/expression.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const literals = new Map([['true', true], ['false', false], ['null', null]]);
66
export default function readExpression(parser: Parser) {
77
const start = parser.index;
88

9-
const name = parser.readUntil(/\s*}}/);
9+
const name = parser.readUntil(parser.v2 ? /\s*}/ : /\s*}}/);
1010
if (name && /^[a-z]+$/.test(name)) {
1111
const end = start + name.length;
1212

src/parse/state/fragment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function fragment(parser: Parser) {
88
return tag;
99
}
1010

11-
if (parser.match('{{')) {
11+
if (parser.match(parser.v2 ? '{' : '{{')) {
1212
return mustache;
1313
}
1414

src/parse/state/mustache.ts

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) {
3232

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

3737
parser.allowWhitespace();
3838

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

6262
parser.eat(expected, true);
6363
parser.allowWhitespace();
64-
parser.eat('}}', true);
64+
parser.eat(parser.v2 ? '}' : '}}', true);
6565

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

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

9797
parser.allowWhitespace();
98-
parser.eat('}}', true);
98+
parser.eat(parser.v2 ? '}' : '}}', true);
9999

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

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

125125
parser.allowWhitespace();
126-
parser.eat('}}', true);
126+
parser.eat(parser.v2 ? '}' : '}}', true);
127127

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

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

147147
parser.allowWhitespace();
148-
parser.eat('}}', true);
148+
parser.eat(parser.v2 ? '}' : '}}', true);
149149

150150
const thenBlock: Node = {
151151
start,
@@ -157,7 +157,7 @@ export default function mustache(parser: Parser) {
157157
awaitBlock.then = thenBlock;
158158
parser.stack.push(thenBlock);
159159
}
160-
} else if (parser.eat('catch')) {
160+
} else if (parser.eat(parser.v2 ? ':catch' : 'catch')) {
161161
const thenBlock = parser.current();
162162
if (thenBlock.type === 'ThenBlock') {
163163
thenBlock.end = start;
@@ -168,7 +168,7 @@ export default function mustache(parser: Parser) {
168168
awaitBlock.error = parser.readIdentifier();
169169

170170
parser.allowWhitespace();
171-
parser.eat('}}', true);
171+
parser.eat(parser.v2 ? '}' : '}}', true);
172172

173173
const catchBlock: Node = {
174174
start,
@@ -274,7 +274,27 @@ export default function mustache(parser: Parser) {
274274
parser.allowWhitespace();
275275
}
276276

277-
if (parser.eat('@')) {
277+
if (parser.eat('(')) {
278+
parser.allowWhitespace();
279+
280+
const expression = readExpression(parser);
281+
282+
// TODO eventually, we should accept any expression, and turn
283+
// it into a function. For now, assume that every expression
284+
// follows the `foo.id` pattern, and equates to `@id`
285+
if (
286+
expression.type !== 'MemberExpression' ||
287+
expression.property.computed ||
288+
expression.property.type !== 'Identifier'
289+
) {
290+
parser.error('invalid key', expression.start);
291+
}
292+
293+
block.key = expression.property.name;
294+
parser.allowWhitespace();
295+
parser.eat(')', true);
296+
parser.allowWhitespace();
297+
} else if (parser.eat('@')) {
278298
block.key = parser.readIdentifier();
279299
if (!block.key) parser.error(`Expected name`);
280300
parser.allowWhitespace();
@@ -288,7 +308,7 @@ export default function mustache(parser: Parser) {
288308
parser.allowWhitespace();
289309
}
290310

291-
parser.eat('}}', true);
311+
parser.eat(parser.v2 ? '}' : '}}', true);
292312

293313
parser.current().children.push(block);
294314
parser.stack.push(block);
@@ -302,22 +322,40 @@ export default function mustache(parser: Parser) {
302322
// {{yield}}
303323
// TODO deprecate
304324
parser.allowWhitespace();
305-
parser.eat('}}', true);
306325

307-
parser.current().children.push({
308-
start,
309-
end: parser.index,
310-
type: 'Element',
311-
name: 'slot',
312-
attributes: [],
313-
children: []
314-
});
315-
} else if (parser.eat('{')) {
326+
if (parser.v2) {
327+
const expressionEnd = parser.index;
328+
329+
parser.eat('}', true);
330+
parser.current().children.push({
331+
start,
332+
end: parser.index,
333+
type: 'MustacheTag',
334+
expression: {
335+
start: expressionEnd - 5,
336+
end: expressionEnd,
337+
type: 'Identifier',
338+
name: 'yield'
339+
}
340+
});
341+
} else {
342+
parser.eat('}}', true);
343+
344+
parser.current().children.push({
345+
start,
346+
end: parser.index,
347+
type: 'Element',
348+
name: 'slot',
349+
attributes: [],
350+
children: []
351+
});
352+
}
353+
} else if (parser.eat(parser.v2 ? '@html' : '{')) {
316354
// {{{raw}}} mustache
317355
const expression = readExpression(parser);
318356

319357
parser.allowWhitespace();
320-
parser.eat('}}}', true);
358+
parser.eat(parser.v2 ? '}' : '}}}', true);
321359

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

331369
parser.allowWhitespace();
332-
parser.eat('}}', true);
370+
parser.eat(parser.v2 ? '}' : '}}', true);
333371

334372
parser.current().children.push({
335373
start,

0 commit comments

Comments
 (0)