Skip to content

Fix "no tasks defined" issue (fix #1681) #1663

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eleven-eels-draw.md
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion packages/cli-v3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
25 changes: 25 additions & 0 deletions packages/cli-v3/src/build/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,6 +73,29 @@ export async function bundleWorker(options: BundleOptions): Promise<BundleResult
}
);

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: (result: esbuild.BuildResult) => void;
const initialBuildResultPromise = new Promise<esbuild.BuildResult>(
(resolve) => (initialBuildResult = resolve)
Expand Down
33 changes: 29 additions & 4 deletions packages/cli-v3/src/build/entryPoints.ts
Original file line number Diff line number Diff line change
@@ -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<void>;
};
Expand Down Expand Up @@ -34,10 +36,22 @@ export async function createEntryPointManager(
onEntryPointsChange?: (entryPoints: string[]) => Promise<void>
): Promise<EntryPointManager> {
// 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
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -121,6 +144,8 @@ export async function createEntryPointManager(
return {
entryPoints: initialEntryPoints,
watcher,
patterns,
ignorePatterns,
stop: async () => {
await watcher?.close();
},
Expand Down
91 changes: 82 additions & 9 deletions packages/cli-v3/src/dev/workerRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -352,22 +357,90 @@ 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
const taskIds = manifest.tasks.map((task) => task.id);
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(
Expand Down
23 changes: 21 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions references/v3-catalog/src/trigger2/helloWorld.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { task } from "@trigger.dev/sdk/v3";

const helloWorld = task({
id: "helloWorld",
async run() {
console.log("Hello World!");
},
});
1 change: 1 addition & 0 deletions references/v3-catalog/trigger.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default defineConfig({
instrumentations: [new OpenAIInstrumentation()],
additionalFiles: ["wrangler/wrangler.toml"],
maxDuration: 3600,
dirs: ["./src/trigger"],
retries: {
enabledInDev: true,
default: {
Expand Down