svast
TypeScript icon, indicating that this package has built-in type declarations

0.2.0 • Public • Published

svast

Svelte Abstract Syntax Tree

This AST implements the Unist spec. I think. All node types that implement a unique interface are camelCased and prefixed with svelte. I have inlined the unist nodes that svast extends for reference but the canonical documentation in the unist repo should be prefered.

This AST seeks to be language agnostic and has no opinion on the contents of any expression. Some Svelte syntax is impossible to parse in a language agnostic way, this specification does not conern itself with this problem right now.

Base Unist Nodes

I have prefixed these with Unist for clarity. See the actual spec

UnistNode

interface UnistNode {
  type: string
  data: UnistData?
  position: UnistPosition?
}

This is the base node that pretty much everything extends.

UnistPosition

interface UnistPosition {
  start: UnistPoint
  end: UnistPoint
  indent: [number >= 1]?
}

The UnistPosition node represents the location of a node in the source file.

The start field represents the first character of the parsed source for that node.

The end position represents the last character of the parsed source for that node.

I do not understand what indent is right now.

UnistPoint

interface UnistPoint {
  line: number >= 1
  column: number >= 1
  offset: number >= 0?
}

The UnistPoint node represents one place in a source file.

The line field (1-indexed integer) represents a line in a source file.

The column field (1-indexed integer) represents a column in a source file.

The offset field (0-indexed integer) represents a character in a source file.

UnistData

interface UnistData { }

The UnistData node represents information associated by the ecosystem with the node.

This space is guaranteed to never be specified by unist or specifications implementing unist.

UnistParent

interface UnistParent <: UnistNode {
  children: [UnistNode]
}

Nodes containing other nodes (said to be children) extend the abstract interface UnistParent (Node).

The children field is a list representing the children of a node.

SVAST Nodes

Parent

interface Parent <: UnistParent {
  children: [
    | SvelteElement
    | SvelteComponent
    | Comment
    | Text
    | SvelteExpression
    | VoidBlock
    | BranchingBlock
    | IfBlock
    | EachBlock
    | AwaitBlock
    | SvelteTag
  ]
}

A Parent is a node with children which is a list of nodes.

Literal

interface Literal<T> <: UnistNode {
  type: T
  value: string
}

A node containing a value. It is that simple. This is generic is used by other nodes, such a comment.

Root

interface Root <: Parent {
  type: "root"
}

The root node of a tree.

BaseTag

interface BaseTag <: Parent {
  tagName: string
  properties: [Property | Directive]
  selfClosing: boolean
}

The BaseTag node is the node that all element and component types extend.

The tagName field contains the element's local name.

The properties field is a list of the element's attributes and directives. This field is a list of nodes that implement the Property or Directive interfaces.

The selfClosing field describes whether or not the source element was self closing or not. This isn't strictly abstract but is helpful in certain cases.

Meta

interface SvelteTag <: BaseTag {
  type: "svelteMeta"
}

The SvelteTag represent special svelte namespaced tag names such as <svelte:self />.

The following input:

<svelte:self this={Component} />

Yields:

{
  type: 'svelteTag',
  tagName: 'self',
  properties: [{
    type: 'svelteProperty',
    name: 'this',
    modifiers: [],
    value: [{
      type: 'svelteExpression',
      value: 'Component'
    }]
  }],
  selfClosing: true,
  children: []
}

Element

interface Element <: BaseTag {
  type: "svelteElement"
}

The Element node represents a DOM-element in Svelte.

The following input:

<input on:click|preventDefault={handleClick} />

Yields:

{
  type: 'svelteElement',
  tagName: 'input',
  properties: [{
    type: 'svelteDirective',
    name: 'on',
    specifier: 'click',
    modifiers: [{
      type: 'modifier', value: 'preventDefault'
    }],
    value: [{
      type: 'svelteExpression',
      value: 'handleClick'
    }]
  }],
  selfClosing: true,
  children: []
}

Component

interface Component <: BaseTag {
  type: "svelteComponent"
}

The Component interface represents Svelte components, PascalCased tags.

This input:

<MyComponent on:click|preventDefault={handleClick} />

Yields:

{
  type: 'svelteElement',
  tagName: 'MyComponent',
  properties: [{
    type: 'svelteDirective',
    name: 'on',
    specifier: 'click',
    modifiers: [{
      type: 'svelteModifier', value: 'preventDefault'
    }],
    value: [{
      type: 'svelteExpression',
      value: 'handleClick'
    }]
  }],
  selfClosing: true,
  children: []
}

Script

interface Component <: BaseTag {
  type: "svelteScript"
}

The Script interface represents Svelte script tags. The always have a single child text node containing the script contents.

This input:

<script>
  console.log('boo');
</script>

Yields:

{
  type: 'svelteScript',
  tagName: 'script',
  properties: [],
  selfClosing: false,
  children: [
    {
      type: 'text',
      value: '\n  console.log('boo');\n'
    }
  ]
}

Style

interface Component <: BaseTag {
  type: "svelteScript"
}

The Style interface represents Svelte style tags. The always have a single child text node containing the style contents.

This input:

<style>
  h1 {
    color: pink;
  }
</style>

Yields:

{
  type: 'svelteStyle',
  tagName: 'style',
  properties: [],
  selfClosing: false,
  children: [
    {
      type: 'text',
      value: '\n  h1 {\n    color: pink;\n  }\n'
    }
  ]
}

BaseProperty

interface Property <: UnistNode {
  name: string
  shorthand: 'none' | 'boolean' | 'expression'
  value: [Text | Expression]
  modifiers: [Literal]
}

Property

interface Property <: BaseProperty {
  type: 'svelteProperty'
}

The Property node represents an element's properties and reflect HTML, SVG, ARIA, XML, XMLNS, or XLink attributes.

The name field contains the exact name of the attribute or property as it is in the source. kebal-case names or not modified.

The shorthand field signifies whether or not shorthand property syntax was used. There are two type of shorthand, short hand expressions ({prop}) and shorthand booleans (prop).

The value field is always a list of nodes that implement either the Text or Expression interfaces. In the case of shorthand property expressions, the value field will be a list with one node (an Expression) whose value is the same as the attribute name.

The modifiers field represents any modifiers applied to a property name. In Svelte this takes the form of on:click|once|capture={...}. This value should be a list of Literal nodes, describing the modifier name.

This input:

<a name="hello {friend}!" />

Yields:

{
  type: 'svelteElement',
  tagName: 'a',
  properties: [{
    type: 'svelteProperty',
    name: 'name',
    value: [{
      type: 'text',
      value: 'hello'
    }, {
      type: 'svelteExpression',
      value: 'friend'
    }, {
      type: 'text',
      value: '!'
    }],
    shorthand: 'none',
    modifiers: [],
  }],
  selfClosing: true,
  children: []
}

Directive

interface Directive <: BaseProperty {
  type: 'svelteDirective'
  specifier: string
}

The Directive node represents a Svelte directive x:y={z}.

The name field reprsents the directive 'type', the part of the attrubute before the :.

The specificer field describes the local implementation of that directive type. It is the part of the attribute name after the : but before the = or a whitespace character.

In the case of shorthand being true, value will be a list of one Expression node with a value equal to the specifier value.

The following input:

<a class:myclass={x ? y : z} on:click|preventDefault={(e) => fn(e)} />

Yields:

{
  type: 'svelteElement',
  tagName: 'a',
  properties: [{
    type: 'svelteDirective',
    name: 'class',
    specifier: 'myclass',
    value: [{
      type: 'svelteExpression',
      value: 'x ? y : z'
    }],
    shorthand: 'none',
    modifiers: [],
  }, {
    type: 'svelteDirective',
    name: 'on',
    specifier: 'click',
    value: [{
      type: 'svelteExpression',
      value: '(e) => fn(e)'
    }],
    shorthand: 'none',
    modifiers: [{
      type: 'svelteModifier',
      value: 'preventDefault'
    }],
  }],
  selfClosing: true,
  children: []
}

Comment

interface Comment <: Literal {
  type: "comment"
}

Represents an HTML comment.

The value field should contain the contents of the comment.

This comment:

<!--Some thing here-->

Yields:

{type: 'comment', value: 'Some thing here'}

Text

interface Text <: Literal {
  type: "text"
}

Represents bare text.

The value field should contain the text.

The following input:

<div>Hello there</div>

Yields:

{
  type: 'svelteElement',
  tagName: 'div',
  properties: [],
  selfClosing: false,
  children: [{
    type: 'text',
    value: 'Hello there'
  }]
}

VoidBlock

interface VoidBlock <: Node {
  type: 'svelteVoidBlock'
  name: string
  expression: Expression
}

The VoidBlock node represents a void block. Void blocks do not allow branches.

The name field is be the name of the block.

The expression field is an Expression node containing the expression value for that block.

For the following input:

{@html `<p>something</p>`}

Yields:

{
  type: 'svelteVoidBlock',
  name: 'html',
  expression: {
    type: 'svelteExpression',
    value: '<p>something</p>'
  }
}

BranchingBlock

interface BranchingBlock <: Parent {
  type: 'svelteBranchingBlock'
  name: string
  branches: [Branch]
}

The BranchingBlock node represents a Svelte Block that allows an arbitrary number of named branches, the first branch is alway defined by the opening block statement {#name expresion}.

The name field represents the name of the block.

The branches field contains any branches that block has. A block must have at least one branch, even if it is empty.

This node is used for all non-void blocks other than each blocks.

The following input:

{#custom someExpression}
  Hello
{/custom}

Yields:

{
  type: 'svelteBranchingBlock',
  name: 'custom',
  branches: [{
    type: 'svelteBranch',
    name: 'custom',
    expression: {
      type: 'svelteExpression',
      value: 'someExpression'
    },
    children: [{
      type: 'text',
      value: 'Hello'
    }]
  }]
}

EachBlock

export interface EachBlock <: SvelteParent {
  type: 'svelteEachBlock'
  expression: Expression
  itemName: Expression
  itemIndex: Expression?
  itemKey: Expression?
}

The EachBlock node represents a Svelte #each block.

The expression field is the collection that is being iterated. The value is an Expression node.

The itemName field is the identifier referring to a single element of the collection during the loop. The value is an Expression node.

The itemIndex field is the identifier used to refer to the index of the iterated item during the loop, if one exists.

The itemKey field is optional and is the value that should be used as a key for eachitem, if one exists. The presence of this field signifies that the each block is keyed.

The follwing input:

{#each array.filter(v => v.prop) as { some, thing }, index (thing)}
  <p>{some}</p>
{/each}

Yields:

{
  type: 'svelteEachBlock',
  itemName: {
    type: 'svelteExpression',
    value: '{ some, thing }'
  },
  itemIndex: {
    type: 'svelteExpression',
    value: 'index'
  },
  itemKey: {
    type: 'svelteExpression',
    value: 'thing'
  },
  children: [{
    type: 'svelteElement',
    tagName: 'p',
    properties: [],
    selfClosing: false,
    children: [{
      type: 'svelteExpression',
      value: 'some'
    }]
  }]
}

Branch

interface Branch <: Parent {
  type: 'svelteBranch'
  name: string
  expression: Expression
}

The Branch node describes a branch of a Svelte block.

The expression fields contains the expression associated with that branch.


The end.

Readme

Keywords

Package Sidebar

Install

npm i svast

Weekly Downloads

169

Version

0.2.0

License

MIT

Unpacked Size

18.4 kB

Total Files

5

Last publish

Collaborators

  • evilpingwin