1/* 2 * Copyright (c) 2022 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 16// singleton to parse commandLine infos 17import commandLineArgs from "command-line-args"; 18import commandLineUsage from "command-line-usage"; 19import * as ts from "typescript"; 20import { LOGE } from "./log"; 21import * as path from "path"; 22import { execute } from "./base/util"; 23 24const ts2pandaOptions = [ 25 { name: 'modules', alias: 'm', type: Boolean, defaultValue: false, description: "compile as module." }, 26 { name: 'debug-log', alias: 'l', type: Boolean, defaultValue: false, description: "show info debug log and generate the json file."}, 27 { name: 'dump-assembly', alias: 'a', type: Boolean, defaultValue: false, description: "dump assembly to file." }, 28 { name: 'debug', alias: 'd', type: Boolean, defaultValue: false, description: "compile with debug info." }, 29 { name: 'debug-add-watch', alias: 'w', type: String, lazyMultiple: true, defaultValue: [], description: "watch expression, abc file path and maybe watchTimeOut(in seconds) in debug mode." }, 30 { name: 'keep-persistent-watch', alias: 'k', type: String, lazyMultiple: true, defaultValue: [], description: "keep persistent watch on js file with watched expression." }, 31 { name: 'show-statistics', alias: 's', type: String, lazyMultiple: true, defaultValue: "", description: "show compile statistics(ast, histogram, hoisting, all)." }, 32 { name: 'output', alias: 'o', type: String, defaultValue: "", description: "set output file." }, 33 { name: 'timeout', alias: 't', type: Number, defaultValue: 0, description: "js to abc timeout threshold(unit: seconds)." }, 34 { name: 'opt-log-level', type: String, defaultValue: "error", description: "specifie optimizer log level. Possible values: ['debug', 'info', 'error', 'fatal']" }, 35 { 36 name: 'opt-level', type: Number, defaultValue: 1, description: "Optimization level. Possible values: [0, 1, 2]. Default: 0\n 0: no optimizations\n \ 37 1: basic bytecode optimizations, including valueNumber, lowering, constantResolver, regAccAllocator\n \ 38 2: other bytecode optimizations, unimplemented yet"}, 39 { name: 'help', alias: 'h', type: Boolean, description: "Show usage guide." }, 40 { name: 'bc-version', alias: 'v', type: Boolean, defaultValue: false, description: "Print ark bytecode version" }, 41 { name: 'bc-min-version', type: Boolean, defaultValue: false, description: "Print ark bytecode minimum supported version" }, 42 { name: 'included-files', alias: 'i', type: String, lazyMultiple: true, defaultValue: [], description: "The list of dependent files." }, 43 { name: 'record-type', alias: 'p', type: Boolean, defaultValue: false, description: "Record type info. Default: true" }, 44 { name: 'dts-type-record', alias: 'q', type: Boolean, defaultValue: false, description: "Record type info for .d.ts files. Default: false" }, 45 { name: 'debug-type', alias: 'g', type: Boolean, defaultValue: false, description: "Print type-related log. Default: false" }, 46 { name: 'output-type', type: Boolean, defaultValue: false, description: "set output type."}, 47 { name: 'source-file', type: String, defaultValue: "", description: "specify the file path info recorded in generated abc" }, 48 { name: 'generate-tmp-file', type: Boolean, defaultValue: false, description: "whether to generate intermediate temporary files"}, 49] 50 51 52 53export class CmdOptions { 54 private static parsedResult: ts.ParsedCommandLine; 55 private static options: commandLineArgs.CommandLineOptions; 56 57 static isEnableDebugLog(): boolean { 58 if (!this.options) { 59 return false; 60 } 61 return this.options["debug-log"]; 62 } 63 64 static isAssemblyMode(): boolean { 65 if (!this.options) { 66 return false; 67 } 68 return this.options["dump-assembly"]; 69 } 70 71 static isDebugMode(): boolean { 72 if (!this.options) { 73 return false; 74 } 75 return this.options["debug"]; 76 } 77 78 static setWatchEvaluateExpressionArgs(watchArgs: string[]) { 79 this.options["debug-add-watch"] = watchArgs; 80 } 81 82 static getDeamonModeArgs(): string[] { 83 if (!this.options) { 84 return []; 85 } 86 return this.options["keep-persistent-watch"]; 87 } 88 89 static isWatchEvaluateDeamonMode(): boolean { 90 return CmdOptions.getDeamonModeArgs()[0] == "start"; 91 } 92 93 static isStopEvaluateDeamonMode(): boolean { 94 return CmdOptions.getDeamonModeArgs()[0] == "stop"; 95 } 96 97 static getEvaluateDeamonPath(): string { 98 return CmdOptions.getDeamonModeArgs()[1]; 99 } 100 101 static isWatchEvaluateExpressionMode(): boolean { 102 if (!this.options) { 103 return false; 104 } 105 return this.options["debug-add-watch"].length != 0; 106 } 107 108 static getEvaluateExpression(): string { 109 return this.options["debug-add-watch"][0]; 110 } 111 112 static getWatchJsPath(): string { 113 return this.options["debug-add-watch"][1]; 114 } 115 116 static getWatchTimeOutValue(): number { 117 if (this.options["debug-add-watch"].length == 2) { 118 return 0; 119 } 120 return this.options["debug-add-watch"][2]; 121 } 122 123 static isModules(): boolean { 124 if (!this.options) { 125 return false; 126 } 127 return this.options["modules"]; 128 } 129 130 static getOptLevel(): number { 131 return this.options["opt-level"]; 132 } 133 134 static getOptLogLevel(): string { 135 return this.options["opt-log-level"]; 136 } 137 138 static showASTStatistics(): boolean { 139 if (!this.options) { 140 return false; 141 } 142 return this.options["show-statistics"].includes("ast") || this.options["show-statistics"].includes("all"); 143 } 144 145 static showHistogramStatistics(): boolean { 146 if (!this.options) { 147 return false; 148 } 149 return this.options["show-statistics"].includes("all") || this.options["show-statistics"].includes("histogram"); 150 } 151 152 static showHoistingStatistics(): boolean { 153 if (!this.options) { 154 return false; 155 } 156 return this.options["show-statistics"].includes("all") || this.options["show-statistics"].includes("hoisting"); 157 } 158 159 static getInputFileName(): string { 160 let path = this.parsedResult.fileNames[0]; 161 let inputFile = path.substring(0, path.lastIndexOf('.')); 162 return inputFile; 163 } 164 165 static getOutputBinName(): string { 166 let outputFile = this.options.output; 167 if (outputFile == "") { 168 outputFile = CmdOptions.getInputFileName() + ".abc"; 169 } 170 return outputFile; 171 } 172 173 static getTimeOut(): Number { 174 if (!this.options) { 175 return 0; 176 } 177 return this.options["timeout"]; 178 } 179 180 static isOutputType(): false { 181 if (!this.options) { 182 return false; 183 } 184 return this.options["output-type"]; 185 } 186 187 static showHelp(): void { 188 const usage = commandLineUsage([ 189 { 190 header: "Ark JavaScript Compiler", 191 content: 'node --expose-gc index.js [options] file.js' 192 }, 193 { 194 header: 'Options', 195 optionList: ts2pandaOptions 196 }, 197 { 198 content: 'Project Ark' 199 } 200 ]) 201 LOGE(usage); 202 } 203 204 static isBcVersion(): boolean { 205 if (!this.options) { 206 return false; 207 } 208 return this.options["bc-version"]; 209 } 210 211 static getVersion(isBcVersion: boolean = true): void { 212 let js2abc = path.join(path.resolve(__dirname, '../bin'), "js2abc"); 213 let version_arg = isBcVersion ? "--bc-version" : "--bc-min-version" 214 execute(`${js2abc}`, [version_arg]); 215 } 216 217 static isBcMinVersion(): boolean { 218 if (!this.options) { 219 return false; 220 } 221 return this.options["bc-min-version"]; 222 } 223 224 static getIncludedFiles(): string[] { 225 if (!this.options) { 226 return []; 227 } 228 229 return this.options["included-files"]; 230 } 231 232 static needRecordType(): boolean { 233 if (!this.options) { 234 return false; 235 } 236 237 return !this.options["record-type"]; 238 } 239 240 static needRecordDtsType(): boolean { 241 if (!this.options) { 242 return false; 243 } 244 return this.options["dts-type-record"]; 245 } 246 247 static enableTypeLog(): boolean { 248 if (!this.options) { 249 return false; 250 } 251 return this.options["debug-type"]; 252 } 253 254 static getSourceFile(): string { 255 return this.options["source-file"]; 256 } 257 258 static needGenerateTmpFile(): boolean { 259 if (!this.options) { 260 return false; 261 } 262 return this.options["generate-tmp-file"]; 263 } 264 265 // @ts-ignore 266 static parseUserCmd(args: string[]): ts.ParsedCommandLine | undefined { 267 this.options = commandLineArgs(ts2pandaOptions, { partial: true }); 268 if (this.options.help) { 269 this.showHelp(); 270 return undefined; 271 } 272 273 if (this.isBcVersion() || this.isBcMinVersion()) { 274 this.getVersion(this.isBcVersion()); 275 return undefined; 276 } 277 278 if (!this.options._unknown) { 279 LOGE("options at least one file is needed"); 280 this.showHelp(); 281 return undefined; 282 } 283 284 this.parsedResult = ts.parseCommandLine(this.options._unknown!); 285 return this.parsedResult; 286 } 287 288} 289