Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/feat-libreoffice-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@trigger.dev/build": minor
"references/libreoffice-convert": patch
---

feat(build): add `libreoffice()` build extension for docx/pptx to PDF conversion

Adds a `libreoffice(options?)` BuildExtension that installs LibreOffice system packages and `libreoffice-convert` npm package at deploy time, enabling direct Office-to-PDF conversion in Trigger.dev tasks.
2 changes: 2 additions & 0 deletions packages/build/src/extensions/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from "./core/aptGet.js";
export * from "./core/ffmpeg.js";
export * from "./core/neonSyncEnvVars.js";
export * from "./core/vercelSyncEnvVars.js";
export * from "./core/syncSupabaseEnvVars.js";
export * from "./core/libreoffice.js";
109 changes: 109 additions & 0 deletions packages/build/src/extensions/core/libreoffice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { BuildExtension } from "@trigger.dev/core/v3/build";
import { dirname } from "node:path";
import { readPackageJSON } from "pkg-types";

export type LibreOfficeOptions = {
/**
* Use the minimal LibreOffice writer package (libreoffice-writer) instead of full LibreOffice.
* Sufficient for docx/pptx/xlsx to PDF conversion. Saves ~400MB vs. full install.
* @default false
*/
minimal?: boolean;
};

/**
* Build extension that installs LibreOffice for docx/pptx/xlsx → PDF conversion.
*
* Adds the `libreoffice-convert` npm package and the required system packages
* to the Docker build image. The extension is scoped to "deploy" target only
* (dev uses the host machine's LibreOffice if available).
*
* @example
* ```ts
* // In trigger.config.ts
* import { libreoffice } from "@trigger.dev/build/extensions/core";
*
* build: {
* extensions: [
* libreoffice(),
* ]
* }
* ```
*
* Then in your task:
* ```ts
* import libreofficeConvert from "libreoffice-convert";
* // Convert docx → PDF
* const pdfBuffer = await libreofficeConvert(docxBuffer, "pdf");
* ```
*/
export function libreoffice(options: LibreOfficeOptions = {}): BuildExtension {
const { minimal = false } = options;

// The npm package used for conversion
const NPM_PACKAGE = "libreoffice-convert";

return {
name: "libreoffice",

async onBuildStart(context) {
if (context.target !== "deploy") {
return;
}

// ── System packages (apt) ─────────────────────────────────────────────
// `aptGet` is defined in the same core directory.
// We delegate to it by registering an apt-get layer.
const systemPackages = minimal
? [
"libreoffice-writer",
"libreoffice-calc",
"libreoffice-impress",
"fonts-liberation",
"--no-install-recommends",
]
: [
"libreoffice",
"--no-install-recommends",
];

context.addLayer({
id: "libreoffice-apt",
image: {
pkgs: systemPackages,
},
});

// ── npm package ────────────────────────────────────────────────────────
// Resolve the locally installed version of libreoffice-convert if present,
// otherwise fall back to "latest".
let version = "latest";
try {
const modulePath = await context.resolvePath(NPM_PACKAGE);
if (modulePath) {
const packageJSON = await readPackageJSON(dirname(modulePath));
version = packageJSON.version ?? "latest";
context.logger.debug(
`[libreoffice] Resolved ${NPM_PACKAGE} version: ${version}`
);
}
} catch (error) {
context.logger.debug(
`[libreoffice] Could not resolve ${NPM_PACKAGE} version, using "latest"`,
{ error }
);
}

context.addLayer({
id: "libreoffice-npm",
dependencies: {
[NPM_PACKAGE]: version,
},
});

context.logger.debug(
`[libreoffice] Added LibreOffice layer (minimal=${minimal})`
);
},
};
}
1 change: 1 addition & 0 deletions references/libreoffice-convert/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
18 changes: 18 additions & 0 deletions references/libreoffice-convert/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "references-libreoffice-convert",
"private": true,
"type": "module",
"devDependencies": {
"trigger.dev": "workspace:*"
},
"dependencies": {
"@trigger.dev/build": "workspace:*",
"@trigger.dev/sdk": "workspace:*",
"libreoffice-convert": "^1.4.3",
"zod": "3.25.76"
},
"scripts": {
"dev": "trigger dev",
"deploy": "trigger deploy"
}
}
41 changes: 41 additions & 0 deletions references/libreoffice-convert/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { createTask } from "@trigger.dev/sdk/v3";
import libreofficeConvert from "libreoffice-convert";
import { z } from "zod";

/**
* Task that converts Office documents (docx, pptx, xlsx) to PDF using LibreOffice.
* Requires the `libreoffice()` build extension in trigger.config.ts.
*
* @example
* ```ts
* await client.tasks.call("libreoffice-convert", {
* params: {
* input: docxBuffer, // Buffer containing the Office document
* outputFormat: "pdf", // Output format (default: "pdf")
* }
* });
* ```
*/
export const libreofficeConvertTask = createTask({
rpc: "libreoffice/convert",
queue: {
name: "libreoffice-convert",
parallelLimit: 2,
},
schema: z.object({
/** Buffer containing the Office document (docx, pptx, xlsx) */
input: z.string().describe("Base64-encoded document buffer"),
/** Output format. Only "pdf" is supported by libreoffice-convert. */
outputFormat: z.string().optional().default("pdf"),
}),
async run(params): Promise<{ output: string }> {
const inputBuffer = Buffer.from(params.input, "base64");
const format = (params.outputFormat || "pdf") as Parameters<typeof libreofficeConvert>[1];

const pdfBuffer = await libreofficeConvert(inputBuffer, format);

return {
output: pdfBuffer.toString("base64"),
};
},
});
15 changes: 15 additions & 0 deletions references/libreoffice-convert/trigger.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from "@trigger.dev/sdk/v3";
import { syncEnvVars } from "@trigger.dev/build/extensions/core";
import { libreoffice } from "@trigger.dev/build/extensions/core";

export default defineConfig({
compatibilityFlags: ["run_engine_v2"],
project: "proj_rrkpdguyagvsoktglnod",
logLevel: "debug",
build: {
extensions: [
syncEnvVars(),
libreoffice(),
],
},
});
15 changes: 15 additions & 0 deletions references/libreoffice-convert/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2023",
"module": "Node16",
"moduleResolution": "Node16",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"customConditions": ["@triggerdotdev/source"],
"jsx": "preserve",
"lib": ["DOM", "DOM.Iterable"],
"noEmit": true
},
"include": ["./src/**/*.ts", "trigger.config.ts"]
}