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