diff --git a/.changeset/eleven-eels-draw.md b/.changeset/eleven-eels-draw.md new file mode 100644 index 0000000000..b1d239196c --- /dev/null +++ b/.changeset/eleven-eels-draw.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Fix "No tasks defined" issue because of misconfigured dir search paths. Also improve errors around no files or no tasks found during in dev" diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index b5cff5fce4..80624151ec 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -120,7 +120,7 @@ "terminal-link": "^3.0.0", "tiny-invariant": "^1.2.0", "tinyexec": "^0.3.1", - "tinyglobby": "^0.2.2", + "tinyglobby": "^0.2.10", "ws": "^8.18.0", "xdg-app-paths": "^8.3.0", "zod": "3.23.8", diff --git a/packages/cli-v3/src/build/bundle.ts b/packages/cli-v3/src/build/bundle.ts index d06fdbf9a9..1126991072 100644 --- a/packages/cli-v3/src/build/bundle.ts +++ b/packages/cli-v3/src/build/bundle.ts @@ -18,6 +18,8 @@ import { } 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 interface BundleOptions { target: BuildTarget; @@ -71,6 +73,29 @@ export async function bundleWorker(options: BundleOptions): Promise void; const initialBuildResultPromise = new Promise( (resolve) => (initialBuildResult = resolve) diff --git a/packages/cli-v3/src/build/entryPoints.ts b/packages/cli-v3/src/build/entryPoints.ts index 95ca763764..24629cd4e0 100644 --- a/packages/cli-v3/src/build/entryPoints.ts +++ b/packages/cli-v3/src/build/entryPoints.ts @@ -1,12 +1,14 @@ import { BuildTarget } from "@trigger.dev/core/v3"; import { ResolvedConfig } from "@trigger.dev/core/v3/build"; import * as chokidar from "chokidar"; -import { glob } from "tinyglobby"; +import { glob, escapePath, isDynamicPattern } from "tinyglobby"; import { logger } from "../utilities/logger.js"; import { deployEntryPoints, devEntryPoints, telemetryEntryPoint } from "./packageModules.js"; type EntryPointManager = { entryPoints: string[]; + patterns: string[]; + ignorePatterns: string[]; watcher?: chokidar.FSWatcher; stop: () => Promise; }; @@ -34,10 +36,22 @@ export async function createEntryPointManager( onEntryPointsChange?: (entryPoints: string[]) => Promise ): Promise { // Patterns to match files - const patterns = dirs.flatMap((dir) => [`${dir}/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`]); + const patterns = dirs.flatMap((dir) => [ + `${ + isDynamicPattern(dir) + ? `${dir}/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` + : `${escapePath(dir)}/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` + }`, + ]); // Patterns to ignore - const ignorePatterns = config.ignorePatterns ?? DEFAULT_IGNORE_PATTERNS; + let ignorePatterns = config.ignorePatterns ?? DEFAULT_IGNORE_PATTERNS; + ignorePatterns = ignorePatterns.concat([ + "**/node_modules/**", + "**/.git/**", + "**/.trigger/**", + "**/.next/**", + ]); async function getEntryPoints() { // Get initial entry points @@ -47,6 +61,10 @@ export async function createEntryPointManager( cwd: config.workingDir, }); + if (entryPoints.length === 0) { + return []; + } + // Add required entry points if (config.configFile) { entryPoints.push(config.configFile); @@ -80,7 +98,12 @@ export async function createEntryPointManager( let watcher: chokidar.FSWatcher | undefined; if (watch && onEntryPointsChange) { - logger.debug("Watching entry points for changes", { dirs, cwd: config.workingDir }); + logger.debug("Watching entry points for changes", { + dirs, + cwd: config.workingDir, + patterns, + ignorePatterns, + }); // Watch the parent directories watcher = chokidar.watch(patterns, { ignored: ignorePatterns, @@ -121,6 +144,8 @@ export async function createEntryPointManager( return { entryPoints: initialEntryPoints, watcher, + patterns, + ignorePatterns, stop: async () => { await watcher?.close(); }, diff --git a/packages/cli-v3/src/dev/workerRuntime.ts b/packages/cli-v3/src/dev/workerRuntime.ts index 1f816d12f2..02474295a7 100644 --- a/packages/cli-v3/src/dev/workerRuntime.ts +++ b/packages/cli-v3/src/dev/workerRuntime.ts @@ -19,7 +19,7 @@ import { WebSocket } from "partysocket"; import { ClientOptions, WebSocket as wsWebSocket } from "ws"; import { CliApiClient } from "../apiClient.js"; import { DevCommandOptions } from "../commands/dev.js"; -import { chalkError, chalkTask } from "../utilities/cliOutput.js"; +import { chalkError, chalkTask, cliLink, prettyError } from "../utilities/cliOutput.js"; import { resolveDotEnvVars } from "../utilities/dotEnv.js"; import { eventBus } from "../utilities/eventBus.js"; import { logger } from "../utilities/logger.js"; @@ -189,10 +189,15 @@ class DevWorkerRuntime implements WorkerRuntime { throw new Error("Could not initialize worker"); } - const issues = validateWorkerManifest(backgroundWorker.manifest); + const validationIssue = validateWorkerManifest(backgroundWorker.manifest); + + if (validationIssue) { + prettyError( + generationValidationIssueHeader(validationIssue), + generateValidationIssueMessage(validationIssue, backgroundWorker.manifest!, manifest), + generateValidationIssueFooter(validationIssue) + ); - if (issues.length > 0) { - issues.forEach((issue) => logger.error(issue)); stop(); return; } @@ -352,11 +357,20 @@ function gatherProcessEnv() { return Object.fromEntries(Object.entries($env).filter(([key, value]) => value !== undefined)); } -function validateWorkerManifest(manifest: WorkerManifest): string[] { - const issues: string[] = []; +type ValidationIssue = + | { + type: "duplicateTaskId"; + duplicationTaskIds: string[]; + } + | { + type: "noTasksDefined"; + }; + +function validateWorkerManifest(manifest: WorkerManifest): ValidationIssue | undefined { + const issues: ValidationIssue[] = []; if (!manifest.tasks || manifest.tasks.length === 0) { - issues.push("No tasks defined. Make sure you are exporting tasks."); + return { type: "noTasksDefined" }; } // Check for any duplicate task ids @@ -364,10 +378,69 @@ function validateWorkerManifest(manifest: WorkerManifest): string[] { const duplicateTaskIds = taskIds.filter((id, index) => taskIds.indexOf(id) !== index); if (duplicateTaskIds.length > 0) { - issues.push(createDuplicateTaskIdOutputErrorMessage(duplicateTaskIds, manifest.tasks)); + return { type: "duplicateTaskId", duplicationTaskIds: duplicateTaskIds }; + } + + return undefined; +} + +function generationValidationIssueHeader(issue: ValidationIssue) { + switch (issue.type) { + case "duplicateTaskId": { + return `Duplicate task ids detected`; + } + case "noTasksDefined": { + return `No tasks exported from your trigger files`; + } } +} - return issues; +function generateValidationIssueFooter(issue: ValidationIssue) { + switch (issue.type) { + case "duplicateTaskId": { + return cliLink("View the task docs", "https://trigger.dev/docs/tasks/overview"); + } + case "noTasksDefined": { + return cliLink("View the task docs", "https://trigger.dev/docs/tasks/overview"); + } + } +} + +function generateValidationIssueMessage( + issue: ValidationIssue, + manifest: WorkerManifest, + buildManifest: BuildManifest +) { + switch (issue.type) { + case "duplicateTaskId": { + return createDuplicateTaskIdOutputErrorMessage(issue.duplicationTaskIds, manifest.tasks); + } + case "noTasksDefined": { + return ` + Files: + ${buildManifest.files.map((file) => file.entry).join("\n")} + + Make sure you have at least one task exported from your trigger files. + + You may have defined a task and forgot to add the export statement: + + \`\`\`ts + import { task } from "@trigger.dev/sdk/v3"; + + 👇 Don't forget this + export const myTask = task({ + id: "myTask", + async run() { + // Your task logic here + } + }); + \`\`\` + `.replace(/^ {8}/gm, ""); + } + default: { + return `Unknown validation issue: ${issue}`; + } + } } function createDuplicateTaskIdOutputErrorMessage( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a56f97b3d2..c4e8dc9811 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1211,8 +1211,8 @@ importers: specifier: ^0.3.1 version: 0.3.1 tinyglobby: - specifier: ^0.2.2 - version: 0.2.2 + specifier: ^0.2.10 + version: 0.2.10 ws: specifier: ^8.18.0 version: 8.18.0 @@ -22205,6 +22205,17 @@ packages: picomatch: 4.0.2 dev: false + /fdir@6.4.3(picomatch@4.0.2): + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 + dev: false + /fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -30434,6 +30445,14 @@ packages: resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} dev: false + /tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + dev: false + /tinyglobby@0.2.2: resolution: {integrity: sha512-mZ2sDMaySvi1PkTp4lTo1In2zjU+cY8OvZsfwrDrx3YGRbXPX1/cbPwCR9zkm3O/Fz9Jo0F1HNgIQ1b8BepqyQ==} engines: {node: '>=12.0.0'} diff --git a/references/v3-catalog/src/trigger2/helloWorld.ts b/references/v3-catalog/src/trigger2/helloWorld.ts new file mode 100644 index 0000000000..8081bc6e7c --- /dev/null +++ b/references/v3-catalog/src/trigger2/helloWorld.ts @@ -0,0 +1,8 @@ +import { task } from "@trigger.dev/sdk/v3"; + +const helloWorld = task({ + id: "helloWorld", + async run() { + console.log("Hello World!"); + }, +}); diff --git a/references/v3-catalog/trigger.config.ts b/references/v3-catalog/trigger.config.ts index 18ac1ec734..830ac38999 100644 --- a/references/v3-catalog/trigger.config.ts +++ b/references/v3-catalog/trigger.config.ts @@ -18,6 +18,7 @@ export default defineConfig({ instrumentations: [new OpenAIInstrumentation()], additionalFiles: ["wrangler/wrangler.toml"], maxDuration: 3600, + dirs: ["./src/trigger"], retries: { enabledInDev: true, default: {