• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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