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 { newSourceMaps } from '../transform'; 29import { 30 mkdirsSync, 31 toUnixPath, 32 validateFilePathLength 33} from '../../../utils'; 34import { 35 createAndStartEvent, 36 stopEvent 37} from '../../../ark_utils'; 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:ERROR oldMapFilePath is not specified, hotReload Fail'); 49 } 50 } 51 52 generateAbc(rollupObject: Object, parentEvent: Object): void { 53 if (isFirstBuild) { 54 this.compileAllFiles(rollupObject, parentEvent); 55 isFirstBuild = false; 56 } else { 57 this.compileChangeListFiles(rollupObject, parentEvent); 58 } 59 } 60 61 addHotReloadArgs() { 62 if (isFirstBuild) { 63 this.cmdArgs.push('--dump-symbol-table'); 64 this.cmdArgs.push(`"${this.symbolMapFilePath}"`); 65 return; 66 } 67 this.addCacheFileArgs(); 68 this.cmdArgs.push('--input-symbol-table'); 69 this.cmdArgs.push(`"${this.symbolMapFilePath}"`); 70 this.cmdArgs.push('--hot-reload'); 71 } 72 73 private compileAllFiles(rollupObject: Object, parentEvent: Object): void { 74 this.prepareForCompilation(rollupObject, parentEvent); 75 this.buildModuleSourceMapInfo(parentEvent); 76 this.generateAbcByEs2abc(parentEvent); 77 } 78 79 private compileChangeListFiles(rollupObject: Object, parentEvent: Object): void { 80 if (!fs.existsSync(this.projectConfig.changedFileList)) { 81 this.logger.debug(blue, `ArkTS: Cannot find file: ${ 82 this.projectConfig.changedFileList}, skip hot reload build`, reset); 83 return; 84 } 85 86 const changedFileListJson: string = fs.readFileSync(this.projectConfig.changedFileList).toString(); 87 const changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles; 88 if (typeof changedFileList === 'undefined' || changedFileList.length === 0) { 89 this.logger.debug(blue, `ArkTS: No changed files found, skip hot reload build`, reset); 90 return; 91 } 92 93 let needHotreloadBuild: boolean = true; 94 let changedFileListInAbsolutePath: Array<string> = changedFileList.map((file) => { 95 if (isJsonSourceFile(file)) { 96 this.logger.debug(blue, `ARKTS: json source file: ${file} changed, skip hot reload build!`, reset); 97 needHotreloadBuild = false; 98 } 99 return path.join(this.projectConfig.projectPath, file); 100 }); 101 102 if (!needHotreloadBuild) { 103 return; 104 } 105 106 const eventCollectModuleFileList = createAndStartEvent(parentEvent, 'collect module file list'); 107 this.collectModuleFileList(rollupObject, changedFileListInAbsolutePath[Symbol.iterator]()); 108 stopEvent(eventCollectModuleFileList); 109 110 if (!fs.existsSync(this.projectConfig.patchAbcPath)) { 111 mkdirsSync(this.projectConfig.patchAbcPath); 112 } 113 114 this.updateSourceMapFromFileList(changedFileList, parentEvent); 115 const outputABCPath: string = path.join(this.projectConfig.patchAbcPath, MODULES_ABC); 116 validateFilePathLength(outputABCPath, this.logger); 117 this.moduleAbcPath = outputABCPath; 118 this.generateAbcByEs2abc(parentEvent); 119 } 120 121 private updateSourceMapFromFileList(fileList: Array<string>, parentEvent: Object): void { 122 const eventUpdateSourceMapFromFileList = createAndStartEvent(parentEvent, 'update source map from file list'); 123 const relativeProjectPath: string = this.projectConfig.projectPath.slice( 124 this.projectConfig.projectRootPath.length + path.sep.length); 125 let hotReloadSourceMap: Object = {}; 126 for (const file of fileList) { 127 const sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file)); 128 validateFilePathLength(sourceMapPath, this.logger); 129 hotReloadSourceMap[sourceMapPath] = newSourceMaps[sourceMapPath]; 130 } 131 this.modifySourceMapKeyToCachePath(hotReloadSourceMap); 132 const sourceMapFilePath: string = path.join(this.projectConfig.patchAbcPath, SOURCEMAPS); 133 validateFilePathLength(sourceMapFilePath, this.logger); 134 fs.writeFileSync(sourceMapFilePath, 135 JSON.stringify(hotReloadSourceMap, null, 2), 'utf-8'); 136 stopEvent(eventUpdateSourceMapFromFileList); 137 } 138 139 private generateAbcByEs2abc(parentEvent: Object): void { 140 this.generateEs2AbcCmd(); 141 this.addHotReloadArgs(); 142 this.generateMergedAbcOfEs2Abc(parentEvent); 143 } 144} 145