Skip to content

Add output markdown option#765

Open
nicodevs wants to merge 6 commits intomainfrom
nd/markdown-output
Open

Add output markdown option#765
nicodevs wants to merge 6 commits intomainfrom
nd/markdown-output

Conversation

@nicodevs
Copy link
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
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

@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

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

tonysm commented Mar 17, 2026

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

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.

3 participants