Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ build/
coverage/
*.tsbuildinfo
.next
docs/

# prevent declaration files in src directories
packages/*/src/**/*.d.ts
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"dev": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"docs": "typedoc --out docs src/parser/ast.ts"
},
"dependencies": {
"acorn": "^8.14.0",
Expand All @@ -27,6 +28,7 @@
"@types/estree": "^1.0.8",
"@types/moo": "^0.5.9",
"@types/node": "^20.11.24",
"typedoc": "^0.28.7",
"typescript": "^5.7.2",
"vitest": "^2.1.8"
}
Expand Down
188 changes: 188 additions & 0 deletions packages/compiler/src/parser/ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**
* AST (Abstract Syntax Tree) type definitions for Astro templates
*
* These types represent the structure of parsed .astro files,
* converting raw tokens into a hierarchical tree structure.
*
* @module ast
*/

/**
* The root node of an Astro AST
*
* Represents the entire parsed .astro file as a program containing
* multiple top-level nodes (frontmatter, elements, etc.)
*
* @example
* ```typescript
* const ast: AstroAST = {
* type: 'Program',
* children: [frontmatterNode, elementNode]
* }
* ```
*/
export interface AstroAST {
/** Node type identifier */
type: 'Program'
/** Array of top-level child nodes */
children: AstroNode[]
}

/**
* Union type representing all possible AST node types
*
* This type encompasses all nodes that can appear in an Astro AST,
* including the root program node and all child node types.
*/
export type AstroNode =
| AstroAST
| FrontmatterNode
| TemplateNode
| ElementNode
| TextNode
| ExpressionNode

/**
* Represents the frontmatter section of an Astro file
*
* Frontmatter is JavaScript/TypeScript code enclosed in --- markers
* at the beginning of an .astro file. It's executed at build time.
*
* @example
* ```astro
* ---
* const title = 'My Page'
* import Layout from './Layout.astro'
* ---
* ```
*/
export interface FrontmatterNode {
/** Node type identifier */
type: 'Frontmatter'
/** The JavaScript/TypeScript code content */
value: string
}

/**
* Represents the template body of an Astro file
*
* The template node contains all content after the frontmatter,
* including HTML elements, components, and expressions.
* This is a container node that groups all template content.
*/
export interface TemplateNode {
/** Node type identifier */
type: 'Template'
/** Child nodes within the template */
children: AstroNode[]
}

/**
* Represents an HTML element or Astro component
*
* This node type covers both standard HTML elements (div, span, etc.)
* and Astro components (both .astro components and framework components).
*
* @example
* ```astro
* <div class="container" id={dynamicId}>
* <h1>Hello</h1>
* </div>
* ```
*/
export interface ElementNode {
/** Node type identifier */
type: 'Element'
/** Element or component name (e.g., 'div', 'Button', 'Layout') */
name: string
/** Array of element attributes */
attributes: Array<{
/** Attribute name */
name: string
/** Attribute value (can be static string or dynamic expression) */
value: string | ExpressionNode
}>
/** Child nodes nested within this element */
children: AstroNode[]
/** Whether the element is self-closing (e.g., <img />) */
selfClosing: boolean
}

/**
* Represents plain text content
*
* Text nodes contain literal text that appears between elements
* or within elements. Whitespace is preserved.
*
* @example
* ```astro
* <p>This is a text node</p>
* ```
*/
export interface TextNode {
/** Node type identifier */
type: 'Text'
/** The text content */
value: string
}

/**
* Represents a JavaScript expression in the template
*
* Expressions are JavaScript code wrapped in curly braces {}
* that get evaluated and rendered in the template.
*
* @example
* ```astro
* <div>{count * 2}</div>
* <p>{user.name}</p>
* ```
*/
export interface ExpressionNode {
/** Node type identifier */
type: 'Expression'
/** The JavaScript expression code */
value: string
}

/**
* Type guard to check if a node is an AstroAST (Program) node
*/
export function isAstroAST(node: AstroNode): node is AstroAST {
return node.type === 'Program'
}

/**
* Type guard to check if a node is a FrontmatterNode
*/
export function isFrontmatterNode(node: AstroNode): node is FrontmatterNode {
return node.type === 'Frontmatter'
}

/**
* Type guard to check if a node is a TemplateNode
*/
export function isTemplateNode(node: AstroNode): node is TemplateNode {
return node.type === 'Template'
}

/**
* Type guard to check if a node is an ElementNode
*/
export function isElementNode(node: AstroNode): node is ElementNode {
return node.type === 'Element'
}

/**
* Type guard to check if a node is a TextNode
*/
export function isTextNode(node: AstroNode): node is TextNode {
return node.type === 'Text'
}

/**
* Type guard to check if a node is an ExpressionNode
*/
export function isExpressionNode(node: AstroNode): node is ExpressionNode {
return node.type === 'Expression'
}
11 changes: 10 additions & 1 deletion packages/compiler/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ export type {
AstroAST,
AstroNode,
FrontmatterNode,
TemplateNode,
ElementNode,
TextNode,
ExpressionNode,
} from './types.js'
} from './ast.js'
export {
isAstroAST,
isFrontmatterNode,
isTemplateNode,
isElementNode,
isTextNode,
isExpressionNode,
} from './ast.js'
export type { WalkHandler, AsyncWalkHandler } from './utils.js'
2 changes: 1 addition & 1 deletion packages/compiler/src/parser/parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { tokenize } from '../tokenizer/index.js'
import type { Token } from '../tokenizer/types.js'
import type { AstroAST, AstroNode, ElementNode, ExpressionNode } from './types.js'
import type { AstroAST, AstroNode, ElementNode, ExpressionNode } from './ast.js'

/**
* Parser state that maintains the current position in the token stream
Expand Down
32 changes: 0 additions & 32 deletions packages/compiler/src/parser/types.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/compiler/src/parser/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AstroAST, AstroNode, ElementNode } from './types.js'
import type { AstroAST, AstroNode, ElementNode } from './ast.js'

/**
* Walk handler function type for visiting AST nodes
Expand Down
Loading
Loading