asciidoctor / asciidoctor.js

:scroll: A JavaScript port of Asciidoctor, a modern implementation of AsciiDoc
https://asciidoctor.org
MIT License
734 stars 135 forks source link

Make asciidoctor.js sized for the web #647

Open rmannibucau opened 5 years ago

rmannibucau commented 5 years ago

Minified asciidoctor.js is > 1Mb which makes it just not usable in browser case. Compared to markdown (I agree it has less feature but it is the main rival) which is 36k it is a dead end, even gzipped.

This issue is about ensuring adoc.js is reduced enough - likely ~100k - to be usable in that context. I understand it likely means to drop opal but this is worth it to stay portable (environments).

mojavelinux commented 5 years ago

You can't drop Opal entirely since Asciidoctor.js depends on it to function. But if what you mean is to split it into to separate JavaScript files, then that would work.

Currently, Asciidoctor.js produces bundles that target different environments. One of them is browser. This file includes the Opal code and is not minified. We could consider producing a minimized version with and without Opal. However, this would bloat the npm package. So perhaps there is better way to publish it, or we could provide a post-install script that produces it after installation.

It's not that hard to output the file. You'd simply cut out all the lines before // UMD Module, then run it through a minimizer.

mojavelinux commented 5 years ago

We're not comparing ourselves to Markdown. Asciidoctor.js is made the way it is, and that's not going to change until we have a spec and could consider developing an implementation from scratch. So we can do what we can do to minimize, but Asciidoctor.js is going to rely on Opal for the foreseeable future.

mojavelinux commented 5 years ago

The result I get is 734K after removing Opal (which you still have to load, but you can do it separately) and minimizing it with uglify-js. If I use the google-closure-compiler, I can get it down to 434K.

If you drop the extensions API, you can get down to 674K (402K with google-closure-compiler). If you only need to parse, and not convert, you can cut out the HTML5 converter to get down to 600K (359K with google-closure-compiler).

I wasn't able to get google-closure-compiler to run in advanced mode because of 4 errors, but that could likely reduce it further. Got it to run. Result was 301K (without Opal, extensions API, and HTML5 converter).

If I just take out Opal and run the google-closure-compiler in advanced mode, I get get the size down to 367K. (I'm not sure if that runs, but in theory it will). If I leave Opal in (which gives you an idea of total size), it ends up being 632K.

(Some of the changes for 2.0 could further reduce the transpiled size).

ggrossetie commented 5 years ago

You could also try to do tree shaking on Opal/Asciidoctor.js since we do not use everything included in the Opal Runtime: https://webpack.js.org/guides/tree-shaking/

mojavelinux commented 5 years ago

@Mogztter I'm thinking this would be some great information for the docs, at least to start. "How to optimize asciidoctor.js for the browser". Perhaps the project could provide a minimizer tool that would execute what the docs talk about with some presets.

rmannibucau commented 5 years ago

This doesnt help since you still need to include Opal, and renderer at some point so just splitting and minimizing doesn't solve the issue today, this is why I suggested that to solve this issue, dropping opal can be the only way. Automated tools don't reduce it enough on the overall bundle.

mojavelinux commented 5 years ago

Dropping Opal is dropping Asciidoctor.js. What you are suggestion just doesn't make any sense.

rmannibucau commented 5 years ago

@mojavelinux happy to not drop opal but my reflection path is the following one:

  1. current browser bundle is not usable into the bundle
  2. what takes size in the bundle and prevent the minification is mainly opal

Therefore I think it is likely the option with the most chances to work. However I'm fine while the functional bundle size of adoctor.js becomes acceptable, whatever it means technically.

ggrossetie commented 5 years ago

I can get Asciidoctor.js with Opal included down to 175 Ko gzipped using Google Clojure Compiler in ADVANCED mode.

I removed the following modules from Opal:

We should be able to remove corelib/time (1K lines of code) if we introduce a few methods in Asciidoctor core (but maybe this Ruby module is used by other modules: Logger ?). corelib/numeric could also be trimmed down.

As mentioned by @mojavelinux, depending on your use case you could also remove unused modules in Asciidoctor.

The code generated by Opal is modular. You can remove a whole Opal.modules["xyz"] definition and the associated require to reduce the size of the bundle.

This issue is about ensuring adoc.js is reduced enough - likely ~100k - to be usable in that context. I understand it likely means to drop opal but this is worth it to stay portable (environments).

@rmannibucau 100k gzipped ? If this is the case then we are not very far with a few cleanups.

rmannibucau commented 5 years ago

I was more on 100k not gzipped but I agree a quick win for 100k gzip is likely worth it, but it can't be the final resolution IMHO

ggrossetie commented 5 years ago

We can use the coverage tab on Chrome to find out which functions are used or not.

I'm using serve (https://www.npmjs.com/package/serve) to run a local HTTP server and then browse to: http://localhost:5000/spec/browser/

To open the coverage tag, open the DevTools (F12), then click on the 3 vertical dots, then "More tools" > "Coverage".

Using the tests suite about 48% of asciidoctor-browser.js is unused but we do not have 100% tests coverage.

The upload feature on GitHub is currently unavailable but you get the idea :wink:

ggrossetie commented 5 years ago

@mojavelinux I think we can safely remove corelib/math, corelib/complex and corelib/rationale ? Asciidoctor is not using any of them right ?

We should probably also remove corelib/random but I know this module is used in other API (like Array#sample) so it might be used somewhere...

I've created a new repository where I intend to make a distribution of the Opal Runtime specifically designed for Asciidoctor.js (browser edition): https://github.com/mogztter/asciidoctor-opal-runtime

https://github.com/mogztter/opal-node-runtime should remain for general purpose use.

Arguably we could also create a distribution of the Opal Runtime for Asciidoctor.js desktop edition (Node.js, Electron...). In this case the size does not matter that much but the execution should be faster (less code).

ggrossetie commented 5 years ago

I've created #653 and #654 to track concret actions to reduce the runtime size.