Files
triggerdotdev--trigger.dev/packages/cli-v3/src/dev/devSession.ts
T
2024-08-22 14:31:32 +01:00

189 lines
5.3 KiB
TypeScript

import { DEFAULT_RUNTIME, ResolvedConfig } from "@trigger.dev/core/v3/build";
import { BuildManifest } from "@trigger.dev/core/v3/schemas";
import * as esbuild from "esbuild";
import { CliApiClient } from "../apiClient.js";
import {
BundleResult,
bundleWorker,
getBundleResultFromBuild,
logBuildFailure,
logBuildWarnings,
} from "../build/bundle.js";
import {
createBuildContext,
notifyExtensionOnBuildComplete,
notifyExtensionOnBuildStart,
resolvePluginsForContext,
} from "../build/extensions.js";
import { createExternalsBuildExtension } from "../build/externals.js";
import { devEntryPoint, telemetryEntryPoint } from "../build/packageModules.js";
import { type DevCommandOptions } from "../commands/dev.js";
import { logger } from "../utilities/logger.js";
import { EphemeralDirectory, getTmpDir } from "../utilities/tempDirectories.js";
import { copyManifestToDir } from "../build/manifests.js";
import { startWorkerRuntime } from "./workerRuntime.js";
import { chalkGrey } from "../utilities/cliOutput.js";
export type DevSessionOptions = {
name: string | undefined;
dashboardUrl: string;
initialMode: "local";
showInteractiveDevSession: boolean | undefined;
rawConfig: ResolvedConfig;
rawArgs: DevCommandOptions;
client: CliApiClient;
};
export async function startDevSession({
rawConfig,
name,
rawArgs,
client,
dashboardUrl,
}: DevSessionOptions) {
const destination = getTmpDir(rawConfig.workingDir, "build");
const runtime = await startWorkerRuntime({
name,
config: rawConfig,
args: rawArgs,
client,
dashboardUrl,
});
logger.debug("Starting dev session", { destination: destination.path, rawConfig });
const externalsExtension = createExternalsBuildExtension("dev", rawConfig);
const buildContext = createBuildContext("dev", rawConfig);
buildContext.prependExtension(externalsExtension);
await notifyExtensionOnBuildStart(buildContext);
const pluginsFromExtensions = resolvePluginsForContext(buildContext);
async function updateBundle(bundle: BundleResult, workerDir?: EphemeralDirectory) {
try {
let buildManifest = await createBuildManifestFromBundle(
bundle,
destination.path,
rawConfig,
workerDir?.path
);
buildManifest = await notifyExtensionOnBuildComplete(buildContext, buildManifest);
logger.debug("Updated bundle", { bundle, buildManifest });
await runtime.initializeWorker(buildManifest);
} catch (error) {
logger.error("Error updating bundle", { error });
}
}
async function updateBuild(build: esbuild.BuildResult, workerDir: EphemeralDirectory) {
const bundle = await getBundleResultFromBuild("dev", rawConfig.workingDir, build);
if (bundle) {
await updateBundle({ ...bundle, stop: undefined }, workerDir);
}
}
let bundled = false;
const onEnd = {
name: "on-end",
setup(b: esbuild.PluginBuild) {
b.onStart(() => {
logger.debug("on-end plugin started");
if (bundled) {
logger.log(chalkGrey("○ Rebuilding background worker…"));
}
});
b.onEnd(async (result: esbuild.BuildResult) => {
const errors = result.errors;
const warnings = result.warnings;
if (errors.length > 0) {
logBuildFailure(errors, warnings);
return;
}
if (warnings.length > 0) {
logBuildWarnings(warnings);
}
if (!bundled) {
// First bundle, no need to update bundle
bundled = true;
} else {
const workerDir = getTmpDir(rawConfig.workingDir, "build");
await updateBuild(result, workerDir);
}
});
},
};
async function runBundle() {
const bundleResult = await bundleWorker({
target: "dev",
cwd: rawConfig.workingDir,
destination: destination.path,
watch: true,
resolvedConfig: rawConfig,
plugins: [...pluginsFromExtensions, onEnd],
jsxFactory: rawConfig.build.jsx.factory,
jsxFragment: rawConfig.build.jsx.fragment,
jsxAutomatic: rawConfig.build.jsx.automatic,
});
logger.log(chalkGrey("○ Building background worker…"));
await updateBundle(bundleResult);
return bundleResult.stop;
}
const stopBundling = await runBundle();
return {
stop: () => {
logger.debug("Stopping dev session");
destination.remove();
stopBundling?.().catch((error) => {});
runtime.shutdown().catch((error) => {});
},
};
}
async function createBuildManifestFromBundle(
bundle: BundleResult,
destination: string,
resolvedConfig: ResolvedConfig,
workerDir: string | undefined
): Promise<BuildManifest> {
const buildManifest: BuildManifest = {
contentHash: bundle.contentHash,
runtime: resolvedConfig.runtime ?? DEFAULT_RUNTIME,
target: "dev",
files: bundle.files,
externals: [],
config: {
project: resolvedConfig.project,
dirs: resolvedConfig.dirs,
},
outputPath: destination,
workerEntryPoint: bundle.workerEntryPoint ?? devEntryPoint,
loaderEntryPoint: bundle.loaderEntryPoint ?? telemetryEntryPoint,
configPath: bundle.configPath,
deploy: {
env: {},
},
build: {},
};
if (!workerDir) {
return buildManifest;
}
return copyManifestToDir(buildManifest, destination, workerDir);
}