Merge branch 'master' into blog-anchors-2609

pull/2675/head
Richard Harris 5 years ago
commit 16430dc6bb

@ -153,6 +153,30 @@ compiled: {
-->
### `svelte.parse`
```js
ast: object = svelte.parse(
source: string,
options?: {
filename?: string,
customElement?: boolean
}
)
```
---
The `parse` function parses a component, returning only its abstract syntax tree. Unlike compiling with the `generate: false` option, this will not perform any validation or other analysis of the component beyond parsing it.
```js
const svelte = require('svelte/compiler');
const ast = svelte.parse(source, { filename: 'App.svelte' });
```
### `svelte.preprocess`
```js
@ -280,6 +304,38 @@ const { code } = svelte.preprocess(source, [
```
### `svelte.walk`
```js
walk(ast: Node, {
enter(node: Node, parent: Node)?: void,
leave(node: Node, parent: Node)?: void
})
```
---
The `walk` function provides a way to walk to abstract syntax trees generated by the parser, using the compiler's own built-in instance of [estree-walker](https://github.com/Rich-Harris/estree-walker).
The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node.
```js
const svelte = require('svelte/compiler');
svelte.walk(ast, {
enter(node, parent) {
do_something(node);
if (should_skip_children(node)) {
this.skip();
}
},
leave(node, parent) {
do_something_else(node);
}
});
```
### `svelte.VERSION`
---

@ -1,12 +1,9 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class CatchBlock extends Node {
block: Block;
export default class CatchBlock extends AbstractBlock {
scope: TemplateScope;
children: Node[];
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
@ -15,6 +12,8 @@ export default class CatchBlock extends Node {
this.scope.add(parent.error, parent.expression.dependencies, this);
this.children = map_children(component, parent, this.scope, info.children);
this.warn_if_empty_block();
if (!info.skip) {
this.warn_if_empty_block();
}
}
}
}

@ -1,9 +1,9 @@
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import { Node as INode } from '../../interfaces';
function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) {
@ -25,10 +25,9 @@ function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, n
}
}
export default class EachBlock extends Node {
export default class EachBlock extends AbstractBlock {
type: 'EachBlock';
block: Block;
expression: Expression;
context_node: Node;
@ -41,7 +40,6 @@ export default class EachBlock extends Node {
has_animation: boolean;
has_binding = false;
children: Node[];
else?: ElseBlock;
constructor(component, parent, scope, info) {
@ -85,7 +83,7 @@ export default class EachBlock extends Node {
}
}
this.warn_if_empty_block(); // TODO would be better if EachBlock, IfBlock etc extended an abstract Block class
this.warn_if_empty_block();
this.else = info.else
? new ElseBlock(component, this, this.scope, info.else)

@ -224,7 +224,21 @@ export default class Element extends Node {
}
if (this.name === 'figcaption') {
if (this.parent.name !== 'figure') {
let { parent } = this;
let is_figure_parent = false;
while (parent) {
if (parent.name === 'figure') {
is_figure_parent = true;
break;
}
if (parent.type === 'Element') {
break;
}
parent = parent.parent;
}
if (!is_figure_parent) {
this.component.warn(this, {
code: `a11y-structure`,
message: `A11y: <figcaption> must be an immediate child of <figure>`

@ -1,11 +1,8 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class ElseBlock extends Node {
export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock';
children: Node[];
block: Block;
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
@ -13,4 +10,4 @@ export default class ElseBlock extends Node {
this.warn_if_empty_block();
}
}
}

@ -1,17 +1,13 @@
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression';
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class IfBlock extends Node {
export default class IfBlock extends AbstractBlock {
type: 'IfBlock';
expression: Expression;
children: any[];
else: ElseBlock;
block: Block;
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
@ -24,4 +20,4 @@ export default class IfBlock extends Node {
this.warn_if_empty_block();
}
}
}

@ -1,15 +1,14 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class PendingBlock extends Node {
block: Block;
children: Node[];
export default class PendingBlock extends AbstractBlock {
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.children = map_children(component, parent, scope, info.children);
this.warn_if_empty_block();
if (!info.skip) {
this.warn_if_empty_block();
}
}
}
}

@ -1,12 +1,9 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class ThenBlock extends Node {
block: Block;
export default class ThenBlock extends AbstractBlock {
scope: TemplateScope;
children: Node[];
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
@ -15,6 +12,8 @@ export default class ThenBlock extends Node {
this.scope.add(parent.value, parent.expression.dependencies, this);
this.children = map_children(component, parent, this.scope, info.children);
this.warn_if_empty_block();
if (!info.skip) {
this.warn_if_empty_block();
}
}
}
}

@ -0,0 +1,25 @@
import Block from '../../render-dom/Block';
import Component from './../../Component';
import Node from './Node';
export default class AbstractBlock extends Node {
block: Block;
children: Node[];
constructor(component: Component, parent, scope, info: any) {
super(component, parent, scope, info);
}
warn_if_empty_block() {
if (!this.children || this.children.length > 1) return;
const child = this.children[0];
if (!child || (child.type === 'Text' && !/[^ \r\n\f\v\t]/.test(child.data))) {
this.component.warn(this, {
code: 'empty-block',
message: 'Empty block'
});
}
}
}

@ -1,3 +1,4 @@
import Attribute from './../Attribute';
import Component from './../../Component';
export default class Node {
@ -12,6 +13,7 @@ export default class Node {
can_use_innerhtml: boolean;
var: string;
attributes: Attribute[];
constructor(component: Component, parent, scope, info: any) {
this.start = info.start;
@ -64,18 +66,4 @@ export default class Node {
this.parent.type === type || this.parent.has_ancestor(type) :
false;
}
warn_if_empty_block() {
if (!/Block$/.test(this.type) || !this.children) return;
if (this.children.length > 1) return;
const child = this.children[0];
if (!child || (child.type === 'Text' && !/[^ \r\n\f\v\t]/.test(child.data))) {
this.component.warn(this, {
code: 'empty-block',
message: 'Empty block'
});
}
}
}

@ -1,4 +1,6 @@
export { default as compile } from './compile/index';
export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
export const VERSION = '__VERSION__';

@ -61,6 +61,11 @@ export interface CompileOptions {
preserveWhitespace?: boolean;
}
export interface ParserOptions {
filename?: string;
customElement?: boolean;
}
export interface Visitor {
enter: (node: Node) => void;
leave?: (node: Node) => void;

@ -3,14 +3,9 @@ import fragment from './state/fragment';
import { whitespace } from '../utils/patterns';
import { reserved } from '../utils/names';
import full_char_code_at from '../utils/full_char_code_at';
import { Node, Ast } from '../interfaces';
import { Node, Ast, ParserOptions } from '../interfaces';
import error from '../utils/error';
interface ParserOptions {
filename?: string;
customElement?: boolean;
}
type ParserState = (parser: Parser) => (ParserState | void);
export class Parser {

@ -172,7 +172,8 @@ export default function mustache(parser: Parser) {
start,
end: null,
type: 'ThenBlock',
children: []
children: [],
skip: false
};
await_block.then = then_block;
@ -196,7 +197,8 @@ export default function mustache(parser: Parser) {
start,
end: null,
type: 'CatchBlock',
children: []
children: [],
skip: false
};
await_block.catch = catch_block;
@ -235,19 +237,22 @@ export default function mustache(parser: Parser) {
start: null,
end: null,
type: 'PendingBlock',
children: []
children: [],
skip: true
},
then: {
start: null,
end: null,
type: 'ThenBlock',
children: []
children: [],
skip: true
},
catch: {
start: null,
end: null,
type: 'CatchBlock',
children: []
children: [],
skip: true
},
} :
{
@ -303,7 +308,15 @@ export default function mustache(parser: Parser) {
parser.stack.push(block);
if (type === 'AwaitBlock') {
const child_block = await_block_shorthand ? block.then : block.pending;
let child_block;
if (await_block_shorthand) {
block.then.skip = false;
child_block = block.then;
} else {
block.pending.skip = false;
child_block = block.pending;
}
child_block.start = parser.index;
parser.stack.push(child_block);
}

@ -19,6 +19,7 @@
"pending": {
"start": 19,
"end": 39,
"skip": false,
"type": "PendingBlock",
"children": [
{
@ -53,6 +54,7 @@
"then": {
"start": 39,
"end": 88,
"skip": false,
"type": "ThenBlock",
"children": [
{
@ -98,6 +100,7 @@
"catch": {
"start": 88,
"end": 140,
"skip": false,
"type": "CatchBlock",
"children": [
{
@ -158,4 +161,4 @@
"css": null,
"instance": null,
"module": null
}
}

@ -0,0 +1,10 @@
<script>
let caption = 'a foo in its natural habitat';
</script>
<figure>
<img src='foo.jpg' alt='a picture of a foo'>
{#if caption}
<figcaption>{caption}</figcaption>
{/if}
</figure>

@ -0,0 +1,9 @@
<script>
let promise;
</script>
{#await promise}
<p>Loading</p>
{:then data}
<p>Data: {data}</p>
{/await}

@ -0,0 +1,7 @@
<script>
let promise;
</script>
{#await promise then data}
<p>Data: {data}</p>
{/await}
Loading…
Cancel
Save