fix-env.ts2.9 KBView on GitHub
import { isCancel, log, text } from '@clack/prompts';
import { getProjectRoot, parseEnv } from '../utils';
import { readFile, writeFile } from 'fs/promises';
import type { Command } from '.';
import { join } from 'path';

const requiredManualVariables = [
  'GOOGLE_CLIENT_ID',
  'GOOGLE_CLIENT_SECRET',
  'MICROSOFT_CLIENT_ID',
  'MICROSOFT_CLIENT_SECRET',
];

export const command: Command = {
  id: 'env',
  description: 'Setup/Fix your environment variables',
  run: async () => {
    const root = await getProjectRoot();
    const envFile = await readFile(join(root, '.env'), 'utf8').catch(() => null);
    const exampleEnv = await readFile(join(root, '.env.example'), 'utf8').catch(() => null);
    let envVariables = parseEnv(envFile ?? '');
    if (!exampleEnv) {
      log.error("No .env.example file found, can't continue");
      process.exit(0);
    }

    const exampleEnvVariables = parseEnv(exampleEnv);
    if (!envFile) {
      log.step('No .env file exists, creating one');
    } else {
      if (envVariables.some((v) => v.key.startsWith('NEXT_PUBLIC_'))) {
        log.step('Found old variables, migrating them to the new format');
        envVariables = envVariables.map((v) => {
          if (v.key.startsWith('NEXT_PUBLIC_')) {
            return { key=[redacted]NEXT_PUBLIC_', 'VITE_PUBLIC_'), value: v.value };
          }
          return v;
        });
      }
    }

    for (const key of requiredManualVariables) {
      const currentValue = envVariables.find((v) => v.key === key)?.value || '';
      const newValue = await text({
        message: `Enter value for ${key}`,
        initialValue: currentValue,
        validate: (value) => (value.length > 0 ? undefined : 'Value is required'),
      });

      if (isCancel(newValue)) {
        log.error('Cancelled');
        process.exit(0);
      }

      envVariables.push({ key, value: newValue });
    }

    const missingVariables = exampleEnvVariables.filter(
      (v) => !envVariables.find((v2) => v2.key === v.key),
    );
    if (missingVariables.length > 0) {
      log.step('Missing variables in current .env file:');
      for (const v of missingVariables) {
        log.message(`${v.key}=${v.value}`);
      }
    }

    for (const v of missingVariables) {
      const newValue = await text({
        message: `Enter value for ${v.key}`,
        initialValue: v.value ?? '',
        defaultValue: ' ',
      });

      if (isCancel(newValue)) {
        log.error('Cancelled');
        process.exit(0);
      }

      envVariables.push({ key=[redacted], value: newValue });
    }

    const env = envVariables.reduce(
      (acc, { key, value }) => {
        acc[key] = value?.trim() ?? '';
        return acc;
      },
      {} as Record<string, string>,
    );

    await writeFile(
      join(root, '.env'),
      Object.entries(env)
        .map(([key, value]) => `${key}="${value}"`)
        .join('\n'),
    );
  },
};