• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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
16import fs from 'fs';
17import path from 'path';
18
19import { ModuleMode } from './module_mode';
20import {
21  blue,
22  reset,
23  MODULES_ABC,
24  SOURCEMAPS,
25  SYMBOLMAP
26} from '../common/ark_define';
27import { isJsonSourceFile } from '../utils';
28import {
29  mkdirsSync,
30  toUnixPath,
31  validateFilePathLength
32} from '../../../utils';
33import {
34  createAndStartEvent,
35  stopEvent
36} from '../../../ark_utils';
37import { SourceMapGenerator } from '../generate_sourcemap';
38
39let isFirstBuild: boolean = true;
40
41export class ModuleHotreloadMode extends ModuleMode {
42  symbolMapFilePath: string;
43  constructor(rollupObject: Object) {
44    super(rollupObject);
45    if (this.projectConfig.oldMapFilePath) {
46      this.symbolMapFilePath = path.join(this.projectConfig.oldMapFilePath, SYMBOLMAP);
47    } else {
48      this.throwArkTsCompilerError('ArkTS:INTERNAL ERROR: Hotreload failed, ' +
49        'symbolMap file is not correctly configured.');
50    }
51  }
52
53  generateAbc(rollupObject: Object, parentEvent: Object): void {
54    if (isFirstBuild) {
55      this.compileAllFiles(rollupObject, parentEvent);
56      isFirstBuild = false;
57    } else {
58      this.compileChangeListFiles(rollupObject, parentEvent);
59    }
60  }
61
62  addHotReloadArgs() {
63    if (isFirstBuild) {
64      this.cmdArgs.push('--dump-symbol-table');
65      this.cmdArgs.push(`"${this.symbolMapFilePath}"`);
66      return;
67    }
68    this.addCacheFileArgs();
69    this.cmdArgs.push('--input-symbol-table');
70    this.cmdArgs.push(`"${this.symbolMapFilePath}"`);
71    this.cmdArgs.push('--hot-reload');
72  }
73
74  private compileAllFiles(rollupObject: Object, parentEvent: Object): void {
75    this.prepareForCompilation(rollupObject, parentEvent);
76    SourceMapGenerator.getInstance().buildModuleSourceMapInfo(parentEvent);
77    this.generateAbcByEs2abc(parentEvent, !!this.projectConfig.byteCodeHarInfo);
78  }
79
80  private compileChangeListFiles(rollupObject: Object, parentEvent: Object): void {
81    if (!fs.existsSync(this.projectConfig.changedFileList)) {
82      this.logger.debug(blue, `ArkTS: Cannot find file: ${
83        this.projectConfig.changedFileList}, skip hot reload build`, reset);
84      return;
85    }
86
87    const changedFileListJson: string = fs.readFileSync(this.projectConfig.changedFileList).toString();
88    const changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles;
89    if (typeof changedFileList === 'undefined' || changedFileList.length === 0) {
90      this.logger.debug(blue, `ArkTS: No changed files found, skip hot reload build`, reset);
91      return;
92    }
93
94    let needHotreloadBuild: boolean = true;
95    let changedFileListInAbsolutePath: Array<string> = changedFileList.map((file) => {
96      if (isJsonSourceFile(file)) {
97        this.logger.debug(blue, `ARKTS: json source file: ${file} changed, skip hot reload build!`, reset);
98        needHotreloadBuild = false;
99      }
100      return path.join(this.projectConfig.projectPath, file);
101    });
102
103    if (!needHotreloadBuild) {
104      return;
105    }
106
107    const eventCollectModuleFileList = createAndStartEvent(parentEvent, 'collect module file list');
108    this.collectModuleFileList(rollupObject, changedFileListInAbsolutePath[Symbol.iterator]());
109    stopEvent(eventCollectModuleFileList);
110
111    if (!fs.existsSync(this.projectConfig.patchAbcPath)) {
112      mkdirsSync(this.projectConfig.patchAbcPath);
113    }
114
115    this.updateSourceMapFromFileList(
116      SourceMapGenerator.getInstance().isNewSourceMaps() ? changedFileListInAbsolutePath : changedFileList,
117      parentEvent);
118    const outputABCPath: string = path.join(this.projectConfig.patchAbcPath, MODULES_ABC);
119    validateFilePathLength(outputABCPath, this.logger);
120    this.moduleAbcPath = outputABCPath;
121    // During incremental compilation, the bytecode har path must be blocked from being written to filesInfo.
122    this.generateAbcByEs2abc(parentEvent, false);
123  }
124
125  private updateSourceMapFromFileList(fileList: Array<string>, parentEvent: Object): void {
126    const eventUpdateSourceMapFromFileList = createAndStartEvent(parentEvent, 'update source map from file list');
127    const sourceMapGenerator = SourceMapGenerator.getInstance();
128    const relativeProjectPath: string = this.projectConfig.projectPath.slice(
129      this.projectConfig.projectRootPath.length + path.sep.length);
130    let hotReloadSourceMap: Object = {};
131    for (const file of fileList) {
132      if (sourceMapGenerator.isNewSourceMaps()) {
133        validateFilePathLength(file, this.logger);
134        hotReloadSourceMap[sourceMapGenerator.genKey(file)] = sourceMapGenerator.getSourceMap(file);
135      } else {
136        const sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file));
137        validateFilePathLength(sourceMapPath, this.logger);
138        hotReloadSourceMap[sourceMapPath] = sourceMapGenerator.getSourceMap(sourceMapPath);
139      }
140    }
141    if (!sourceMapGenerator.isNewSourceMaps()) {
142      sourceMapGenerator.modifySourceMapKeyToCachePath(hotReloadSourceMap);
143    }
144    const sourceMapFilePath: string = path.join(this.projectConfig.patchAbcPath, SOURCEMAPS);
145    validateFilePathLength(sourceMapFilePath, this.logger);
146    fs.writeFileSync(sourceMapFilePath,
147      JSON.stringify(hotReloadSourceMap, null, 2), 'utf-8');
148    stopEvent(eventUpdateSourceMapFromFileList);
149  }
150
151  private generateAbcByEs2abc(parentEvent: Object, includeByteCodeHarInfo: boolean): void {
152    this.generateEs2AbcCmd();
153    this.addHotReloadArgs();
154    this.genDescriptionsForMergedEs2abc(includeByteCodeHarInfo);
155    this.generateMergedAbcOfEs2Abc(parentEvent);
156  }
157}
158