Skip to content

Add output markdown option#765

Merged
damiani merged 6 commits intomainfrom
nd/markdown-output
Apr 3, 2026
Merged

Add output markdown option#765
damiani merged 6 commits intomainfrom
nd/markdown-output

Conversation

@nicodevs
Copy link
Copy Markdown
Contributor

@nicodevs nicodevs commented Feb 13, 2026

In this AI era, many websites offer a Markdown version of their pages using the same URL with a .md extension (like this: https://laravel.com/docs/12.x/ai-sdk.md) to make them easier for LLMs to consume.

This PR adds a collection option to Jigsaw to automatically generate these .md files.

Usage

In config.php:

  'collections' => [
      'posts' => [
          'path' => 'blog/{filename}',
+          'output_markdown' => true,
      ],
  ],

This produces both blog/my-post/index.html (or blog/my-post.html) and blog/my-post.md for each collection item.

output_markdown=true is in fact a shortcut of the longer config options, which are detailed below:

'output_markdown' => ['title_as_h1' => true, 'table_of_contents' => true]

Title as H1

By default, the title of the resource is used as the level-one heading in the generated Markdown. For this resource:

---
title: How to Install Jigsaw
---

Hello! Today...

…the output will be:

# How to Install Jigsaw

Hello! Today...

If the user does not want this behavior, they can opt out by setting:

'output_markdown' => ['title_as_h1' => false],

Table of Contents

Additionally, by default, the body headings are parsed to build a table of contents at the top of the generated Markdown. For example, given the following document:

---
title: Test
---

## Introduction

This is the intro.

## Installation

You can install the Laravel AI SDK via Composer.

The output will be:

# Test

- [Introduction](#introduction)
- [Installation](#installation)

<a name="introduction"></a>
## Introduction

This is the intro.

<a name="installation"></a>
## Installation

You can install the Laravel AI SDK via Composer.

If the user does not want this behavior, they can opt out by setting:

'output_markdown' => ['table_of_contents' => false],

Implementation Details

When output_markdown is enabled, CollectionItemHandler now returns two OutputFile instances to the build output: the original file and a raw Markdown version.

To add the title and the table of contents to the markdown we have a new class, PlainMarkdownBuilder.

No new dependencies are introduced.

Note

Note for reviewers: many of the listed “Files changed” reflect linting-only modifications.

@nicodevs nicodevs self-assigned this Feb 13, 2026
@nicodevs nicodevs marked this pull request as ready for review February 13, 2026 18:32
@nicodevs nicodevs requested a review from gcavanunez February 13, 2026 18:33
Copy link
Copy Markdown
Contributor

@gcavanunez gcavanunez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like it! Only spot I've been struggling on is whether it should be

'collections' => [
      'posts' => [
          'path' => 'blog/{filename}',
          'extra_output' => ['markdown'],
      ],
  ],

that way if we include automatic TOC enable/disabling can be something like

'collections' => [
      'posts' => [
          'path' => 'blog/{filename}',
          'extra_output' => ['markdown' => [ 'toc' => false ] ],
      ],
  ],

Lots of nesting, so might be best to keep output_markdown and then output_markdown => [ 'toc' => false] but been wondering commit-ing to a flat config could be more painful down the road

and if we decided to also enable it per collection item be something, on a collection's front matter we could - though maybe this something for future exploration

---
...
extra_output:
  - markdown
---

Copy link
Copy Markdown

@tonysm tonysm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Looks good, only left one comment around a deprecated method


$yaml_header = implode("\n", ['---', 'extends: _layouts.collection_item', 'section: content', '---']);

$files = $this->setupSource([
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think setupSource is deprecated, right? We should use createSource instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I followed the rest of the codebase. Perhaps we need to change all occurrences of setupSource in another PR, agree?

@nicodevs
Copy link
Copy Markdown
Contributor Author

Thanks @gcavanunez and @tonysm!

I've added the title as H1 and table of contents features. I settled on the config shape detailed in the updated PR description. Let me know what you think.

nicodevs and others added 6 commits March 13, 2026 16:10
......................................................................✓.....
..........................................

──────────────────────────────────────────────────────────────────── Laravel
FIXED ................................... 118 files, 1 style issue fixed
✓ src/Handlers/CollectionItemHandler.php unary_operator_spaces, no_unused_i…
)
.................✓........................✓..........✓.✓.✓..................
.........................✓...✓.............

──────────────────────────────────────────────────────────────────── Laravel
FIXED .................................. 119 files, 7 style issues fixed
✓ src/Bootstrap/HandleExceptions.php fully_qualified_strict_types, unary_op…
✓ src/Container.php fully_qualified_strict_types, ordered_imports
✓ src/Events/EventBus.php fully_qualified_strict_types, ordered_imports
✓ src/Exceptions/Handler.php fully_qualified_strict_types, unary_operator_s…
✓ src/Support/helpers.php fully_qualified_strict_types, unary_operator_spac…
✓ stubs/site/bootstrap.php fully_qualified_strict_types, ordered_imports
✓ tests/TestCase.php class_definition, fully_qualified_strict_types, braces…
)
................................................................✓......✓....
............................................

──────────────────────────────────────────────────────────────────── Laravel
FIXED .................................. 120 files, 2 style issues fixed
✓ src/Handlers/CollectionItemHandler.php unary_operator_spaces, not_operato…
✓ src/Providers/CollectionServiceProvider.php ordered_imports
)
@nicodevs nicodevs force-pushed the nd/markdown-output branch from 8dd037a to 866d489 Compare March 13, 2026 19:10
@tonysm
Copy link
Copy Markdown

tonysm commented Mar 17, 2026

@nicodevs looks good to me. Nice work on this! 👍🏼

@damiani
Copy link
Copy Markdown
Contributor

damiani commented Apr 3, 2026

Love this, thanks @nicodevs!!

@damiani damiani merged commit 192f493 into main Apr 3, 2026
5 checks passed
@damiani damiani deleted the nd/markdown-output branch April 3, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants