Weave Code
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

Commonmark Attributes Extension Laravel Package

webuni/commonmark-attributes-extension

Adds Kramdown-style attribute lists to League/CommonMark markdown, letting you assign HTML ids, classes, and other attributes to block and span elements. Deprecated: use the built-in Attributes extension in league/commonmark 1.5+ instead.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps for Laravel Integration

  1. Upgrade league/commonmark (if not already on v1.5+):
    composer require league/commonmark:^1.5
    
  2. Register the Extension In a service provider (e.g., AppServiceProvider):
    use League\CommonMark\Extension\Attributes\AttributesExtension;
    use League\CommonMark\MarkdownConverter;
    
    public function boot()
    {
        $this->app->singleton(MarkdownConverter::class, function ($app) {
            $config = new \League\CommonMark\Config\Config();
            $config->addExtension(new AttributesExtension());
            return new MarkdownConverter($config);
        });
    }
    
  3. Use in Blade or Controllers Inject the converter and parse Markdown with attributes:
    $markdown = "# Heading {#id .class}\nThis is *text*{style=\"color:red\"}.";
    echo $this->app->make(MarkdownConverter::class)->convert($markdown);
    
  4. Laravel Helper (Optional) Extend Laravel’s Markdown facade (if using spatie/laravel-markdown):
    // app/Providers/AppServiceProvider.php
    use Spatie\Markdown\Markdown;
    
    public function boot()
    {
        Markdown::defaultConfig(function ($config) {
            $config->addExtension(new AttributesExtension());
        });
    }
    

Implementation Patterns

Common Workflows

  1. Dynamic Attribute Assignment Use attributes for conditional styling or tooling hooks:

    {:data-tooltip="Hover text"}
    Click me
    

    Output:

    <p data-tooltip="Hover text">Click me</p>
    
  2. Syntax Highlighting in Docs Annotate code blocks for Prism.js or Highlight.js:

    ```php {.language-php .line-numbers}
    echo "Highlighted code";
    
    
    
  3. Semantic Annotations Add data-* attributes for JavaScript interactions:

    [Link](#){.btn .btn-primary data-action="modal"}
    
  4. Laravel-Specific: Blade + Markdown Combine with Blade directives for dynamic attributes:

    @markdown
        # Dynamic Title {#title-"{{ $dynamicId }}"}
    @endmarkdown
    

Integration Tips

  • Extension Priority Register AttributesExtension before other extensions that modify HTML (e.g., GithubFlavoredMarkdownExtension) to avoid attribute loss:

    $config->addExtension(new AttributesExtension());
    $config->addExtension(new GithubFlavoredMarkdownExtension());
    
  • Validation Rules Use Laravel’s FormRequest to validate Markdown with attributes:

    public function rules()
    {
        return [
            'content' => ['required', function ($attribute, $value, $fail) {
                if (str_contains($value, '{#invalid}')) {
                    $fail('Invalid attributes in Markdown.');
                }
            }],
        ];
    }
    
  • Caching Cache the converter instance in Laravel:

    $this->app->singleton(MarkdownConverter::class, function () {
        static $converter;
        return $converter ?? new MarkdownConverter($config);
    });
    
  • Testing Use League\CommonMark\Test\TestCase for unit tests:

    public function testAttributes()
    {
        $markdown = "Text {style=\"color:red\"}";
        $expected = '<p>Text <span style="color:red">...</span></p>';
        $this->assertEquals($expected, $this->converter->convert($markdown));
    }
    

Gotchas and Tips

Pitfalls

  1. Deprecated Package Risk

    • Issue: This package is archived and replaced by league/commonmark’s built-in Attributes extension.
    • Fix: Migrate immediately to avoid future breakage. Use composer why-not league/commonmark:^1.5 to check constraints.
  2. Attribute Scope Confusion

    • Issue: Attributes on block-level elements (e.g., headers) must be on the same line or directly after the block:
      # Header {#id}  # Correct
      # Header
      {#id}           # Incorrect (ignored)
      
    • Fix: Validate Markdown syntax with regex or a linter (e.g., markdownlint).
  3. HTML Escaping

    • Issue: Unescaped attributes (e.g., {style="<script>alert()</script>"}) can inject XSS.
    • Fix: Sanitize input or use Laravel’s Str::of($value)->markdown() with htmlspecialchars.
  4. Extension Conflicts

    • Issue: Some extensions (e.g., TableOfContents) may strip attributes.
    • Fix: Register AttributesExtension last or use a custom renderer to preserve attributes.
  5. PHP 8.x Compatibility

    • Issue: The deprecated package may not support PHP 8.x features (e.g., named arguments, union types).
    • Fix: Use league/commonmark:^1.5 (PHP 8.0+ compatible) and its Attributes extension.

Debugging Tips

  • Inspect Parsed AST Use League\CommonMark\Node\Node::dump() to debug parsing:

    $document = $parser->parse($markdown);
    $document->dump(); // Outputs AST structure
    
  • Enable Debug Renderer Extend HtmlRenderer to log rendered HTML:

    $renderer = new class($environment) extends HtmlRenderer {
        public function renderNode($node) {
            $html = parent::renderNode($node);
            \Log::debug("Rendered: {$node->getType()} => {$html}");
            return $html;
        }
    };
    
  • CommonMark CLI Test syntax interactively:

    vendor/bin/commonmark --extensions=AttributesExtension "Test {#id}"
    

Extension Points

  1. Custom Attribute Validation Extend AttributesExtension to validate attributes:

    use League\CommonMark\Extension\Attributes\AttributesExtension;
    use League\CommonMark\Extension\Attributes\AttributesListener;
    
    class CustomAttributesExtension extends AttributesExtension {
        protected function getListeners(): array {
            return [
                new CustomAttributesListener(),
            ];
        }
    }
    
    class CustomAttributesListener extends AttributesListener {
        public function onRenderAttribute($attribute, $element) {
            if ($attribute->getName() === 'data-role' && $attribute->getValue() !== 'safe') {
                throw new \RuntimeException('Unsafe data-role attribute');
            }
        }
    }
    
  2. Dynamic Attribute Transformation Modify attributes during rendering:

    $renderer = new class($environment) extends HtmlRenderer {
        public function renderAttribute($attribute, $element) {
            $value = $attribute->getValue();
            if ($attribute->getName() === 'data-id') {
                $value = 'dynamic-' . Str::uuid();
            }
            return $attribute->setValue($value)->render();
        }
    };
    
  3. Laravel Service Provider Hooks Bind the converter to Laravel’s container with dynamic config:

    $this->app->bind(MarkdownConverter::class, function ($app) {
        $config = new \League\CommonMark\Config\Config();
        $config->addExtension(new AttributesExtension());
        if ($app->environment('production')) {
            $config->setOption('html_input', 'allow');
        }
        return new MarkdownConverter($config);
    });
    

Performance Quirks

  • Attribute Parsing Overhead Attributes add minimal overhead (~5–10% parsing time). Benchmark with:

    $start = microtime(true);
    $converter->convert($largeMarkdown);
    \Log::info("Parsing time: " . (microtime(true) - $start) . "s");
    
  • Caching Converter Instances Reuse the converter instance to avoid re-parsing the environment:

    $converter = app(MarkdownConverter::class);
    $html = $converter->convert($markdown); // Reuses cached config
    
  • Avoid Redundant Extensions If using league/commonmark-html, ensure AttributesExtension is registered after it to avoid duplicate processing.

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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor