= Contributing to Asciidoctor Spring Backends

Unless otherwise noted, code in this project is released under the Apache 2.0 license.
If you would like to contribute something, or want to hack on the code this document should help you get started.


== Code of Conduct
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.



== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you to https://cla.pivotal.io/sign/spring[sign the Contributor License Agreement].
Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do.
Active contributors might be asked to join the core team, and given the ability to merge pull requests.



== Building from Source
Source can be built from the command line using https://gradle.org[Gradle] on JDK 1.8 or above.
We include https://docs.gradle.org/current/userguide/gradle_wrapper.html[Gradle's wrapper scripts] (`./gradlew` or `gradlew.bat`) that you can run rather than needing to install Gradle locally.

The project can be built from the root directory using the standard Gradle command:

[indent=0]
----
	$ ./gradlew build
----



== Working with the Code
There are quite a few moving parts to this project and a lot of different technologies involved.
The project can broadly be split into two parts:

. An AsciidoctorJ compatible extension providing the backends
. A set of web assets (CSS & Javascript) used by the HTML generated by the backend.



=== AsciidoctorJ Backend
The AsciidoctorJ backend is Ruby and Java code that provides a https://www.rubydoc.info/gems/asciidoctor/Asciidoctor/Converter[Asciidoctor::Converter] implementation.



==== Ruby Code
The main converter implementation is written in Ruby.
You can find the code in the link:src/main/ruby[`src/main/ruby`] folder.

The code delegates to the standard `html5` converter, but changes a few areas to give us the HTML that we need.
It also writes the CSS, Javascript and Images to the output directory.

The code needs to be packaged as a Gem so that it can be picked up by AsciidoctorJ.

This means that the Jar needs to have the following layout:

----
specifications/
  spring-asciidoctor-backends-0.0.0.gemspec
gems/
  spring-asciidoctor-backends-0.0.0/
    lib/
      spring-asciidoctor-backends
    data/
----

NOTE: The version number of the Gem isn't really important in our case so we set it to `0.0.0`.

If you need to edit the `gemspec` file you can find it in link:src/main/ruby[`src/main/gemspec`].

AsciidoctorJ won't find the converter from the Gem alone, it needs to be told that it's a `requireLibrary`.
This is done with the `SpringBackendsExtensionRegistry` class which is registered using `META-INF/services`.



==== Java Code
The code folding and chomping features are implemented in Java.
You can find the code in link:src/main/java[src/main/java].

There's a `convert_listing_content` method in the Ruby code that uses JRuby features to call the `ListingContentConverters` class.
The `ListingContentConverters` class is responsible for taking an Asciidoctor node and returning its content in a potentially modified form.
There a bit of adapter logic needed to convert the `RubyObject`, but nothing too onerous.

All the project tests are also written in Java.
There's a mixture of both unit tests and integration tests.
They're available at link:src/test/java[src/test/java].



==== Hacking on the Ruby Code
If you want to iterate quickly on the Ruby code it's often easier to run it directly rather the using the Gradle build.
You can do this by using the `asciidoctor` CLI command.

[source,shell]
----
$ asciidoctor --trace -r ./src/main/ruby/lib/spring-asciidoctor-backends.rb -b spring-html -D build/converted-asciidoc/ src/test/asciidoc/standard.adoc
----

NOTE: Using asciidoctor directly means that JRuby doesn't run.
Java calls will not occur and `ListingContentConverters` features will not apply.



=== Web Assets
Web assets are bundled up inside the Jar and copied out by the Ruby converter.
The following web assets are used:

* `site.css` file in the `/css` folder.
* `setup.js` and `site.js` files in the `/js` folder.
* Images in the `/img` folder.



==== Build
The web assets are all build using GulpJS which is invoked via NPM from the Gradle build.
You can also build the web assets directly using the gulp CLI.

Install it using NPM:

[source,shell]
----
$ npm install
----

Then run it with the `build` target:

[source,shell]
----
$ gulp build
----

The gulp build will do the usual front-end related tasks such as minification and linting.
Take a look `gulpfile.js` if you want to see the exact details.



==== Javascript
There are two javascript assets produced by the build:

* `setup.js` provides any initial setup and is loaded with a regular `<script>` element.
* `site.js` provides the majority of the Javascript and is loaded with a `<script async>` element.

The source code used to build these files is in link:src/main/js/setup[`src/main/js/setup`] and link:src/main/js/setup[`src/main/js/site`] respectively.

TIP: The `setup.js` file will block page loading and as such should only be used for critical functionality.
For example, it includes the dark theme switching logic so that the page doesn't flash when loading.

The following files are used to create the `setup.js` file:

* `layout.js` - Adds a `js` class to the `html` element for CSS to use for Javascript detection.
* `switchtheme.js` - Provides theme switcher logic

The following files are used to create the `site.js` file:

* `codetools.js` - Provides the copy and fold/unfold buttons
* `highlight.js` - Provide a HighlightJS bundle
* `tabs.js` - Provides tab switching support
* `toc.js` - Updates the table of contents when scrolling



==== CSS
The single `site.css` file is build from a number of smaller CSS files.
CSS files can be found in link:src/main/css[`src/main/css`].
PostCSS is used to follow `@Import` declarations and create a single file.

Although it's a little overkill for this project, CSS files are organized using the https://www.freecodecamp.org/news/managing-large-s-css-projects-using-the-inverted-triangle-architecture-3c03e4b1e6df/[inverted triangle] architecture.


----
--------------  _
\            / __ settings   : (settings.css, settings-dark.css)
 \          / ___ tools      : (none)
  \        / ____ generic    : (generic.css)
   \      / _____ elements   : (elements.css)
    \    / ______ objects    : (none)
     \  / _______ components : (components.css)
      \/ _________utilities  : (none)
----



===== CSS Variables
CSS variables are used extensively for colors and variable styling.
The `settings.css` file contains initial settings and `settings-dark.css` provides dark-mode overrides.

Variables are usually scoped to a specific area and then reference a more generic value.
For example, `--toc-font-color` is the font color of the TOC.
It has the value `var(--body-font-color)` which references the general body font color.



==== Images
Images are available in the link:src/main/img[`src/main/img`] folder.
It's best to use SVG images as much as possible.

A single `octicons-16.svg` file provides all the 16x16 icons used by the CSS.
This file contains a subset of the icons from https://github.com/primer/octicons.



==== Hacking on the web assets
If you need to work on the web assets you can run the following Gradle task:

[source,shell]
----
$ ./gradlew dev
----

Alternatively you can generate some test HTML and the run `gulp` directly:

[source,shell]
----
$ ./gradlew convertTestAsciidoc
$ gulp dev
----

NOTE: The `convertTestAsciidoc` Gradle task will convert the contents of the link:src/test/asciidoc[`src/test/asciidoc`] directory.

The `dev` task will start a server at `http://localhost:8080` and watch the source files for changes.
Saving any source file will trigger a rebuild and "live reload".



=== Formatting
Java, Javascipt and CSS files can be formatted by running `./gradlew format`.
Java files are formatted using `spring-javaformat`.
Other files are formatted using https://prettier.io/[prettier].
