Skip to content

Commit ec19a10

Browse files
committed
add output-suffix option to customize output filename suffix
1 parent c7b25ea commit ec19a10

File tree

9 files changed

+64
-9
lines changed

9 files changed

+64
-9
lines changed

src/command/render/output-tex.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
kKeepTex,
1616
kOutputExt,
1717
kOutputFile,
18+
kOutputSuffix,
1819
kTargetFormat,
1920
} from "../../config/constants.ts";
2021
import { Format } from "../../config/types.ts";
@@ -61,7 +62,8 @@ export function texToPdfOutputRecipe(
6162
}`;
6263
}
6364

64-
const texStem = texSafeFilename(`${inputStem}${fixupInputName}`);
65+
const suffix = format.render[kOutputSuffix] || "";
66+
const texStem = texSafeFilename(`${inputStem}${suffix}${fixupInputName}`);
6567

6668
// calculate output and args for pandoc (this is an intermediate file
6769
// which we will then compile to a pdf and rename to .tex)

src/command/render/output-typst.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
kKeepTyp,
1313
kOutputExt,
1414
kOutputFile,
15+
kOutputSuffix,
1516
kVariant,
1617
} from "../../config/constants.ts";
1718
import { Format } from "../../config/types.ts";
@@ -45,7 +46,8 @@ export function typstPdfOutputRecipe(
4546
// calculate output and args for pandoc (this is an intermediate file
4647
// which we will then compile to a pdf and rename to .typ)
4748
const [inputDir, inputStem] = dirAndStem(input);
48-
const output = inputStem + ".typ";
49+
const suffix = format.render[kOutputSuffix] || "";
50+
const output = inputStem + suffix + ".typ";
4951
let args = options.pandocArgs || [];
5052
const pandoc = { ...format.pandoc };
5153
if (options.flags?.output) {
@@ -62,7 +64,7 @@ export function typstPdfOutputRecipe(
6264

6365
// run typst
6466
await validateRequiredTypstVersion();
65-
const pdfOutput = join(inputDir, inputStem + ".pdf");
67+
const pdfOutput = join(inputDir, inputStem + suffix + ".pdf");
6668
const typstOptions: TypstCompileOptions = {
6769
quiet: options.flags?.quiet,
6870
fontPaths: asArray(format.metadata?.[kFontPaths]) as string[],
@@ -111,7 +113,7 @@ export function typstPdfOutputRecipe(
111113
? finalOutput === kStdOut
112114
? undefined
113115
: normalizeOutputPath(input, finalOutput)
114-
: normalizeOutputPath(input, join(inputDir, inputStem + ".pdf"));
116+
: normalizeOutputPath(input, join(inputDir, inputStem + suffix + ".pdf"));
115117

116118
// return recipe
117119
const recipe: OutputRecipe = {

src/command/render/output.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import {
2626
kOutputExt,
2727
kOutputFile,
28+
kOutputSuffix,
2829
kPreserveYaml,
2930
kVariant,
3031
} from "../../config/constants.ts";
@@ -184,18 +185,21 @@ export function outputRecipe(
184185
const deriveAutoOutput = () => {
185186
// no output specified: derive an output path from the extension
186187

188+
// Get the suffix if specified
189+
const suffix = format.render[kOutputSuffix] || "";
190+
187191
// derive new output file
188-
let output = inputStem + "." + ext;
192+
let output = `${inputStem}${suffix}.${ext}`;
189193
// special case for .md to .md, need to append the writer to create a
190194
// non-conflicting filename
191195
if (extname(input) === ".md" && ext === "md") {
192-
output = `${inputStem}-${format.identifier["base-format"]}.md`;
196+
output = `${inputStem}${suffix}-${format.identifier["base-format"]}.md`;
193197
}
194198

195199
// special case if the source will overwrite the destination (note: this
196200
// behavior can be customized with a custom output-ext)
197201
if (output === basename(context.target.source)) {
198-
output = inputStem + `.${kOutExt}.` + ext;
202+
output = `${inputStem}${suffix}.${kOutExt}.${ext}`;
199203
}
200204

201205
// assign output

src/command/render/render-contexts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import {
4949
kMetadataFormat,
5050
kOutputExt,
5151
kOutputFile,
52+
kOutputSuffix,
5253
kServer,
5354
kTargetFormat,
5455
kWarning,
@@ -330,7 +331,8 @@ export async function renderFormats(
330331
// resolve output-file
331332
if (!format.pandoc[kOutputFile]) {
332333
const [_dir, stem] = dirAndStem(file);
333-
format.pandoc[kOutputFile] = `${stem}.${format.render[kOutputExt]}`;
334+
const suffix = format.render[kOutputSuffix] || "";
335+
format.pandoc[kOutputFile] = `${stem}${suffix}.${format.render[kOutputExt]}`;
334336
}
335337
// provide engine
336338
format.execute[kEngine] = context.engine.name;

src/config/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export const kKeepIpynb = "keep-ipynb";
9090
export const kKeepSource = "keep-source";
9191
export const kVariant = "variant";
9292
export const kOutputExt = "output-ext";
93+
export const kOutputSuffix = "output-suffix";
9394
export const kOutputDivs = "output-divs";
9495
export const kPageWidth = "page-width";
9596
export const kFigAlign = "fig-align";
@@ -191,6 +192,7 @@ export const kRenderDefaultsKeys = [
191192
kClearHiddenClasses,
192193
kVariant,
193194
kOutputExt,
195+
kOutputSuffix,
194196
kOutputDivs,
195197
kPreferHtml,
196198
kPageWidth,

src/config/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ import {
172172
kOutputDivs,
173173
kOutputExt,
174174
kOutputFile,
175+
kOutputSuffix,
175176
kPageWidth,
176177
kPdfEngine,
177178
kPdfEngineOpt,
@@ -469,6 +470,7 @@ export interface FormatRender {
469470
[kOutputDivs]?: boolean;
470471
[kVariant]?: string;
471472
[kOutputExt]?: string;
473+
[kOutputSuffix]?: string;
472474
[kPageWidth]?: number;
473475
[kFigAlign]?: "left" | "right" | "center" | "default";
474476
[kFigPos]?: string | null;

src/core/render.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Copyright (C) 2020-2022 Posit Software, PBC
55
*/
66

7-
import { kOutputExt, kOutputFile, kServer } from "../config/constants.ts";
7+
import { kOutputExt, kOutputFile, kOutputSuffix, kServer } from "../config/constants.ts";
88
import { Format, Metadata } from "../config/types.ts";
99
import { kJupyterEngine, kKnitrEngine } from "../execute/types.ts";
1010
import { dirAndStem } from "./path.ts";
@@ -47,6 +47,19 @@ export function isServerShinyKnitr(
4747
export function formatOutputFile(format: Format) {
4848
let outputFile = format.pandoc[kOutputFile];
4949
if (outputFile) {
50+
// Apply output-suffix if specified (insert before extension)
51+
const suffix = format.render[kOutputSuffix];
52+
if (suffix) {
53+
const ext = extname(outputFile);
54+
if (ext) {
55+
const stem = outputFile.slice(0, -ext.length);
56+
outputFile = `${stem}${suffix}${ext}`;
57+
} else {
58+
// No extension, just append suffix
59+
outputFile = `${outputFile}${suffix}`;
60+
}
61+
}
62+
5063
if (format.render[kOutputExt]) {
5164
// Don't append the output extension if the same output
5265
// extension is already present. If you update this logic,

src/resources/schema/document-render.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
description: |
1919
Extension to use for generated output file
2020
21+
- name: output-suffix
22+
schema: string
23+
description: |
24+
Suffix to append to the output filename stem (before the extension).
25+
For example, with `output-suffix: "-slides"`, `presentation.qmd`
26+
becomes `presentation-slides.html`.
27+
2128
- name: template
2229
disabled: [$office-all, ipynb]
2330
schema: path
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
title: "Output Suffix Test"
3+
format:
4+
html:
5+
output-suffix: "-readable"
6+
revealjs:
7+
output-suffix: "-slides"
8+
---
9+
10+
# Test Document
11+
12+
This document tests the `output-suffix` feature.
13+
14+
## Purpose
15+
16+
When rendered with `output-suffix: "-readable"`, this file should produce:
17+
- `13111-output-suffix-readable.html` (from HTML format)
18+
- `13111-output-suffix-slides.html` (from RevealJS format)
19+
20+
This addresses GitHub issue #13111 where using compound extensions like
21+
`.onepage.html` in `output-ext` caused double-extension bugs.

0 commit comments

Comments
 (0)