Search by

markup-carve / laravel-carve

dereuromark

Laravel integration for markup-carve/carve-php — Blade directives, services, validation, and caching

Package info

github.com/markup-carve/laravel-carve

pkg:composer/markup-carve/laravel-carve

Statistics

Installs: 60

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.6 2026-09-20 10:02 UTC

README

CI PHP PHPStan License

Carve markup language integration for Laravel: Blade directives, services, validation, and caching.

View the live demo →

Installation

composer require markup-carve/laravel-carve

The service provider and Carve facade alias are auto-discovered via Laravel's package discovery.

Optionally publish the config:

php artisan vendor:publish --tag=carve-config

Usage

Blade Directives

{{-- Safe by default - XSS protection enabled --}}
@carve($article->body)

{{-- For trusted content only - no XSS protection --}}
@carveRaw($trustedContent)

{{-- Plain text output (escaped) --}}
@carveText($article->body)

Facade

use MarkupCarve\LaravelCarve\Facades\Carve;

$html = Carve::toHtml($source);
$text = Carve::toText($source);
$md   = Carve::toMarkdown($source);
$ansi = Carve::toAnsi($source); // terminal output
$raw  = Carve::toHtmlRaw($trustedSource);

Dependency Injection

use MarkupCarve\LaravelCarve\Service\CarveConverterInterface;
use MarkupCarve\LaravelCarve\Service\CarveManager;

class ArticleController
{
    public function __construct(
        private CarveConverterInterface $carve,
        private CarveManager $manager,
    ) {}

    public function show(Article $article): View
    {
        return view('article.show', [
            'html' => $this->carve->toHtml($article->body),
            'text' => $this->carve->toText($article->body),
            'docs' => $this->manager->toHtml($article->body, 'docs'),
        ]);
    }
}

Configuration

// config/carve.php
return [
    'include_root' => null,
    'converters' => [
        // Default has safe_mode: true (XSS protection enabled)
        'default' => [
            'safe_mode' => true,
            'symbols' => [],
            'source_lines' => false,
        ],

        // For trusted content (admin, CMS)
        'trusted' => [
            'safe_mode' => false,
        ],
    ],
    'cache' => [
        'enabled' => false,
        'store' => null,
    ],
];

symbols maps :name: shortcodes to trusted raw HTML. Symbol values are inserted without escaping, so never populate this option from user input. Set source_lines to true to add 1-based data-source-line attributes to rendered blocks for editor preview scroll-sync. Both options are configured per converter profile.

File includes

Blade directives and string methods never read files. To enable includes for trusted file-backed content, configure an absolute containment root and call the explicit file API:

// config/carve.php
'include_root' => '/srv/app/content',

$report = Carve::toHtmlFileWithReport('/srv/app/content/book/main.crv');
$html = $report['value'];
$warnings = $report['warnings'];
$dependencies = $report['dependencies'];

Carve::toHtmlFile() returns only the HTML. Nested paths resolve relative to the file containing each directive. Traversal and symlink escapes are refused. Warnings sent to Laravel's logger omit resolver details and replace outside paths.

include_root has to be an absolute path. A relative one is refused rather than resolved against the working directory, which is arbitrary with respect to the document, so a misconfigured root raises at boot instead of widening silently.

When package caching is enabled, a cached file render is served only while every recorded dependency still hashes the same, so editing an included file cannot serve the parent page's stale HTML. Missing targets are recorded too: creating one is what makes its directive start working.

Multiple Converter Profiles

Use different configurations for different contexts:

{{-- Default is safe --}}
@carve($comment->body)

{{-- Use named converter for trusted content --}}
{!! Carve::toHtml($article->body, 'trusted') !!}

{{-- Or use @carveRaw for quick trusted rendering --}}
@carveRaw($article->body)

Safe Mode

Safe mode is enabled by default for XSS protection. Disable only for trusted content:

'converters' => [
    'trusted' => [
        'safe_mode' => false,
    ],
],

Extensions

Enable carve-php extensions per converter:

'converters' => [
    'default' => [
        'extensions' => [
            ['type' => 'autolink'],
            ['type' => 'smart_quotes'],
            [
                'type' => 'heading_permalinks',
                'symbol' => '#',
                'position' => 'after',
            ],
        ],
    ],
    'with_mentions' => [
        'extensions' => [
            [
                'type' => 'mentions',
                'user_url_template' => 'https://github.com/{username}',
            ],
            'table_of_contents',
        ],
    ],
],

Available extensions:

  • admonition - Admonition blocks (note, tip, warning, danger, etc.)
  • autolink - Auto-convert URLs to clickable links
  • citations - Bracketed citations with an in-document bibliography (numbered or author-date)
  • code_callouts - Numbered callout markers on fenced-code lines with a bound explanation list
  • code_group - Transform code-group divs into tabbed interfaces
  • color_swatch - Inline color swatches for CSS color tokens via the color role
  • default_attributes - Add default attributes to elements by type
  • details - Render ::: details blocks as native <details>/<summary> widgets
  • external_links - Configure external link behavior (target, rel)
  • fenced_render - Emit fenced blocks of a chosen language as client-rendered hydration elements
  • frontmatter - Parse YAML/TOML/JSON frontmatter blocks
  • glossary - Glossary definition lists with linked term references
  • heading_level_shift - Shift heading levels up/down
  • heading_numbers - Auto-number sections and rewrite heading cross-references
  • heading_permalinks - Add anchor links to headings
  • heading_reference - Link to headings with [text](#heading) syntax
  • index - Collect :index[term] markers into a sorted index block
  • inline_footnotes - Convert spans with class to inline footnotes
  • list_table - Author tables as nested lists (::: list-table) with block content in cells
  • math_block - Render math fenced code blocks as display math
  • mentions - Convert @username to profile links
  • mermaid - Render Mermaid diagram code blocks
  • plantuml - Render PlantUML/puml diagram code blocks (needs a client renderer; see below)
  • semantic_span - Convert spans to <kbd>, <dfn>, <abbr> elements
  • smart_quotes - Convert straight quotes to typographic quotes
  • spoiler - Hidden spoiler content revealed on interaction
  • tab_normalize - Expand tabs in code content to spaces at render time
  • table_of_contents - Generate TOC from headings
  • tabs - Tabbed content blocks (CSS or ARIA mode)
  • toc_placement - Render the TOC exactly where a ::: toc block appears
  • wikilinks - Support [[Page Name]] wiki-style links

See Extensions documentation for detailed configuration options.

Client-side diagram rendering

The diagram extensions emit a <pre class="LANG"> hydration element; the browser turns it into a picture. Mermaid, WaveDrom, Vega-Lite and Chart each render once you load their library. Graphviz and D2 render fully offline with the WebAssembly helpers from @markup-carve/carve-grammars (no server, no external call):

import { renderDiagrams } from '@markup-carve/carve-grammars/diagrams'

await renderDiagrams(document.querySelector('.carve-content')) // graphviz + d2, offline

PlantUML is the exception - it has no practical in-browser renderer and needs a Kroki server:

import { renderKrokiDiagrams } from '@markup-carve/carve-grammars/diagrams/kroki'

await renderKrokiDiagrams(document.querySelector('.carve-content'), {
    server: 'https://kroki.internal', // your own instance
})

⚠️ Privacy / GDPR: the default Kroki server is the public https://kroki.io, so the PlantUML source is sent to a third party outside your domain. For sensitive content, or to stay offline, point server at a self-hosted or localhost Kroki so no data leaves your control, and disclose the external call to end users where required. For a build-time / SSR pipeline, render diagrams server-side instead so no client JS ships.

Validation Rule

Validate that a field contains valid Carve markup:

use MarkupCarve\LaravelCarve\Rules\ValidCarve;

$request->validate([
    'body' => ['required', 'string', new ValidCarve()],
]);

Documentation

Full documentation: markup-carve.github.io/laravel-carve

Demo Application

A full runnable demo app lives at laravel-carve-demo: the Blade directives, facade and service usage, form validation, safe-mode comparison, static render mode, plain text extraction and the extension set.

laravel-carve demo

What is Carve?

Carve is a lightweight markup language for structured documents, with clear, consistent syntax. It supports rich document features such as footnotes, definition lists, task lists, and math.

Learn more about Carve syntax at github.com/markup-carve/carve.

Ecosystem

This package is part of the Carve organization - the spec with its conformance corpus, three byte-identical reference implementations (JS, PHP, Rust), editor plugins, and framework integrations. See awesome-carve for a curated list of everything Carve.