npm package: @hitsuji_no_shippo/gatsby-transformer-asciidoc
Description: Gatsby transformer plugin for Asciidocs using the Asciidoctor.js library
Author: hitsuji no shippo
Latest version: 0.12.0

= gatsby-transformer-asciidoc
:author-name: hitsuji no shippo
:!author-email:
:author: {author-name}
:!email: {author-email}
:revnumber: v1.35.0
:revdate: 2020-01-25T15:57:43+0900
:revmark: Add default value of `enablesEmptyAttribute`
:doctype: article
:copyright: Copyright (c) 2019 {author-name}
:title-separtor: :
:!showtitle:
:!sectnums:
:sectids:
:toc: preamble
:toclevels: 3
:sectlinks:
:sectanchors:
:idprefix:
:idseparator: -
:xrefstyle: full
:!example-caption:
:!figure-caption:
:!table-caption:
:!listing-caption:
ifdef::env-github[]
:caution-caption: :fire:
:important-caption: :exclamation:
:note-caption: :paperclip:
:tip-caption: :bulb:
:warning-caption: :warning:
endif::[]
ifndef::env-github[:icons: font]
// Page Attributes
:page-create-date: 2019-08-13T16:42:02+0900
// Variables
:author-link-url: https://github.com/hitsuji-no-shippo
:asciidoctor-official-docs-url: https://asciidoctor.org/docs
:asciidoctor-js-docs-antora-url: https://asciidoctor-docs.netlify.com/asciidoctor.js
:github-url: https://github.com
:github-account-url: {github-url}/hitsuji-no-shippo
:issues-url: {github-account-url}/gatsby-transformer-asciidoc/issues
:yaml-example-url: https://en.wikipedia.org/wiki/YAML#Example

Parses AsciiDoc files using
link:{asciidoctor-official-docs-url}/asciidoctor.js/[Asciidoctor.js].

[WARNING]
====
Tested only with link:https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem[
gatsby-source-filesystem^]. Have not tested with other source plugins.
====

== Install

`npm install --save @hitsuji_no_shippo/gatsby-transformer-asciidoc`

== How to use

.gatsby-config.js
[source, JavaScript]
----
plugins: [`gatsby-transformer-asciidoc`]
----

A full explanation of asciidoc can be found here:
link:{github-url}/asciidoctor/asciidoctor.js[Asciidoctor.js]

You can also pass all
link:{asciidoctor-js-docs-antora-url}/processor/convert-options/[
Asciidoctor's convert options] to the transformer. An example would be:

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        values: {
          showtitle: true,
        },
      },
    },
  },
]
----

[[use-boolean-to-value]]
[CAUTION]
====
.Boolean
[source, JavaScript]
----
attributes: {
  values: {
    page-index: true,   // <1>
    page-draft: false,  // <2>
  },
},
----
<1> same as `:page-index: true`. *Not the same as `:page-index:`*
<2> same as `:!page-draft:`
====

== How to query

A sample GraphQL query to get AsciiDoc nodes:

[source, GraphQL]
----
{
  allAsciidoc {
    edges {
      node {
        html
        timeToRead
        document {
          title
          subtitle
          main
          description
        }
        author {
          fullName
          firstName
          lastName
          middleName
          authorInitials
          email
        }
        revision {
          date
          number
          remark
        }
        paths {
          absolute {
            file  // <1>
          }
          from {
            project {
              dir
              full
            }
            source {
              file  // <2>
              full  // <3>
            }
          }
        }
      }
    }
  }
}
----
<1> `node.absolutePath`
<2> [[path-file-from-source-dir-field]]
    Relative Directory (`node.relativeDirectory`) + Name (`node.name`)
<3> `node.relativePath`

[[paths-field-example]]
[example]
====
.gatsby-config.js
[source, JavaScript]
----
{
  resolve: `gatsby-source-filesystem`,
  options: {
    path: `${__dirname}/content/blog`,
    name: `blog`,
  },
},
----

[cols="5*m", options="header, autowidth"]
|===
|path
|paths.from.project.dir
|paths.from.project.full
|paths.from.source.file
|paths.from.source.full

|<gatsby-project-root>/content/blog/about.adoc
|content/blog
|content/blog/about.adoc
|about
|about.adoc

|<gatsby-project-root>/content/blog/post/gatsby.adoc
|content/blog/post
|content/blog/post/gatsby.adoc
|post/gatsby
|post/gatsby.adoc
|===
====

== Parsing algorithm

It recognizes files with the following
link:{asciidoctor-official-docs-url}/asciidoc-recommended-practices/#document-extension[
extensions] as AsciiDoc:

- `adoc`
- `asciidoc`

Additional extensions can be configured via the fileExtensions option:

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        values: {
          showtitle: true,
        },
      },
      fileExtensions: [`ad`, `adoc`],
    },
  },
]
----

Each AsciiDoc file is parsed into a node of type `asciidoc`.

== Set imagesdir

You also can define where the asciidoc file can find the images by setting the
imagesdir attribute.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        values: {
          'imagesdir@': `/images`,
        },
      },
    },
  },
]
----

In the asciidoc file you can insert your image just by using:
`image::myimage.png[]`

[NOTE]
====
* If no `imagesdir@` is set the default value is `/images`
* Don't use relative images paths because the images might not be copied
  automatically to the location where the converted asciidoc html file will to
  located.
* In case a `pathPrefix` is set it will altered the images location.
* In case you want to be able to override the defined imagesdir inside of your
  asciidoc file you have to end the attribute name with a `@`
  (e.g. `imagesdir@`).
====

== Add new node attributes in the asciidoc file

You can define in the asciidoc file your own data that will be automatically
be attached to the node attributes.

.Example
[source, AsciiDoc]
----
= AsciiDoc Article Title
Firstname Lastname <author@example.org>
1.0, July 29, 2018, Asciidoctor article template

:page-title: Article
:page-path: /my-blog-entry
:page-category: My Category
----

Each attribute with the prefix xref:pageAttributePrefix[] option value
(default: `page-`) will be automatically added under `pageAttributes` so it can
be used with GraphQL. Name is the attribute name with the prefix removed.
Attribute value is processed as link:{yaml-example-url}[YAML] data.

[source, GraphQL]
----
{
  allAsciidoc {
    edges {
      node {
        pageAttributes {
          title
          path
          category
        }
      }
    }
  }
}
----

[CAUTION]
====
.Error

If the value starts with `{` (e.g. `{github-url}/hitsuji_no_shippo`),
the following error occurs:

[literal]
....
YAMLException: end of the stream or a document separator is expected at line 1, column 13:
    {github-url}/hitsuji_no_shippo
                ^
....

There is a way to avoid the error, but I'm not sure if it should be avoided.
(link:{issues-url}/56[#56])
====


== Options

=== Attributes

:self-reference-object-url: {github-account-url}/self-referenced-object/tree/60f18190c552116aa5a4e30879b7a7d4832b9362
==== Refer to self attributes

You can use `attributes.options.selfReferendObject` to refer to the value in
`options.attributes.values.all`.
*Please read
link:{self-reference-object-url}[
self-reference-object] README for details.*

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        values: {
          'github-*': {
            url: 'https://github.com',
            'my-*': {
              id: 'hitsuji-no-shippo',
              'page-url': '${this.(../url)}/${this.(./id)}',
            }
          },
          country: 'Japan',
          comment: 'Live in ${this.country}. link:${this.github-my-page-url}[My page].'
        },
        options: {
          selfReferendObject: {
            runs: true,  // <1>
            shouldConvert: true  // <2>
          },
        },
      },
    },
  },
]
----
<1> [[self-referenced-object-runs]] Whether to run refer processing.
    (Default is `true`)
<2> [[self-referenced-should-convert]] Whether to convert reference from
    directory to key
    (e.g. `this.(./id)` => `this.github-my-id`) processing. (Default is `true`)

.Result
[source, JavaScript]
----
{
  'github-url': 'https://github.com',
  'github-my-id': 'hitsuji-no-shippo',
  'github-my-page-url': 'https://github.com/hitsuji-no-shippo'
  country: 'Japan',
  comment:
   'Live in Japan. link:https://github.com/hitsuji-no-shippo[My page].',
}
----

==== Add partials attributes

Use `attributes.options.partials` to add attributes to files with specific path.
Used path value is xref:path-file-from-source-dir-field[
`paths.from.source.file` field] value.

*Please read
link:{github-account-url}/extract-matching-values-in-patterns/tree/c8f32e9528a74d8be9c041c1d21c735393d63663[
README] for details.*

.Example
[%collapsible]
====
.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      enablesEmptyAttribute: true,  // <5>
      pageAttributePrefix: `page-`,
      attributes: {
        values: {
          ...
        },
        options: {
          selfReferendObject: {
            ...
          },
          partials: {
            shouldReferSelf: true,  // <1>
            shouldConvert: true,    // <2>
            shouldReferToAttributesToAddToAll: true,  // <3>
            attributes: {  // <4>
              posts: {
                values: {
                  page-posts: '',  // <5>
                  page-index: '',
                  page-disqus: '',
                },
              }
              pages: {
                values: {
                  page-pages: '',
                },
              },
            },
          },
        },
      },
    },
  },
]
----
<1> Same xref:self-referenced-object-runs[selfReferendObject.runs].
    The target is `attributes.partials.attributes`. (Default is `true`)
<2> Same xref:self-referenced-should-convert[selfReferendObject.shouldConvert].
    The target is `attributes.partials.attributes`. (Default is `true`)
<3> link:{self-reference-object-url}#refer-another-object-key-values[
    Whether to refer to `attributes.values`]. (Default is `true`)
<4> An object that holds path and attributes to add.
    If the value is undefined, the process is not run.
<5> xref:empty-value[`enablesEmptyAttribute`] useing to convert `''` to `true`
    and `null` to `false`. If xref:use-boolean-to-value[use `true` and `false`],
    the value of field using `false` is `null`.

[cols=3*a, options="header, autowidth"]
|===
|`paths.from.source.file`
|Additional attributes
|GraphQL

|`posts/gatsby`
|

[source, JavaScript]
----
{
  page-posts: '',  <1>
  page-index: '',
  page-disqus:''
}
----
<1> same as `:page-posts:`
|

[source, GraphQL]
----
"pageAttributes": {
  "disqus": true,
  "pages": false,  // <1>
  "index": true,
  "posts": true
}
----
<1> If input `page-pages: false`, the value is `null`.

|`pages/profile`
|

[source, JavaScript]
----
{
  page-pages: ''
}
----
|

[source, GraphQL]
----
"pageAttributes": {
  "disqus": false,
  "pages": true,
  "index": false,
  "posts": false
}
----
|===
====

==== Replace attributes to field value

Values that cannot be referenced when createing a node, such as slug,
can be replaced as attributes in HTML (`node.html`). Default is empty.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        options: {
          replace: {
            slug: 'fields.slug'  // <1>
          }
        },
      },
    },
  },
]
----
<1> Replace `{slug}` to `node.fields.slug` value.

==== Add path attributes

The values of xref:paths-field-example[
`node.paths` field] is avaliable as an attributes. Default is empty.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      attributes: {
        options: {
          paths: {
            dir: `from.project.dir`   // <1>
            full: `from.source.full`  // <2>
          }
        },
      },
    },
  },
]
----

.Asciidoc
[source, AsciiDoc]
----
:include-dir: {dir}/_includes  // <1>
:github-edit-link: link:{repository-url}/edit/master/{full}[Edit on GitHub]  // <2>
----
<1> Replace `{dir}` to `node.paths.from.project.dir`
<2> Replace `{full}` to `node.paths.from.source.full`


=== pageAttributePrefix

The value specifies the prefix of attribute to be added to the `pageAttributes`
field. Default is `page-`.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      pageAttributePrefix: `gatsby-`,
    },
  },
]
----

All attribute Add under `pageAttributes` when the value is `''`.

=== Empty Value

Attribute with empty value (`:page-draft:`) is convert `true`. If attributes is
not set for page, returns `false` instead of `null`.

If even one Attribute value is empty, all attributes with the same name will
return Boolean. So non-boolean values are converted to `false`.
`true` and `false` are not convert.

If Attribute is not set and only unset, the attribute is not defined in
GraphQL field.

[NOTE]
====
This process is not executed when `enablesEmptyAttribute` option is `false`
(Default is `false`). The empty attribute is not defined in GraphQL field.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      enablesEmptyAttribute: false,
    }
  },
]
----
====

.Examples
[%collapsible]
====
include::./_includes/attribute-with-empty-value-examples.adoc[tag=main]
====

=== Ignore

If the attribute matches the value, no Ascidoc node is created.
Default is `null`.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      ignore: {
        unpublish: ['', 'true']  // <1>
      }
    }
  },
]
----
<1> `''` is `:unpublish:`, `'true'` is `:unpublish: true`, not `true`.

=== OptionFile

You can change the option file with the `OptionFile` option.

.gatsby-config.js
[source, JavaScript]
----
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      OptionFile: {
        path: `file-path` <1>
        encoding: `file-encoding` <2>
      }
    },
  },
]
----
<1> Default `./asciidoctor-option.yaml`
<2> Default `utf8`

[IMPORTANT]
====
When reading, the attributes option must be of type object.
====

==== Option File

link:{yaml-example-url}[YAML] file can be load. Default option file path is
`./asciidoctor-option.yaml`.  The value of *`gatsby-config.js` takes precedence
over YAML file*.

.Example
[source, YAML]
----
attributes:
  values:
    gatsby-url: https://www.gatsbyjs.org
    asciidoctor-url: https://asciidoctor

pageAttributePrefix: gatsby-
enablesEmptyAttribute: true
----

[TIP]
====
You can also xref:refer-to-self-attributes[refer to the attributes value] in
option file.
====

JSON is hard to write, so I chose yaml.


== Define a custom converter

You can define a custom converter by adding the `converterFactory` option.

.gatsby-config.js
[source, JavaScript]
----
// Make sure to import or declare TemplateConverter
plugins: [
  {
    resolve: `@hitsuji_no_shippo/gatsby-transformer-asciidoc`,
    options: {
      converterFactory: TemplateConverter,
    },
  },
]
----

`TemplateConverter` is a custom javascript class you'll need to create.
Information on how to write a custom `TemplateConverter` can be found at the
link:{asciidoctor-js-docs-antora-url}/extend/converter/custom-converter/[
asciidoctor docs].

In the example below, we will use a custom converter to convert paragraphs but
the other nodes will be converted using the built-in HTML5 converter:

[source, JavaScript]
----
const asciidoc = require(`asciidoctor.js`)()

class TemplateConverter {
  constructor() {
    this.baseConverter = asciidoc.Html5Converter.$new()
  }

  convert(node, transform) {
    if (node.getNodeName() === "paragraph") {
      return `<p>${node.getContent()}</p>`
    }

    return this.baseConverter.convert(node, transform)
  }
}
----

== Front Matter support

You can front matter as `frontmatter` fields. Front Matter value is processed
link:{yaml-example-url}[YAML] data.

To make the same field name for each
link:{github-url}/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-remark#how-to-query[
gatsby-transformer-remark], I named it `frontmatter`.

.AsciiDoc file
[source, Asciidoc]
----
---
author: {name: hitsuji no shippo, email: xxx@gmail.com}
category: transformer
tags: [gatsby, asciidoc]
---
= doctitle
:author: hitsuji no shippo
----

.GraphQL
[source, GraphQL]
----
{
  allAsciidoc {
    edges {
      node {
        frontmatter {
          author {
            name
            email
          }
          category
          tags
        }
      }
    }
  }
}
----

[NOTE]
====
Asciidoctor attribute `skip-front-matter` is always `true`.
====

== Auto update for attributes

If xref:pageAttributePrefix[`pageAttributePrefix`] is different from cache,
the `pageAttributes` field is automatically updated.

[horizontal]
attributs:: Reload asciidoc using new attributes and update asciidoc fields
            values
+
--
[CAUTION]
====
The fields are *not added and deleted*.
Only update the values of existing fields. If you clear the cache, it's OK.
This issue will take time, so if there is a request, will fix it.
(link:{issues-url}/54[#54])
====
--
pageAttributePrefix:: Update `pageAttributePrefix` field

[NOTE]
====
.Cache does not store default
Cache stores input values every time. Default value is not included.
Therefore, the update process is run in the following cases, although the
result is the same.

.options of last time
[source, JavaScript]
----
{
  enablesEmptyAttribute: true,
}
----

.options of this time
[source, JavaScript]
----
{
  enablesEmptyAttribute: true,
  pageAttributePrefix: `page-`,
}
----
====