1/* 2 * Copyright (c) 2021 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 16var path = require('path') 17var fs = require('fs') 18 19var ResourcePlugin = require('./lib/resource-plugin') 20var ResultStates = require('./lib/compile-plugin') 21var GenBinPlugin = require('./lib/genBin-plugin') 22var GenAbcPlugin = require('./lib/genAbc-plugin').GenAbcPlugin 23var AfterEmitPlugin = require('./lib/cardJson-plugin').AfterEmitPlugin 24const ReadJsonPlugin = require('./lib/read-json-plugin') 25 26const { PLATFORM }= require('./lib/lite/lite-enum') 27const util = require('./lib/util') 28const TerserPlugin = require('terser-webpack-plugin') 29const CopyPlugin = require("copy-webpack-plugin") 30const webpack = require('webpack') 31let watchMode = (process.env.watchMode && process.env.watchMode === 'true') || false 32const { 33 deleteFolderRecursive, 34 readManifest, 35 loadEntryObj, 36 hashProjectPath, 37 checkMultiResourceBuild, 38 readWorkerFile, 39 compareCache, 40 parseAbilityName 41} = require('./main.product') 42 43const richModule = { 44 rules: [ 45 { 46 test: /\.visual$/, 47 use: [{ 48 loader: path.resolve(__dirname, './lib/loader-gen.js') 49 }] 50 }, 51 { 52 test: /(\.hml)(\?[^?]+)?$/, 53 use: [{ 54 loader: path.resolve(__dirname, './index.js') 55 }] 56 }, 57 { 58 test: /\.png$/, 59 use: [{ 60 loader: 'file-loader', 61 options: { 62 name: '[name].[ext]', 63 outputPath: 'common' 64 } 65 }] 66 }, 67 { 68 test: /\.css$/, 69 use: [{ 70 loader: 'css-loader' 71 }] 72 }, 73 { 74 test: /\.less$/, 75 use: [{ 76 loader: 'less-loader' 77 }] 78 }, 79 { 80 test: /\.(scss|sass)$/, 81 use: [{ 82 loader: 'style-loader!css-loader!sass-loader' 83 }] 84 }, 85 { 86 test: /\.jsx?$/, 87 type: 'javascript/auto', 88 use: [ 89 { 90 loader: path.resolve(__dirname, 'lib/module-script.js') 91 }, 92 { 93 loader: util.loadBabelModule('babel-loader'), 94 options: { 95 presets: [util.loadBabelModule('@babel/preset-env')], 96 targets: 'node 8', 97 plugins: [ 98 [ 99 util.loadBabelModule('@babel/plugin-transform-modules-commonjs'), 100 {'allowTopLevelThis': true} 101 ], 102 util.loadBabelModule('@babel/plugin-proposal-class-properties') 103 ], 104 compact: false 105 } 106 } 107 ] 108 } 109 ] 110} 111 112const cardModule = { 113 rules: [ 114 { 115 test: /\.visual$/, 116 use: [{ 117 loader: path.resolve(__dirname, './lib/loader-gen.js') 118 }] 119 }, 120 { 121 test: /\.hml$/, 122 use: [{ 123 loader: path.resolve(__dirname, './lib/card-loader.js') 124 }] 125 }, 126 { 127 test: /\.css$/, 128 use: [{ 129 loader: 'css-loader' 130 }] 131 }, 132 { 133 test: /\.less$/, 134 use: [{ 135 loader: 'less-loader' 136 }] 137 }, 138 { 139 test: /\.(scss|sass)$/, 140 use: [{ 141 loader: 'css-loader!sass-loader' 142 }] 143 }, 144 { 145 test: /\.jsx?$/, 146 type: 'javascript/auto', 147 use: [ 148 { 149 loader: path.resolve(__dirname, 'lib/module-script.js') 150 }, 151 { 152 loader: util.loadBabelModule('babel-loader'), 153 options: { 154 presets: [util.loadBabelModule('@babel/preset-env')], 155 targets: 'node 8', 156 plugins: [util.loadBabelModule('@babel/plugin-transform-modules-commonjs'), 157 util.loadBabelModule('@babel/plugin-proposal-class-properties')], 158 compact: false 159 } 160 } 161 ] 162 } 163 ] 164} 165 166let config = { 167 cache: { 168 type: 'filesystem' 169 }, 170 watch: watchMode, 171 watchOptions: { 172 aggregateTimeout: 10, 173 poll: false, 174 ignored: ["**/node_modules", "**/oh_modules", "**/*.json~"] 175 }, 176 177 output: { 178 filename: '[name].js', 179 devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]', 180 globalObject: 'globalThis' 181 }, 182 devtool: 'nosources-source-map', 183 mode: 'development', 184 module: richModule, 185 node: { 186 global: false 187 }, 188 stats: 'none' 189} 190 191function setConfigs(env) { 192 if (process.env.aceModuleJsonPath || env.aceModuleJsonPath) { 193 process.env.compileMode = 'moduleJson'; 194 } 195 process.env.error = env.error === undefined ? true : env.error 196 process.env.warning = env.warning === undefined ? true : env.warning 197 process.env.note = env.note === undefined ? true : env.note 198 process.env.buildMode = env.buildMode || 'debug' 199 process.env.logLevel = env.logLevel || '1' 200 process.env.isPreview = env.isPreview || false 201 process.env.projectPath = env.aceModuleRoot || process.env.aceModuleRoot || process.cwd(); 202 hashProjectPath(process.env.projectPath); 203 process.env.buildPath = env.aceModuleBuild || process.env.aceModuleBuild || 204 path.resolve(process.env.projectPath, 'build'); 205 process.env.cachePath = env.cachePath || process.env.cachePath || path.resolve(__dirname, 'node_modules/.cache'); 206 process.env.aceManifestPath = process.env.aceManifestPath || path.resolve(process.env.projectPath, 'manifest.json'); 207 process.env.abilityType = process.env.abilityType || 'page'; 208 process.env.DEVICE_LEVEL = env.DEVICE_LEVEL || process.env.DEVICE_LEVEL || 'rich'; 209 process.env.aceModuleJsonPath = env.aceModuleJsonPath || process.env.aceModuleJsonPath; 210 process.env.aceProfilePath = env.aceProfilePath || process.env.aceProfilePath; 211 process.env.watchCSSFiles = process.env.watchCSSFiles || path.resolve(process.env.cachePath, '.rich_cache', 'preview_css.json'); 212 watchMode = (process.env.watchMode && process.env.watchMode === 'true') || 213 (env.watchMode && env.watchMode === 'true') || false; 214 if (process.env.abilityType === 'page' || process.env.abilityType === 'form') { 215 const manifest = readManifest(process.env.aceManifestPath) 216 if (process.env.compileMode !== 'moduleJson') { 217 process.env.DEVICE_LEVEL = manifest.type === 'form' ? 'card' : 'rich' 218 } 219 process.env.PLATFORM_VERSION = PLATFORM.VERSION6; 220 const version = parseInt(manifest.minPlatformVersion); 221 if (version == 5) { 222 process.env.PLATFORM_VERSION = PLATFORM.VERSION5; 223 } 224 if (version <= 4) { 225 process.env.PLATFORM_VERSION = PLATFORM.VERSION3; 226 } 227 } 228 process.env.aceBuildJson = env.aceBuildJson || process.env.aceBuildJson; 229 checkMultiResourceBuild(process.env.aceBuildJson); 230} 231 232function setArkPlugin(env, workerFile) { 233 if (env.isPreview === "true" || env.compilerType && env.compilerType === 'ark') { 234 let arkDir = path.join(__dirname, 'bin', 'ark'); 235 if (env.arkFrontendDir) { 236 arkDir = env.arkFrontendDir; 237 } 238 let nodeJs = 'node'; 239 if (env.nodeJs) { 240 nodeJs = env.nodeJs; 241 } 242 config.plugins.push(new GenAbcPlugin(process.env.buildPath, arkDir, nodeJs, workerFile, 243 env.buildMode === 'debug')) 244 if (env.buildMode === 'release') { 245 config.output.path = path.join(process.env.cachePath, "releaseAssets", 246 path.basename(process.env.buildPath)); 247 process.env.configOutput = config.output.path; 248 } 249 } else { 250 if (env.deviceType) { 251 let deviceArr = env.deviceType.split(/,/) 252 let isDefault = deviceArr.indexOf('tv') >= 0 || deviceArr.indexOf('wearable') >= 0 ? true : false 253 if (isDefault) { 254 config.plugins.push(new GenBinPlugin(process.env.buildPath, path.join(__dirname, 'bin', workerFile))) 255 } 256 } 257 } 258} 259 260function existsPackageJson(config, rootPackageJsonPath, modulePackageJsonPath) { 261 if (config.cache) { 262 config.cache.buildDependencies = { 263 config: [] 264 }; 265 if (fs.existsSync(rootPackageJsonPath)) { 266 config.cache.buildDependencies.config.push(rootPackageJsonPath); 267 } 268 if (fs.existsSync(modulePackageJsonPath)) { 269 config.cache.buildDependencies.config.push(modulePackageJsonPath); 270 } 271 } 272} 273 274function excludeWorker(workerFile, name) { 275 if (workerFile) { 276 return Object.keys(workerFile).includes(name); 277 } 278 return /^\.\/workers\//.test(name); 279} 280 281module.exports = (env) => { 282 setConfigs(env); 283 compareCache(path.resolve(process.env.cachePath, '.rich_cache')); 284 const workerFile = readWorkerFile(); 285 if (process.env.compileMode === 'moduleJson') { 286 process.env.DEVICE_LEVEL = 'card'; 287 config.entry = {}; 288 } else { 289 deleteFolderRecursive(process.env.buildPath); 290 config.entry = loadEntryObj(process.env.projectPath, process.env.DEVICE_LEVEL, 291 process.env.abilityType, process.env.aceManifestPath); 292 existsPackageJson(config, path.resolve(process.env.projectPath, '../../../../../package.json'), 293 path.resolve(process.env.projectPath, '../../../../package.json')); 294 } 295 config.cache.cacheDirectory = path.resolve(process.env.cachePath, '.rich_cache', 296 path.basename(process.env.projectPath)); 297 config.output.path = path.resolve(__dirname, process.env.buildPath) 298 config.plugins = [ 299 new ResourcePlugin(process.env.projectPath, process.env.buildPath, 300 process.env.aceManifestPath, process.env.watchCSSFiles, workerFile), 301 new ResultStates({ 302 build: process.env.buildPath 303 }), 304 new webpack.DefinePlugin({ 305 STANDARD: JSON.stringify(true), 306 LITE: JSON.stringify(false) 307 }) 308 ] 309 config.resolve = { 310 modules: [ 311 process.env.projectPath, 312 path.join(process.env.projectPath, '../../../../../'), 313 path.join(__dirname, 'node_modules'), 314 './node_modules', 315 './oh_modules' 316 ], 317 descriptionFiles: ['package.json', 'oh-package.json5'], 318 plugins: [new ReadJsonPlugin()], 319 } 320 if (fs.existsSync(path.resolve(process.env.projectPath, 'i18n'))) { 321 config.plugins.push(new CopyPlugin({ 322 patterns: [ 323 { 324 from: path.resolve(process.env.projectPath, 'i18n'), 325 to: path.resolve(process.env.buildPath, 'i18n'), 326 noErrorOnMissing: true 327 } 328 ] 329 })) 330 } 331 if (process.env.aceConfigPath && fs.existsSync(process.env.aceConfigPath)) { 332 config.plugins.push(new CopyPlugin({ 333 patterns: [ 334 { 335 from: path.resolve(process.env.aceConfigPath), 336 to: path.resolve(process.env.buildPath, 'config.json'), 337 noErrorOnMissing: true 338 } 339 ] 340 })) 341 } 342 if (process.env.DEVICE_LEVEL === 'card') { 343 config.module = cardModule 344 config.plugins.push(new AfterEmitPlugin()) 345 setArkPlugin(env, workerFile); 346 } else { 347 if (process.env.compileMode !== 'moduleJson' && process.env.abilityType === 'page') { 348 config.optimization = { 349 splitChunks: { 350 chunks(chunk) { 351 return !excludeWorker(workerFile, chunk.name) && !/^\.\/TestAbility/.test(chunk.name); 352 }, 353 minSize: 0, 354 cacheGroups: { 355 vendors: { 356 test: /[\\/](node|oh)_modules[\\/]/, 357 priority: 20, 358 name: "vendors", 359 }, 360 commons: { 361 test: /\.js|css|hml$/, 362 name: 'commons', 363 priority: 10, 364 minChunks: 2, 365 } 366 } 367 }, 368 } 369 } 370 setArkPlugin(env, workerFile); 371 if (env.sourceMap === 'none') { 372 config.devtool = false 373 } 374 if (env.buildMode === 'release') { 375 config.mode = 'production' 376 if (process.env.compileMode !== 'moduleJson' && process.env.abilityType === 'page') { 377 config.optimization = config.optimization; 378 } else { 379 config.optimization = {}; 380 } 381 Object.assign(config.optimization, { 382 minimize: true, 383 minimizer: [new TerserPlugin({ 384 terserOptions: { 385 compress: { 386 defaults: false, 387 dead_code: true, 388 collapse_vars: true, 389 unused: true, 390 drop_debugger: true, 391 if_return: true, 392 reduce_vars: true, 393 join_vars: false, 394 sequences: 0 395 }, 396 format: { 397 semicolons: false, 398 beautify: true, 399 braces: true, 400 indent_level: 2 401 } 402 } 403 })] 404 }) 405 config.output.devtoolModuleFilenameTemplate = (info) => { 406 return `webpack:///${info.absoluteResourcePath.replace(process.env.projectRootPath, '')}`; 407 } 408 config.output.sourceMapFilename = '_releaseMap/[name].js.map' 409 } 410 } 411 if (process.env.abilityType === 'testrunner') { 412 config.module.rules = []; 413 config.module.rules.unshift({ 414 test: /TestRunner/, 415 use: [{ 416 loader: path.resolve(__dirname, './index.js') 417 }] 418 }) 419 } else { 420 config.module.rules.unshift({ 421 test: parseAbilityName(process.env.abilityType, process.env.projectPath), 422 use: [{ 423 loader: path.resolve(__dirname, './index.js') 424 }] 425 }) 426 } 427 428 config.output.library = process.env.hashProjectPath; 429 return config 430} 431