UNPKG

akfun

Version:

前端脚手架:支持Vue技术栈和react技术栈

434 lines (373 loc) 11.3 kB
/** * 配置验证器 * 提供配置项的验证功能 */ const chalk = require('chalk'); const path = require('path'); class ConfigValidator { constructor() { this.errors = []; this.warnings = []; } /** * 验证配置对象 * @param {Object} config - 配置对象 * @returns {Object} 验证后的配置 */ validate(config) { this.errors = []; this.warnings = []; if (!config || typeof config !== 'object') { throw new Error('配置必须是一个对象'); } // 验证各个部分 this._validateSettings(config.settings); this._validateWebpack(config.webpack); this._validateDev(config.dev); this._validateBuild(config.build); this._validateBuild2Lib(config.build2lib); this._validateBuild2Esm(config.build2esm); this._validateEnvParams(config.envParams); // 输出警告 if (this.warnings.length > 0) { console.log(chalk.yellow('\n⚠️ 配置异常提示:')); this.warnings.forEach((warning) => { console.log(chalk.yellow(` - ${warning}`)); }); console.log(''); } // 如果有错误,抛出异常 if (this.errors.length > 0) { console.error(chalk.red('\n❌ 配置验证失败:')); this.errors.forEach((error) => { console.error(chalk.red(` - ${error}`)); }); console.log(''); throw new Error('配置验证失败,请检查以上错误信息'); } return config; } /** * 验证 settings 配置 * @private */ _validateSettings(settings) { if (!settings) return; const booleanFields = [ 'enableESLint', 'enableESLintFix', 'enableStyleLint', 'enableStyleLintFix' ]; booleanFields.forEach((field) => { if (settings[field] !== undefined && typeof settings[field] !== 'boolean') { this.errors.push(`settings.${field} 必须是布尔值`); } }); } /** * 验证 webpack 配置 * @private */ _validateWebpack(webpack) { // 验证 entry if (webpack && webpack.entry !== undefined) { this._validateEntry(webpack.entry); } // 验证 resolve if (webpack && webpack.resolve) { this._validateResolve(webpack.resolve); } // 验证 externals if (webpack && webpack.externals !== undefined) { const validTypes = ['object', 'string', 'function']; if (!validTypes.includes(typeof webpack.externals)) { this.errors.push('webpack.externals 类型不正确'); } } // 验证 sassResources if (webpack && webpack.sassResources !== undefined) { if (!Array.isArray(webpack.sassResources)) { this.errors.push('webpack.sassResources 必须是数组'); } } // 验证 babelPlugins if (webpack && webpack.babelPlugins !== undefined) { const validTypes = ['function', 'object']; const type = Array.isArray(webpack.babelPlugins) ? 'object' : typeof webpack.babelPlugins; if (!validTypes.includes(type)) { this.errors.push('webpack.babelPlugins 必须是数组或函数'); } } // 验证 moduleRules if (webpack && webpack.moduleRules !== undefined && !Array.isArray(webpack.moduleRules)) { this.errors.push('webpack.moduleRules 必须是数组'); } // 验证 plugins if (webpack && webpack.plugins !== undefined && !Array.isArray(webpack.plugins)) { this.errors.push('webpack.plugins 必须是数组'); } } /** * 验证 entry 配置 * @private */ _validateEntry(entry) { if (entry === undefined || entry === null) { return; } const type = typeof entry; if (type === 'string') { // 单个入口文件 return; } if (Array.isArray(entry)) { // 数组形式的入口 entry.forEach((item, index) => { if (typeof item !== 'string') { this.errors.push(`entry[${index}] 必须是字符串`); } }); return; } if (type === 'object') { // 对象形式的入口 Object.keys(entry).forEach((key) => { const value = entry[key]; if (typeof value !== 'string' && !Array.isArray(value)) { this.errors.push(`entry.${key} 必须是字符串或数组`); } if (Array.isArray(value)) { value.forEach((item, index) => { if (typeof item !== 'string') { this.errors.push(`entry.${key}[${index}] 必须是字符串`); } }); } }); return; } this.errors.push('entry 配置格式不正确'); } /** * 验证 resolve 配置 * @private */ _validateResolve(resolve) { if (resolve.extensions !== undefined) { if (!Array.isArray(resolve.extensions)) { this.errors.push('webpack.resolve.extensions 必须是数组'); } else { resolve.extensions.forEach((ext, index) => { if (typeof ext !== 'string') { this.errors.push(`webpack.resolve.extensions[${index}] 必须是字符串`); } else if (!ext.startsWith('.')) { this.warnings.push(`webpack.resolve.extensions[${index}] (${ext}) 建议以 "." 开头`); } }); } } if (resolve.alias !== undefined && typeof resolve.alias !== 'object') { this.errors.push('webpack.resolve.alias 必须是对象'); } } /** * 验证 dev 配置 * @private */ _validateDev(dev) { if (!dev) { return; } // 验证 NODE_ENV if (dev.NODE_ENV && typeof dev.NODE_ENV !== 'string') { this.errors.push('dev.NODE_ENV 必须是字符串'); } // 验证端口 if (dev.port !== undefined) { this._validatePort(dev.port, 'dev.port'); } // 验证布尔值字段 const booleanFields = ['autoOpenBrowser', 'cssSourceMap', 'https', 'closeHotReload']; booleanFields.forEach((field) => { if (dev[field] !== undefined && typeof dev[field] !== 'boolean') { this.errors.push(`dev.${field} 必须是布尔值`); } }); // 验证字符串字段 const stringFields = ['assetsPublicPath', 'assetsSubDirectory', 'hostname']; stringFields.forEach((field) => { if (dev[field] !== undefined && typeof dev[field] !== 'string') { this.errors.push(`dev.${field} 必须是字符串`); } }); // 验证 proxyTable if (dev.proxyTable !== undefined && typeof dev.proxyTable !== 'object') { this.errors.push('dev.proxyTable 必须是对象'); } } /** * 验证 build 配置 * @private */ _validateBuild(build) { if (!build) { return; } this._validateBuildCommon(build, 'build'); } /** * 验证 build2lib 配置 * @private */ _validateBuild2Lib(build2lib) { if (!build2lib) return; this._validateBuildCommon(build2lib, 'build2lib'); // 验证 libraryName if (build2lib.libraryName !== undefined && typeof build2lib.libraryName !== 'string') { this.errors.push('build2lib.libraryName 必须是字符串'); } } /** * 验证 build2esm 配置 * @private */ _validateBuild2Esm(build2esm) { if (!build2esm) return; // 验证 input if (build2esm.input !== undefined && typeof build2esm.input !== 'string') { this.errors.push('build2esm.input 必须是字符串'); } // 验证 fileName if (build2esm.fileName !== undefined && typeof build2esm.fileName !== 'string') { this.errors.push('build2esm.fileName 必须是字符串'); } // 验证 type if (build2esm.type !== undefined) { const validTypes = ['ts', 'js']; if (!validTypes.includes(build2esm.type)) { this.errors.push(`build2esm.type 必须是 ${validTypes.join(' 或 ')}`); } } // 验证 compress if (build2esm.compress !== undefined && typeof build2esm.compress !== 'boolean') { this.errors.push('build2esm.compress 必须是布尔值'); } // 验证 declaration if (build2esm.declaration !== undefined && typeof build2esm.declaration !== 'boolean') { this.errors.push('build2esm.declaration 必须是布尔值'); } // 验证 excludeList if (build2esm.excludeList !== undefined && !Array.isArray(build2esm.excludeList)) { this.errors.push('build2esm.excludeList 必须是数组'); } } /** * 验证通用的 build 配置项 * @private */ _validateBuildCommon(buildConfig, prefix) { // 验证 NODE_ENV if (buildConfig.NODE_ENV && typeof buildConfig.NODE_ENV !== 'string') { this.errors.push(`${prefix}.NODE_ENV 必须是字符串`); } // 验证路径字段 const pathFields = ['assetsRoot', 'assetsPublicPath', 'assetsSubDirectory']; pathFields.forEach((field) => { if (buildConfig[field] !== undefined && typeof buildConfig[field] !== 'string') { this.errors.push(`${prefix}.${field} 必须是字符串`); } }); // 验证布尔值字段 const booleanFields = ['productionSourceMap', 'productionGzip', 'bundleAnalyzerReport']; booleanFields.forEach((field) => { if (buildConfig[field] !== undefined && typeof buildConfig[field] !== 'boolean') { this.errors.push(`${prefix}.${field} 必须是布尔值`); } }); // 验证 productionGzipExtensions if (buildConfig.productionGzipExtensions !== undefined) { if (!Array.isArray(buildConfig.productionGzipExtensions)) { this.errors.push(`${prefix}.productionGzipExtensions 必须是数组`); } } } /** * 验证 envParams 配置 * @private */ _validateEnvParams(envParams) { if (!envParams) return; if (typeof envParams !== 'object') { this.errors.push('envParams 必须是对象'); return; } // 验证每个环境的参数 Object.keys(envParams).forEach((env) => { if (typeof envParams[env] !== 'object') { this.errors.push(`envParams.${env} 必须是对象`); } }); } /** * 验证端口号 * @private */ _validatePort(port, fieldName) { const portNum = parseInt(port, 10); if (isNaN(portNum)) { this.errors.push(`${fieldName} 必须是数字`); return; } if (portNum < 1 || portNum > 65535) { this.errors.push(`${fieldName} 必须在 1-65535 之间`); return; } if (portNum < 1024 && process.platform === 'win32') { this.warnings.push( `${fieldName} (${portNum}) 小于 1024,在 Windows 系统上可能需要管理员权限。` ); } } /** * 获取错误列表 * @returns {Array} */ getErrors() { return this.errors; } /** * 获取警告列表 * @returns {Array} */ getWarnings() { return this.warnings; } /** * 是否有错误 * @returns {boolean} */ hasErrors() { return this.errors.length > 0; } /** * 是否有警告 * @returns {boolean} */ hasWarnings() { return this.warnings.length > 0; } } /** * 验证配置(便捷方法) * @param {Object} config - 配置对象 * @returns {Object} 验证后的配置 */ function validateConfig(config) { const validator = new ConfigValidator(); return validator.validate(config); } module.exports = { ConfigValidator, validateConfig };