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 optimization: { 177 moduleIds: 'deterministic', 178 chunkIds: 'deterministic', 179 }, 180 output: { 181 filename: '[name].js', 182 pathinfo: false, 183 devtoolModuleFilenameTemplate: (info) => { 184 const newInfo = info.absoluteResourcePath.replace(process.env.projectRootPath + path.sep, '') 185 .replace(process.env.projectRootPath + path.sep, '') 186 .replace(path.join(__dirname, path.sep), ''); 187 return newInfo; 188 }, 189 globalObject: 'globalThis' 190 }, 191 devtool: 'nosources-source-map', 192 mode: 'development', 193 module: richModule, 194 node: { 195 global: false 196 }, 197 stats: 'none' 198} 199 200function setConfigs(env) { 201 if (process.env.aceModuleJsonPath || env.aceModuleJsonPath) { 202 process.env.compileMode = 'moduleJson'; 203 } 204 process.env.error = env.error === undefined ? true : env.error 205 process.env.warning = env.warning === undefined ? true : env.warning 206 process.env.note = env.note === undefined ? true : env.note 207 process.env.buildMode = env.buildMode || 'debug' 208 process.env.logLevel = env.logLevel || '1' 209 process.env.isPreview = env.isPreview || false 210 process.env.projectPath = env.aceModuleRoot || process.env.aceModuleRoot || process.cwd(); 211 hashProjectPath(process.env.projectPath); 212 process.env.buildPath = env.aceModuleBuild || process.env.aceModuleBuild || 213 path.resolve(process.env.projectPath, 'build'); 214 process.env.cachePath = env.cachePath || process.env.cachePath || path.resolve(__dirname, 'node_modules/.cache'); 215 process.env.aceManifestPath = process.env.aceManifestPath || path.resolve(process.env.projectPath, 'manifest.json'); 216 process.env.abilityType = process.env.abilityType || 'page'; 217 process.env.DEVICE_LEVEL = env.DEVICE_LEVEL || process.env.DEVICE_LEVEL || 'rich'; 218 process.env.aceModuleJsonPath = env.aceModuleJsonPath || process.env.aceModuleJsonPath; 219 process.env.aceProfilePath = env.aceProfilePath || process.env.aceProfilePath; 220 process.env.watchCSSFiles = process.env.watchCSSFiles || path.resolve(process.env.cachePath, '.rich_cache', 'preview_css.json'); 221 watchMode = (process.env.watchMode && process.env.watchMode === 'true') || 222 (env.watchMode && env.watchMode === 'true') || false; 223 if (process.env.abilityType === 'page' || process.env.abilityType === 'form') { 224 const manifest = readManifest(process.env.aceManifestPath) 225 if (process.env.compileMode !== 'moduleJson') { 226 process.env.DEVICE_LEVEL = manifest.type === 'form' ? 'card' : 'rich' 227 } 228 process.env.PLATFORM_VERSION = PLATFORM.VERSION6; 229 const version = parseInt(manifest.minPlatformVersion); 230 if (version == 5) { 231 process.env.PLATFORM_VERSION = PLATFORM.VERSION5; 232 } 233 if (version <= 4) { 234 process.env.PLATFORM_VERSION = PLATFORM.VERSION3; 235 } 236 } 237 process.env.aceBuildJson = env.aceBuildJson || process.env.aceBuildJson; 238 checkMultiResourceBuild(process.env.aceBuildJson); 239} 240 241function setArkPlugin(env, workerFile) { 242 if (env.isPreview === "true" || env.compilerType && env.compilerType === 'ark') { 243 let arkDir = path.join(__dirname, 'bin', 'ark'); 244 if (env.arkFrontendDir) { 245 arkDir = env.arkFrontendDir; 246 } 247 let nodeJs = 'node'; 248 if (env.nodeJs) { 249 nodeJs = env.nodeJs; 250 } 251 config.plugins.push(new GenAbcPlugin(process.env.buildPath, arkDir, nodeJs, workerFile, 252 env.buildMode === 'debug')) 253 if (env.buildMode === 'release') { 254 config.output.path = path.join(process.env.cachePath, "releaseAssets", 255 path.basename(process.env.buildPath)); 256 process.env.configOutput = config.output.path; 257 } 258 } else { 259 if (env.deviceType) { 260 let deviceArr = env.deviceType.split(/,/) 261 let isDefault = deviceArr.indexOf('tv') >= 0 || deviceArr.indexOf('wearable') >= 0 ? true : false 262 if (isDefault) { 263 config.plugins.push(new GenBinPlugin(process.env.buildPath, path.join(__dirname, 'bin', workerFile))) 264 } 265 } 266 } 267} 268 269function existsPackageJson(config, rootPackageJsonPath, modulePackageJsonPath) { 270 if (config.cache) { 271 config.cache.buildDependencies = { 272 config: [] 273 }; 274 if (fs.existsSync(rootPackageJsonPath)) { 275 config.cache.buildDependencies.config.push(rootPackageJsonPath); 276 } 277 if (fs.existsSync(modulePackageJsonPath)) { 278 config.cache.buildDependencies.config.push(modulePackageJsonPath); 279 } 280 } 281} 282 283function excludeWorker(workerFile, name) { 284 if (workerFile) { 285 return Object.keys(workerFile).includes(name); 286 } 287 return /^\.\/workers\//.test(name); 288} 289 290module.exports = (env) => { 291 setConfigs(env); 292 compareCache(path.resolve(process.env.cachePath, '.rich_cache')); 293 const workerFile = readWorkerFile(); 294 if (process.env.compileMode === 'moduleJson') { 295 process.env.DEVICE_LEVEL = 'card'; 296 config.entry = {}; 297 } else { 298 deleteFolderRecursive(process.env.buildPath); 299 config.entry = loadEntryObj(process.env.projectPath, process.env.DEVICE_LEVEL, 300 process.env.abilityType, process.env.aceManifestPath); 301 existsPackageJson(config, path.resolve(process.env.projectPath, '../../../../../package.json'), 302 path.resolve(process.env.projectPath, '../../../../package.json')); 303 } 304 config.cache.cacheDirectory = path.resolve(process.env.cachePath, '.rich_cache', 305 path.basename(process.env.projectPath)); 306 config.output.path = path.resolve(__dirname, process.env.buildPath) 307 config.plugins = [ 308 new ResourcePlugin(process.env.projectPath, process.env.buildPath, 309 process.env.aceManifestPath, process.env.watchCSSFiles, workerFile), 310 new ResultStates({ 311 build: process.env.buildPath 312 }), 313 new webpack.DefinePlugin({ 314 STANDARD: JSON.stringify(true), 315 LITE: JSON.stringify(false) 316 }) 317 ] 318 config.resolve = { 319 modules: [ 320 process.env.projectPath, 321 path.join(process.env.projectPath, '../../../../../'), 322 path.join(__dirname, 'node_modules'), 323 './node_modules', 324 './oh_modules' 325 ], 326 descriptionFiles: ['package.json', 'oh-package.json5'], 327 plugins: [new ReadJsonPlugin()], 328 } 329 if (fs.existsSync(path.resolve(process.env.projectPath, 'i18n'))) { 330 config.plugins.push(new CopyPlugin({ 331 patterns: [ 332 { 333 from: path.resolve(process.env.projectPath, 'i18n'), 334 to: path.resolve(process.env.buildPath, 'i18n'), 335 noErrorOnMissing: true 336 } 337 ] 338 })) 339 } 340 if (process.env.aceConfigPath && fs.existsSync(process.env.aceConfigPath)) { 341 config.plugins.push(new CopyPlugin({ 342 patterns: [ 343 { 344 from: path.resolve(process.env.aceConfigPath), 345 to: path.resolve(process.env.buildPath, 'config.json'), 346 noErrorOnMissing: true 347 } 348 ] 349 })) 350 } 351 if (process.env.DEVICE_LEVEL === 'card') { 352 config.module = cardModule 353 config.plugins.push(new AfterEmitPlugin()) 354 config.optimization = {}; 355 setArkPlugin(env, workerFile); 356 } else { 357 if (process.env.compileMode !== 'moduleJson' && process.env.abilityType === 'page') { 358 Object.assign(config.optimization, { 359 splitChunks: { 360 chunks(chunk) { 361 return !excludeWorker(workerFile, chunk.name) && !/^\.\/TestAbility/.test(chunk.name); 362 }, 363 minSize: 0, 364 cacheGroups: { 365 vendors: { 366 test: /[\\/](node|oh)_modules[\\/]/, 367 priority: 20, 368 name: "vendors", 369 }, 370 commons: { 371 test: /\.js|css|hml$/, 372 name: 'commons', 373 priority: 10, 374 minChunks: 2, 375 } 376 } 377 }, 378 }); 379 } 380 setArkPlugin(env, workerFile); 381 if (env.sourceMap === 'none') { 382 config.devtool = false 383 } 384 if (env.buildMode === 'release') { 385 config.mode = 'production'; 386 Object.assign(config.optimization, { 387 minimize: true, 388 minimizer: [new TerserPlugin({ 389 terserOptions: { 390 compress: { 391 defaults: false, 392 dead_code: true, 393 collapse_vars: true, 394 unused: true, 395 drop_debugger: true, 396 if_return: true, 397 reduce_vars: true, 398 join_vars: false, 399 sequences: 0, 400 }, 401 format: { 402 semicolons: false, 403 beautify: true, 404 braces: true, 405 indent_level: 2, 406 }, 407 }, 408 })], 409 }); 410 config.output.devtoolModuleFilenameTemplate = (info) => { 411 const newInfo = info.absoluteResourcePath.replace(process.env.projectRootPath + path.sep, '') 412 .replace(process.env.projectRootPath + path.sep, '') 413 .replace(path.join(__dirname, path.sep), ''); 414 return newInfo; 415 } 416 config.output.sourceMapFilename = '_releaseMap/[name].js.map' 417 } 418 } 419 if (process.env.abilityType === 'testrunner') { 420 config.module.rules = []; 421 config.module.rules.unshift({ 422 test: /TestRunner/, 423 use: [{ 424 loader: path.resolve(__dirname, './index.js') 425 }] 426 }) 427 } else { 428 config.module.rules.unshift({ 429 test: parseAbilityName(process.env.abilityType, process.env.projectPath), 430 use: [{ 431 loader: path.resolve(__dirname, './index.js') 432 }] 433 }) 434 } 435 436 config.output.library = process.env.hashProjectPath; 437 return config 438} 439