• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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')
18var shell = require('shelljs');
19const crypto = require("crypto")
20
21const red = '\u001b[31m';
22const reset = '\u001b[39m';
23const multiResourceBuild = {};
24const systemModules = [];
25
26;(function readSystemModules() {
27  const systemModulesPath = path.resolve(__dirname,'../../api');
28  if (fs.existsSync(systemModulesPath)) {
29    systemModules.push(...fs.readdirSync(systemModulesPath));
30  }
31})();
32
33/**
34 * Delete files.
35 * @param {string} url The path of the file you want to delete.
36 */
37function deleteFolderRecursive(url) {
38  let files = [];
39  if (fs.existsSync(url)) {
40    files = fs.readdirSync(url);
41    files.forEach(function(file) {
42      const curPath = path.join(url, file);
43      if (fs.statSync(curPath).isDirectory()) {
44        deleteFolderRecursive(curPath);
45      } else {
46        fs.unlinkSync(curPath);
47      }
48    });
49    fs.rmdir(url, function(err) {});
50  }
51}
52
53/**
54 * Read manifest file for configuration information.
55 * @param {string} manifestFilePath Path to configuration file.
56 * @returns
57 */
58function readManifest(manifestFilePath) {
59  let manifest = {};
60  try {
61    if (fs.existsSync(manifestFilePath)) {
62      const jsonString = fs.readFileSync(manifestFilePath).toString();
63      manifest = JSON.parse(jsonString);
64    }
65  } catch (e) {
66    throw Error('\u001b[31m' + 'ERROR: the manifest.json file format is invalid.' +
67      '\u001b[39m').message;
68  }
69  return manifest;
70}
71
72/**
73 * Load pages from manifest infomation.
74 * @param {string} projectPath Current compiled file.
75 * @param {string} device_level Current device version.
76 * @param {string} abilityType Current Ability type.
77 * @param {string} manifestFilePath Path to configuration file.
78 * @returns {object} result of compiling the configuration infomation.
79 */
80function loadEntryObj(projectPath, device_level, abilityType, manifestFilePath) {
81  let entryObj = {};
82  switch (abilityType) {
83    case 'page':
84      const appJSPath = path.resolve(projectPath, 'app.js');
85      if (device_level === 'card') {
86        entryObj = addPageEntryObj(readManifest(manifestFilePath), projectPath);
87      } else {
88        if (!fs.existsSync(appJSPath)) {
89          throw Error(red + 'ERROR: missing app.js' + reset).message;
90        }
91        entryObj['./app'] = path.resolve(projectPath, './app.js?entry');
92      }
93      break;
94    case 'form':
95      entryObj = addPageEntryObj(readManifest(manifestFilePath), projectPath);
96      entryObj[`./${abilityType}`] =  path.resolve(projectPath, `./${abilityType}.js?entry`);
97      break;
98    case 'testrunner':
99      break;
100    default:
101      entryObj[`./${abilityType}`] =  path.resolve(projectPath, `./${abilityType}.js?entry`);
102      break;
103  }
104  return entryObj;
105}
106
107/**
108 * Read papes from manifest infomation.
109 * @param {object} manifest Configuration infomation.
110 * @param {string} projectPath Current compiled file.
111 * @returns {object} Pages in configuration infomation.
112 */
113function addPageEntryObj(manifest, projectPath) {
114  let entryObj = {};
115  const pages = manifest.pages;
116  if (pages === undefined) {
117    throw Error('ERROR: missing pages').message;
118  }
119  pages.forEach((element) => {
120    const sourcePath = element;
121    const hmlPath = path.join(projectPath, sourcePath + '.hml');
122    const aceSuperVisualPath = process.env.aceSuperVisualPath || '';
123    const visualPath = path.join(aceSuperVisualPath, sourcePath + '.visual');
124    const isHml = fs.existsSync(hmlPath);
125    const isVisual = fs.existsSync(visualPath);
126    if (isHml && isVisual) {
127      throw Error(red + 'ERROR: ' + sourcePath + ' cannot both have hml && visual').message;
128    } else if (isHml) {
129      entryObj['./' + element] = path.resolve(projectPath, './' + sourcePath + '.hml?entry');
130    } else if (isVisual) {
131      entryObj['./' + element] = path.resolve(aceSuperVisualPath, './' + sourcePath +
132        '.visual?entry');
133    }
134  })
135  return entryObj;
136}
137
138/**
139 * Match card mode.
140 * @param {object} env Collection of environmental variables.
141 */
142
143function compileCardModule(env) {
144  if (process.env.aceModuleJsonPath && fs.existsSync(process.env.aceModuleJsonPath)) {
145    const moduleJsonConfig = JSON.parse(fs.readFileSync(process.env.aceModuleJsonPath).toString());
146    if (moduleJsonConfig.module &&
147      (moduleJsonConfig.module.uiSyntax === 'ets' || moduleJsonConfig.module.language === 'ets')) {
148      process.env.DEVICE_LEVEL = 'card';
149    } else if (validateCardModule(moduleJsonConfig) && !process.env.compileCardModule) {
150      process.env.compileCardModule = true;
151      const cmd = `webpack --config webpack.rich.config.js --env compilerType=${env.compilerType} ` +
152        `DEVICE_LEVEL=card aceModuleRoot=${process.env.projectPath} ` +
153        `aceModuleJsonPath=${process.env.aceModuleJsonPath} aceProfilePath=${process.env.aceProfilePath} ` +
154        `watchMode=${process.env.watchMode} cachePath=${process.env.cachePath} ` +
155        `aceModuleBuild=${process.env.buildPath}`;
156      shell.exec(cmd, (err) => {
157        if (err) {
158          throw Error(err).message;
159        }
160      })
161    }
162  }
163}
164
165/**
166 * Determine whether the current compilation is in card mode.
167 * @param {object} moduleJsonConfig Configration for module.
168 * @returns
169 */
170function validateCardModule(moduleJsonConfig) {
171  if (moduleJsonConfig.module && moduleJsonConfig.module.extensionAbilities) {
172    for (let i = 0; i < moduleJsonConfig.module.extensionAbilities.length; i++) {
173      const extensionAbility = moduleJsonConfig.module.extensionAbilities[i];
174      if (extensionAbility.type && extensionAbility.type === 'form') {
175        return true;
176      }
177    }
178  }
179  return false;
180}
181
182/**
183 * Hash for projectPath.
184 * @param {string} projectPath Current compiled file.
185 */
186function hashProjectPath(projectPath) {
187  const hash = crypto.createHash('sha256')
188  hash.update(projectPath.toString())
189  process.env.hashProjectPath = "_" + hash.digest('hex');
190}
191
192/**
193 * Check if the current compilation requires multiple resource.
194 * @param {string} aceBuildJson Current compilation result path folder.
195 */
196function checkMultiResourceBuild(aceBuildJson) {
197  if (aceBuildJson && fs.existsSync(aceBuildJson)) {
198    try {
199      const content = JSON.parse(fs.readFileSync(aceBuildJson));
200      if (content["multiResourceBuild"]) {
201        multiResourceBuild.value = content["multiResourceBuild"]
202      }
203      if (content["projectRootPath"]) {
204        process.env.projectRootPath = content["projectRootPath"]
205      }
206    } catch (error) {
207    }
208  }
209}
210
211/**
212 * Read worker configuration information.
213 * @returns {object || null} Worker enttry pages.
214 */
215function readWorkerFile() {
216  const workerEntryObj = {};
217  if (process.env.aceBuildJson && fs.existsSync(process.env.aceBuildJson)) {
218    const workerConfig = JSON.parse(fs.readFileSync(process.env.aceBuildJson).toString());
219    if(workerConfig.workers) {
220      workerConfig.workers.forEach(worker => {
221        if (!/\.(js)$/.test(worker)) {
222          worker += '.js';
223        }
224        const relativePath = path.relative(process.env.projectPath, worker);
225        if (filterWorker(relativePath)) {
226          workerEntryObj[relativePath.replace(/\.(ts|js)$/,'').replace(/\\/g, '/')] = worker;
227        }
228      })
229      return workerEntryObj;
230    }
231  }
232  return null;
233}
234
235/**
236 * Regular for worker.
237 * @param {string} workerPath Path of worker.
238 * @returns {boolean} is pages.
239 */
240function filterWorker(workerPath) {
241  return /\.(ts|js)$/.test(workerPath);
242}
243
244/**
245 * Determine cache and decide whether to delete it.
246 * @param {string} cachePath Current compilation cache directory.
247 * @returns
248 */
249function compareCache(cachePath) {
250  const entryFile = path.join(cachePath, 'entry.json');
251  const cssFile = process.env.watchCSSFiles;
252
253  let files = [];
254  let cssObject = {};
255  if (fs.existsSync(cssFile)) {
256    cssObject = JSON.parse(fs.readFileSync(cssFile));
257    if (cssObject['clear'] === true) {
258      deleteFolderRecursive(cachePath);
259      return;
260    }
261    Object.keys(cssObject).forEach(key => {
262      if (key !== 'entry') {
263        files.push(key);
264      }
265    })
266  }
267
268  if (fs.existsSync(entryFile)) {
269    files = files.concat(JSON.parse(fs.readFileSync(entryFile)));
270  }
271
272  for(let file of files) {
273    if (!fs.existsSync(file)) {
274      deleteFolderRecursive(cachePath);
275      break;
276    } else if (cssObject['atime'] && cssObject['atime'][file]) {
277      if (cssObject['atime'][file] !== fs.statSync(file).atime.toString()) {
278        deleteFolderRecursive(cachePath);
279        break;
280      }
281    }
282  }
283}
284
285/**
286 * Parse ability filePath.
287 * @param {string} abilityType Current Ability type.
288 * @param {string} projectPath Current compiled file.
289 * @returns {string} ability filePath.
290 */
291function parseAbilityName(abilityType, projectPath) {
292  if (abilityType === 'page') {
293    return path.resolve(__dirname, projectPath, 'app.js');
294  } else {
295    return path.resolve(__dirname, projectPath, abilityType + '.js');
296  }
297}
298
299module.exports = {
300  deleteFolderRecursive: deleteFolderRecursive,
301  readManifest: readManifest,
302  loadEntryObj: loadEntryObj,
303  compileCardModule: compileCardModule,
304  hashProjectPath: hashProjectPath,
305  checkMultiResourceBuild: checkMultiResourceBuild,
306  multiResourceBuild: multiResourceBuild,
307  readWorkerFile: readWorkerFile,
308  compareCache: compareCache,
309  systemModules: systemModules,
310  parseAbilityName: parseAbilityName
311};