Skip to content
Closed
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"]
}