Skip to content

Derive framework integration config instead of requiring duplicate Workflow config #2716

Description

@NathanColosimo

Summary

Follow-up from investigating #2074: Nitro/Nuxt should derive the builder projectRoot from Nitro's own workspace options instead of requiring Workflow-specific duplicate config. While checking that, several other framework integrations have similar root/config derivation gaps.

The goal is not to add more Workflow config. The goal is the opposite: when a framework already knows the app root, workspace root, routes directory, compiler output, or module format, the Workflow integration should reuse that framework state.

Background

Workflow builders have two important roots:

  • workingDir: the app/project directory used for scanning source and writing framework-local generated files.
  • projectRoot: the broader root used by SWC transforms for tracing, discovery, tsconfig lookup, and module identity. It defaults to workingDir when omitted.

That default is fine for single-package apps, but monorepos often need projectRoot to be the workspace root while workingDir remains the framework app directory. If an integration does not derive this, workspace imports can be transformed as if they are outside the project or can produce unstable module identities.

Current findings

Nitro / Nuxt

Related to #2074.

Nitro currently has all the information needed to make this zero-config. The integration should keep workingDir as nitro.options.rootDir, but derive projectRoot from Nitro's workspace root when available:

projectRoot: nitro.options.workspaceDir || nitro.options.rootDir

Nuxt delegates to Nitro through workflow/nuxt, so fixing Nitro also fixes Nuxt.

Also noticed: ModuleOptions.dirs is public and documented, but the source builder hardcodes dirs: ['.']. This may be dead config or source/dist drift; it should be checked separately and not necessarily bundled into the minimal #2074 fix.

Next.js

Next mostly does the right thing already:

  • derives distDir from nextConfig.distDir
  • derives pageExtensions from nextConfig.pageExtensions
  • passes projectRoot from nextConfig.outputFileTracingRoot

Docs still tell monorepo users to set outputFileTracingRoot as a workaround. A follow-up could attempt to reuse Next's own detected tracing/workspace root when outputFileTracingRoot is not explicitly set, but this is lower priority than Nitro because Next already has an existing framework-native escape hatch.

SvelteKit

The public plugin API is good: it only exposes sourcemap, which is Workflow-specific.

Internally, though, the builder currently falls back to process.cwd(), hardcodes scan dirs, and guesses the routes directory from src/routes or routes. SvelteKit already has route directory config via kit.files.routes, and the Vite config has root plus workspace-root discovery.

Follow-up: derive:

  • workingDir from resolved Vite/SvelteKit root
  • generated route directory from SvelteKit's configured routes directory
  • projectRoot from the workspace root/Vite fs allow root where possible

Astro

The public API is also good: only sourcemap is exposed.

Internally, the source builder uses process.cwd() and hardcodes src/pages / src/workflows. Astro exposes config.root and config.srcDir, so the integration should derive app root and generated route placement from Astro's resolved config instead of assuming src/pages.

Follow-up: derive:

  • workingDir from AstroConfig.root
  • pages route directory from AstroConfig.srcDir + /pages
  • projectRoot from framework/Vite workspace detection if available

Vitest

The Vitest plugin currently makes monorepo users pass cwd when workflows do not live at process.cwd(). That should usually be derivable from the resolved Vite/Vitest config root.

Some options should remain explicit because they control test artifacts and isolation:

  • rootDir
  • dataDir
  • outDir

Follow-up: derive cwd from the resolved Vitest/Vite root and separately consider deriving projectRoot from workspace root. Keep artifact directory overrides.

NestJS

Nest is the biggest double-config surface today. Users can be asked to pass:

  • dirs
  • moduleType
  • distDir
  • workingDir

Much of this already exists in Nest/TypeScript config:

  • dirs can usually derive from nest-cli.json sourceRoot (default src).
  • distDir can derive from tsconfig.json compilerOptions.outDir, or the Nest/SWC builder output config.
  • moduleType can derive from .swcrc module type or TypeScript module output where possible.
  • workingDir can stay as the app root, but projectRoot should still be derived for monorepos.

This would reduce the CommonJS setup docs where users currently need to keep moduleType: 'commonjs' and distDir: 'dist' in sync with their compiler config.

Proposed priority

  1. Keep the @workflow/nitro: steps bundle externalizes sibling workspace packages — no way to set projectRoot (breaks Nuxt/Nitro monorepo dev) #2074 fix minimal: Nitro derives projectRoot from workspaceDir || rootDir, with a focused regression test.
  2. Audit/fix Nitro dirs behavior separately if it is truly ignored in source.
  3. Add Nest config derivation for sourceRoot, module type, and compiler output directory.
  4. Add Astro/SvelteKit root and route-directory derivation from framework config.
  5. Add Vitest cwd derivation from resolved Vite/Vitest root while preserving artifact directory overrides.
  6. Consider whether Next can safely reuse a stable internal/public root-detection API when outputFileTracingRoot is absent.

Acceptance criteria

  • Framework integrations should not require Workflow-specific config for values that are already present in the host framework's config.
  • Monorepo workspace imports should work without setting a duplicate Workflow projectRoot option in framework integrations.
  • Explicit config should remain only for genuinely Workflow-specific behavior, such as sourcemaps, test artifact locations, or TypeScript plugin opt-in/out.
  • Each integration fix should include a small regression test showing the derived value flows into the builder.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions