@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.
105 lines • 4.21 kB
JavaScript
/**
* TypeScript Config Loader
*
* Loads TypeScript configuration files by transpiling them on the fly
*/
import { existsSync, readFileSync, unlinkSync } from 'fs';
import { exec } from 'child_process';
import { promisify } from 'util';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { ConfigError } from '../errors/index.js';
const execAsync = promisify(exec);
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* Check if esbuild is available (now always returns true since it's a dependency)
*/
async function hasEsbuild() {
// esbuild is now a direct dependency of OATS, so it's always available
return true;
}
/**
* Load TypeScript config using esbuild
*/
export async function loadTypeScriptConfig(configPath) {
// Check if we can use esbuild
const esbuildAvailable = await hasEsbuild();
if (!esbuildAvailable) {
// Fallback to simple parsing
return parseTypeScriptConfig(configPath);
}
const tempFile = configPath.replace('.ts', '.temp.js');
try {
// Transpile TypeScript to JavaScript using esbuild
// Use the esbuild that's bundled with OATS for consistency
const esbuildPath = join(__dirname, '../../node_modules/.bin/esbuild');
await execAsync(`${esbuildPath} ${configPath} --bundle --platform=node --format=cjs --external:@tryloop/oats --outfile=${tempFile}`);
// Load the transpiled JavaScript
const config = await import(tempFile);
// Clean up temp file
if (existsSync(tempFile)) {
unlinkSync(tempFile);
}
// Handle the config object - esbuild wraps ESM default exports in a default property
// when converting to CJS, but we need to check if it's a double-wrapped scenario
if (config.default &&
typeof config.default === 'object' &&
'default' in config.default) {
// Double wrapped - take the inner default
return config.default.default;
}
else if (config.default !== undefined) {
// Single wrapped - take the default
return config.default;
}
else {
// Not wrapped - return as is
return config;
}
}
catch (error) {
// Clean up temp file on error
if (existsSync(tempFile)) {
unlinkSync(tempFile);
}
// Fallback to simple parsing
return parseTypeScriptConfig(configPath);
}
}
/**
* Simple TypeScript config parser (fallback)
*/
function parseTypeScriptConfig(configPath) {
const content = readFileSync(configPath, 'utf-8');
// Extract the config object using eval (safe since it's user's own config)
try {
// Remove TypeScript-specific syntax
const jsContent = content
// Remove import statements
.replace(/import\s+.*?from\s+['"].*?['"];?\s*/g, '')
// Remove type annotations
.replace(/:\s*(string|number|boolean|any)(\[\])?/g, '')
// Replace export default with module.exports
.replace(/export\s+default\s+/, 'module.exports = ')
// Remove defineConfig wrapper
.replace(/defineConfig\s*\(/g, '(')
// Handle 'as const' assertions
.replace(/as\s+const/g, '');
// Create a function that returns the config
const configFunc = new Function('module', 'exports', jsContent);
const module = { exports: {} };
configFunc(module, module.exports);
return module.exports;
}
catch (error) {
throw new ConfigError(`Failed to parse TypeScript config.\n\n` +
`For better TypeScript support, install esbuild:\n` +
` npm install -D esbuild\n\n` +
`Or convert your config to JavaScript format:\n` +
` 1. Rename oats.config.ts to oats.config.js\n` +
` 2. Change import to: const { defineConfig } = require('@tryloop/oats')\n` +
` 3. Change export to: module.exports = defineConfig({...})\n\n` +
`Error: ${error instanceof Error ? error.message : String(error)}`);
}
}
//# sourceMappingURL=ts-loader.js.map