asciidoctor / asciidoctor.js

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

How to load asciidoc file in browser environment and display as html? #522

Closed stefaneidelloth closed 6 years ago

stefaneidelloth commented 6 years ago

I created a text file raw.asciidoc with following content:

raw content

I also created an html file to load the asciidoc file and display it as html:

<html>
  <head>
    <script src="../../node_modules/asciidoctor.js/dist/browser/asciidoctor.js"></script>
  </head>

  <body>
    <div id="target"></div>
    <script>
      var asciidoctor = Asciidoctor();
      const doc = asciidoctor.loadFile('raw.asciidoc');

      var html = asciidoctor.convert(doc, {doctype: 'inline'});
      document.getElementById('target').innerHTML = html;
    </script>
  </body>
</html>

a) The line that should load the document causes following error in Chrome:

asciidoctor.js:5136 Uncaught $NoMethodError {$$id: 1088, name: "mtime", message: "asciidoctor: FAILED: <stdin>: Failed to load Ascii…ment - undefined method `mtime' for #<File:0x43e>", args: Array(0), backtrace: Array(11)}

=>How to resolve this?

b) I am not sure how to use the doc as input for the conversion. Is the line

var html = asciidoctor.convert(doc, {doctype: 'inline'});

right? If not, how to use the doc correctly to convert it?

c) Does the loadFile command automatically load nested/included files that are referenced by the main asciidoc file? If not, how can I load those referenced files?

=> Could you please provide a complete example on how to load an asciidoc file including includes and show it as html?

ggrossetie commented 6 years ago

Hello @stefaneidelloth

The code should be:

var asciidoctor = Asciidoctor();
const doc = asciidoctor.loadFile('raw.asciidoc');

var html = doc.convert(doc, {doctype: 'inline'}); // call the "convert" method on the Asciidoctor.Document object "doc"
      document.getElementById('target').innerHTML = html;

You can also use convertFile method:

const html = asciidoctor.convertFile('raw.asciidoc', {doctype: 'inline'});

Although the error message indicates that mtime method is undefined because you are in a browser environment and the modification time on a file is not available. That's why the method is undefined.

I need to read the code again because I think that the convertFile and loadFile should work on a browser environment.

Does the loadFile command automatically load nested/included files that are referenced by the main asciidoc file?

Yes using XMLHttpRequest. You can read more about how the include directive is working in a browser environment: https://asciidoctor-docs.netlify.com/asciidoctor.js/spec/browser-include-spec/

What version of Asciidoctor.js are you using ?

stefaneidelloth commented 6 years ago

Thank you for the fast reply.

What version of Asciidoctor.js are you using ?

After installing asciidoctor.js with npm, I have following version entry in my package.json: "asciidoctor.js": "^1.5.7-rc.1",

The convertFile variant gives the same error in the browser.

ggrossetie commented 6 years ago

I've just checked the code and mtime method is not implemented in a browser environment. Meaning that convertFile and loadFile won't work in a browser environment.

Though Asciidoctor will only call the mtime method on the main document (file). As a workaround, you can use the convert method with an AsciiDoc-formatted string:

const html = asciidoctor.convert('http://asciidoc.org[AsciiDoc] is a _lightweight_ markup language...', {doctype: 'inline'});

Note: I'm not sure that you want to use an inline doctype. An inline doctype will only convert a single paragraph. By default the doctype is article and will convert a full document.

If you use the convert function, the header and the footer won't be included but since you want to inject the content on an existing HTML page I think this is what you want. To show the document title you will need to set the showtitle attribute.

If I'm not mistaken, the final code should look like this:

const content = '= Document with includes\n\n== First section\ninclude::./section1.adoc[]';
const html = asciidoctor.convert(content, {attributes: ['showtitle']});
ggrossetie commented 6 years ago

OH! I didn't read your first comment:

I created a text file raw.asciidoc with following content: raw content

In this case you should use definitely use an inline doctype:

const html = asciidoctor.convert('raw content', {doctype: 'inline'});

Let me know if it's working.

I will create a new issue to implement File.mtime in a browser environment. We will most likely have to return the current date or maybe the Last-Modified response header ? @mojavelinux What do you think ?

stefaneidelloth commented 6 years ago

The purpose of the content "raw content" was to provide a min example. In fact I would like to be able to convert a full article/book. Therefore, the default doctype article might be just fine for me. (The doctype inline resulted from copy past from your example section and I did not really know about the meaning, yet. )

I tried to define the main document as inline string in the html page and to use includes inside that string, pointing to referenced file raw.asciidoc.

I do not get an error any more. However, the include is rendered as link and does not include the content of the referenced document.

var asciidoctor = Asciidoctor();
const content = "include::raw.asciidoc[]"
const html = asciidoctor.convert(content,{doctype: 'article'});      
document.getElementById('target').innerHTML = html;

Output: raw.asciidoc

=> The strategy to define the main document in html and define some includes in the string does not seem to be a viable alternative.

Therefore I guess I have to wait until you fixed the mtime issue.

ggrossetie commented 6 years ago

Sorry I didn't mention that you need to define the safe mode: https://asciidoctor.org/docs/user-manual/#running-asciidoctor-securely

Include directive is enabled if the safe mode is safe or unsafe.

var asciidoctor = Asciidoctor();
const content = "include::raw.asciidoc[]"
const html = asciidoctor.convert(content,{safe: 'safe', header_footer: false});      
document.getElementById('target').innerHTML = html;
stefaneidelloth commented 6 years ago

Ok, now I got it. Maybe you want to include the example below in the user manual https://github.com/asciidoctor/asciidoctor.js/blob/master/docs/manual.adoc

Working solution if files are provided by http server:

raw.asciidoc:

Hello world from asciidoc

main.html:

<html>
  <head>
    <script src="./node_modules/asciidoctor.js/dist/browser/asciidoctor.js"></script>
  </head>

  <body>
       <div id="target"></div>
    <script>
      var asciidoctor = Asciidoctor();
      const content = "include::raw.asciidoc[]"
      const html = asciidoctor.convert(content,{safe: 'safe'});      
      document.getElementById('target').innerHTML = html;
    </script>
  </body>
</html>

Serving directly from the file system does not work due to cross orgin request restrictions of Chrome. A http-server can for example be started with the npm package http-server. Install the package globally, navigate to the directory and run http-server, e.g.

npm install http-server -g
cd d:\project
http-server

Then open a browser and go to http://localhost:8080/main.html if your http file is called "main.html" and located in d:\project. If you change files and want to refresh the browser, you might need to clear the cache to see the expected results (e.g. with Ctrl+F5).

Without http server following error occurs in Chrome:

Unresolved directive in <stdin> - include::raw.asciidoc[]

And the debugging window shows

asciidoctor.js:22327 [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
$$read @ asciidoctor.js:22327

asciidoctor.js:22336 Failed to load file:///D:/treezjs/raw.asciidoc: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

$$read @ asciidoctor.js:22336
asciidoctor.js:21309 asciidoctor: ERROR: <stdin>: line 1: include file not readable: raw.asciidoc

$writer @ asciidoctor.js:21309

If you know of an alternative that works directly on the file system without starting an extra http-server and without installing a browser extension (Asciidoctor.js Live Preview) please let me know.

ggrossetie commented 6 years ago

Ok, now I got it. Maybe you want to include the example below in the user manual https://github.com/asciidoctor/asciidoctor.js/blob/master/docs/manual.adoc

Sure thing! Thanks for the detailed example.

If you know of an alternative that works directly on the file system without starting an extra http-server and without installing a browser extension (Asciidoctor.js Live Preview) please let me know.

No I don't. You can disable CORS on your browser but that's not recommended.

For reference, we are currently migrating the documentation. The user manual is available at: https://asciidoctor-docs.netlify.com/asciidoctor.js/

gggeek commented 1 year ago

Sorry to comment on an old issue, but I have a bit of a hard time finding instructions on how to use asciidoctor(.js) from within an html page to render live an asciidoc file, as is the original ticket here.

  1. the situation is a bit messy when searching for 'asciidoctor' on npm: there are packages named asciidoctor, asciidoctor.js, asciidoctor.js-2 and may more. How do they relate? Is there some "official" package or set of packages?
  2. is the (current) asciidoctor available from an npm cdn? I can not seem to find it on jsdelivr.com, and on cdnjs there is an old version listed (up to 1.5.9)
  3. what is the preferred method to load the asciidoc content from a separate file?
stefaneidelloth commented 1 year ago

The doc on https://github.com/asciidoctor/asciidoctor says:

In addition to running on Ruby, Asciidoctor can be executed on a JVM using [AsciidoctorJ](https://github.com/asciidoctor/asciidoctorj) or in any JavaScript environment using [Asciidoctor.js](https://github.com/asciidoctor/asciidoctor.js).

And on

https://github.com/asciidoctor/asciidoctor.js

npm i asciidoctor --save

gggeek commented 1 year ago

@stefaneidelloth thanks, but what I am looking for is the possibility of using asciidoctor.js to deliver to end users documentation written as asciidoc, without having to convert it first (locally or in a CI/CD environment) to another format.

This means that I am trying to run asciidoctor.js from the end user's browser without having to install anything via npm - similarly to the scenario you described above, where the end user could run php -S, http-server or any other webserver capable of serving static files. That's why it would be nice to have asciidoctor.js available for loading on a cdn.

stefaneidelloth commented 1 year ago

Once you have asciidoctor.js in your node_modules folder, you can serve it together with your http file, can't you? For the case that your end users are in Europe, you want to locally serve js files anyway. Otherwise you need to ask for permission to redirect their IP and browser meta data.

gggeek commented 1 year ago

Once you have asciidoctor.js in your node_modules folder, you can serve it together with your http file, can't you?

For hosting the file somewhere on a server of mine, I surely can serve the node_modules folder. But then the point is moot - I can just transform the source docs to html and host the html.

For delivering the docs to the end users, I could add the asciidoctor.min.js, and any other required js/css file, to the tarball, but I'd rather avoid doing that if I can - 170KB might not look big, but it still is much bigger than the size of the docs themselves

mojavelinux commented 1 year ago

Rather than comment on close issues, we prefer if you ask open-ended questions in the project chat at https://chat.asciidoctor.org. You'll find that it is much more suitable place for dialog and allows us to manage the project more efficiently.

gggeek commented 1 year ago

Sure, sorry for the spam