mirror of
https://github.com/sveltejs/svelte.git
synced 2024-11-29 00:22:05 +01:00
Merge branch 'master' into blog-anchors-2609
This commit is contained in:
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
src/compile/nodes/shared/AbstractBlock.ts
Normal file
25
src/compile/nodes/shared/AbstractBlock.ts
Normal file
@ -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
test/validator/samples/await-no-catch/input.svelte
Normal file
9
test/validator/samples/await-no-catch/input.svelte
Normal file
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
let promise;
|
||||
</script>
|
||||
|
||||
{#await promise}
|
||||
<p>Loading</p>
|
||||
{:then data}
|
||||
<p>Data: {data}</p>
|
||||
{/await}
|
1
test/validator/samples/await-no-catch/warnings.json
Normal file
1
test/validator/samples/await-no-catch/warnings.json
Normal file
@ -0,0 +1 @@
|
||||
[]
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let promise;
|
||||
</script>
|
||||
|
||||
{#await promise then data}
|
||||
<p>Data: {data}</p>
|
||||
{/await}
|
@ -0,0 +1 @@
|
||||
[]
|
Loading…
Reference in New Issue
Block a user