1/* 2 * Copyright (c) 2020 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const path = require('path'); 17const fs = require('fs'); 18const CopyPlugin = require('copy-webpack-plugin'); 19const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 20const Webpack = require('webpack'); 21const { GenAbcPlugin } = require('./lib/gen_abc_plugin'); 22const buildPipeServer = require('./server/build_pipe_server'); 23 24const { 25 projectConfig, 26 loadEntryObj, 27 readAppResource, 28 loadWorker, 29 abilityConfig, 30 readWorkerFile, 31 checkAppResourcePath, 32 addSDKBuildDependencies 33} = require('./main'); 34const { ResultStates } = require('./lib/compile_info'); 35const { processUISyntax } = require('./lib/process_ui_syntax'); 36const { IGNORE_ERROR_CODE } = require('./lib/utils'); 37const { BUILD_SHARE_PATH } = require('./lib/pre_define'); 38 39process.env.watchMode = (process.env.watchMode && process.env.watchMode === 'true') || 'false'; 40 41function initConfig(config) { 42 const projectPath = path.resolve(projectConfig.projectPath); 43 Object.assign(config, { 44 entry: projectConfig.entryObj, 45 watch: process.env.watchMode === 'true', 46 watchOptions: { 47 aggregateTimeout: 10, 48 poll: false, 49 ignored: /node_modules/ 50 }, 51 output: { 52 path: path.resolve(__dirname, projectConfig.buildPath), 53 filename: '[name].js', 54 devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]', 55 globalObject: 'globalThis' 56 }, 57 devtool: 'nosources-source-map', 58 mode: 'development', 59 module: { 60 rules: [ 61 { 62 test:/\.(jpg|png|gif|jpeg|mp3|mp4|svg)$/, 63 use:{ 64 loader:'url-loader' 65 } 66 }, 67 { 68 test: /\.d\.ts/, 69 loader: 'ignore-loader' 70 }, 71 { 72 test: /(?<!\.d)\.(ets|ts)$/, 73 use: [ 74 { loader: path.resolve(__dirname, 'lib/result_process.js') }, 75 { 76 loader: 'ts-loader', 77 options: { 78 reportFiles: ['*.js'], 79 onlyCompileBundledFiles: true, 80 transpileOnly: true, 81 configFile: path.resolve(__dirname, 'tsconfig.json'), 82 getCustomTransformers(program) { 83 return { 84 before: [processUISyntax(program)], 85 after: [] 86 }; 87 }, 88 ignoreDiagnostics: IGNORE_ERROR_CODE 89 } 90 }, 91 { loader: path.resolve(__dirname, 'lib/pre_process.js') } 92 ] 93 }, 94 setJsConfigRule() 95 ] 96 }, 97 node: { 98 global: false 99 }, 100 resolve: { 101 extensions: ['.js', '.ets', '.ts', '.d.ts'], 102 modules: [ 103 projectPath, 104 './node_modules', 105 path.join(__dirname, 'node_modules'), 106 path.join(__dirname, '../../api') 107 ] 108 }, 109 stats: { preset: 'none' }, 110 plugins: [ 111 new Webpack.WatchIgnorePlugin({ 112 paths: [ 113 /\.d\.ts$/ 114 ] 115 }), 116 new ResultStates() 117 ] 118 }); 119 if (!projectConfig.xtsMode) { 120 config.cache = { 121 type: "filesystem", 122 cacheDirectory: path.resolve(projectConfig.cachePath, '.ets_cache', 123 path.basename(projectConfig.projectPath)) 124 }; 125 } 126 if (!projectConfig.aceModuleJsonPath) { 127 config.resolve.modules.push(path.join(projectPath, '../../../../../')); 128 config.resolve.modules.push(path.resolve(projectPath, '../../../../node_modules')); 129 config.resolve.modules.push(path.resolve(projectPath, '../../../../../node_modules')); 130 config.resolve.modules.push(path.resolve(projectPath, '../../')); 131 existsPackageJson(config, path.resolve(projectPath, '../../../../../package.json'), 132 path.resolve(projectPath, '../../../../package.json')); 133 } else { 134 config.resolve.modules.push(path.join(projectPath, '../../../../')); 135 config.resolve.modules.push(path.resolve(projectPath, '../../../node_modules')); 136 config.resolve.modules.push(path.resolve(projectPath, '../../../../node_modules')); 137 config.resolve.modules.push(path.resolve(projectPath, '../')); 138 existsPackageJson(config, path.resolve(projectPath, '../../../../package.json'), 139 path.resolve(projectPath, '../../../package.json')); 140 } 141} 142 143function setJsConfigRule() { 144 const jsRule = { 145 test: /\.js$/, 146 use: [ 147 { 148 loader: 'babel-loader', 149 options: { 150 plugins: ['@babel/plugin-proposal-class-properties'], 151 compact: false 152 } 153 }, 154 { loader: path.resolve(__dirname, 'lib/process_system_module.js') } 155 ] 156 }; 157 if (projectConfig.compileMode !== 'esmodule') { 158 jsRule.type = 'javascript/auto'; 159 jsRule.use[0].options.plugins.unshift([ 160 '@babel/plugin-transform-modules-commonjs', 161 { 162 'allowTopLevelThis': true 163 } 164 ]); 165 } 166 return jsRule; 167} 168 169function existsPackageJson(config, rootPackageJsonPath, modulePackageJsonPath) { 170 if (config.cache) { 171 config.cache.buildDependencies = { 172 config: [] 173 }; 174 if (fs.existsSync(rootPackageJsonPath)) { 175 config.cache.buildDependencies.config.push(rootPackageJsonPath); 176 } 177 if (fs.existsSync(modulePackageJsonPath)) { 178 config.cache.buildDependencies.config.push(modulePackageJsonPath); 179 } 180 } 181} 182 183function setProjectConfig(envArgs) { 184 const args = Object.keys(envArgs); 185 if (args.indexOf('projectName') === args.length - 2) { 186 projectConfig.projectPath = path.join(process.cwd(), args[args.length - 1]); 187 } 188 if (envArgs.aceModuleRoot) { 189 projectConfig.projectPath = envArgs.aceModuleRoot; 190 } 191 if (envArgs.aceModuleBuild) { 192 projectConfig.buildPath = envArgs.aceModuleBuild; 193 } 194 if (envArgs.aceManifestPath) { 195 projectConfig.manifestFilePath = envArgs.aceManifestPath; 196 } 197 if (envArgs.aceProfilePath) { 198 projectConfig.aceProfilePath = envArgs.aceProfilePath; 199 } 200 if (envArgs.aceModuleJsonPath) { 201 projectConfig.aceModuleJsonPath = envArgs.aceModuleJsonPath; 202 } 203 if (envArgs.cachePath) { 204 projectConfig.cachePath = envArgs.cachePath; 205 } 206} 207 208function setReleaseConfig(config) { 209 const TerserPlugin = require('terser-webpack-plugin'); 210 config.mode = 'production'; 211 if (process.env.compileMode !== 'moduleJson' && abilityConfig.abilityType === 'page') { 212 config.optimization = config.optimization; 213 } else { 214 config.optimization = {}; 215 } 216 Object.assign(config.optimization, { 217 emitOnErrors: true, 218 usedExports: false, 219 minimize: true, 220 minimizer: [new TerserPlugin({ 221 terserOptions: { 222 compress: { 223 defaults: false, 224 dead_code: true, 225 collapse_vars: true, 226 unused: true, 227 drop_debugger: true, 228 if_return: true, 229 reduce_vars: true, 230 join_vars: false, 231 sequences: 0 232 }, 233 format: { 234 semicolons: false, 235 beautify: true, 236 braces: true, 237 indent_level: 2 238 } 239 } 240 })] 241 }); 242 config.output.sourceMapFilename = '_releaseMap/[name].js.map'; 243 config.performance = { 244 hints: false 245 }; 246} 247 248function setCopyPluginConfig(config) { 249 const copyPluginPattrens = []; 250 copyPluginPattrens.push({ 251 from: '**/*', 252 context: path.resolve(__dirname, projectConfig.projectPath), 253 globOptions: { 254 ignore: [ 255 '**/*.ets', 256 '**/*.ts', 257 '**/*.js', 258 path.resolve(__dirname, projectConfig.buildPath, '**').replace(/\\/g, '/') 259 ] 260 }, 261 to: path.resolve(__dirname, projectConfig.buildPath), 262 noErrorOnMissing: true 263 }); 264 const sharePath = path.resolve(__dirname, projectConfig.projectPath, BUILD_SHARE_PATH); 265 if (fs.existsSync(sharePath)) { 266 copyPluginPattrens.push({ 267 from: '**/*', 268 context: path.resolve(__dirname, projectConfig.projectPath, BUILD_SHARE_PATH), 269 to: path.resolve(__dirname, projectConfig.buildPath, BUILD_SHARE_PATH), 270 globOptions: { 271 ignore: [ 272 '**/*.ets', 273 '**/*.ts', 274 '**/*.js', 275 ] 276 }, 277 noErrorOnMissing: true 278 }); 279 } 280 if (abilityConfig.abilityType === 'page') { 281 if (fs.existsSync(projectConfig.manifestFilePath)) { 282 copyPluginPattrens.push({ 283 from: projectConfig.manifestFilePath, 284 to: path.resolve(__dirname, projectConfig.buildPath) 285 }); 286 } else if (fs.existsSync(projectConfig.aceConfigPath)) { 287 copyPluginPattrens.push({ 288 from: projectConfig.aceConfigPath, 289 to: path.resolve(__dirname, projectConfig.buildPath) 290 }); 291 } 292 } 293 config.plugins.push(new CopyPlugin({ patterns: copyPluginPattrens })); 294} 295 296function excludeWorker(workerFile, name) { 297 if (workerFile) { 298 return Object.keys(workerFile).includes(name); 299 } 300 return /^\.\/workers\//.test(name); 301} 302 303function setOptimizationConfig(config, workerFile) { 304 if (process.env.compileMode !== 'moduleJson' && abilityConfig.abilityType === 'page') { 305 config.optimization = { 306 splitChunks: { 307 chunks(chunk) { 308 return !excludeWorker(workerFile, chunk.name) && !/^\.\/TestAbility/.test(chunk.name); 309 }, 310 minSize: 0, 311 cacheGroups: { 312 vendors: { 313 test: /[\\/]node_modules[\\/]/, 314 priority: -10, 315 name: "vendors", 316 }, 317 commons: { 318 name: 'commons', 319 priority: -20, 320 minChunks: 2, 321 reuseExistingChunk: true 322 } 323 } 324 } 325 } 326 } 327} 328 329function setCleanWebpackPlugin(workerFile, config) { 330 const cleanPath = []; 331 cleanPath.push(projectConfig.buildPath); 332 if (workerFile) { 333 const workerFilesPath = Object.keys(workerFile); 334 for (const workerFilePath of workerFilesPath) { 335 cleanPath.push(path.resolve(projectConfig.buildPath, workerFilePath, '..')); 336 } 337 } 338 339 config.plugins.push( 340 new CleanWebpackPlugin({ 341 dry: false, 342 dangerouslyAllowCleanPatternsOutsideProject: true, 343 cleanOnceBeforeBuildPatterns: cleanPath 344 }) 345 ); 346} 347 348module.exports = (env, argv) => { 349 const config = {}; 350 setProjectConfig(env); 351 loadEntryObj(projectConfig); 352 initConfig(config); 353 const workerFile = readWorkerFile(); 354 setOptimizationConfig(config, workerFile); 355 setCopyPluginConfig(config); 356 setCleanWebpackPlugin(workerFile, config); 357 if (env.isPreview !== "true") { 358 loadWorker(projectConfig, workerFile); 359 if (env.compilerType && env.compilerType === 'ark') { 360 let arkDir = path.join(__dirname, 'bin', 'ark'); 361 if (env.arkFrontendDir) { 362 arkDir = env.arkFrontendDir; 363 } 364 let nodeJs = 'node'; 365 if (env.nodeJs) { 366 nodeJs = env.nodeJs; 367 } 368 config.plugins.push(new GenAbcPlugin(projectConfig.buildPath, arkDir, nodeJs, 369 env.buildMode === 'debug')); 370 if (env.buildMode === 'release') { 371 config.output.path = path.join(projectConfig.cachePath, 'releaseAssets', 372 path.basename(projectConfig.buildPath)) 373 } 374 } 375 } else { 376 projectConfig.isPreview = true; 377 projectConfig.checkEntry = env.checkEntry; 378 let port; 379 process.argv.forEach((val, index) => { 380 if(val.startsWith('port=')){ 381 port = val.split('=')[1]; 382 } 383 }); 384 if (port) { 385 buildPipeServer.init(port); 386 } 387 } 388 389 if (env.sourceMap === 'none') { 390 config.devtool = false; 391 } 392 393 if (env.buildMode === 'release') { 394 setReleaseConfig(config); 395 } 396 397 const appResourcePath = env.appResource || process.env.appResource; 398 checkAppResourcePath(appResourcePath, config); 399 addSDKBuildDependencies(config); 400 config.output.library = projectConfig.hashProjectPath; 401 return config; 402}