UNPKG

@gulibs/vgrove-core

Version:

VGrove CLI - A unified development tool for React applications with auto-routes and i18n

213 lines (212 loc) 7.05 kB
import path from 'path'; import fs from 'fs'; import { pathToFileURL } from 'url'; import { tmpdir } from 'os'; export function defineConfig(config) { return config; } export async function loadConfig(root = process.cwd()) { const configFile = findConfigFile(root); if (!configFile) { return getDefaultConfig(root); } try { const config = await loadConfigFile(configFile); return resolveConfig(config, root, configFile); } catch (error) { console.error(`Failed to load config from ${configFile}:`, error); return getDefaultConfig(root); } } function findConfigFile(root) { const configFiles = [ 'vgrove.config.ts', 'vgrove.config.js', 'vgrove.config.mjs' ]; for (const file of configFiles) { const filePath = path.resolve(root, file); if (fs.existsSync(filePath)) { return filePath; } } return null; } async function loadConfigFile(configFile) { if (configFile.endsWith('.ts')) { return await loadTypeScriptConfig(configFile); } // 处理 JS/MJS 文件 const fileUrl = pathToFileURL(configFile).href; const module = await import(fileUrl); return module.default || module; } async function loadTypeScriptConfig(configFile) { try { const { build } = await import('esbuild'); // 获取配置文件的目录路径(用于注入 __dirname) const configDir = path.dirname(configFile); // 使用 esbuild 编译 TypeScript 配置文件 const result = await build({ entryPoints: [configFile], bundle: true, // 需要打包才能处理导入 format: 'esm', platform: 'node', target: 'node18', write: false, // 不写入文件,直接获取结果 external: [ // 保留 Node.js 内置模块为外部依赖 'path', 'fs', 'url', 'os', 'crypto', 'util', 'events', 'stream' ], loader: { '.ts': 'ts' }, define: { // 注入 CommonJS 环境变量,模拟 Vite 的行为 '__dirname': JSON.stringify(configDir), '__filename': JSON.stringify(configFile) }, logLevel: 'silent', // 静默模式,我们自己处理错误 plugins: [ { name: 'vgrove-config-transform', setup(build) { build.onResolve({ filter: /@gulibs\/vgrove/ }, () => { return { path: 'vgrove-stub', namespace: 'vgrove-stub' }; }); // 提供 defineConfig 的存根实现 build.onLoad({ filter: /.*/, namespace: 'vgrove-stub' }, () => { return { contents: 'export function defineConfig(config) { return config; }', loader: 'js' }; }); } } ] }); if (result.errors.length > 0) { throw new Error(`TypeScript compilation errors:\n${result.errors.map(e => e.text).join('\n')}`); } // 生成临时文件名 const tempDir = tmpdir(); const tempFile = path.join(tempDir, `vgrove-config-${Date.now()}-${Math.random().toString(36).substr(2, 9)}.mjs`); try { // 写入编译结果到临时文件 fs.writeFileSync(tempFile, result.outputFiles[0].text); // 导入临时文件 const fileUrl = pathToFileURL(tempFile).href; const module = await import(fileUrl); return module.default || module; } finally { // 清理临时文件 try { if (fs.existsSync(tempFile)) { fs.unlinkSync(tempFile); } } catch (cleanupError) { // 忽略清理错误,但可以记录日志 console.warn(`Warning: Failed to cleanup temporary config file: ${tempFile}`); } } } catch (error) { // 提供更友好的错误信息 if (error instanceof Error) { if (error.message.includes('Cannot resolve')) { throw new Error(`Failed to resolve dependencies in config file: ${configFile}\n` + `Make sure all imported packages are installed.\n` + `Original error: ${error.message}`); } else if (error.message.includes('TypeScript compilation errors')) { throw new Error(`TypeScript compilation failed for config file: ${configFile}\n` + `${error.message}`); } else { throw new Error(`Failed to load TypeScript config file: ${configFile}\n` + `Error: ${error.message}`); } } throw error; } } function resolveConfig(config, root, configFile) { const defaultConfig = getDefaultConfig(root, configFile); return { root, configFile, vgrove: { routes: { ...defaultConfig.vgrove.routes, ...config.vgrove?.routes }, i18n: { ...defaultConfig.vgrove.i18n, ...config.vgrove?.i18n }, ui: { ...defaultConfig.vgrove.ui, ...config.vgrove?.ui }, build: { ...defaultConfig.vgrove.build, ...config.vgrove?.build } }, vite: { ...defaultConfig.vite, ...config.vite } }; } function getDefaultConfig(root, configFile) { return { root, configFile, vgrove: { routes: { dirs: [{ dir: 'src/pages', baseRoute: '/' }], routeMode: 'flat', cache: true }, i18n: { basePath: 'src/locales', extensions: ['.json', '.ts', '.js'], localePattern: 'directory', hmr: true, deep: true, exclude: [], include: [], debug: false, keysOutput: 'src/i18n-keys.ts' }, ui: { framework: 'none' }, build: { target: 'es2020', outDir: 'dist', sourcemap: true, minify: true } }, vite: { root, resolve: { alias: { '@': path.resolve(root, 'src') } } } }; }