import { getProjectRoot, runCommand } from '../utils';
import { readFile, writeFile } from 'fs/promises';
import { log } from '@clack/prompts';
import type { Command } from '.';
import { join } from 'path';
const LOCAL_ENV_OVERRIDES = {
NODE_ENV: 'development',
COOKIE_DOMAIN: '',
} as const;
function formatEnvValue(value: string): string {
return `"${value.replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
}
function upsertEnvValue(contents: string, key=[redacted], value: string): string {
const nextLine = `${key}=${formatEnvValue(value)}`;
const pattern = new RegExp(`^${key}=.*$`, 'm');
if (pattern.test(contents)) {
return contents.replace(pattern, nextLine);
}
return `${contents.trimEnd()}\n${nextLine}\n`;
}
function buildLocalEnvFile(contents: string): string {
let nextContents = contents;
for (const [key, value] of Object.entries(LOCAL_ENV_OVERRIDES)) {
nextContents = upsertEnvValue(nextContents, key, value);
}
return nextContents;
}
export const command: Command = {
id: 'sync',
description: 'Sync your environment variables and types',
run: async () => {
const root = await getProjectRoot();
const envFile = await readFile(join(root, '.env'), 'utf8').catch(() => null);
if (!envFile) {
log.step('No .env file exists, creating one using `pnpm nizzy env`');
process.exit(0);
}
const localEnvFile = buildLocalEnvFile(envFile);
log.step('Syncing environment variables');
await writeFile(join(root, 'apps/mail/.env'), localEnvFile);
await writeFile(join(root, 'apps/server/.env'), localEnvFile);
await writeFile(join(root, 'apps/mastra/.env'), envFile);
if (process.env.CEDAR_SYNC_TYPES === 'true') {
log.step('Syncing frontend types');
await runCommand('pnpm', ['run', 'types'], { cwd: join(root, 'apps/mail') });
log.step('Syncing backend types');
await runCommand('pnpm', ['run', 'types'], { cwd: join(root, 'apps/server') });
} else {
log.step('Skipping package type sync (set CEDAR_SYNC_TYPES=true to enable)');
}
log.success('Synced environment variables and types');
},
};