Skip to content

Latest commit

 

History

History
233 lines (160 loc) · 10.3 KB

CONTRIBUTING-CODE.adoc

File metadata and controls

233 lines (160 loc) · 10.3 KB

Contributing Code

This guide will give you all the necessary information you need to become a successful Asciidoctor.js developer and code contributor!

Introduction

Asciidoctor.js is a direct port of Asciidoctor from Ruby to JavaScript using Opal, a Ruby-to-JavaScript transpiler. The project includes a npm build script that executes the Opal compiler on the Asciidoctor source code to produce the Asciidoctor.js scripts.

Opal parses the Ruby code and any required libraries, then rewrites the code into JavaScript under the Opal namespace. The resulting JavaScript can be executed within any JavaScript runtime environment (such as a browser).

To interact with the generated code, you either invoke the JavaScript APIs directly, or you can invoke native JavaScript objects from within the Ruby code prior to compilation.

Setup

To build Asciidoctor.js, you’ll need some tools:

  • Node.js >= 8 and npm (we recommend nvm to manage multiple active Node.js versions)

Start by cloning the source from GitHub:

$ git clone https://github.com/asciidoctor/asciidoctor.js.git

Next, run npm’s install command:

$ npm i
$ npm i --prefix packages/core

You’re now ready to build Asciidoctor.js.

Tip
Opal.js, the Ruby runtime in JavaScript is available in packages/core/node_modules/asciidoctor-opal-runtime/src/opal.js

Building

This section describes how to build Asciidoctor.js from source, execute the tests and try out the examples.

Building Asciidoctor.js

To build Asciidoctor.js, simply run the npm build script from the root of the project:

$ npm run build --prefix packages/core
Warning
The build system does not automate updating the dependency on ruby asciidoctor. If there is any chance the asciidoctor.js copy of the asciidoctor ruby code is out of date, run npm clean --prefix packages/core.
Note
The build task will make some minor code changes on Asciidoctor Ruby code. As you may know, strings are immutable in JavaScript, so we need to replace the gsub! and sub! methods. These changes are made at build time to keep the Ruby code as fast as possible.

This command produces one JavaScript file per environment in the packages/core/build directory:

  • asciidoctor-browser.js

  • asciidoctor-node.js

  • asciidoctor-graalvm.js

These files contain everything you need to use Asciidoctor in a JavaScript environment.

You’ll see this script in action when you run the examples, described next.

Building against a release

By default Asciidoctor.js will build against the main branch of Asciidoctor (Ruby). If you want to build against a release of Asciidoctor, you will need to define the ASCIIDOCTOR_CORE_VERSION environment variable.

For instance to build against Asciidoctor (Ruby) v2.0.10 use:

$ ASCIIDOCTOR_CORE_VERSION=v2.0.10 npm run build --prefix packages/core

Building against a local copy of Ruby Asciidoctor

The transpilation relies on a copy of the Ruby source in packages/core/build/asciidoctor. The build instructions above supply this by unpacking a tar.gz of the repository constructed by GitHub. Alternatively, you can supply this copy yourself. For the following posix-centric suggestions, assume ASCIIDOCTOR_RB is the local location of your Asciidoctor Ruby project.

  • Remove the current (contents of the) Ruby source directory:

    rm -rf packages/core/build/asciidoctor
  • Choose a method:

    • Using a symlink:

      ln -s $ASCIIDOCTOR_RB packages/core/build/asciidoctor`
    • Copying:

      cp -r $ASCIIDOCTOR_RB packages/core/build/
    • Unpacking a gem:

      mkdir packages/core/build/asciidoctor
      tar -xf $ASCIIDOCTOR_RB/pkg/asciidoctor-*.gem -C packages/core/build --include data.tar.gz
      tar -xzf packages/core/build/data.tar.gz -C packages/core/build/asciidoctor
  • For all of these, touch packages/core/build/asciidoctor.tar.gz will prevent npm build --prefix packages/core from overwriting your copy with a downloaded one.

  • To actually use your local source you’ll need to force recompile by:

    npm run clean:core --prefix packages/core

Using "quick" mode

By default the build will run the whole tests suite but if you want to quickly iterate on your code, we recommend to use the task build:quick:

$ npm run build:quick --prefix packages/core

This task will run only the tests against a Node.js environment.

Building and running the examples

To build the examples, simply run the npm examples task from the root of the project:

$ npm run examples --prefix packages/core

This command produces 3 examples in the packages/core/build/examples directory:

  • userguide_test.html demonstrate how a Ruby script can be compiled to JavaScript using the Opal Compiler and displayed within the browser as a normal HTML file. The Ruby script userguide_test.rb includes:

    • a string that contains an AsciiDoc source document

    • a call to the Asciidoctor API to render the content of that string to HTML

    • an event listener that inserts the generated HTML into the page

  • basic.html demonstrates how to convert an inline AsciiDoc content and insert the result in the HTML page

  • asciidoctor_example.html demonstrates how to convert a file using the include directive and insert the result in the HTML page

In order to visualize the result, a local http server must be started within the root of this project otherwise you will have a cross origin issue which prevents to load some resources and render the HTML. For this purpose, you can run the following command to start a HTTP server locally: npm run server --prefix packages/core

Once the server is listening, you can point your browser at:

If you open the first link, you should see the Asciidoctor.js README. The content on the page was rendered from AsciiDoc by Asciidoctor.js when you loaded the page!

Building and running benchmark

Currently, you can run benchmark against 3 JavaScript environment:

  • node

  • chrome

The following command will run benchmark against Node.js:

$ npm run benchmark node --prefix packages/core

Once packages/core/build/benchmark is initialized, you can run benchmark again (without building the project):

$ node packages/core/build/benchmark/node.js

Debugging

Compiling a Ruby application to JavaScript and getting it to run is a process of eliminating fatal errors. When the JavaScript fails, the message isn’t always clear or even close to where things went wrong. The key to working through these failures is to use the browser’s JavaScript console.

Chrome / Chromium

Chrome (and Chromium) has a very intuitive JavaScript console. To open it, press Ctrl+Shift+J or right-click on the page, select Inspect Element from the context menu and click the Console tab.

When an error occurs in the JavaScript, Chrome will print the error message to the console. The error message is interactive. Click on the arrow at the start of the line to expand the call trace, as shown here:

error in chrome console

When you identify the entry you want to inspect, click the link to the source location. If you want to inspect the state, add a breakpoint and refresh the page.

Chrome tends to cache the JavaScript files too aggressively when running local scripts. Make a habit of holding down Ctrl when you click refresh to force Chrome to reload the JavaScript.

Another option is to start Chrome with the application cache disabled.

$ chrome --disable-application-cache

Firefox

Firefox also has a JavaScript console. To open it, press Ctrl+Shift+J or right-click on the page, select Inspect Element from the context menu and click the Web Console tab.

When an error occurs in the JavaScript, Firefox will print the error message to the console. Unlike Chrome, the error message is not interactive. Its power, instead, lies under the hood.

To see the call trace when an exception occurs, you need to configure the Debugger to pause on an exception. Click the Debugger tab, click the configuration gear icon in the upper right corner of that tab and click Pause on exceptions. Refresh the page and you’ll notice that the debugger has paused at the location in the source where the exception is thrown.

error in javascript debugger

The call trace is displayed as breadcrumb navigation, which you can use to jump through the stack. You can inspect the state at any location by looking through the panels on the right.

Modifications to Asciidoctor (for compatibility)

Compiling Asciidoctor to JavaScript currently requires some changes in Asciidoctor. The goal is to eventually eliminate all of these differences so that Asciidoctor can be compiled to JavaScript as is.

Here’s a list of some of the changes that are currently needed:

  • Named posix groups in regular expressions are replaced with their ASCII equivalent

    • JavaScript doesn’t support named posix groups, such as [[:alpha:]])

  • A shim library is needed to implement missing classes in Opal, such as File and Dir

  • All mutable String operations have been replaced with assignments (this is done at build time)

    • JavaScript doesn’t support mutable strings

  • $~[0] used in place of $& and $~[n] in place of $n after running a regular expression (where n is 1, 2, 3…​)

  • Opal doesn’t recognize modifiers on a regular expression (e.g., multiline)

  • Optional, non-matching capture groups resolve to empty string in gsub block in Firefox (see http://www.bennadel.com/blog/1916-different-browsers-use-different-non-matching-captured-regex-pattern-values.htm)

  • Assignments without a matching value are set to empty string instead of nil (in the following example, b is set to empty string)

    a, b = "value".split ',', 2