Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Peast Laravel Package

mck89/peast

Peast is a PHP 5.4+ library that parses JavaScript per the ECMAScript spec and builds an abstract syntax tree following the ESTree standard. It also supports tokenization, tree traversal, selector queries, and rendering.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation: Add to composer.json:

    "require": {
        "mck89/peast": "^1.17"
    }
    

    Run composer install.

  2. 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).
  3. 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).

First Use Case: Extract Function Names

$traverser = new \Peast\Traverser();
$traverser->enterFunctionDeclaration(function (\Peast\Node $node) {
    echo "Found function: {$node->name}\n";
});

$traverser->traverse($ast);

Output:

Found function: foo

Implementation Patterns

1. Parsing with Options

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();

2. Traversing the AST

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;
    }
}

3. Querying Nodes

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]');

4. Rendering AST Back to JS

Convert AST to code (e.g., for minification or debugging):

$renderer = new \Peast\Renderer();
$renderedCode = $renderer->render($ast);

5. Handling Comments

Parse and preserve comments:

$options = ['comment' => true];
$ast = \Peast\Peast::latest($source, $options)->parse();

// Access comments via node->leadingComments or node->trailingComments

6. ES Version Targeting

Specify ECMAScript version to avoid parsing unsupported syntax:

// Parse as ES2015 (avoids ES2020+ features)
$ast = \Peast\Peast::version2015($source)->parse();

7. Error Handling

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()}";
}

Gotchas and Tips

Pitfalls

  1. Xdebug Nesting Limit:

    • Issue: Deeply nested JS (e.g., recursive functions) triggers Maximum function nesting level errors.
    • Fix: Increase Xdebug setting:
      ini_set('xdebug.max_nesting_level', 1000);
      
      Or disable Xdebug in production.
  2. Unicode Identifiers:

    • Issue: rawName vs. name properties differ for Unicode escapes (e.g., \u0041).
    • Fix: Use $node->rawName for original source representation.
  3. BlockStatement Brackets:

    • Issue: Single-line blocks (e.g., if (x) y) may lose brackets when re-rendered.
    • Fix: Manually ensure brackets in traversal:
      $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]]);
          }
      });
      
  4. ES Version Mismatches:

    • Issue: Parsing ES2020+ code with ES2015 settings fails silently or throws errors.
    • Fix: Explicitly set ecmaVersion to match your target.
  5. Comment Handling:

    • Issue: Comments are parsed but not automatically preserved in transformations.
    • Fix: Use leadingComments/trailingComments properties and reattach during traversal.
  6. Large Files:

    • Issue: Memory spikes with 1MB+ JS files.
    • Fix: Stream input or parse incrementally (e.g., split by semicolons).

Debugging Tips

  1. Visualize AST: Use var_export($ast, true) or a library like league/container to inspect nodes.

  2. Selector Testing: Test Query selectors incrementally:

    $query = new \Peast\Query($ast);
    var_dump($query->find('FunctionDeclaration')); // Start broad
    
  3. Renderer Quirks:

    • Problem: Rendered code may not match input due to AST normalization (e.g., implicit semicolons).
    • Solution: Compare render($ast) with original source to identify discrepancies.
  4. Performance Profiling: For large ASTs, profile traversal:

    $start = microtime(true);
    $traverser->traverse($ast);
    echo "Traversal time: " . (microtime(true) - $start) . "s";
    

Extension Points

  1. Custom Node Visitors: Extend \Peast\Visitor to add domain-specific logic:

    class MyVisitor extends \Peast\Visitor {
        public function enterFunctionDeclaration(\Peast\Node $node) {
            // Custom logic
        }
    }
    
  2. 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';
        }
    });
    
  3. 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();
        });
    }
    
  4. 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
    

Laravel-Specific Tips

  1. Caching Parsed ASTs: Cache ASTs in Laravel’s cache system:
    $cacheKey = 'js_ast_' . md5($jsCode);
    $ast = Cache::remember($cache
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk