Skip to content
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/next-root-detection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/next": patch
---

Derive the workflow builder project root from Next.js workspace root configuration.
88 changes: 84 additions & 4 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { statSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import type { NextConfig } from 'next';
import semver from 'semver';
import { getNextBuilder } from './builder.js';
Expand Down Expand Up @@ -193,6 +195,82 @@ function resolveNextVersion(workingDir: string): string {
}
}

function fileExists(path: string): boolean {
try {
return statSync(path).isFile();
} catch {
return false;
}
}

function findRootFile(names: string[], workingDir: string): string | undefined {
let current = resolve(workingDir);

while (true) {
for (const name of names) {
const file = join(current, name);
if (fileExists(file)) {
return file;
}
}

const parent = dirname(current);
if (parent === current) {
return undefined;
}
current = parent;
}
}

function findNextRootFile(workingDir: string): string | undefined {
return (
findRootFile(['pnpm-workspace.yaml'], workingDir) ??
findRootFile(
[
'pnpm-lock.yaml',
'package-lock.json',
'yarn.lock',
'bun.lock',
'bun.lockb',
],
workingDir
)
);
}

function resolveNextProjectRoot(
nextConfig: NextConfig,
workingDir: string
): string {
const configuredRoot =
nextConfig.outputFileTracingRoot ?? nextConfig.turbopack?.root;

if (configuredRoot) {
return isAbsolute(configuredRoot)
? configuredRoot
: resolve(workingDir, configuredRoot);
}

let rootFile = findNextRootFile(workingDir);
if (!rootFile) {
return workingDir;
}

while (true) {
const currentDir = dirname(rootFile);
const parentDir = dirname(currentDir);
if (parentDir === currentDir) {
return currentDir;
}

const parentRootFile = findNextRootFile(parentDir);
if (!parentRootFile) {
return currentDir;
}
rootFile = parentRootFile;
}
}

export function withWorkflow(
nextConfigOrFn:
| NextConfig
Expand Down Expand Up @@ -288,7 +366,9 @@ export function withWorkflow(
nextConfig.turbopack.rules = {};
}
const existingRules = nextConfig.turbopack.rules as any;
const nextVersion = resolveNextVersion(process.cwd());
const workingDir = process.cwd();
const nextVersion = resolveNextVersion(workingDir);
const projectRoot = resolveNextProjectRoot(nextConfig, workingDir);
const supportsTurboCondition = semver.gte(nextVersion, 'v16.0.0');

const shouldWatch = process.env.NODE_ENV === 'development';
Expand All @@ -308,9 +388,9 @@ export function withWorkflow(
'jsx',
'js',
],
projectRoot: nextConfig.outputFileTracingRoot,
moduleSpecifierRoot: process.cwd(),
workingDir: process.cwd(),
projectRoot,
moduleSpecifierRoot: workingDir,
workingDir,
distDir: nextConfig.distDir || '.next',
buildTarget: 'next',
workflowsBundlePath: '', // not used in base
Expand Down
Loading