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 { Code, ConstantValue, StringResourceId } from '../utils/constant'; 17import { FileUtils } from '../utils/fileUtils'; 18import { LogLevelUtil, LogUtil } from '../utils/logUtil'; 19import { StringUtils, StringResource } from '../utils/stringUtils'; 20import { ApiSplitProcessor } from './apiSplitProcessor'; 21import { AsynchronousFunctionProcessor } from './asynchronousFunctionProcessor'; 22import { ContextImpl, InputParameter, OutputFileHelper } from './coreImpls'; 23import { CommentModificationProcessor } from './modificationProcessor'; 24import { OutputProcessor } from './outputProcessor'; 25import { RawSourceCodeProcessor } from './rawCodeProcessor'; 26import type { Context, IJSDocModifier, ISourceCodeProcessor, LogReporter, ProcessResult } from './typedef'; 27 28/** 29 * JSDoc 整改流程入口。 30 */ 31export class JSDocModifierImpl implements IJSDocModifier { 32 tag: string = 'jsdoc-tool'; 33 async start(): Promise<void> { 34 await this.startInternal(); 35 } 36 37 async startInternal(): Promise<void> { 38 try { 39 const inputParameter = new InputParameter(); 40 inputParameter.parse(); 41 LogUtil.logLevel = LogLevelUtil.get(inputParameter.logLevel); 42 const sourceProcessor: ISourceCodeProcessor = this.getSourceProcessor(inputParameter); 43 const baseContext: Context = this.getBaseContext(inputParameter); 44 LogUtil.i(this.tag, StringResource.getString(StringResourceId.START_MESSAGE)); 45 const result: ProcessResult = await sourceProcessor.process(baseContext, ''); 46 if (result.code !== Code.OK) { 47 LogUtil.e(this.tag, result.content); 48 } else { 49 LogUtil.i(this.tag, result.content); 50 } 51 } catch (error) { 52 LogUtil.e(this.tag, error as string); 53 } 54 } 55 56 getSourceProcessor(inputParam: InputParameter): ISourceCodeProcessor { 57 return inputParam.isHandleMultiFiles() ? new MultiFileProcessor(inputParam) : new SingleFileProcessor(inputParam); 58 } 59 60 getBaseContext(inputParam: InputParameter): Context { 61 return new ContextImpl(inputParam.inputFilePath, 62 inputParam.outputFilePath!, 63 inputParam.getOptions()); 64 } 65} 66 67abstract class BaseSourceCodeProcessor implements ISourceCodeProcessor { 68 inputParam: InputParameter; 69 70 constructor(inputParam: InputParameter) { 71 this.inputParam = inputParam; 72 } 73 74 abstract process(context: Context, code: string): Promise<ProcessResult>; 75 76 buildProcessorContext(parentContext: Context, inputFile: string): Context { 77 return new ContextImpl(inputFile, 78 OutputFileHelper.getOutputFilePath(this.inputParam, inputFile), 79 parentContext.getOptions()); 80 } 81} 82 83/** 84 * 处理单个 d.ts 文件。 85 */ 86export class SingleFileProcessor extends BaseSourceCodeProcessor { 87 88 async process(context: Context, content: string): Promise<ProcessResult> { 89 const inputFilePath = context.getInputFile(); 90 if (!inputFilePath) { 91 return { 92 code: Code.ERROR, 93 content: StringResource.getString(StringResourceId.INPUT_FILE_NOT_FOUND) 94 }; 95 } 96 const rawCodeStr = FileUtils.readFileContent(inputFilePath); 97 if (StringUtils.isEmpty(rawCodeStr)) { 98 return { 99 code: Code.ERROR, 100 content: StringResource.getString(StringResourceId.INPUT_FILE_CONTENT_EMPTY) 101 }; 102 } 103 104 let preResult = { 105 code: Code.OK, 106 content: rawCodeStr ? rawCodeStr : '' 107 }; 108 const newContext = this.buildProcessorContext(context, context.getInputFile()); 109 const logReporter: LogReporter = context.getLogReporter(); 110 newContext.setLogReporter(logReporter); 111 for (let processor of processorRegistry) { 112 preResult = await processor.process(newContext, preResult.content); 113 if (preResult.code === Code.ERROR) { 114 break; 115 } 116 } 117 // 报告落盘 118 const reportFilePath: string = OutputFileHelper.getLogReportFilePath(this.inputParam); 119 await context.getLogReporter().writeAllResults(reportFilePath); 120 if (context.getOptions().isTest) { 121 LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}.json`); 122 } else { 123 LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}.xlsx`); 124 } 125 if (preResult.code === Code.OK) { 126 preResult.content = `new d.ts file is ${newContext.getOutputFile()}`; 127 } 128 return preResult; 129 } 130} 131 132/** 133 * 处理文件夹。 134 */ 135export class MultiFileProcessor extends BaseSourceCodeProcessor { 136 async process(context: Context, content: string): Promise<ProcessResult> { 137 const intpuDir = context.getInputFile(); 138 if (!intpuDir) { 139 return { 140 code: Code.ERROR, 141 content: StringResource.getString(StringResourceId.INPUT_FILE_NOT_FOUND) 142 }; 143 } 144 const allSourceFiles = FileUtils.readFilesInDir(intpuDir, (name) => { 145 return name.endsWith(ConstantValue.DTS_EXTENSION); 146 }); 147 const errorSet: Array<ProcessResult> = new Array(); 148 const logReporter: LogReporter = context.getLogReporter(); 149 for (const childFile of allSourceFiles) { 150 const rawCodeStr = FileUtils.readFileContent(childFile); 151 if (StringUtils.isEmpty(rawCodeStr)) { 152 errorSet.push({ 153 code: Code.ERROR, 154 content: `${childFile}: ${StringResource.getString(StringResourceId.INPUT_FILE_CONTENT_EMPTY)}` 155 }); 156 continue; 157 } 158 const newContext = this.buildProcessorContext(context, childFile); 159 newContext.setLogReporter(logReporter); 160 let preValue = { 161 code: Code.OK, 162 content: rawCodeStr! 163 }; 164 165 for (let processor of processorRegistry) { 166 preValue = await processor.process(newContext, preValue.content); 167 if (preValue.code !== Code.OK) { 168 errorSet.push(preValue); 169 break; 170 } 171 } 172 } 173 // 报告落盘 174 const reportFilePath: string = OutputFileHelper.getLogReportFilePath(this.inputParam); 175 await context.getLogReporter().writeAllResults(reportFilePath); 176 if (context.getOptions().isTest) { 177 LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}.json`); 178 } else { 179 LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}.xlsx`); 180 } 181 return { 182 code: errorSet.length > 0 ? Code.ERROR : Code.OK, 183 content: errorSet.length > 0 ? JSON.stringify(errorSet) : 184 `new d.ts file is in ${OutputFileHelper.getMultiOutputDir(this.inputParam)}` 185 }; 186 } 187} 188 189/** 190 * 整改流程处理器配置,按需添加必要处理流程。 191 */ 192const processorRegistry: Array<ISourceCodeProcessor> = [ 193 // 原始文件解析 194 new RawSourceCodeProcessor(), 195 // 同名异步函数处理 196 new AsynchronousFunctionProcessor(), 197 // 注释整改 198 new CommentModificationProcessor(), 199 // API调整 200 new ApiSplitProcessor(), 201 // 新文件输出,日志输出 202 new OutputProcessor() 203];