saneef / eleventy-plugin-asciidoc

Eleventy plugin to add support for AsciiDoc.
MIT License
25 stars 6 forks source link

Need ability to provide dynamic options per-page #14

Closed tbroyer closed 10 months ago

tbroyer commented 10 months ago

Context:

We've been using this plugin to process our pages and initially didn't configure an imagesdir. That way, a page at foo/bar.adoc will output to _site/foo/bar/index.html, and image: macros would generate relative URLs so image:baz.png[] in that page would generate an <img src=baz.png, and we organized our images as foo/bar/baz.png with a addPassthroughCopy("**/*") (not the actual glob, setup is more complex than that) to copy them to _site/foo/bar/baz.html.

We've then added asciidoctor-kroki to be able to put diagrams in our asciidoc. This extension however will either generate images next to the source file (due to how base_dir is set) or require setting an imagesdir. This means that either diagrams are generated at the wrong place and don't display, or our previous images no longer display as they're now generated as <img src=/img/baz.png (assuming imagesdir: "/img")

Workaround:

The workaround to this is to rewrite our addPassthroughCopy to copy our images to that same imagesdir, but this creates another problem: we can no longer have 2 image files with the same name! (also, because we have more than just images, we need two addPassthroughCopy: one to match the non-image files, with the same behavior as previously, and one to match the image files to put them to the imagesdir.

Proposed solution:

I'd like to propose that a mechanism be added to this plugin to allow computing attributes to be set dynamically for each page (just like the base_dir nowadays). This should (that's my expectation at least) allow us to configure asciidoctor-kroki's outdir to the output directory of the page; that way, we could revert to our previous configuration for our images, and have asciidoctor-kroki output to that exact same directory as well, e.g. _site/foo/bar/diag-xxxx.svg.

This might not work (depending on how Eleventy calls the plugin) so I'm obviously open to other ideas.

Different tree representations for each case Original situation: ``` ├── .eleventy.js ├── foo/ │ ├── bar/ │ │ └── baz.png │ └── bar.adoc └── _site/ └── foo/ └── bar/ ├── baz.png └── index.html ``` With Kroki, without imagesdir (broken): ``` ├── .eleventy.js ├── foo/ │ ├── bar/ │ │ └── baz.png │ ├── bar.adoc │ └── diag-xxx.svg 👈 generated by Kroki └── _site/ └── foo/ └── bar/ ├── baz.png └── index.html ``` With Kroki, with imagesdir=/img (broken): ``` ├── .eleventy.js ├── foo/ │ ├── bar/ │ │ └── baz.png │ └── bar.adoc └── _site/ ├── foo/ │ └── bar/ │ ├── baz.png │ └── index.html 👈 references /img/baz.png └── img/ └── diag-xxx.svg ``` With Kroki, imagesdir, and addPassthroughCopy workaround: ``` ├── .eleventy.js ├── foo/ │ ├── bar/ │ │ └── baz.png │ └── bar.adoc └── _site/ ├── foo/ │ └── bar/ │ └── index.html └── img/ ├── baz.png 👈 could conflict with another baz.png for another page └── diag-xxx.svg ``` With proposed solution: ``` ├── .eleventy.js ├── foo/ │ ├── bar/ │ │ └── baz.png │ └── bar.adoc └── _site/ └── foo/ └── bar/ ├── baz.png 👈 can be referenced with as initially ├── diag-xxx.svg └── index.html ```
saneef commented 10 months ago

@tbroyer I'll look into this.

To recreate a the sample project for me, am I only need to add asciidoctor-kroki as extension, and add blocks like below in a couple of adoc pages?:

[graphviz]
....
digraph foo {
  node [style=rounded]
  node1 [shape=box]
  node2 [fillcolor=yellow, style="rounded,filled", shape=diamond]
  node3 [shape=record, label="{ a | b | c }"]

  node1 -> node2 -> node3
}
....
tbroyer commented 10 months ago

I'll try to provide a repro tomorrow but that should be enough yes iirc (afk rn so I'll check tomorrow the required conf for asciidoctor-kroki).

tbroyer commented 10 months ago

OK so here's a repro script:

mkdir eslint-plugin-asciidoc-issue14-repro
cd !$
npm init -y
npm add -D @11ty/eleventy eleventy-plugin-asciidoc asciidoctor-kroki
cat >.eleventy.js <<EOF
const eleventyAsciidoc = require("eleventy-plugin-asciidoc");
const asciidoctorKroki = require("asciidoctor-kroki");

module.exports = function (eleventyConfig) {
  eleventyConfig.addPlugin(eleventyAsciidoc, {
    safe: "unsafe",
    configure_extension_registry: function (registry) {
      asciidoctorKroki.register(registry);
    },
    attributes: {
      "kroki-fetch-diagram": true,
      // outdir: "./_site/",
      // imagesdir: "/img/",
    },
  });
  eleventyConfig.addPassthroughCopy("foo/*/*");
};
EOF
mkdir -p foo/bar
cat >foo/bar.adoc <<EOF
image:bar.png

[mermaid]
....
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
....
EOF
# Dummy "image" file
touch foo/bar/baz.png
# track changes with Git to make it easier to reset to that state
git init
cat >.gitignore <<EOF
/node_modules/
/_site/
EOF
git add .
git commit -m "Initial state"

Now run rm -rf _site && npx eleventy and look at the "shape" of the output and content of _site/foo/bar/index.html. Uncomment the outdir and/or imagesdir and repeat to see how they affect asciidoctor-kroki (and the image: macro).

saneef commented 10 months ago

Here is what I understood about the problem. When using relative paths for assets, the below scenario seems a common problem with Eleventy:

├── foo/
│   ├── bar.adoc # (or bar.md)
│   └── baz.png
│   # Builds into this structure
└── _site/
    └── foo/
         ├── baz.png
         └── bar/            
              └── index.html # Reference to `./baz.png` is broken

Whereas the below approach keeps generated path in sync with files. But, you'll end up with a lot of index files.

├── foo/
│   └── bar/
│      ├── index.adoc # (or index.md)
│      └── baz.png
│   # Builds into this structure
└── _site/
     └── foo/
          └── bar/            
               ├── baz.png
               └── index.html

Irrespective of the approach, the relative path to assets becomes harder to solve when permalink is overwritten or dynamically generated (in the cases like blog posts, events, etc.) So in my past projects, and the consensus I have seen on the Eleventy community is to have a common place for the images (or for any assets like PDF, sound files, …) then refer using path relative to dir.input.

Coming back to your use case, I'm thinking to have attributes.outdir to point to the destination folder for the .adoc file by default. Users can override through options, just like we had today.

Example: In the case of foo/bar.adoc, will have attributes.outdir as _site/foo/bar.

Will that work for your use case? If you'd like, I can create a branch with that change, and you can test with your project.

saneef commented 10 months ago

I have created a new branch, fix/issue-14, with above mentioned change. Could you check if that works for you?

tbroyer commented 10 months ago

Tested with npm add -D 'eleventy-plugin-asciidoc@saneef/eleventy-plugin-asciidoc#fix/issue-14' (resolved to commit d6aca74f2ed48485c0baf6174be3bbcc2aec5876) on both the above repro and our actual project (reverting the workaround) and it Just Works™ :tada:

saneef commented 10 months ago

Glad to hear!

I've made a new release v3.1.1.

tbroyer commented 10 months ago

Thank you, change is already in review on our end to update to that version and remove our workaround :rocket: