Skip to content
Open
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
6 changes: 6 additions & 0 deletions .changeset/fix-monorepo-vitest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"varlock": patch
"@varlock/vite-integration": patch
---

Fix Vitest workspace projects in monorepos: when running Vitest from the monorepo root using the `projects` config, varlock now correctly resolves `.env.schema` and `.env` files from each child package's directory instead of only looking in the monorepo root.
28 changes: 23 additions & 5 deletions packages/integrations/vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ function resetStaticReplacements() {


let loadCount = 0;
function reloadConfig() {
debug('loading config - count =', ++loadCount);
function reloadConfig(cwd?: string) {
debug('loading config - count =', ++loadCount, cwd ? `(cwd: ${cwd})` : '');
try {
const execResult = execSyncVarlock('load --format json-full --compact', {
env: originalProcessEnv,
...(cwd && { cwd }),
});
process.env.__VARLOCK_ENV = execResult;
varlockLoadedEnv = JSON.parse(process.env.__VARLOCK_ENV) as SerializedEnvGraph;
Expand Down Expand Up @@ -140,11 +141,28 @@ See https://varlock.dev/integrations/vite/ for more details.

isDevCommand = env.command === 'serve';

// this gets re-triggered after .env file updates
// TODO: be smarter about only reloading if the env files changed?
if (isFirstLoad) {
// Determine the project root for the current Vite/Vitest project.
// In monorepo setups with Vitest workspace projects, config.root
// points to the child package directory rather than the monorepo root
// where process.cwd() points. We need to reload varlock from the
// correct directory so it can find .env.schema and .env files.
const projectRoot = config.root ? path.resolve(config.root) : undefined;
const rootDiffersFromCwd = !!(projectRoot && path.relative(projectRoot, process.cwd()) !== '');

if (rootDiffersFromCwd) {
// Always reload with the correct project root when it differs from
// cwd. This handles monorepo Vitest workspace setups where each child
// project has its own env files — even if the monorepo root also has
// env files (which would cause the initial module-level load to
// succeed with the wrong project's config).
reloadConfig(projectRoot);
} else if (isFirstLoad) {
isFirstLoad = false;
// Roots match — the module-level reloadConfig() already loaded from
// the correct directory, no need to reload.
} else if (isDevCommand) {
// Dev mode re-trigger (e.g., after .env file updates)
// TODO: be smarter about only reloading if the env files changed?
reloadConfig();
}

Expand Down
5 changes: 4 additions & 1 deletion packages/varlock/src/lib/exec-sync-varlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function execSyncVarlock(
try {
const result = execSync(`varlock ${command}`, {
...opts?.env && { env: opts.env },
...opts?.cwd && { cwd: opts.cwd },
stdio: 'pipe',
});
return result.toString();
Expand All @@ -73,11 +74,13 @@ export function execSyncVarlock(

// if varlock was not found, it either means it is not installed
// or we must find the path to node_modules/.bin ourselves.
// Search from callerDir first (if provided), then from process.cwd().
// Search from cwd (if provided), callerDir, then process.cwd().
// This handles monorepo setups where cwd may be an unrelated workspace
// root while varlock is only installed in a sub-package - the callerDir
// supplied by auto-load.ts points inside that sub-package's node_modules.
const cwdStr = opts?.cwd ? String(opts.cwd) : undefined;
const searchDirs = [
...(cwdStr ? [cwdStr] : []),
...(opts?.callerDir ? [opts.callerDir] : []),
process.cwd(),
];
Expand Down
Loading