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