asciidoctor.js icon indicating copy to clipboard operation
asciidoctor.js copied to clipboard

Implement File.mtime in a browser environment

Open ggrossetie opened this issue 7 years ago • 10 comments

I've opened an issue upstream: https://github.com/opal/opal/issues/1870 But I'm still not sure that we can return a reliable value and/or that this method make sense in a browser environment.

@mojavelinux Would you consider adding a special case in Asciidoctor core in https://github.com/asciidoctor/asciidoctor/blob/8c11ef85071c778f1d5428dc1bd841c47eea1b42/lib/asciidoctor.rb#L1326

Basically what we need to do is check that we are running in a browser environment (Opal) and return the current date.

Ref: #522

ggrossetie avatar Jul 26 '18 16:07 ggrossetie

I'm happy to make an exception, but why would Asciidoctor.js be attempting to load a file in a browser environment in the first place? I would think that it can only use load with a string. I'm actually surprised this is the first place it fails.

mojavelinux avatar Jul 27 '18 03:07 mojavelinux

I'm happy to make an exception, but why would Asciidoctor.js be attempting to load a file in a browser environment in the first place?

I guess that since the API is available, why not ? 😄 One use case could be an HTML page with an input text where you enter an URL to an AsciiDoc file and the JavaScript would to:

const html = asciidoctor.convertFile(url);

Arguably the user could load the content and then call the convert function or use this workaround:

const html = asciidoctor.convert('include::https://path.to/document.adoc');

But since most of the IO operations are implemented in a browser environment, I think we should handle this case. Otherwise we should document that convertFile and loadFile are not available in a browser environment and return a better exception than mtime is not implemented.

ggrossetie avatar Jul 27 '18 16:07 ggrossetie

I agree we could treat a URL as a file, but that requires much more than just a change to mtime. There are a lot of assumptions down the load/convert file path that the resource is, in fact, a file. So if/when we make this change, we need to think about it fully and not just do a quick hack. Right now, anything that is not filesystem based should be using load/convert.

mojavelinux avatar Jul 27 '18 23:07 mojavelinux

Otherwise we should document that convertFile and loadFile are not available in a browser environment

I agree this is the right change in the shortterm. But it's not just "not available in a browser environment". It's more, when we say file, we mean a real filesystem path.

mojavelinux avatar Jul 27 '18 23:07 mojavelinux

(because even Antora has to avoid loadFile because Antora is using a virtual file system).

mojavelinux avatar Jul 27 '18 23:07 mojavelinux

So if/when we make this change, we need to think about it fully and not just do a quick hack. Right now, anything that is not filesystem based should be using load/convert.

Indeed I didn't think it through.

I agree we could treat a URL as a file, but that requires much more than just a change to mtime. There are a lot of assumptions down the load/convert file path that the resource is, in fact, a file.

Actually the following code is working in a browser environment if I implement mtime:

class File
  def mtime
    Time.now
  end
end
const opts = {to_file: false};
const html = asciidoctor.convertFile('https://raw.githubusercontent.com/asciidoctor/asciidoctor.js/master/spec/fixtures/include.adoc', opts);
expect(html).to.include('include content');

Having said that, I still think that you are right. We need to think about it fully otherwise it will be fragile. I've experienced two issues during my experimentation:

  • It's "mandatory" to use to_file: false, otherwise an exception will be thrown since Asciidoctor will try to write a file (unlikely to succeed in a browser environment): TypeError: self.write_proc is not a function
  • If the safe mode is safe, Asciidoctor will try to embed the default stylesheet but we don't know for sure where the file is located: IOError: Error reading file or directory: css/asciidoctor.css; reason: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'file:///path/to/document/css/asciidoctor.css'.

ggrossetie avatar Jul 28 '18 08:07 ggrossetie

Hello, I'm working in Angular project and i want to générate html page from .adoc file,

but i'm stuck with this error, how can i fix it ?

core.js:6498 ERROR Error: Uncaught (in promise): mtime: asciidoctor: FAILED: <stdin>: Failed to load AsciiDoc document - undefined method `mtime' for #<File:0x4c0>
    at resolvePromise (zone.js:1213)
    at resolvePromise (zone.js:1167)
    at zone.js:1279
    at ZoneDelegate.invokeTask (zone.js:406)
    at Object.onInvokeTask (core.js:28679)
    at ZoneDelegate.invokeTask (zone.js:405)
    at Zone.runTask (zone.js:178)
    at drainMicroTaskQueue (zone.js:582)
    at ZoneTask.invokeTask [as invoke] (zone.js:491)
    at invokeTask (zone.js:1600)

my typescript class

import { Component, OnInit } from '@angular/core';
import Processor, { Asciidoctor } from 'asciidoctor';

import asciidoctorHtml5s  from 'asciidoctor-html5s';

@Component({
  selector: 'app-doc',
  templateUrl: './doc.component.html',
  styleUrls: ['./doc.component.scss']
})
export class DocComponent implements OnInit {

  processor : Asciidoctor;
  html5: string | Asciidoctor.Document;

  constructor() {
    this.processor  = Processor();
    // Register the HTML5s converter and supporting extension.
    asciidoctorHtml5s.register()

    // default option
    const defaultOptions = {
      safe: "safe",
      backend: "html5s",
      to_file: false,
      attributes: ['showtitle']
    }
    this.html5 = this.processor.convertFile('./../../doc/index.adoc', defaultOptions)
  }

  ngOnInit(): void {
  }
}

jean-Box avatar Dec 29 '21 10:12 jean-Box

It depends on how you want to manage your AsciiDoc files. You can fetch the content of your file from your Angular application (client side) using fetch or XMLHttpRequest:

fetch('http://localhost:1234/doc/index.adoc')
  .then(response => response.text())
  .then(data => {
  	processor.convert(data, defaultOptions)
  })

It might also be possible to import the file at build time and use convert with the file contents instead of convertFile.

ggrossetie avatar Dec 29 '21 23:12 ggrossetie

You can join the community chat at https://asciidoctor.zulipchat.com/ to ask usage questions, someone in the community might provide a better answer.

ggrossetie avatar Dec 29 '21 23:12 ggrossetie

thank @Mogztter

i do this and it's work i convert adoc file in build time, and i load it in componant with http.get

doc.component.html <div [innerHTML]="myExternalHTML"></div>

doc.component.ts

import { Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-doc',
  templateUrl: './doc.component.html',
  styleUrls: ['./doc.component.scss']
})
export class DocComponent implements OnInit {

  myExternalHTML :any;

  constructor(private http: HttpClient,  private sanitizer: DomSanitizer) {
    this.http.get('./../../assets/html/index.html', { responseType: 'text' })
    .subscribe((response :any) => {
      this.myExternalHTML = this.sanitizer.bypassSecurityTrustHtml(response);;
    }, (error: any) => {
      console.log('Error: ' + error);
    });
  }

  ngOnInit(): void {
  }

}

Hello, I'm working in Angular project and i want to générate html page from .adoc file,

but i'm stuck with this error, how can i fix it ?

core.js:6498 ERROR Error: Uncaught (in promise): mtime: asciidoctor: FAILED: <stdin>: Failed to load AsciiDoc document - undefined method `mtime' for #<File:0x4c0>
    at resolvePromise (zone.js:1213)
    at resolvePromise (zone.js:1167)
    at zone.js:1279
    at ZoneDelegate.invokeTask (zone.js:406)
    at Object.onInvokeTask (core.js:28679)
    at ZoneDelegate.invokeTask (zone.js:405)
    at Zone.runTask (zone.js:178)
    at drainMicroTaskQueue (zone.js:582)
    at ZoneTask.invokeTask [as invoke] (zone.js:491)
    at invokeTask (zone.js:1600)

my typescript class

import { Component, OnInit } from '@angular/core';
import Processor, { Asciidoctor } from 'asciidoctor';

import asciidoctorHtml5s  from 'asciidoctor-html5s';

@Component({
  selector: 'app-doc',
  templateUrl: './doc.component.html',
  styleUrls: ['./doc.component.scss']
})
export class DocComponent implements OnInit {

  processor : Asciidoctor;
  html5: string | Asciidoctor.Document;

  constructor() {
    this.processor  = Processor();
    // Register the HTML5s converter and supporting extension.
    asciidoctorHtml5s.register()

    // default option
    const defaultOptions = {
      safe: "safe",
      backend: "html5s",
      to_file: false,
      attributes: ['showtitle']
    }
    this.html5 = this.processor.convertFile('./../../doc/index.adoc', defaultOptions)
  }

  ngOnInit(): void {
  }
}

jean-Box avatar Dec 30 '21 10:12 jean-Box