• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}