UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev (v3) projects

239 lines (237 loc) 9.09 kB
import * as esbuild from "esbuild"; import { createHash } from "node:crypto"; import { join, relative, resolve } from "node:path"; import { createFile } from "../utilities/fileSystem.js"; import { logger } from "../utilities/logger.js"; import { deployEntryPoints, devEntryPoints, isIndexControllerForTarget, isIndexWorkerForTarget, isLoaderEntryPoint, isRunControllerForTarget, isRunWorkerForTarget, shims, telemetryEntryPoint, } from "./packageModules.js"; import { buildPlugins } from "./plugins.js"; import { createEntryPointManager } from "./entryPoints.js"; import { cliLink, prettyError } from "../utilities/cliOutput.js"; import { SkipLoggingError } from "../cli/common.js"; export async function bundleWorker(options) { const { resolvedConfig } = options; let currentContext; const entryPointManager = await createEntryPointManager(resolvedConfig.dirs, resolvedConfig, options.target, typeof options.watch === "boolean" ? options.watch : false, async (newEntryPoints) => { if (currentContext) { // Rebuild with new entry points await currentContext.cancel(); await currentContext.dispose(); const buildOptions = await createBuildOptions({ ...options, entryPoints: newEntryPoints, }); logger.debug("Rebuilding worker with options", buildOptions); currentContext = await esbuild.context(buildOptions); await currentContext.watch(); } }); if (entryPointManager.entryPoints.length === 0) { const errorMessageBody = ` Dirs config: ${resolvedConfig.dirs.join("\n- ")} Search patterns: ${entryPointManager.patterns.join("\n- ")} Possible solutions: 1. Check if the directory paths in your config are correct 2. Verify that your files match the search patterns 3. Update the search patterns in your config `.replace(/^ {6}/gm, ""); prettyError("No trigger files found", errorMessageBody, cliLink("View the config docs", "https://trigger.dev/docs/config/config-file")); throw new SkipLoggingError(); } let initialBuildResult; const initialBuildResultPromise = new Promise((resolve) => (initialBuildResult = resolve)); const buildResultPlugin = { name: "Initial build result plugin", setup(build) { build.onEnd(initialBuildResult); }, }; const buildOptions = await createBuildOptions({ ...options, entryPoints: entryPointManager.entryPoints, buildResultPlugin, }); let result; let stop; logger.debug("Building worker with options", buildOptions); if (options.watch) { currentContext = await esbuild.context(buildOptions); await currentContext.watch(); result = await initialBuildResultPromise; if (result.errors.length > 0) { throw new Error("Failed to build"); } stop = async function () { await entryPointManager.stop(); await currentContext?.dispose(); }; } else { result = await esbuild.build(buildOptions); stop = async function () { await entryPointManager.stop(); }; } const bundleResult = await getBundleResultFromBuild(options.target, options.cwd, options.resolvedConfig, result); if (!bundleResult) { throw new Error("Failed to get bundle result"); } return { ...bundleResult, stop }; } // Helper function to create build options async function createBuildOptions(options) { const customConditions = options.resolvedConfig.build?.conditions ?? []; const conditions = [...customConditions, "trigger.dev", "module", "node"]; const $buildPlugins = await buildPlugins(options.target, options.resolvedConfig); return { entryPoints: options.entryPoints, outdir: options.destination, absWorkingDir: options.cwd, bundle: true, metafile: true, write: false, minify: false, splitting: true, charset: "utf8", platform: "node", sourcemap: true, sourcesContent: options.target === "dev", conditions, format: "esm", target: ["node20", "es2022"], loader: { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx", ".wasm": "copy", }, outExtension: { ".js": ".mjs" }, inject: [...shims], // TODO: copy this into the working dir to work with Yarn PnP jsx: options.jsxAutomatic ? "automatic" : undefined, jsxDev: options.jsxAutomatic && options.target === "dev" ? true : undefined, plugins: [ ...$buildPlugins, ...(options.plugins ?? []), ...(options.buildResultPlugin ? [options.buildResultPlugin] : []), ], ...(options.jsxFactory && { jsxFactory: options.jsxFactory }), ...(options.jsxFragment && { jsxFragment: options.jsxFragment }), logLevel: "silent", logOverride: { "empty-glob": "silent", }, }; } export async function getBundleResultFromBuild(target, workingDir, resolvedConfig, result) { const hasher = createHash("md5"); for (const outputFile of result.outputFiles) { hasher.update(outputFile.hash); await createFile(outputFile.path, outputFile.contents); } const files = []; let configPath; let loaderEntryPoint; let runWorkerEntryPoint; let runControllerEntryPoint; let indexWorkerEntryPoint; let indexControllerEntryPoint; const configEntryPoint = resolvedConfig.configFile ? relative(resolvedConfig.workingDir, resolvedConfig.configFile) : "trigger.config.ts"; for (const [outputPath, outputMeta] of Object.entries(result.metafile.outputs)) { if (outputPath.endsWith(".mjs")) { const $outputPath = resolve(workingDir, outputPath); if (!outputMeta.entryPoint) { continue; } if (outputMeta.entryPoint.startsWith(configEntryPoint)) { configPath = $outputPath; } else if (isLoaderEntryPoint(outputMeta.entryPoint)) { loaderEntryPoint = $outputPath; } else if (isRunControllerForTarget(outputMeta.entryPoint, target)) { runControllerEntryPoint = $outputPath; } else if (isRunWorkerForTarget(outputMeta.entryPoint, target)) { runWorkerEntryPoint = $outputPath; } else if (isIndexControllerForTarget(outputMeta.entryPoint, target)) { indexControllerEntryPoint = $outputPath; } else if (isIndexWorkerForTarget(outputMeta.entryPoint, target)) { indexWorkerEntryPoint = $outputPath; } else { if (!outputMeta.entryPoint.startsWith("..") && !outputMeta.entryPoint.includes("node_modules")) { files.push({ entry: outputMeta.entryPoint, out: $outputPath, }); } } } } if (!configPath) { return undefined; } return { files, configPath: configPath, loaderEntryPoint, runWorkerEntryPoint, runControllerEntryPoint, indexWorkerEntryPoint, indexControllerEntryPoint, contentHash: hasher.digest("hex"), }; } async function getEntryPoints(target, config) { const projectEntryPoints = config.dirs.flatMap((dir) => dirToEntryPointGlob(dir)); if (config.configFile) { projectEntryPoints.push(config.configFile); } if (target === "dev") { projectEntryPoints.push(...devEntryPoints); } else { projectEntryPoints.push(...deployEntryPoints); } if (config.instrumentedPackageNames?.length ?? 0 > 0) { projectEntryPoints.push(telemetryEntryPoint); } return projectEntryPoints; } // Converts a directory to a glob that matches all the entry points in that function dirToEntryPointGlob(dir) { return [ join(dir, "**", "*.ts"), join(dir, "**", "*.tsx"), join(dir, "**", "*.mts"), join(dir, "**", "*.cts"), join(dir, "**", "*.js"), join(dir, "**", "*.jsx"), join(dir, "**", "*.mjs"), join(dir, "**", "*.cjs"), ]; } export function logBuildWarnings(warnings) { const logs = esbuild.formatMessagesSync(warnings, { kind: "warning", color: true }); for (const log of logs) { console.warn(log); } } /** * Logs all errors/warnings associated with an esbuild BuildFailure in the same * style esbuild would. */ export function logBuildFailure(errors, warnings) { const logs = esbuild.formatMessagesSync(errors, { kind: "error", color: true }); for (const log of logs) { console.error(log); } logBuildWarnings(warnings); } //# sourceMappingURL=bundle.js.map