# web.compiler

A precompiler to run, compile and update (multi-layered) web projects.

| Language     | Compiles | Info                                                                                                                                  |
|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------|
| `java`       | ✓        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Ajava+-label%3Av1)        |
| `typescript` | ✓        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Atypescript+-label%3Av1+) |
| `twirl`      | ✓        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Atwirl+-label%3Av1+)      |
| `svelte`     | ✓        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Asvelte+-label%3Av1+)     |
| `sbt`        | ✖        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Asbt+-label%3Av1+)        |
| `scala`      | ✖        | [Known Issues](https://github.com/informaticon/web.compiler/issues?q=is%3Aopen+is%3Aissue+label%3Acompiler%3Ascala+-label%3Av1+)      |

## Requirements

1. Current version of [SBT](https://www.scala-sbt.org/download.html)
2. Current version of [watchman](https://facebook.github.io/watchman/)

### Compatibility

Wompiler aims to support the Node versions "LTS", "Current", and "Maintenance".
See [Node Release Schedule](https://github.com/nodejs/release?tab=readme-ov-file#release-schedule).

> [!NOTE]
> Use Node 14 for Wompiler Versions before `2.12`.

See [Contributing](#contributing) for compatibility while developing.

## Usage

> [!IMPORTANT]
> Refer to your project's README file for information about how to use wompiler with it.  
> If your project uses a plugin to run webpack commands, use the `-l` flag for `wompiler watch` and
> `wompiler precompile`. Otherwise, if you omit it, wompiler will run the webpack commands for you.

General tipps:

- Run `wompiler update` and `wompiler precompile -l` after you modify a `version.json` file.
- Run `wompiler clean` to delete generated files (See [Note about `wompiler clean`](#note-about-wompiler-clean))
- For a full list of commands please execute the `wompiler --help` command.

For product owners: Use [lib.sbt.base.cli-hook-plugin](https://github.com/informaticon/lib.sbt.base.cli-hook-plugin) to
hook into the build tasks to run the appropriate wompiler commands.

> [!CAUTION]
> Do not use `wompiler dist` if your project uses an SBT plugin that integrates wompiler.
> This would lead to the compilation being run multiple times.
> Use `sbt dist` in these cases.


### Note about `wompiler clean`

This will delete the following directories or files relative to your current working directory.  
Wompiler will not check whether you are located in a valid project.
It also will not validate the type of file it deletes (file / directory).


<details>
<summary>Open to see the deleted directories and files</summary>

| Path                    | Reason                                            |
|:------------------------|:--------------------------------------------------|
| ./target                | Generated by scala                                |
| ./project/target        | Generated by scala                                |
| ./public/bundles        | Generated by webpack                              |
| ./package-lock.json     | Generated by npm (using a generated package.json) |
| ./package.json          | Generated by wompiler                             |
| ./settings.json         | Generated by wompiler                             |
| ./conf/application.conf | Generated by wompiler                             |
| ./conf/routes           | Generated by wompiler                             |
| ./app/mirror            | Generated by wompiler                             |
| ./app/inclMirror        | Generated by wompiler                             |
| ./node_modules          | Generated by npm (using a generated package.json) |

</details>

## Limitations

### Annotations

No `extends` clause is permitted in Java annotation types. Therefore, they cannot be overridden in the traditional
sense.  
See [Java Language Specification - Chapter 9.6](https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6).

Create a layered type like `public @interface MyAnnotationL1 {..}` anyway, because wompiler copies the members of the
highest layer to the mirror. This means that if you "override" an annotation using wompiler, you have to copy the member
declarations from all other layers! When you add a new member, it must have a default value so that the usages of the
annotation in the code of the other layers are not broken.

### Twirl

Function types like `(String, Int) => Html` are currently not fully supported by wompiler. This affects the apply
signature (`@(...)`) and the constructor signature (`@this(...)`).

Only function types that describe exactly one parameter without using neither parentheses (`(`) nor square brackets
(`[`) in any part of the declaration, will lead to correct syntax of the mirror file (e.g., `String => Int`).

_Notice that the types in function types are not processed for creating the necessary `@import` statements!_

### Ebean Models

Ebean Model classes are handled in a different way from other java classes. The code of all layers including fields,
constructors, and methods are copied to the mirror instead of the mirror class extending from the highest layer class.
The reason is that ebean doesn't handle fields of parent classes.
The Ebean Model compiler is limitted in the following ways:

- **Generic Supertype**: Nested generic supertypes are not supported  
  OK:
  ```java
  public class XyzL2 extends SuperModel<Abc> {}
  ```
  Unsupported:
  ```java
  public class XyzL2 extends SuperModel<List<String>> {}
  ```
- **Additional Bounds in Class Type Arguments** have no defined behaviour and may not be precompiled correctly.  
  OK:
  ```java
  public class MyClass<T extends SuperType<X>> { }
  ```
  Unsupported:
  ```java
  public class MyClass<T extends SuperType<X> & Interface<UUID>> { }
  ```
- **Method and Constructor Annotations**: Only repeatable or the following marker annotations are supported when the
  annotation is repeated on the overridden method in further layers. Using a non-marker variant of one of the following
  annotations or any other annotation might lead to a compiler error stating that a non-repeatable annotation was
  repeated in the mirror class.  
  Supported annotations are: `@Override`, `@Nonnull`, `@Nullable`, `@CheckReturnValue`, `@CheckForNull`, `@Deprecated`,
  `@Transactional`  
  OK:
  ```java
  @Nonnull @Override public void test() {}
  ```
  Unsupported:
  ```java
  @Nonnull(when = When.MAYBE) @Override public void test() {}
  ```
- **Super Calls**: Calling `super(...)` or `super.method(...)` only has a defined behaviour in the constructor or method
  that overrides the excact super method to call.  
  OK:
  ```java
  public void test() {
    super.test();
  }
  ```  
  Unsupported:
  ```java
  public void test() {
    super.doSomething();
  }
  ```

## Contributing

> [!NOTE]
> Build the project using a "Current" or "LTS" (Active) Node version.
> See [Node Release Schedule](https://github.com/nodejs/release?tab=readme-ov-file#release-schedule).

If you want to contribute to the wompiler project, follow the steps below.

1. Clone the Repository
2. Run `npm i` to install dependencies
3. Implement your fixes or features **on a separate branch**
4. Test your code
   1. "Install" your changes locally by running `npm link` in the root folder of the wompiler project. (Only once)
   2. Build the project after each change: `npm run build`
   3. Change directories to a web project you can test your changes with
   4. Run wompiler using the commands you're familiar with, like `wompiler precompile` or any custom alias. Because you
      ran the `link` command, this now runs your development version of wompiler. &ast;
   5. When you're done testing, use `npm i -g @informaticon/web.compiler` to re-install the production version of
     wompiler.
5. Commit your changes
6. Open a Pull Request on GitHub

&ast; If you want to attach a debugger, you need to run `node --inspect-brk <PATH-TO-WOMPILER>/dist/cli.js <command>`.
On Windows, an example might look like this:\
`node --inspect-brk %userprofile%\AppData\Roaming\npm\node_modules\@informaticon\web.compiler\dist\cli.js precompile`.

## Release

Only create a new release if your changes have been reviewed and merged into `master`/`next`.

1. Update version and automatically create git tag\
   `npm version (major|minor|patch)` on `master`\
   or `npm version (premajor|preminor|prepatch|prerelease) --preid=rc` on `next`
2. Push the newly created tag\
   `git push origin --tags`
