Installation:
Add to composer.json:
"require": {
"mck89/peast": "^1.17"
}
Run composer install.
First Parse:
require __DIR__.'/vendor/autoload.php';
$source = 'const x = 1; function foo() { return x; }';
$ast = \Peast\Peast::latest($source)->parse();
$ast is now an ESTree node structure (e.g., Program root with VariableDeclaration and FunctionDeclaration children).Key Classes:
\Peast\Peast: Entry point for parsing.\Peast\Traverser: Walk the AST (e.g., find all FunctionDeclaration nodes).\Peast\Query: Select nodes via CSS-like selectors (e.g., Program > FunctionDeclaration).$traverser = new \Peast\Traverser();
$traverser->enterFunctionDeclaration(function (\Peast\Node $node) {
echo "Found function: {$node->name}\n";
});
$traverser->traverse($ast);
Output:
Found function: foo
Configure parser behavior (e.g., ECMAScript version, source maps):
$options = [
'ecmaVersion' => 2020, // ES2020 (default: latest)
'sourceType' => 'module', // 'script' or 'module'
'range' => true, // Include start/end positions
'comment' => true, // Preserve comments
];
$ast = \Peast\Peast::latest($source, $options)->parse();
Pattern: Use Traverser to modify or analyze nodes.
$traverser = new \Peast\Traverser();
$traverser->enterVariableDeclaration(function (\Peast\Node $node) {
if ($node->kind === 'const') {
echo "Constant: {$node->declarations[0]->id->name}\n";
}
});
$traverser->traverse($ast);
Laravel Integration: Wrap in a service class:
class JSASTService {
public function parse(string $jsCode): \Peast\Node
{
return \Peast\Peast::latest($jsCode)->parse();
}
public function findFunctions(\Peast\Node $ast): array
{
$traverser = new \Peast\Traverser();
$functions = [];
$traverser->enterFunctionDeclaration(function (\Peast\Node $node) use (&$functions) {
$functions[] = $node->name;
});
$traverser->traverse($ast);
return $functions;
}
}
Use Query for selector-based searches (e.g., find all arrow functions):
$query = new \Peast\Query($ast);
$arrowFunctions = $query->find('FunctionDeclaration[async=false][expression=true]');
Convert AST to code (e.g., for minification or debugging):
$renderer = new \Peast\Renderer();
$renderedCode = $renderer->render($ast);
Parse and preserve comments:
$options = ['comment' => true];
$ast = \Peast\Peast::latest($source, $options)->parse();
// Access comments via node->leadingComments or node->trailingComments
Specify ECMAScript version to avoid parsing unsupported syntax:
// Parse as ES2015 (avoids ES2020+ features)
$ast = \Peast\Peast::version2015($source)->parse();
Catch syntax errors gracefully:
try {
$ast = \Peast\Peast::latest('invalid js {')->parse();
} catch (\Peast\Error $e) {
echo "Syntax error: {$e->getMessage()} at line {$e->getLine()}";
}
Xdebug Nesting Limit:
Maximum function nesting level errors.ini_set('xdebug.max_nesting_level', 1000);
Or disable Xdebug in production.Unicode Identifiers:
rawName vs. name properties differ for Unicode escapes (e.g., \u0041).$node->rawName for original source representation.BlockStatement Brackets:
if (x) y) may lose brackets when re-rendered.$traverser->enterIfStatement(function (\Peast\Node $node) {
if (count($node->consequent->body) === 1 && !$node->consequent->body[0] instanceof \Peast\BlockStatement) {
$node->consequent->body = new \Peast\BlockStatement([$node->consequent->body[0]]);
}
});
ES Version Mismatches:
ecmaVersion to match your target.Comment Handling:
leadingComments/trailingComments properties and reattach during traversal.Large Files:
Visualize AST:
Use var_export($ast, true) or a library like league/container to inspect nodes.
Selector Testing:
Test Query selectors incrementally:
$query = new \Peast\Query($ast);
var_dump($query->find('FunctionDeclaration')); // Start broad
Renderer Quirks:
render($ast) with original source to identify discrepancies.Performance Profiling: For large ASTs, profile traversal:
$start = microtime(true);
$traverser->traverse($ast);
echo "Traversal time: " . (microtime(true) - $start) . "s";
Custom Node Visitors:
Extend \Peast\Visitor to add domain-specific logic:
class MyVisitor extends \Peast\Visitor {
public function enterFunctionDeclaration(\Peast\Node $node) {
// Custom logic
}
}
AST Transformations:
Use Traverser to rewrite nodes (e.g., rename variables):
$traverser = new \Peast\Traverser();
$traverser->enterIdentifier(function (\Peast\Node $node) {
if ($node->name === 'oldName') {
$node->name = 'newName';
}
});
Plugin Architecture: Encapsulate traversal logic in Laravel service providers:
// app/Providers/JSASTServiceProvider.php
public function register() {
$this->app->singleton(JSASTService::class, function () {
return new JSASTService();
});
}
Integration with Laravel Blade: Parse JS in Blade templates:
// app/Helpers/JSASTHelper.php
function parseInlineJS(string $js): string {
$ast = \Peast\Peast::latest($js)->parse();
// Validate or transform AST
return $ast->type; // Example: return AST type
}
@php
$js = '<script>const x = 1;</script>';
$astType = parseInlineJS($js);
@endphp
$cacheKey = 'js_ast_' . md5($jsCode);
$ast = Cache::remember($cache
How can I help you explore Laravel packages today?