UNPKG

@honor-minigame/cli

Version:

honor minigame pack cli

268 lines (256 loc) 8.93 kB
import path from 'path' import CopyPlugin from 'copy-webpack-plugin' import ZipWebpackPlugin from '../plugins/webpack-zip-plugin.js' import MainWebpackPlugin from '../plugins/webpack-main-plugin.js' import { merge } from 'webpack-merge' import { info, error } from '../../utils/logger.js' import { update } from '../lib/manifest.js' import * as paths from '../lib/paths.js' import exConfig from '../lib/exConfig.js' import { FILE_EXT_IGNORES, MANIFEST, ENTRY_NAME, FILE_EXT } from '../lib/constanst.js' import prettyjson from 'prettyjson' import NodePolyfillPlugin from 'node-polyfill-webpack-plugin' import prettierCode from './pretty-loader.js' import TerserPlugin from 'terser-webpack-plugin' import webpackConfig from './webpack.config.js' import { require } from '../lib/require.js' /** * 基于给定的参数生成Webpack配置 * * @param {boolean} isRelease - 是否为发布模式 * @param {Object} entry - 入口文件配置 * @param {boolean} amd - 是否支持AMD模块 * @returns {Object} 生成的Webpack配置对象 */ export function base(isRelease, entry, amd, aliases = {}) { const ignoreRes = [] const config = merge(webpackConfig, { mode: isRelease ? 'none' : 'development', optimization: { minimize: isRelease ? true : false, minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: false // 可选:设置为 true 可以移除 console 命令 }, format: { beautify: isRelease ? false : true, // 设置为 true 会输出格式良好的代码 comments: true // 若需要保留注释,设置为 true 或 'all',保留注释,否则Unity分包加载失败 } }, extractComments: false // 若不希望将注释提取到单独的文件中,设置为 false }) ] }, context: paths.PROJECT_PATH, output: { path: paths.BUILD, }, module: { rules: [ { test: /\.js$/, include: [paths.SRC], exclude: (filePath) => { return ( filePath.includes('node_modules') || filePath.includes('@babel') ) } }, { test: /^(?!.*\.(js|json)$).*$/i, use: [ { loader: require.resolve('file-loader'), options: { context: paths.SRC, esModule: false, name(resourcePath, resourceQuery) { return '[path][name].[ext]' } } } ] } ], exprContextRecursive: true, unknownContextCritical: true, unknownContextRecursive: true, wrappedContextRecursive: true, noParse: /\.json$/ }, resolve: { extensions: FILE_EXT, modules: [ paths.SRC, 'node_modules' ], preferRelative: true, alias: { // 如果有动态导入的模块,可以使用该方法添加 // '@qgame/adapter': require.resolve('@qgame/adapter', { paths: [__dirname] }) } }, devtool: isRelease ? false : 'cheap-module-source-map', stats: 'errors-only', entry: Object.assign({ game: paths.ENTRY }, entry), plugins: [ new NodePolyfillPlugin(), new CopyPlugin({ patterns: [ { from: paths.SRC, // 使用 globOptions.ignore 替代旧版 ignore globOptions: { ignore: [ // 使用 glob 语法 **/* 匹配所有子目录 ...FILE_EXT_IGNORES.map(ext => `**/*${ext}`), ...ignoreRes.map(pattern => `**/${pattern}`) ] }, // transform 函数保持原有逻辑 transform(content, path) { if (path.includes(MANIFEST)) { // 更现代的写法替代 indexOf return update({ content, release: isRelease }) } return content } } ] }) ] }) // 若amd设置为false,则关闭amd配置,表示不支持amd define转换,默认为true if (!amd) { config.amd = false } // 合并分包 alias 规则,让分包的 main.js 能以相对根目录的路径 require game.js if (Object.keys(aliases).length) { config.resolve = merge(config.resolve || {}, { alias: aliases }) } // 对小游戏工程目录的webpack配置做postHook处理 exConfig(config, { context: paths.PROJECT_PATH, src: paths.SRC, build: paths.BUILD }) return config } /** * 原整包打包webpack配置生成 * * @param {string} entry - 入口文件路径 * @param {boolean} isRelease - 是否为发布模式 * @param {Object} externals - 外部依赖配置 * @param {boolean} inject - 是否注入分包模块的依赖 * @param {boolean} amd - 是否使用AMD模块 * @returns {Object} 合并后的Webpack配置对象 */ export function singleConfig(entry, isRelease, externals, inject, amd, aliases) { // 格式化微小相关的代码,便于调试和排查问题 prettierCode(paths.SRC) const baseConfig = base(isRelease, entry, amd, aliases) const injectPlugins = [] // 是否注入对分包模块的依赖,如果不注入,由CP用户注入,能力开放给CP用户 // const injectPlugins = inject ? [ // new InjectPlugin(() => { // 打原整包时,通过在主入口 src/game.js 注入其它分包的依赖,并结合externals来实现兼容 // return Object.keys(externals).reduce((acc, cur) => `require('${cur}');${acc}`, '') // }, { // entryName: ENTRY_NAME, // entryOrder: ENTRY_ORDER.Last // })] : [] // const compileStartTime = (new Date()).valueOf() return merge(baseConfig, { externals, plugins: [ new MainWebpackPlugin({ singlePackage: true }), ...injectPlugins ] }) } /** * 生成分包的Webpack配置对象 * * @param {string} entry - 入口文件路径 * @param {boolean} isRelease - 是否为发布版本 * @param {string} packageName - 包名称 * @param {Array} externals - 外部依赖 * @param {Array} subpackages - 子包配置 * @param {Object} manifest - 清单文件 * @param {string} buildType - 构建类型 * @param {boolean} amd - 是否使用AMD模块 * @returns {Object} 合并后的Webpack配置对象 */ export function multiConfig(entry, isRelease, packageName, externals, subpackages, manifest, buildType, amd, aliases) { const baseConfig = base(isRelease, entry, amd, aliases) const compileStartTime = (new Date()).valueOf() return merge(baseConfig, { externals, plugins: [ new MainWebpackPlugin({ singlePackage: false, subpackages }), new ZipWebpackPlugin({ isRelease, packageName, singlePackage: false, // 打新分包,为false必须提供subpackages subpackages, manifest, buildType, compileStartTime }) ] }) } /** * 检查webpack编译完成后是否有错误 * @param {Object} err webpack错误 * @param {Object} stats 编译状态 */ export function checkError(err, stats) { if (err) { // webpack错误,配置文件出错等 error(err.stack || err) if (err.details) { error(err.details) } return true } const statsJson = stats.toJson() if (stats.hasWarnings()) { // 编译警告, 只打印前十个警告 let warns = statsJson.warnings if (warns.length > 10) { warns = warns.splice(0, 10) info('编译结果warnings超过10个,只打印前10个!!!', statsJson.warnings.length) } const warnings = warns.map((warning) => { const { moduleIdentifier, moduleName, loc, message } = warning return { moduleIdentifier, moduleName, loc, message } }) info(prettyjson.render(warnings)) } if (stats.hasErrors()) { // 编译错误,譬如缺失Module,语法错误等 const errors = statsJson.errors.map((er) => { const { moduleIdentifier, moduleName, loc, message, stack } = er let errMsg = message if (!message) { errMsg = er } // 找不到模块代码不影响rpk打包 if (errMsg.includes('Module not found')) { const tip = `【不影响rpk打包】请检查对应模块 ${moduleName}下的代码!` info(prettyjson.render({ moduleIdentifier, moduleName, loc, errMsg, tip })) } else { return { moduleIdentifier, moduleName, loc, errMsg, stack } } }).filter(Boolean) if (errors.length) { error(prettyjson.render(errors)) return true } } }