diff --git a/doc/jsweet-language-specifications.md b/doc/jsweet-language-specifications.md index 5e6bf96d..c77163a0 100644 --- a/doc/jsweet-language-specifications.md +++ b/doc/jsweet-language-specifications.md @@ -1507,7 +1507,7 @@ Here is a more complete example with a full `jsweetconfig.json` configuration fi "include": [ "x.y.z" ] }, // do not generate any TypeScript code for Java-specific methods - "@Erase": { + "@Erased": { "include": [ "**.writeObject(..)", "**.readObject(..)", "**.hashCode(..)" ] }, // inject logging in all setters and getters of the x.y.z.A class diff --git a/doc/jsweet-language-specifications.pdf b/doc/jsweet-language-specifications.pdf index 462ef0e1..ad0a6103 100644 Binary files a/doc/jsweet-language-specifications.pdf and b/doc/jsweet-language-specifications.pdf differ diff --git a/doc/jsweet-language-specifications.tex b/doc/jsweet-language-specifications.tex index c998f8fe..0ec1a003 100644 --- a/doc/jsweet-language-specifications.tex +++ b/doc/jsweet-language-specifications.tex @@ -1521,7 +1521,7 @@ Here is a more complete example with a full \texttt{jsweetconfig.json} configura "include": [ "x.y.z" ] }, // do not generate any TypeScript code for Java-specific methods - "@Erase": { + "@Erased": { "include": [ "**.writeObject(..)", "**.readObject(..)", "**.hashCode(..)" ] }, // inject logging in all setters and getters of the x.y.z.A class diff --git a/transpiler/src/main/java/org/jsweet/JSweetConfig.java b/transpiler/src/main/java/org/jsweet/JSweetConfig.java index 6d2e8976..1d705d68 100644 --- a/transpiler/src/main/java/org/jsweet/JSweetConfig.java +++ b/transpiler/src/main/java/org/jsweet/JSweetConfig.java @@ -450,7 +450,7 @@ public abstract class JSweetConfig { public static final String FIELD_METHOD_CLASH_RESOLVER_PREFIX = "__"; /** - * The configuration file name. + * The default configuration file name. */ public static final String CONFIGURATION_FILE_NAME = "jsweetconfig.json"; diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java index a96f699a..41264c78 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java @@ -904,7 +904,7 @@ public class JSweetContext extends Context { footerStatements.add(0, footerStatement); } - private Map headers = new LinkedHashMap(); + private Map headers = new LinkedHashMap(); /** * Gets and clears the headers. @@ -938,7 +938,7 @@ public class JSweetContext extends Context { public String getHeader(String key) { return headers.get(key); } - + private Map globalsMapping = new HashMap<>(); /** @@ -1148,6 +1148,9 @@ public class JSweetContext extends Context { Collection filterDescriptors = annotationFilters.get(annotationType); if (filterDescriptors != null) { for (AnnotationFilterDescriptor filterDescriptor : filterDescriptors) { + if (filterDescriptor.inclusionPatterns == null) { + logger.error("no inclusion patterns found for annotation filter: " + annotationType); + } for (Pattern include : filterDescriptor.inclusionPatterns) { if (include.matcher(signature).matches()) { boolean excluded = false; diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java index 5b19080c..c7c3abd9 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java @@ -50,7 +50,7 @@ public interface JSweetOptions { /** * Constant string for the 'encoding' option. */ - String encoding = "bundle"; + String encoding = "encoding"; /** * Constant string for the 'enableAssertions' option. */ @@ -103,7 +103,7 @@ public interface JSweetOptions { * Constant string for the 'extraSystemPath' option. */ String extraSystemPath = "extraSystemPath"; - + /** * All the supported options. */ @@ -287,4 +287,10 @@ public interface JSweetOptions { */ ModuleResolution getModuleResolution(); + /** + * The configuration file, which is by default the + * jsweetconfig.json file in the current project. + */ + File getConfigurationFile(); + } \ No newline at end of file diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java index 4ad4a0f1..de617297 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java @@ -222,6 +222,7 @@ public class JSweetTranspiler implements JSweetOptions { private boolean skipTypeScriptChecks = false; private boolean disableSingleFloatPrecision = false; private ArrayList adapters = new ArrayList<>(); + private File configurationFile; /** * Manually sets the transpiler to use (or not use) a Java runtime. @@ -287,81 +288,91 @@ public class JSweetTranspiler implements JSweetOptions { private Map configuration; @SuppressWarnings("unchecked") - private T getConfigurationValue(String key) { - return (T) configuration.get(key); + private T getMapValue(Map map, String key) { + return (T) map.get(key); } /** * Applies the current configuration map. */ - public void applyConfiguration() { - for (String key : configuration.keySet()) { - if (!ArrayUtils.contains(JSweetOptions.options, key)) { - logger.error("unsupported option: " + key); + private void applyConfiguration() { + if (configuration.containsKey("options")) { + + @SuppressWarnings("unchecked") + Map options = (Map) configuration.get("options"); + + for (String key : options.keySet()) { + if (!ArrayUtils.contains(JSweetOptions.options, key)) { + logger.error("unsupported option: " + key); + } + } + if (options.containsKey(JSweetOptions.bundle)) { + setBundle(getMapValue(options, JSweetOptions.bundle)); + } + if (options.containsKey(JSweetOptions.noRootDirectories)) { + setNoRootDirectories(getMapValue(options, JSweetOptions.noRootDirectories)); + } + if (options.containsKey(JSweetOptions.sourceMap)) { + setGenerateSourceMaps(getMapValue(options, JSweetOptions.sourceMap)); + } + if (options.containsKey(JSweetOptions.module)) { + setModuleKind(ModuleKind.valueOf(getMapValue(options, JSweetOptions.module))); + } + if (options.containsKey(JSweetOptions.encoding)) { + setEncoding(getMapValue(options, JSweetOptions.encoding)); + } + if (options.containsKey(JSweetOptions.enableAssertions)) { + setIgnoreAssertions(!(Boolean) getMapValue(options, JSweetOptions.enableAssertions)); + } + if (options.containsKey(JSweetOptions.declaration)) { + setGenerateDeclarations(getMapValue(options, JSweetOptions.declaration)); + } + if (options.containsKey(JSweetOptions.tsOnly)) { + setGenerateJsFiles(!(Boolean) getMapValue(options, JSweetOptions.tsOnly)); + } + if (options.containsKey(JSweetOptions.ignoreDefinitions)) { + setGenerateDefinitions(!(Boolean) getMapValue(options, JSweetOptions.ignoreDefinitions)); + } + if (options.containsKey(JSweetOptions.header)) { + setHeaderFile(new File((String) getMapValue(options, JSweetOptions.header))); + } + if (options.containsKey(JSweetOptions.disableSinglePrecisionFloats)) { + setDisableSinglePrecisionFloats(getMapValue(options, JSweetOptions.disableSinglePrecisionFloats)); + } + if (options.containsKey(JSweetOptions.targetVersion)) { + setEcmaTargetVersion( + JSweetTranspiler.getEcmaTargetVersion(getMapValue(options, JSweetOptions.targetVersion))); + } + if (options.containsKey(JSweetOptions.tsout)) { + setTsOutputDir(new File((String) getMapValue(options, JSweetOptions.tsout))); + } + if (options.containsKey(JSweetOptions.dtsout)) { + setDeclarationsOutputDir(new File((String) getMapValue(options, JSweetOptions.dtsout))); + } + if (options.containsKey(JSweetOptions.jsout)) { + setJsOutputDir(new File((String) getMapValue(options, JSweetOptions.jsout))); + } + if (options.containsKey(JSweetOptions.candiesJsOut)) { + setJsOutputDir(new File((String) getMapValue(options, JSweetOptions.candiesJsOut))); + } + if (options.containsKey(JSweetOptions.moduleResolution)) { + setModuleResolution(getMapValue(options, JSweetOptions.moduleResolution)); + } + if (options.containsKey(JSweetOptions.extraSystemPath)) { + ProcessUtil.addExtraPath(extraSystemPath); } } - if (configuration.containsKey(JSweetOptions.bundle)) { - setBundle(getConfigurationValue(JSweetOptions.bundle)); - } - if (configuration.containsKey(JSweetOptions.noRootDirectories)) { - setNoRootDirectories(getConfigurationValue(JSweetOptions.noRootDirectories)); - } - if (configuration.containsKey(JSweetOptions.sourceMap)) { - setGenerateSourceMaps(getConfigurationValue(JSweetOptions.sourceMap)); - } - if (configuration.containsKey(JSweetOptions.module)) { - setModuleKind(ModuleKind.valueOf(getConfigurationValue(JSweetOptions.module))); - } - if (configuration.containsKey(JSweetOptions.encoding)) { - setEncoding(getConfigurationValue(JSweetOptions.encoding)); - } - if (configuration.containsKey(JSweetOptions.enableAssertions)) { - setIgnoreAssertions(!(Boolean) getConfigurationValue(JSweetOptions.enableAssertions)); - } - if (configuration.containsKey(JSweetOptions.declaration)) { - setGenerateDeclarations(getConfigurationValue(JSweetOptions.declaration)); - } - if (configuration.containsKey(JSweetOptions.tsOnly)) { - setGenerateJsFiles(!(Boolean) getConfigurationValue(JSweetOptions.tsOnly)); - } - if (configuration.containsKey(JSweetOptions.ignoreDefinitions)) { - setGenerateDefinitions(!(Boolean) getConfigurationValue(JSweetOptions.ignoreDefinitions)); - } - if (configuration.containsKey(JSweetOptions.header)) { - setHeaderFile(new File((String) getConfigurationValue(JSweetOptions.header))); - } - if (configuration.containsKey(JSweetOptions.disableSinglePrecisionFloats)) { - setDisableSinglePrecisionFloats(getConfigurationValue(JSweetOptions.disableSinglePrecisionFloats)); - } - if (configuration.containsKey(JSweetOptions.targetVersion)) { - setEcmaTargetVersion( - JSweetTranspiler.getEcmaTargetVersion(getConfigurationValue(JSweetOptions.targetVersion))); - } - if (configuration.containsKey(JSweetOptions.tsout)) { - setTsOutputDir(new File((String) getConfigurationValue(JSweetOptions.tsout))); - } - if (configuration.containsKey(JSweetOptions.dtsout)) { - setDeclarationsOutputDir(new File((String) getConfigurationValue(JSweetOptions.dtsout))); - } - if (configuration.containsKey(JSweetOptions.jsout)) { - setJsOutputDir(new File((String) getConfigurationValue(JSweetOptions.jsout))); - } - if (configuration.containsKey(JSweetOptions.candiesJsOut)) { - setJsOutputDir(new File((String) getConfigurationValue(JSweetOptions.candiesJsOut))); - } - if (configuration.containsKey(JSweetOptions.moduleResolution)) { - setModuleResolution(getConfigurationValue(JSweetOptions.moduleResolution)); - } - if (configuration.containsKey(JSweetOptions.extraSystemPath)) { - ProcessUtil.addExtraPath(extraSystemPath); - } + } + /** + * Reads configuration from current configuration file. + */ private void readConfiguration() { - File confFile = new File(JSweetConfig.CONFIGURATION_FILE_NAME); + File confFile = configurationFile == null ? new File(JSweetConfig.CONFIGURATION_FILE_NAME) : configurationFile; if (confFile.exists()) { try { - logger.info("configuration file found"); + logger.info("configuration file found: " + confFile); @SuppressWarnings("unchecked") Map fromJson = new Gson().fromJson(FileUtils.readFileToString(confFile), Map.class); configuration = fromJson; @@ -394,6 +405,31 @@ public class JSweetTranspiler implements JSweetOptions { */ public JSweetTranspiler(JSweetFactory factory, File workingDir, File tsOutputDir, File jsOutputDir, File extractedCandiesJavascriptDir, String classPath) { + this(null, factory, workingDir, tsOutputDir, tsOutputDir, extractedCandiesJavascriptDir, classPath); + } + + /** + * Creates a JSweet transpiler. + * + * @param configurationFile + * the configurationFile (uses default one if null) + * @param factory + * the factory used to create the transpiler objects + * @param workingDir + * the working directory (uses default one if null) + * @param tsOutputDir + * the directory where TypeScript files are written + * @param jsOutputDir + * the directory where JavaScript files are written + * @param extractedCandiesJavascriptDir + * see {@link #getExtractedCandyJavascriptDir()} + * @param classPath + * the classpath as a string (check out system-specific + * requirements for Java classpaths) + */ + public JSweetTranspiler(File configurationFile, JSweetFactory factory, File workingDir, File tsOutputDir, + File jsOutputDir, File extractedCandiesJavascriptDir, String classPath) { + this.configurationFile = configurationFile; this.factory = factory; readConfiguration(); if (tsOutputDir == null) { @@ -2041,4 +2077,9 @@ public class JSweetTranspiler implements JSweetOptions { this.adapters = new ArrayList<>(adapters); } + @Override + public File getConfigurationFile() { + return configurationFile; + } + } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/candy/CandyProcessor.java b/transpiler/src/main/java/org/jsweet/transpiler/candy/CandyProcessor.java index eb79bd72..1e64ef26 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/candy/CandyProcessor.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/candy/CandyProcessor.java @@ -213,10 +213,25 @@ public class CandyProcessor { return jarFilesCollector; } + private String normalizeVersion(String version) { + if (version == null) { + return null; + } + String[] v = JSweetConfig.getVersionNumber().split("\\."); + if (v.length == 2) { + return version; + } else if (v.length == 1) { + return v[0] + ".0"; + } else { + return v[0] + "." + v[1]; + } + } + private void checkCandyVersion(CandyDescriptor candy, TranspilationHandler transpilationHandler) { - String actualTranspilerVersion = JSweetConfig.getVersionNumber().split("-")[0]; - String candyTranspilerVersion = candy.transpilerVersion == null ? null : candy.transpilerVersion.split("-")[0]; + String actualTranspilerVersion = normalizeVersion(JSweetConfig.getVersionNumber().split("-")[0]); + String candyTranspilerVersion = normalizeVersion( + candy.transpilerVersion == null ? null : candy.transpilerVersion.split("-")[0]); if (candyTranspilerVersion == null || !candyTranspilerVersion.equals(actualTranspilerVersion)) { transpilationHandler.report(JSweetProblem.CANDY_VERSION_DISCREPANCY, null, diff --git a/transpiler/src/test/java/org/jsweet/test/transpiler/AbstractTest.java b/transpiler/src/test/java/org/jsweet/test/transpiler/AbstractTest.java index bd20bf20..053c9c3e 100644 --- a/transpiler/src/test/java/org/jsweet/test/transpiler/AbstractTest.java +++ b/transpiler/src/test/java/org/jsweet/test/transpiler/AbstractTest.java @@ -104,7 +104,11 @@ public class AbstractTest { protected static final String TMPOUT_DIR = "tempOut"; protected static void createTranspiler(JSweetFactory factory) { - transpiler = new JSweetTranspiler(factory, new File(TMPOUT_DIR), null, + createTranspiler(null, factory); + } + + protected static void createTranspiler(File configurationFile, JSweetFactory factory) { + transpiler = new JSweetTranspiler(configurationFile, factory, null, new File(TMPOUT_DIR), null, new File(JSweetTranspiler.TMP_WORKING_DIR_NAME + "/candies/js"), System.getProperty("java.class.path")); transpiler.setEcmaTargetVersion(EcmaScriptComplianceLevel.ES5); transpiler.setEncoding("UTF-8"); diff --git a/transpiler/src/test/java/org/jsweet/test/transpiler/TranspilerTests.java b/transpiler/src/test/java/org/jsweet/test/transpiler/TranspilerTests.java index cb8abcbe..84f65c99 100644 --- a/transpiler/src/test/java/org/jsweet/test/transpiler/TranspilerTests.java +++ b/transpiler/src/test/java/org/jsweet/test/transpiler/TranspilerTests.java @@ -493,4 +493,38 @@ public class TranspilerTests extends AbstractTest { transpiler.setHeaderFile(null); } + @Test + public void testConfigurationFile() { + SourceFile f = getSourceFile(CanvasDrawing.class); + File configurationFile = new File(f.getJavaFile().getParentFile(), "configuration-nobundle.json"); + createTranspiler(configurationFile, new JSweetFactory()); + assertTrue(transpiler.getHeaderFile().getPath().endsWith("header.txt")); + assertFalse(transpiler.isBundle()); + transpile(logHandler -> { + logHandler.assertNoProblems(); + try { + String generatedCode = FileUtils.readFileToString(f.getTsFile()); + assertTrue(generatedCode.startsWith("// This is a test header...")); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + }, f); + configurationFile = new File(f.getJavaFile().getParentFile(), "configuration-bundle.json"); + createTranspiler(configurationFile, new JSweetFactory()); + assertTrue(transpiler.getHeaderFile().getPath().endsWith("header.txt")); + assertTrue(transpiler.isBundle()); + transpile(ModuleKind.none, logHandler -> { + logHandler.assertNoProblems(); + try { + String generatedCode = FileUtils.readFileToString(f.getTsFile()); + assertTrue(generatedCode.startsWith("// This is a test header...")); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + }, f); + createTranspiler(new JSweetFactory()); + } + } diff --git a/transpiler/src/test/java/source/transpiler/configuration-bundle.json b/transpiler/src/test/java/source/transpiler/configuration-bundle.json new file mode 100644 index 00000000..f2fe2296 --- /dev/null +++ b/transpiler/src/test/java/source/transpiler/configuration-bundle.json @@ -0,0 +1,9 @@ +{ + "options": { + "header": "src/test/java/source/transpiler/header.txt", + "bundle": true + }, + "@Erased": { + "include": "*.main(..)" + } +} \ No newline at end of file diff --git a/transpiler/src/test/java/source/transpiler/configuration-nobundle.json b/transpiler/src/test/java/source/transpiler/configuration-nobundle.json new file mode 100644 index 00000000..73336ada --- /dev/null +++ b/transpiler/src/test/java/source/transpiler/configuration-nobundle.json @@ -0,0 +1,8 @@ +{ + "options": { + "header": "src/test/java/source/transpiler/header.txt" + }, + "@Erased": { + "include": "*.main(..)" + } +} \ No newline at end of file