@tryloop/oats
Version:
🌾 OATS - OpenAPI TypeScript Sync. The missing link between your OpenAPI specs and TypeScript applications. Automatically watch, generate, and sync TypeScript clients from your API definitions.
162 lines • 6.68 kB
JavaScript
/**
* Environment Variable Manager for OATS
*
* Handles framework detection and environment variable injection
* for seamless backend URL configuration across different frontend frameworks
*/
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { Logger } from '../../utils/logger.js';
export class EnvManager {
logger;
// Framework configurations with their environment variable prefixes
static FRAMEWORK_CONFIGS = [
{ framework: 'vite', envPrefix: 'VITE_', description: 'Vite' },
{
framework: 'create-react-app',
envPrefix: 'REACT_APP_',
description: 'Create React App',
},
{ framework: 'vue-cli', envPrefix: 'VUE_APP_', description: 'Vue CLI' },
{ framework: 'next', envPrefix: 'NEXT_PUBLIC_', description: 'Next.js' },
{ framework: 'nuxt', envPrefix: 'NUXT_PUBLIC_', description: 'Nuxt' },
{ framework: 'angular', envPrefix: 'NG_', description: 'Angular' },
{ framework: 'svelte', envPrefix: 'VITE_', description: 'SvelteKit' }, // SvelteKit uses Vite
{ framework: 'remix', envPrefix: 'REMIX_', description: 'Remix' },
];
constructor() {
this.logger = new Logger('EnvManager');
}
/**
* Detect frontend framework from package.json
*/
detectFramework(projectPath) {
try {
const packageJsonPath = join(projectPath, 'package.json');
if (!existsSync(packageJsonPath)) {
return 'unknown';
}
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const deps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
// Check for framework indicators
if (deps['next'])
return 'next';
if (deps['nuxt'] || deps['nuxt3'])
return 'nuxt';
if (deps['@angular/core'])
return 'angular';
if (deps['@sveltejs/kit'])
return 'svelte';
if (deps['@remix-run/react'])
return 'remix';
if (deps['@vitejs/plugin-react'] || deps['vite'])
return 'vite';
if (deps['react-scripts'])
return 'create-react-app';
if (deps['@vue/cli-service'])
return 'vue-cli';
// Check scripts for additional hints
const scripts = packageJson.scripts || {};
if (scripts.dev?.includes('vite') || scripts.start?.includes('vite'))
return 'vite';
if (scripts.start?.includes('react-scripts') ||
scripts.dev?.includes('react-scripts'))
return 'create-react-app';
if (scripts.serve?.includes('vue-cli-service') ||
scripts.dev?.includes('vue-cli-service'))
return 'vue-cli';
}
catch (error) {
this.logger.debug(`Could not detect framework: ${error}`);
}
return 'unknown';
}
/**
* Get the appropriate env prefix for a framework
*/
getEnvPrefix(framework) {
const config = EnvManager.FRAMEWORK_CONFIGS.find((f) => f.framework === framework);
return config?.envPrefix || '';
}
/**
* Generate OATS environment variables for frontend service
*/
generateFrontendEnvVars(frontendPath, runtimeConfig) {
// Construct backend URL from config
const backendConfig = runtimeConfig.services.backend;
const backendUrl = backendConfig.port
? `http://localhost:${backendConfig.port}`
: 'http://localhost:8000';
// Detect framework
const framework = this.detectFramework(frontendPath);
const envPrefix = this.getEnvPrefix(framework);
// Build environment variables
const envVars = {
// Generic OATS variables (always included)
OATS_MODE: 'true',
OATS_BACKEND_BASE_URL: backendUrl,
NODE_ENV: 'development',
};
// Framework-specific variables
if (envPrefix) {
envVars[`${envPrefix}OATS_BACKEND_BASE_URL`] = backendUrl;
envVars[`${envPrefix}OATS_MODE`] = 'true';
// Add common API URL patterns for convenience
envVars[`${envPrefix}API_URL`] = backendUrl;
envVars[`${envPrefix}API_BASE_URL`] = backendUrl;
envVars[`${envPrefix}BACKEND_URL`] = backendUrl;
envVars[`${envPrefix}BACKEND_BASE_URL`] = backendUrl;
}
// TODO: Add support for additional services (agent, worker, etc.)
// when they are added to the ServicesConfig type
// Log environment setup
const frameworkDesc = this.getFrameworkDescription(framework);
this.logger.info(`🌍 Setting up environment for ${frameworkDesc}`);
this.logger.info(` Backend URL: ${backendUrl}`);
if (envPrefix) {
this.logger.info(` Environment prefix: ${envPrefix}*`);
}
this.logger.debug('Environment variables:', envVars);
return envVars;
}
/**
* Get a user-friendly framework description
*/
getFrameworkDescription(framework) {
const config = EnvManager.FRAMEWORK_CONFIGS.find((f) => f.framework === framework);
return config?.description || 'Unknown Framework';
}
/**
* Generate environment variables for any service type
* This can be extended for backend services, workers, etc.
*/
generateServiceEnvVars(serviceType, servicePath, runtimeConfig) {
switch (serviceType) {
case 'frontend':
return this.generateFrontendEnvVars(servicePath, runtimeConfig);
case 'backend':
// Backend services might need database URLs, service URLs, etc.
return {
NODE_ENV: 'development',
OATS_MODE: 'true',
// Add more backend-specific env vars as needed
};
case 'worker':
// Worker services might need queue URLs, backend URLs, etc.
return {
NODE_ENV: 'development',
OATS_MODE: 'true',
BACKEND_URL: `http://localhost:${runtimeConfig.services.backend?.port || 8000}`,
// Add more worker-specific env vars as needed
};
default:
return {};
}
}
}
// Export singleton instance
export const envManager = new EnvManager();
//# sourceMappingURL=env-manager.js.map