dendron-exports

This tools allow you to export your Dendron vault into SSG-compatible Markdown.

This will convert below:

![[anoter-note#first]]

[[anoter-note#second]]

into:

{% comp "ref", targetId="mnj7u1ez5wxg5v5kok8j79u", targetTitle="Anoter Note", targetUrl="/notes/mnj7u1ez5wxg5v5kok8j79u#first" %}

## First

This is first header

{% endcomp %}

[Anoter Note](/notes/mnj7u1ez5wxg5v5kok8j79u#second)

Current supported SSG:

Supported Dendron feature:

  • Ref
  • Wikilink

Getting Started

Make sure you have installed Deno.

First, create deno.json in your Dendron root directory and write this code:

{
  "tasks": {
    "dendron_exports": "echo \"import 'dendron_exports/cli.ts'\" | deno run -A -"
  },
  "imports": {
    "dendron_exports/": "https://deno.land/x/dendron_exports@v0.1.1/"
  }
}

Then, create dendron-exports.config.ts in same directory as the previous file and write this code:

import { LumeNjkRenderer, Options } from "dendron_exports/mod.ts";

export const options: Options = {
  baseDir: "./",
  vaultPath: "vault",
  vaultName: "vault",
  noteDest: ".exports",
  notePathBuilder: (note) =>
    note.getPath() === "root" ? "index" : "notes/" + note.metadata.id,
  noteRenderer: new LumeNjkRenderer(),
};

See Options docs.

Then run this command:

deno task dendron_exports

After that, your notes will exported into .exports directory.

How it Work?

This diagram show how this program work:

flowchart TD
    Index(Indexing all notes in vault) --> Parse(Parsing all notes into AST)
    Parse --> Backlink(Building backlinks)
    subgraph ExportAll [Exports all notes]
        ExportSingle(Export single note's AST) --> Renderer(Calling Renderer)
        Renderer --> SaveFile(Saving to destination file)
    end

    Backlink --> ExportAll
    ExportAll --> Assets(Copying assets)

This program use micromark and mdast-util-from-markdown to parse Markdown into AST.

What is Renderer?

Renderer is class that have responsibility to convert AST into SSG-compatible markdown. Renderer use mdast-util-to-markdown to convert AST back into markdown.

Creating Custom Renderer

You can create custom renderer by extending RendererClass.

Example Case: Hugo Renderer

Hugo support shortcodes to create custom widget in Markdown. We can use this to render ref and block anchor. To render wikilink we just convert it into standard Markdown link.

To start creating Renderer, create HugoRenderer class that extends Renderer.

import { Renderer } from "dendron_exports/mod.ts";

class HugoRenderer extends Renderer {}

We can convert ref into shortcode like this:

{{< ref targetId="id" targetTitle="title" targetUrl="url">}} content {{< /ref
>}}

To convert ref into shortcode like that we can implement getRefText like this:

import { Renderer, RendererRefContext } from "dendron_exports/mod.ts";

class HugoRenderer extends Renderer {
  getRefText(ref: RendererRefContext): string {
    const header = `{{< ref targetId="${
      ref.targetNote!.metadata.id
    }" targetTitle="${ref.targetNote!.metadata.title}" targetUrl="${
      ref.targetUrl
    }">}}`;
    const footer = `{{< /ref >}}`;
    return [header, ref.content, footer].join("\n");
  }
}

We can convert block anchor into shortcode like this:

{{< anchor name="name" >}}

To convert ref into shortcode like that we can implement getBlockAnchorText like this:

class HugoRenderer extends Renderer {
  getBlockAnchorText(name: string): string {
    return `{{< anchor name="${name}" >}}`;
  }
}

For WikiLink handling, implement getWikiLinkText like this:

class HugoRenderer extends Renderer {
  getWikiLinkText(url: string, title: string): string {
    return `[${title}](${url})`;
  }
}

Final HugoRenderer class code:

import { Renderer, RendererRefContext } from "dendron_exports/mod.ts";

class HugoRenderer extends Renderer {
  getRefText(ref: RendererRefContext): string {
    const header = `{{< ref targetId="${
      ref.targetNote!.metadata.id
    }" targetTitle="${ref.targetNote!.metadata.title}" targetUrl="${
      ref.targetUrl
    }">}}`;
    const footer = `{{< /ref >}}`;
    return [header, ref.content, footer].join("\n");
  }

  getBlockAnchorText(name: string): string {
    return `{{< anchor name="${name}" >}}`;
  }

  getWikiLinkText(url: string, title: string): string {
    return `[${title}](${url})`;
  }
}

Now we can use the renderer in config by changing noteRenderer options like this:

export const options: Options = {
  // another config
  noteRenderer: new HugoRenderer(),
};

This renderer will convert below:

![[anoter-note#first]]

[[anoter-note#second]]

into:

{{< ref targetId="mnj7u1ez5wxg5v5kok8j79u" targetTitle="Anoter Note" targetUrl="/notes/mnj7u1ez5wxg5v5kok8j79u#first">}}

## First

This is first header

{{< /ref >}}

[Anoter Note](/notes/mnj7u1ez5wxg5v5kok8j79u#second)