trigger.dev
Version:
A Command-Line Interface for Trigger.dev (v3) projects
216 lines • 9.6 kB
JavaScript
import { DEFAULT_RUNTIME } from "@trigger.dev/core/v3/build";
import * as c12 from "c12";
import { defu } from "defu";
import { readdir } from "node:fs/promises";
import { basename, dirname, isAbsolute, join, relative } from "node:path";
import { findWorkspaceDir, resolveLockfile, resolvePackageJSON, resolveTSConfig } from "pkg-types";
import { generateCode, loadFile } from "./imports/magicast.js";
import { logger } from "./utilities/logger.js";
import { additionalFiles, additionalPackages, syncEnvVars, } from "@trigger.dev/build/extensions/core";
import { prettyWarning } from "./utilities/cliOutput.js";
import { builtinModules } from "node:module";
export async function loadConfig({ cwd = process.cwd(), overrides, configFile, } = {}) {
const result = await c12.loadConfig({
name: "trigger",
cwd,
configFile,
jitiOptions: { debug: logger.loggerLevel === "debug" },
});
return await resolveConfig(cwd, result, overrides);
}
export async function watchConfig({ cwd = process.cwd(), onUpdate, debounce = 100, ignoreInitial = true, overrides, configFile, }) {
const result = await c12.watchConfig({
name: "trigger",
configFile,
cwd,
debounce,
chokidarOptions: { ignoreInitial },
jitiOptions: { debug: logger.loggerLevel === "debug" },
onUpdate: async ({ newConfig }) => {
const resolvedConfig = await resolveConfig(cwd, newConfig, overrides, false);
onUpdate(resolvedConfig);
},
});
const config = await resolveConfig(cwd, result, overrides);
return {
config,
files: result.watchingFiles,
stop: result.unwatch,
};
}
export function configPlugin(resolvedConfig) {
const configFile = resolvedConfig.configFile;
if (!configFile) {
return;
}
// We need to strip the "build" key from the config file, so build dependencies don't make it into the final bundle
return {
name: "trigger-config-strip",
setup(build) {
const filename = basename(configFile);
// Convert the filename to a regex to filter against
const filter = new RegExp(`${filename.replace(/\./g, "\\.")}$`);
logger.debug("trigger-config-strip.filter", filter);
build.onLoad({ filter }, async (args) => {
logger.debug("trigger-config-strip.onLoad", args);
const $mod = await loadFile(args.path);
// Support for both bare object export and `defineConfig` wrapper
const options = $mod.exports.default
? $mod.exports.default.$type === "function-call"
? $mod.exports.default.$args[0]
: $mod.exports.default
: $mod.exports.config?.$type === "function-call"
? $mod.exports.config.$args[0]
: $mod.exports.config;
options.build = {};
// Remove export resolveEnvVars function as well
$mod.exports.resolveEnvVars = undefined;
const contents = generateCode($mod);
logger.debug("trigger-config-strip.onLoad.contents", contents);
return {
contents: contents.code,
loader: "ts",
resolveDir: dirname(args.path),
};
});
},
};
}
async function resolveConfig(cwd, result, overrides, warn = true) {
const packageJsonPath = await resolvePackageJSON(cwd);
const tsconfigPath = await safeResolveTsConfig(cwd);
const lockfilePath = await resolveLockfile(cwd);
const workspaceDir = await findWorkspaceDir(cwd);
const workingDir = result.configFile
? dirname(result.configFile)
: packageJsonPath
? dirname(packageJsonPath)
: cwd;
const config = "config" in result.config ? result.config.config : result.config;
validateConfig(config, warn);
let dirs = config.dirs ? config.dirs : await autoDetectDirs(workingDir);
dirs = dirs.map((dir) => (isAbsolute(dir) ? relative(workingDir, dir) : dir));
const mergedConfig = defu({
workingDir: packageJsonPath ? dirname(packageJsonPath) : cwd,
configFile: result.configFile,
packageJsonPath,
tsconfigPath,
lockfilePath,
workspaceDir,
}, overrides, config, {
dirs,
runtime: DEFAULT_RUNTIME,
tsconfig: tsconfigPath,
build: {
jsx: {
factory: "React.createElement",
fragment: "React.Fragment",
automatic: true,
},
extensions: [],
external: [],
conditions: [],
},
}); // TODO: For some reason, without this, there is a weird type error complaining about tsconfigPath being string | nullish, which can't be assigned to string | undefined
return {
...mergedConfig,
dirs: Array.from(new Set(mergedConfig.dirs)),
instrumentedPackageNames: getInstrumentedPackageNames(mergedConfig),
};
}
async function safeResolveTsConfig(cwd) {
try {
return await resolveTSConfig(cwd);
}
catch {
return undefined;
}
}
const IGNORED_DIRS = ["node_modules", ".git", "dist", "out", "build"];
async function autoDetectDirs(workingDir) {
const entries = await readdir(workingDir, { withFileTypes: true });
const dirs = [];
for (const entry of entries) {
if (!entry.isDirectory() || IGNORED_DIRS.includes(entry.name) || entry.name.startsWith("."))
continue;
const fullPath = join(workingDir, entry.name);
// Ignore the directory if it's <any>/app/api/trigger
if (fullPath.endsWith("app/api/trigger")) {
continue;
}
if (entry.name === "trigger") {
dirs.push(fullPath);
}
dirs.push(...(await autoDetectDirs(fullPath)));
}
return dirs;
}
function validateConfig(config, warn = true) {
if (config.additionalFiles && config.additionalFiles.length > 0) {
warn &&
prettyWarning(`The "additionalFiles" option is deprecated and will be removed. Use the "additionalFiles" build extension instead. See https://trigger.dev/docs/config/config-file#additionalfiles for more information.`);
config.build ??= {};
config.build.extensions ??= [];
config.build.extensions.push(additionalFiles({ files: config.additionalFiles }));
}
if (config.additionalPackages && config.additionalPackages.length > 0) {
warn &&
prettyWarning(`The "additionalPackages" option is deprecated and will be removed. Use the "additionalPackages" build extension instead. See https://trigger.dev/docs/config/config-file#additionalpackages for more information.`);
config.build ??= {};
config.build.extensions ??= [];
config.build.extensions.push(additionalPackages({ packages: config.additionalPackages }));
}
if (config.triggerDirectories) {
warn &&
prettyWarning(`The "triggerDirectories" option is deprecated and will be removed. Use the "dirs" option instead.`);
config.dirs = config.triggerDirectories;
}
if (config.dependenciesToBundle) {
warn &&
prettyWarning(`The "dependenciesToBundle" option is deprecated and will be removed. Dependencies are now bundled by default. If you want to exclude some dependencies from the bundle, use the "build.external" option.`);
}
if (config.tsconfigPath) {
warn &&
prettyWarning(`The "tsconfigPath" option is deprecated and will be removed. Use the "tsconfig" option instead.`);
config.tsconfig = config.tsconfigPath;
}
if ("resolveEnvVars" in config && typeof config.resolveEnvVars === "function") {
warn &&
prettyWarning(`The "resolveEnvVars" option is deprecated and will be removed. Use the "syncEnvVars" build extension instead. See https://trigger.dev/docs/config/config-file#syncenvvars for more information.`);
const resolveEnvVarsFn = config.resolveEnvVars;
config.build ??= {};
config.build.extensions ??= [];
config.build.extensions.push(adaptResolveEnvVarsToSyncEnvVarsExtension(resolveEnvVarsFn));
}
if (config.runtime && config.runtime === "bun") {
warn &&
prettyWarning(`The "bun" runtime is currently experimental, and certain features may not work, especially opentelemetry instrumentation of 3rd party packages.`);
}
}
function adaptResolveEnvVarsToSyncEnvVarsExtension(resolveEnvVarsFn) {
return syncEnvVars(async (ctx) => {
const resolveEnvVarsResult = await resolveEnvVarsFn(ctx);
if (!resolveEnvVarsResult) {
return;
}
return resolveEnvVarsResult.variables;
}, { override: true });
}
function getInstrumentedPackageNames(config) {
const packageNames = [];
if (config.instrumentations) {
for (const instrumentation of config.instrumentations) {
const moduleDefinitions = instrumentation.getModuleDefinitions?.();
if (!Array.isArray(moduleDefinitions)) {
continue;
}
for (const moduleDefinition of moduleDefinitions) {
if (!builtinModules.includes(moduleDefinition.name)) {
packageNames.push(moduleDefinition.name);
}
}
}
}
return packageNames;
}
//# sourceMappingURL=config.js.map