Skip to content

[Repo Assist] Add dotnet fsdocs convert command for single-file conversion#1019

Open
github-actions[bot] wants to merge 17 commits intomainfrom
repo-assist/feature-811-fsdocs-convert-2cf2c2973fcbd211
Open

[Repo Assist] Add dotnet fsdocs convert command for single-file conversion#1019
github-actions[bot] wants to merge 17 commits intomainfrom
repo-assist/feature-811-fsdocs-convert-2cf2c2973fcbd211

Conversation

@github-actions
Copy link
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Closes #811

Summary

This adds a new convert verb to the fsdocs CLI tool, enabling conversion of a single .md, .fsx, or .ipynb file to HTML (or another output format) without needing to build a full documentation site.

Usage examples:

# Convert a markdown file to HTML (output: doc.html in current directory)
dotnet fsdocs convert --input doc.md

# Convert an F# script to HTML with a specific output path
dotnet fsdocs convert --input script.fsx --output script.html

# Convert a notebook to HTML using a custom template
dotnet fsdocs convert --input notebook.ipynb --template _template.html

# Convert to another format
dotnet fsdocs convert --input doc.md --outputformat latex

# Evaluate F# code while converting
dotnet fsdocs convert --input demo.fsx --eval

# Add line numbers
dotnet fsdocs convert --input script.fsx --linenumbers

# Pass extra substitution parameters
dotnet fsdocs convert --input doc.md --parameters fsdocs-title "My Doc"

Changes

  • src/FSharp.Formatting.Literate/Literate.fs: Added Literate.ConvertPynbFile public API method (parallel to the existing ConvertMarkdownFile and ConvertScriptFile)
  • src/fsdocs-tool/BuildCommand.fs: Added ConvertCommand class with [(Verb("convert"))]
  • src/fsdocs-tool/Program.fs: Registered ConvertCommand in the entry point
  • RELEASE_NOTES.md: Added changelog entry

Options

Option Default Description
--input (required) Input file (.md, .fsx, .ipynb)
--output (basename).(ext) Output file path
--template (none) Template file (raw content written if omitted)
--outputformat html Output format: html, ipynb, latex, fsx, markdown
--eval false Evaluate F# fragments
--linenumbers false Add line numbers
--parameters Extra substitution key-value pairs

When no template is provided, raw HTML content is written (just the body, no surrounding page chrome) — the same behaviour as calling Literate.ConvertMarkdownFile with no template.

Root Cause

The Literate library already exposed ConvertMarkdownFile and ConvertScriptFile for programmatic use, and the DocContent class inside the build command already performs per-file conversion. This PR wires those existing capabilities up to a new CLI verb and adds the missing ConvertPynbFile method.

Test Status

  • ✅ Build succeeded (0 errors, warnings only for pre-existing System.Drawing.Common vulnerability)
  • ✅ All 381 tests pass (192 Markdown, 101 Literate, 68 ApiDocs, 20 CodeFormat)
  • ✅ Code formatted with Fantomas

Generated by Repo Assist for issue #811

To install this workflow, run gh aw add githubnext/agentics/workflows/repo-assist.md@b87234850bf9664d198f28a02df0f937d0447295. View source at https://github.com/githubnext/agentics/tree/b87234850bf9664d198f28a02df0f937d0447295/workflows/repo-assist.md.

Implement a new 'convert' verb for the fsdocs CLI tool that converts a
single .md, .fsx, or .ipynb file to HTML (or other formats) without
building a full documentation site.

- Add Literate.ConvertPynbFile public API (parallel to ConvertMarkdownFile
  and ConvertScriptFile)
- Add ConvertCommand CLI verb with --input, --output, --template,
  --outputformat, --eval, --linenumbers, --parameters options
- Register ConvertCommand in Program.fs entry point
- Update RELEASE_NOTES.md

Usage:
  dotnet fsdocs convert --input notebook.ipynb
  dotnet fsdocs convert --input file.fsx --output file.html
  dotnet fsdocs convert --input doc.md --outputformat markdown

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
abstract port_option: int
default x.port_option = 0

[<Verb("convert",
Copy link
Collaborator

Choose a reason for hiding this comment

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

/repo-assist it might be time to split these commands up to multiple files.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup these "/repo-assist" in PR comments still not working

Copy link
Contributor

Choose a reason for hiding this comment

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

Handling this in s a separate issue #1022

@dsyme
Copy link
Contributor

dsyme commented Feb 25, 2026

/repo-assist Add comprehensive unit tests for all new code and integration tests for all new features

- Unit tests for Literate.ConvertPynbFile: HTML without/with template, Markdown output, FSX output
- Integration tests for ConvertCommand.Execute: .md, .fsx, .ipynb conversion, markdown output format, error codes for missing file and unsupported extension

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

Commit pushed: b5e1d1a

Generated by Repo Assist

@github-actions

This comment has been minimized.

@dsyme dsyme marked this pull request as ready for review February 25, 2026 22:15
@dsyme
Copy link
Contributor

dsyme commented Feb 25, 2026

/repo-assist I tried this out and it worked OK. However note how pandoc has this:

pandoc notebook.ipynb -s -o notebook.html

where fsdocs convert has the longer

fsdocs convert --input build.fsx --output build.md --outputformat markdown

I'd like to see more like

fsdocs convert build.fsx -o build.md 

where

  • input is positional
  • output format is implicit from output extension if not overridden and detectable
  • -o and other shorthands for arguments

Also I'd like to see the "Integration tests for ConvertCommand" moved to a set of tests for fsdocs-tool so maybe fsdocs-tool.Tests new assembly if that makes sense

- Make input file positional (Value(0)) instead of --input flag
- Add -o shorthand for --output
- Infer output format from output file extension when --outputformat not specified
- Move ConvertCommand integration tests to new fsdocs-tool.Tests project
- Add new test for format inference from extension
- Add fsdocs-tool.Tests to solution

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment was marked as resolved.

@github-actions

This comment has been minimized.

@nojaf
Copy link
Collaborator

nojaf commented Feb 26, 2026

Hi @nhirschey, would you mind giving this a spin?

@dsyme

This comment was marked as resolved.

dsyme and others added 2 commits February 26, 2026 19:13
…d releases

A blank line within a subsection (e.g. ### Added) causes Ionide.KeepAChangelog
0.1.8 to stop parsing the file, resulting in an empty Releases list. The
previous List.head call gave a cryptic 'The input list was empty' error.
This replaces it with a pattern match and an actionable error message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment was marked as resolved.

@github-actions

This comment has been minimized.

@nojaf nojaf requested a review from nhirschey March 2, 2026 07:21
@nhirschey
Copy link
Collaborator

sorry @nojaf I did not notice this until just now.

/repo-assist when no html template is given you should not include the tooltip type info at the bottom of the html output because it does not get rendered usefully. It just ends up as junk at the bottom of the file.

For example with this input .fsx file and no template given

type RecordExample = 
    { BeginningPrice : float 
      EndingPrice : float 
      Dividend : float }      


let x = { BeginningPrice = 100.0; EndingPrice = 110.0; Dividend = 0.0}

Remove all divs with class "fsdocs-tip" from the resulting output. Instead of this below that you are outputting now

<html>
<body>
<!--StartFragment-->
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs1', 1)" onmouseover="showTip(event, 'fs1', 1)" class="rt">RecordExample</span> <span class="o">=</span>
--
<span class="pn">{</span> <span class="prop">BeginningPrice</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 2)" onmouseover="showTip(event, 'fs2', 2)" class="vt">float</span>
<span class="prop">EndingPrice</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 3)" onmouseover="showTip(event, 'fs2', 3)" class="vt">float</span>
<span class="prop">Dividend</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 4)" onmouseover="showTip(event, 'fs2', 4)" class="vt">float</span> <span class="pn">}</span>
 
 
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs3', 5)" onmouseover="showTip(event, 'fs3', 5)" class="id">x</span> <span class="o">=</span> <span class="pn">{</span> <span class="prop">BeginningPrice</span> <span class="o">=</span> <span class="n">100.0</span><span class="pn">;</span> <span class="prop">EndingPrice</span> <span class="o">=</span> <span class="n">110.0</span><span class="pn">;</span> <span class="prop">Dividend</span> <span class="o">=</span> <span class="n">0.0</span><span class="pn">}</span>
</code></pre>
 
 
<div class="fsdocs-tip" id="fs1">type RecordExample =
{
BeginningPrice: float
EndingPrice: float
Dividend: float
}</div>
<div class="fsdocs-tip" id="fs2">Multiple items<br />val float: value: &#39;T -&gt; float (requires member op_Explicit)<br /><br />--------------------<br />type float = System.Double<br /><br />--------------------<br />type float&lt;&#39;Measure&gt; =
float</div>
<div class="fsdocs-tip" id="fs3">val x: RecordExample</div>

<!--EndFragment-->
</body>
</html>

Give this, the same with the tooltip divs removed

<html>
<body>
<!--StartFragment-->
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs1', 1)" onmouseover="showTip(event, 'fs1', 1)" class="rt">RecordExample</span> <span class="o">=</span>
--
<span class="pn">{</span> <span class="prop">BeginningPrice</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 2)" onmouseover="showTip(event, 'fs2', 2)" class="vt">float</span>
<span class="prop">EndingPrice</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 3)" onmouseover="showTip(event, 'fs2', 3)" class="vt">float</span>
<span class="prop">Dividend</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs2', 4)" onmouseover="showTip(event, 'fs2', 4)" class="vt">float</span> <span class="pn">}</span>
 
 
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs3', 5)" onmouseover="showTip(event, 'fs3', 5)" class="id">x</span> <span class="o">=</span> <span class="pn">{</span> <span class="prop">BeginningPrice</span> <span class="o">=</span> <span class="n">100.0</span><span class="pn">;</span> <span class="prop">EndingPrice</span> <span class="o">=</span> <span class="n">110.0</span><span class="pn">;</span> <span class="prop">Dividend</span> <span class="o">=</span> <span class="n">0.0</span><span class="pn">}</span>
</code></pre>

<!--EndFragment-->
</body>
</html>

@nojaf
Copy link
Collaborator

nojaf commented Mar 3, 2026

/repo-assist do as instructed on #1019 (comment)

When no template is provided (e.g. fsdocs convert without --template),
tooltip divs with class fsdocs-tip are now omitted from the output.
Tooltips require JavaScript/CSS injected by a template to function,
so including them in raw output was not useful and could clutter the result.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 3, 2026

Commit pushed: 991095f

Generated by Repo Assist

@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 3, 2026

🤖 This is an automated response from Repo Assist.

Done! I've pushed a commit that removes fsdocs-tip divs from the output when no template is provided.

Change: In Templating.fs, the ApplySubstitutions function no longer appends fsdocs-tooltips to the content when no template is given. Tooltips require JavaScript/CSS from a template to function, so including them in raw (no-template) output was cluttering the result without being useful.

Test added: ConvertCommand omits fsdocs-tip divs when no template given — verifies that .fsx → HTML conversion without --template produces output free of fsdocs-tip elements.

All 8 tests pass.

Generated by Repo Assist for issue #1019

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@da02dddebe71d9a937665abdcd1f5214dab852a7

Copy link
Collaborator

@nhirschey nhirschey left a comment

Choose a reason for hiding this comment

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

Looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use fsdocs command line tool to convert single files

3 participants