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 16import * as path from "path"; 17import * as ts from "typescript"; 18import * as fs from "fs"; 19import { CmdOptions } from "./cmdOptions"; 20import { CompilerDriver } from "./compilerDriver"; 21import * as diag from "./diagnostic"; 22import * as jshelpers from "./jshelpers"; 23import { LOGE } from "./log"; 24import { setGlobalDeclare, setGlobalStrict } from "./strictMode"; 25import { TypeChecker } from "./typeChecker"; 26import { setPos, isBase64Str } from "./base/util"; 27 28function checkIsGlobalDeclaration(sourceFile: ts.SourceFile) { 29 for (let statement of sourceFile.statements) { 30 if (statement.modifiers) { 31 for (let modifier of statement.modifiers) { 32 if (modifier.kind === ts.SyntaxKind.ExportKeyword) { 33 return false; 34 } 35 } 36 } else if (statement.kind === ts.SyntaxKind.ExportAssignment) { 37 return false; 38 } else if (statement.kind === ts.SyntaxKind.ImportKeyword || statement.kind === ts.SyntaxKind.ImportDeclaration) { 39 return false; 40 } 41 } 42 return true; 43} 44 45function generateDTs(node: ts.SourceFile, options: ts.CompilerOptions) { 46 let outputBinName = getOutputBinName(node); 47 let compilerDriver = new CompilerDriver(outputBinName); 48 setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options)); 49 compilerDriver.compile(node); 50 compilerDriver.showStatistics(); 51} 52 53function main(fileNames: string[], options: ts.CompilerOptions) { 54 const host = ts.createCompilerHost(options); 55 if (!CmdOptions.needGenerateTmpFile()) { 56 host.writeFile = () => {}; 57 } 58 let program = ts.createProgram(fileNames, options, host); 59 let typeChecker = TypeChecker.getInstance(); 60 typeChecker.setTypeChecker(program.getTypeChecker()); 61 62 if (CmdOptions.needRecordDtsType()) { 63 for (let sourceFile of program.getSourceFiles()) { 64 if (sourceFile.isDeclarationFile && !program.isSourceFileDefaultLibrary(sourceFile)) { 65 setGlobalDeclare(checkIsGlobalDeclaration(sourceFile)); 66 generateDTs(sourceFile, options); 67 } 68 } 69 } 70 71 let emitResult = program.emit( 72 undefined, 73 undefined, 74 undefined, 75 undefined, 76 { 77 before: [ 78 // @ts-ignore 79 (ctx: ts.TransformationContext) => { 80 return (node: ts.SourceFile) => { 81 let outputBinName = getOutputBinName(node); 82 let compilerDriver = new CompilerDriver(outputBinName); 83 compilerDriver.compileForSyntaxCheck(node); 84 return node; 85 } 86 } 87 ], 88 after: [ 89 // @ts-ignore 90 (ctx: ts.TransformationContext) => { 91 return (node: ts.SourceFile) => { 92 if (ts.getEmitHelpers(node)) { 93 let newStatements = []; 94 ts.getEmitHelpers(node)?.forEach( 95 item => { 96 let emitHelperSourceFile = ts.createSourceFile(node.fileName, <string>item.text, options.target!, true, ts.ScriptKind.JS); 97 emitHelperSourceFile.statements.forEach(emitStatement => { 98 let emitNode = setPos(emitStatement); 99 newStatements.push(emitNode); 100 }); 101 } 102 ) 103 newStatements.push(...node.statements); 104 node = ts.factory.updateSourceFile(node, newStatements); 105 } 106 let outputBinName = getOutputBinName(node); 107 let compilerDriver = new CompilerDriver(outputBinName); 108 setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options)); 109 compilerDriver.compile(node); 110 compilerDriver.showStatistics(); 111 return node; 112 } 113 } 114 ] 115 } 116 ); 117 118 let allDiagnostics = ts 119 .getPreEmitDiagnostics(program) 120 .concat(emitResult.diagnostics); 121 122 allDiagnostics.forEach(diagnostic => { 123 diag.printDiagnostic(diagnostic); 124 }); 125} 126 127function getOutputBinName(node: ts.SourceFile) { 128 let outputBinName = CmdOptions.getOutputBinName(); 129 let fileName = node.fileName.substring(0, node.fileName.lastIndexOf('.')); 130 let inputFileName = CmdOptions.getInputFileName(); 131 if (/^win/.test(require('os').platform())) { 132 var inputFileTmps = inputFileName.split(path.sep); 133 inputFileName = path.posix.join(...inputFileTmps); 134 } 135 136 if (fileName != inputFileName) { 137 outputBinName = fileName + ".abc"; 138 } 139 return outputBinName; 140} 141 142function getDtsFiles(libDir: string): string[] { 143 let dtsFiles:string[] = []; 144 function finDtsFile(dir){ 145 let files = fs.readdirSync(dir); 146 files.forEach(function (item, _) { 147 let fPath = path.join(dir,item); 148 let stat = fs.statSync(fPath); 149 if(stat.isDirectory() === true) { 150 finDtsFile(fPath); 151 } 152 if (stat.isFile() === true && item.endsWith(".d.ts") === true) { 153 dtsFiles.push(fPath); 154 } 155 }); 156 } 157 finDtsFile(libDir); 158 return dtsFiles; 159} 160 161const stopWatchingStr = "####"; 162const watchAbcFileDefaultTimeOut = 10; 163const watchFileName = "watch_expressions"; 164 165function updateWatchJsFile() { 166 let ideIputStr = CmdOptions.getEvaluateExpression(); 167 if (!isBase64Str(ideIputStr)) { 168 throw new Error("Passed expression string for evaluating is not base64 style."); 169 } 170 let watchAbcFileTimeOut = watchAbcFileDefaultTimeOut; 171 if (CmdOptions.getWatchTimeOutValue() != 0) { watchAbcFileTimeOut = CmdOptions.getWatchTimeOutValue(); } 172 let watchFilePrefix = CmdOptions.getWatchJsPath() + path.sep + watchFileName; 173 let originExpre = Buffer.from(ideIputStr, 'base64').toString(); 174 let jsFileName = watchFilePrefix + ".js"; 175 let abcFileName = watchFilePrefix + ".abc"; 176 let errorMsgFileName = watchFilePrefix + ".err"; 177 178 fs.watchFile(errorMsgFileName, { persistent: true, interval: 50 }, (curr, prev) => { 179 if (+curr.mtime <= +prev.mtime) { 180 fs.unwatchFile(jsFileName); 181 fs.unwatchFile(abcFileName); 182 throw new Error("watched errMsg file has not been initialized"); 183 } 184 console.log("error in genarate abc file for this expression."); 185 fs.unwatchFile(abcFileName); 186 fs.unwatchFile(errorMsgFileName); 187 process.exit(); 188 }); 189 fs.watchFile(abcFileName, { persistent: true, interval: 50 }, (curr, prev) => { 190 if (+curr.mtime <= +prev.mtime) { 191 fs.unwatchFile(jsFileName); 192 fs.unwatchFile(errorMsgFileName); 193 throw new Error("watched abc file has not been initialized"); 194 } 195 let base64data = fs.readFileSync(abcFileName); 196 let watchResStr = Buffer.from(base64data).toString('base64'); 197 console.log(watchResStr); 198 fs.unwatchFile(abcFileName); 199 fs.unwatchFile(errorMsgFileName); 200 process.exit(); 201 }); 202 fs.writeFileSync(jsFileName, originExpre); 203 setTimeout(() => { 204 fs.unwatchFile(jsFileName); 205 fs.unwatchFile(abcFileName); 206 fs.unwatchFile(errorMsgFileName); 207 fs.unlinkSync(jsFileName); 208 fs.unlinkSync(abcFileName); 209 fs.unlinkSync(errorMsgFileName); 210 throw new Error("watchFileServer has not been initialized"); 211 }, watchAbcFileTimeOut*1000); 212} 213 214function compileWatchExpression(jsFileName: string, errorMsgFileName: string, options: ts.CompilerOptions, 215 watchedProgram: ts.Program) { 216 CmdOptions.setWatchEvaluateExpressionArgs(['','']); 217 let fileName = watchFileName + ".js"; 218 let errorMsgRecordFlag = false; 219 let sourceFile = ts.createSourceFile(fileName, fs.readFileSync(jsFileName).toString(), ts.ScriptTarget.ES2017); 220 let jsFileDiagnostics = watchedProgram.getSyntacticDiagnostics(sourceFile); 221 jsFileDiagnostics.forEach(diagnostic => { 222 if (!errorMsgRecordFlag) { 223 fs.writeFileSync(errorMsgFileName, "There are syntax errors in input expression.\n"); 224 errorMsgRecordFlag = true; 225 } 226 diag.printDiagnostic(diagnostic); 227 return; 228 }); 229 if (errorMsgRecordFlag) { 230 return; 231 } 232 watchedProgram.emit( 233 undefined, 234 undefined, 235 undefined, 236 undefined, 237 { 238 before: [ 239 // @ts-ignore 240 (ctx: ts.TransformationContext) => { 241 return (node: ts.SourceFile) => { 242 if (path.basename(node.fileName) == fileName) { node = sourceFile; } 243 let outputBinName = getOutputBinName(node); 244 let compilerDriver = new CompilerDriver(outputBinName); 245 compilerDriver.compileForSyntaxCheck(node); 246 return node; 247 } 248 } 249 ], 250 after: [ 251 // @ts-ignore 252 (ctx: ts.TransformationContext) => { 253 return (node: ts.SourceFile) => { 254 if (ts.getEmitHelpers(node)) { 255 let newStatements = []; 256 ts.getEmitHelpers(node)?.forEach( 257 item => { 258 let emitHelperSourceFile = ts.createSourceFile(node.fileName, <string>item.text, options.target!, true, ts.ScriptKind.JS); 259 emitHelperSourceFile.statements.forEach(emitStatement => { 260 let emitNode = setPos(emitStatement); 261 newStatements.push(emitNode); 262 }); 263 } 264 ) 265 newStatements.push(...node.statements); 266 node = ts.factory.updateSourceFile(node, newStatements); 267 } 268 let outputBinName = getOutputBinName(node); 269 let compilerDriver = new CompilerDriver(outputBinName); 270 setGlobalStrict(jshelpers.isEffectiveStrictModeSourceFile(node, options)); 271 compilerDriver.compile(node); 272 return node; 273 } 274 } 275 ] 276 } 277 ); 278} 279 280function launchWatchEvaluateDeamon(parsed: ts.ParsedCommandLine | undefined) { 281 let deamonFilePrefix = CmdOptions.getEvaluateDeamonPath() + path.sep + watchFileName; 282 let jsFileName = deamonFilePrefix + ".js"; 283 let abcFileName = deamonFilePrefix + ".abc"; 284 let errorMsgFileName = deamonFilePrefix + ".err"; 285 286 if (fs.existsSync(jsFileName)) { 287 console.log("watchFileServer has been initialized supportTimeout"); 288 return; 289 } 290 let files: string[] = parsed.fileNames; 291 fs.writeFileSync(jsFileName, "initJsFile\n"); 292 fs.writeFileSync(errorMsgFileName, "initErrMsgFile\n"); 293 files.unshift(jsFileName); 294 let watchedProgram = ts.createProgram(files, parsed.options); 295 compileWatchExpression(jsFileName, errorMsgFileName, parsed.options, watchedProgram); 296 297 fs.watchFile(jsFileName, { persistent: true, interval: 50 }, (curr, prev) => { 298 if (+curr.mtime <= +prev.mtime) { 299 throw new Error("watched js file has not been initialized"); 300 } 301 if (fs.readFileSync(jsFileName).toString() == stopWatchingStr) { 302 fs.unwatchFile(jsFileName); 303 console.log("stopWatchingSuccess"); 304 return; 305 } 306 compileWatchExpression(jsFileName, errorMsgFileName, parsed.options, watchedProgram); 307 }); 308 console.log("startWatchingSuccess supportTimeout"); 309 310 process.on("exit", () => { 311 fs.unlinkSync(jsFileName); 312 fs.unlinkSync(abcFileName); 313 fs.unlinkSync(errorMsgFileName); 314 }); 315} 316 317namespace Compiler { 318 export namespace Options { 319 export let Default: ts.CompilerOptions = { 320 outDir: "../tmp/build", 321 allowJs: true, 322 noEmitOnError: true, 323 noImplicitAny: true, 324 target: ts.ScriptTarget.ES2017, 325 module: ts.ModuleKind.ES2015, 326 strictNullChecks: true, 327 skipLibCheck: true, 328 alwaysStrict: true 329 }; 330 } 331} 332 333function run(args: string[], options?: ts.CompilerOptions): void { 334 let parsed = CmdOptions.parseUserCmd(args); 335 if (!parsed) { 336 return; 337 } 338 339 if (options) { 340 if (!((parsed.options.project) || (parsed.options.build))) { 341 parsed.options = options; 342 } 343 } 344 try { 345 if (CmdOptions.isWatchEvaluateDeamonMode()) { 346 launchWatchEvaluateDeamon(parsed); 347 return; 348 } 349 if (CmdOptions.isStopEvaluateDeamonMode()) { 350 fs.writeFileSync(CmdOptions.getEvaluateDeamonPath() + path.sep + watchFileName + ".js", stopWatchingStr); 351 return; 352 } 353 if (CmdOptions.isWatchEvaluateExpressionMode()) { 354 updateWatchJsFile(); 355 return; 356 } 357 main(parsed.fileNames.concat(CmdOptions.getIncludedFiles()), parsed.options); 358 } catch (err) { 359 if (err instanceof diag.DiagnosticError) { 360 let diagnostic = diag.getDiagnostic(err.code); 361 if (diagnostic != undefined) { 362 let diagnosticLog = diag.createDiagnostic(err.file, err.irnode, diagnostic, ...err.args); 363 diag.printDiagnostic(diagnosticLog); 364 } 365 } else if (err instanceof SyntaxError) { 366 LOGE(err.name, err.message); 367 } else { 368 throw err; 369 } 370 } 371} 372 373let dtsFiles = getDtsFiles(path["join"](__dirname, "../node_modules/typescript/lib")); 374process.argv.push(...dtsFiles); 375run(process.argv.slice(2), Compiler.Options.Default); 376global.gc(); 377