All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsweet.transpiler.TypeScript2JavaScriptWithTscTranspiler Maven / Gradle / Ivy

The newest version!
package org.jsweet.transpiler;

import static java.util.Arrays.asList;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.jsweet.transpiler.util.ErrorCountTranspilationHandler;
import org.jsweet.transpiler.util.ProcessUtil;

public class TypeScript2JavaScriptWithTscTranspiler extends TypeScript2JavaScriptTranspiler {

    private Process tsCompilationProcess;

    @Override
    protected synchronized void doTranspile( //
            ErrorCountTranspilationHandler transpilationHandler, //
            Collection tsFiles, //
            Collection tsSourceFiles, //
            JSweetOptions options, //
            boolean ignoreErrors, //
            OnTsTranspilationCompletedCallback onTsTranspilationCompleted) throws Exception {

        if (tsCompilationProcess != null && options.isTscWatchMode()) {
            return;
        }
        logger.debug("ts2js with TSC: " + tsFiles);
        if (options.isTscWatchMode()) {
            watchedFiles = tsSourceFiles;
        }

        LinkedList args = new LinkedList<>();
        args.addAll(asList("--target", options.getEcmaTargetVersion().name()));

        if (options.isUsingModules()) {
            args.add("--module");
            args.add(options.getModuleKind().toString());
        }

        args.add("--moduleResolution");
        args.add(options.getModuleResolution().toString());

        if (options.getEcmaTargetVersion().ordinal() >= EcmaScriptComplianceLevel.ES5.ordinal()) {
            args.add("--experimentalDecorators");
            args.add("--emitDecoratorMetadata");
        }

        if (options.isTscWatchMode()) {
            args.add("--watch");
        }
        if (options.isGenerateSourceMaps()) {
            args.add("--sourceMap");
        }
        if (options.isGenerateDeclarations()) {
            args.add("--declaration");
        }

        if (options.getDeclarationsOutputDir() != null) {
            args.addAll(asList("--declarationDir", options.getDeclarationsOutputDir().getAbsolutePath()));
        }

        args.addAll(asList("--rootDir", options.getTsOutputDir().getAbsolutePath()));

        if (options.getJsOutputDir() != null) {
            args.addAll(asList("--outDir", options.getJsOutputDir().getAbsolutePath()));
        }

        File tscRootFile = getOrCreateTscRootFile(options.getTsOutputDir());
        if (tscRootFile.exists()) {
            args.add(relativizeTsFile(options.getTsOutputDir(), tscRootFile).toString());
        }

        for (File file : tsFiles) {
            String filePath = relativizeTsFile(options.getTsOutputDir(), file).toString();
            args.add(filePath);
        }

        logger.info("launching tsc...");
        boolean[] fullPass = { true };

        if (options.isSkipTypeScriptChecks()) {
            args.add("--skipDefaultLibCheck");
            args.add("--skipLibCheck");
        }

        tsCompilationProcess = ProcessUtil.runCommand("tsc", options.getTsOutputDir(), options.isTscWatchMode(),
                line -> {
                    logger.info(line);
                    TscOutput output = parseTscOutput(line);
                    if (output.position != null) {
                        if (ignoreErrors) {
                            return;
                        }
                        SourcePosition position = SourceFile.findOriginPosition(output.position, tsSourceFiles);
                        if (position == null) {
                            transpilationHandler.report(JSweetProblem.INTERNAL_TSC_ERROR, output.position,
                                    output.message);
                        } else {
                            transpilationHandler.report(JSweetProblem.MAPPED_TSC_ERROR, position, output.message);
                        }
                    } else {
                        if (output.message.startsWith("message TS6042:")) {
                            onTsTranspilationCompleted.call(fullPass[0], transpilationHandler, tsSourceFiles);
                            fullPass[0] = false;
                        } else {
                            // TODO enhance tsc feedbacks support: some
                            // messages are swallowed here: for instance
                            // error TS1204: Cannot compile modules into
                            // 'commonjs', 'amd', 'system' or 'umd' when
                            // targeting 'ES6' or higher.
                        }
                    }
                }, process -> {
                    tsCompilationProcess = null;
                    onTsTranspilationCompleted.call(fullPass[0], transpilationHandler, tsSourceFiles);
                    fullPass[0] = false;
                }, () -> {
                    if (!ignoreErrors && transpilationHandler.getProblemCount() == 0) {
                        transpilationHandler.report(JSweetProblem.INTERNAL_TSC_ERROR, null, "Unknown tsc error");
                    }
                }, args.toArray(new String[0]));
    }

    private Collection watchedFiles;
    /**
     * The name of the file generated in the root package to avoid the TypeScript
     * compiler to skip empty directories.
     */
    public final static String TSCROOTFILE = ".tsc-rootfile.ts";

    private Path relativizeTsFile(File tsOutputDir, File file) {
        try {
            return tsOutputDir.getAbsoluteFile().getCanonicalFile().toPath()
                    .relativize(file.getAbsoluteFile().getCanonicalFile().toPath());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static class TscOutput {
        public SourcePosition position;
        public String message;

        @Override
        public String toString() {
            return message + " - " + position;
        }
    }

    private static Pattern errorRE = Pattern.compile("(.*)\\((.*)\\): error TS[0-9]+: (.*)");

    private static TscOutput parseTscOutput(String outputString) {
        Matcher m = errorRE.matcher(outputString);
        TscOutput error = new TscOutput();
        if (m.matches()) {
            String[] pos = m.group(2).split(",");
            error.position = new SourcePosition(new File(m.group(1)), null, Integer.parseInt(pos[0]),
                    Integer.parseInt(pos[1]));
            StringBuilder sb = new StringBuilder(m.group(3));
            sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
            if (sb.charAt(sb.length() - 1) == '.') {
                sb.deleteCharAt(sb.length() - 1);
            }
            error.message = sb.toString();
        } else {
            error.message = outputString;
        }
        return error;
    }

    public Collection getWatchedFiles() {
        return watchedFiles;
    }

    public SourceFile getWatchedFile(File javaFile) {
        if (watchedFiles != null) {
            for (SourceFile f : watchedFiles) {
                if (f.getJavaFile().getAbsoluteFile().equals(javaFile.getAbsoluteFile())) {
                    return f;
                }
            }
        }
        return null;
    }

    public void stopWatch() {
        if (tsCompilationProcess != null) {
            tsCompilationProcess.destroyForcibly();
            while (tsCompilationProcess != null && tsCompilationProcess.isAlive()) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    logger.error(e.getMessage(), e);
                }
                logger.error("tsc did not terminate");
            }
            try {
                if (tsCompilationProcess != null) {
                    tsCompilationProcess.waitFor();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            tsCompilationProcess = null;
            watchedFiles = null;
        }
    }

    private File getOrCreateTscRootFile(File tsOutputDir) throws IOException {
        File tscRootFile = new File(tsOutputDir, TSCROOTFILE);

        if (!tscRootFile.exists()) {
            FileUtils.write(tscRootFile, "// Root empty file generated by JSweet to avoid tsc behavior, which\n"
                    + "// does not preserve the entire file hierarchy for empty directories.", false);
        }
        return tscRootFile;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy