• 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 { 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];