• 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 ts from 'typescript';
17import { Code } from '../utils/constant';
18import { FileUtils } from '../utils/fileUtils';
19import { LogUtil } from '../utils/logUtil';
20import { StringUtils } from '../utils/stringUtils';
21import { CommentHelper } from './coreImpls';
22import type { Context, ISourceCodeProcessor, ProcessResult, sourceParser } from './typedef';
23import type { comment} from './typedef';
24
25export class OutputProcessor implements ISourceCodeProcessor {
26  async process(context: Context, content: string): Promise<ProcessResult> {
27    try {
28      let outputContent = content;
29      const formater = new Formatter(content);
30      outputContent = formater.format(context);
31
32      const prettier = require('prettier');
33      outputContent = prettier.format(outputContent, context.getOptions().formaterOption);
34
35      const numberLiteralCaseRule = new NumberLiteralCaseRule(true, context.getSourceParser(outputContent));
36      outputContent = numberLiteralCaseRule.apply(outputContent);
37
38      FileUtils.writeStringToFile(outputContent, context.getOutputFile());
39      return { code: Code.OK, content: outputContent };
40    } catch (error) {
41      LogUtil.e('OutputProcessor', `error: ${context.getInputFile()}, ${error}`);
42      return { code: Code.OK, content: content };
43    }
44  }
45}
46
47class Formatter implements sourceParser.INodeVisitorCallback {
48  source: string;
49
50  constructor(source: string) {
51    this.source = source;
52  }
53
54  onVisitNode(node: comment.CommentNode): void {
55    if (node.astNode && node.commentInfos && node.commentInfos.length > 0) {
56      CommentHelper.addComment(node.astNode, node.commentInfos);
57    }
58  }
59
60  format(context: Context): string {
61    const sourceParser = context.getSourceParser(this.source);
62    const sourceFile = sourceParser.visitEachNodeComment(this);
63    return sourceFile ? sourceParser.printSourceFile(sourceFile) : this.source;
64  }
65}
66
67/**
68 * 转换16进制字符大小写
69 */
70class NumberLiteralCaseRule {
71  upperCase: boolean;
72  sourceParser: sourceParser.SourceCodeParser;
73  content: string | undefined;
74
75  constructor(upperCase: boolean, sourceParser: sourceParser.SourceCodeParser) {
76    this.upperCase = upperCase;
77    this.sourceParser = sourceParser;
78  }
79
80  apply(content: string): string {
81    const sourceFile = this.sourceParser.createSourceFile(content, 'numberLiteral');
82    if (!sourceFile) {
83      return content;
84    }
85    this.content = content;
86    sourceFile.forEachChild((child) => {
87      this.nodeVisitor(child);
88    });
89    return this.content;
90  }
91
92  private nodeVisitor(node: ts.Node): void {
93    if (!ts.isNumericLiteral(node)) {
94      node.forEachChild((child) => {
95        this.nodeVisitor(child);
96      });
97    }
98    const value = node.getText().trim();
99    if (!this.isHexString(value)) {
100      return;
101    }
102    const START_POSITION_INDEX = 2;
103    let literalStr = value.substring(START_POSITION_INDEX, value.length);
104    let replacement = undefined;
105    if (this.upperCase && !this.isHexUpperCase(literalStr)) {
106      replacement = `0x${literalStr.toUpperCase()}`;
107    }
108    if (!this.upperCase && !this.isHexLowerCase(literalStr)) {
109      replacement = `0x${literalStr.toLowerCase()}`;
110    }
111    if (!replacement) {
112      return;
113    }
114    this.content = StringUtils.replaceAt(this.content!, node.getStart(), replacement);
115  }
116
117  private isHexString(str: string): boolean {
118    return /^0x([abcdefABCDEF\d]*)$/g.test(str);
119  }
120
121  private isHexUpperCase(str: string): boolean {
122    for (let c of str) {
123      if (c >= '0' && c <= '9') {
124        continue;
125      }
126      if (c >= 'a' && c <= 'f') {
127        return false;
128      }
129    }
130    return true;
131  }
132
133  private isHexLowerCase(str: string): boolean {
134    for (let c of str) {
135      if (c >= '0' && c <= '9') {
136        continue;
137      }
138      if (c >= 'A' && c <= 'F') {
139        return false;
140      }
141    }
142    return true;
143  }
144}