Skip to content

Latest commit

 

History

History
584 lines (437 loc) · 24.6 KB

for-library-authors.adoc

File metadata and controls

584 lines (437 loc) · 24.6 KB

Cljdoc for Library Authors

Why Cljdoc?

Cljdoc offers a centralized hosted library of docs which:

  • Are easy to publish

  • Automatically stay up to date

  • Are discoverable by users

  • And look good!

The Bare Minimum

If you publish your library to Clojars, its docs should already be available (or available to build) on cljdoc.org.

Default handling:

Note
Cljdoc currently only processes libraries that have been deployed to clojars. We do have ideas for processing source-based libraries, but have not implemented yet.

Mechanics

When cljdoc is triggered to build your library’s documentation, it:

  1. Downloads your library’s release jar from Clojars

  2. Analyzes your library’s sources on git

  3. Analyzes your library’s jar to discover its public API.

After building your docs, they are available at cljdoc.org for online and offline browsing.

See also: System Overview.

Markup Formats

Cljdoc supports two markup formats:

Cljdoc Badge

Important

The following examples use metosin/malli. Replace with your library’s Clojars group-id/artifact-id (hint: not the GitHub org/repo name).

Include a cljdoc badge in your README. Here malli’s badge: cljdoc badge.

Expressed in Markdown:

[![cljdoc badge](https://cljdoc.org/badge/metosin/malli)](https://cljdoc.org/d/metosin/malli)

and AsciiDoc:

https://cljdoc.org/d/metosin/malli[image:https://cljdoc.org/badge/metosin/malli[cljdoc badge]]

Git Sources

Cljdoc starts with the release jar downloaded from Clojars. A jar is associated with its specific git revision of sources via the <scm> entry in the jar’s pom. This allows cljdoc to consult git hosted:

  • cljdoc config - options on how cljdoc should process your docs

  • articles - to include and present as part of your docs

  • sources files - to allows cljdoc to intelligently link back to articles, API source code, and your git repo

Tip
a valid pom <scm> also allows other services like Clojars to point back to your release’s sources.
Note
  • SCM stands for Source Control Management

  • pom is the Maven Project Object Model

An example <scm> entry from metosin/malli:

  <scm>
    <url>https://github.com/metosin/malli</url>
    <connection>scm:git:git://github.com/metosin/malli.git</connection>
    <developerConnection>scm:git:ssh://git@github.com/metosin/malli.git</developerConnection>
    <tag>0.8.4</tag>
  </scm>

Another from juxt/bidi:

  <scm>
    <url>https://github.com/juxt/bidi</url>
    <connection>scm:git:git://github.com/juxt/bidi.git</connection>
    <developerConnection>scm:git:ssh://git@github.com/juxt/bidi.git</developerConnection>
    <tag>d1bfcc9c4fe247b8ada19cd7ee25acd81dd93f26</tag>
  </scm>

Cljdoc will look at the <url> and the <tag>:

  • <url> points to your hosted git repo

  • <tag> is a valid pointer to the git revision of your release’s sources. It can be a git tag, or commit sha.

Tip
You can optionally override the revision for articles and docstring format.
Note
We strongly recommend you explicitly specify the <tag> in your pom.xml for cljdoc and other tools.
But…​ If you do not specify a <tag>, cljdoc will search for a version tag based on the artifact version. For artifact version 1.2.3 it will look for a git tag 1.2.3 (else v1.2.3).
Note
Cljdoc will assume a found version tag is equivalent to <tag> even when the <tag> specifies a commit sha.

Here are some common ways folks set <scm> values:

Whatever method you choose, take care to ensure that your jar’s pom points back to the exact revision of its sources on git.

Overriding Cljdoc Config & Articles

Sometimes you’ll want cljdoc to present minor adjustments after your library’s release commit. For examples:

  • a README that includes the git sha of the release will necessarily appear in a commit after the library release.

  • perhaps you’d like fix or edit an article without cutting a new release

  • you’d like to adjust your article table of contents

  • you might also want to change how cljdoc presents docstrings.

To support these scenarios, cljdoc recognizes the cljdoc-<version> git tag. For library version 1.2.3 cljdoc will look for git tag cljdoc-1.2.3 (or cljdoc-v1.2.3) and import your articles from that commit instead of the default commit.

If you add/move a cljdoc-<version> tag after the initial cljdoc build is complete, you can request a rebuild.

Tip
This affects all of and only: docstring format, article table of contents, and articles. Any changes, for example, to docstring content will require a new library release.
Tip
You’ll want to make any adjustments before you start working on your next release. All articles are re-imported.

Configuration

When building your docs, cljdoc will look under the doc (else docs) directory for a cljdoc.edn file in your library’s git repo.

You can use this configuration file to tell cljdoc more about your documentation.

  • :cljdoc.doc/tree - Tells cljdoc what articles to present and in what hierarchy.
    By default, cljdoc will automatically discover articles.

  • :cljdoc/languages - Tells cljdoc which languages your API uses.
    By default, cljdoc will automatically detect languages based on the sources it finds in your jar.

  • :cljdoc/docstring-format - Tells cljdoc how you’d like your docstrings displayed.
    By default, cljdoc will render docstrings from Markdown format.

  • :cljdoc/include-namespaces-from-dependencies - Tells cljdoc to amalgamate API docs from multiple modules.
    Rarely used, but very useful when your project is made up of modules.

As an example, a version of honeysql's cljdoc.edn

{:cljdoc.doc/tree
 [["Readme" {:file "README.md"}]
  ["Changes" {:file "CHANGELOG.md"}]
  ["Getting Started" {:file "doc/getting-started.md"}
   ["General Reference" {:file "doc/general-reference.md"}]
   ["SQL Clause Reference" {:file "doc/clause-reference.md"}]
   ["SQL Operator Reference" {:file "doc/operator-reference.md"}]
   ["SQL 'Special Syntax'" {:file "doc/special-syntax.md"}]
   ["PostgreSQL Support" {:file "doc/postgresql.md"}]
   ["Extending HoneySQL" {:file "doc/extending-honeysql.md"}]]
  ["Differences from 1.x" {:file "doc/differences-from-1-x.md"}]]}

Linking to docs on cljdoc

See also: badges.

Tip
If you are locally previewing your docs, there’s no need to replace https://cljdoc.org with some localhost version. Cljdoc will automatically make these URLs work locally.

Sometimes you’ll want to link to a var or a namespace in your library’s API docs on cljdoc.

You can link to other namespaces and functions within your libary from your markdown docstrings using the [[wikilink]] syntax.

Examples:

Wikilink Links to API docs for

[[some-fn]]

some-fn var in the current namespace

[[my-lib.ns1/some-other-fn]]

some-other-fn var in the my-lib.ns1 namespace

[[my-lib.ns1]]

my-lib.ns1 namespace

Note
  • Wikilinks only work from docstrings.

  • Namespace aliases are not considered. We don’t want to burden the reader to have to understand what namespace an alias maps to.

  • There is some support for relative linking to vars in other namespaces For example you can link to compojure.core/GET from compojure.route via [[core/GET]]. Frankly I feel this is more confusing than helpful for both authors and readers and recommend the fully qualified [[compujure.core/GET]]

Use full API URLS from articles

Use the full cljdoc API URL when linking to from an article or from outside your git repo.

For example to link to namespace malli.core in version 0.7.5 use:
https://cljdoc.org/d/metosin/malli/0.7.5/api/malli.core

You can replace the explicit version with CURRENT. For example, to link to malli.core/explain in the current version use:
https://cljdoc.org/d/metosin/malli/CURRENT/api/malli.core#explain

CURRENT will be replaced with:

  • the current version the user is already viewing on cljdoc

  • the latest available version of the library when the user is navigating to cljdoc from some outside source

API Documentation

Declaring your Public API

Cljdoc will document all namespaces and public vars it finds. To exclude namespaces and/or vars from API documentation, annotate them with :no-doc metadata:

(defn ^:no-doc hidden "Won't see me on cljdoc.org!" [x])
(ns ^:no-doc namespace.hidden
  "This ns shouldn't show in the docs.")
(ns namespace.hidden
  "This ns shouldn't show in the docs."
  {:no-doc true})

API Languages

Cljdoc will auto-detect which languages your library supports based on the types of source files it finds. You can choose to override this auto-detection in your doc/cljdoc.edn file via the :cljdoc/languages option.

Example :cljdoc/languages value API Analysis run for

["clj"]

Clojure only

["cljs"]

ClojureScript only

["clj" "cljs"]

Both Clojure and ClojureScript

:auto-detect

Dependent upon source files found in your library, the default behavior.

Example usage:

{:cljdoc/languages ["clj"]}

Getting Dependencies Right

Cljdoc discovers your API via dynamic runtime analysis. It will try to load all namespaces found in your jar. If a dependency is not found, the load, and therefore API analysis, will fail.

If you include namespaces that require additional/optional dependencies, make sure you declare them in your pom.xml.

If these dependencies are expected to be provided by, for example, some container or JVM, mark them with a scope of "provided" in our pom.xml. Provided dependencies are skipped at runtime, but inform cljdoc they are required for API analysis.

Tip
You can express provided in a project.clj, for example. The deps.edn file does not support scoping, you’ll have to express these directly in your pom.xml.

Cljdoc will automatically search Clojars and Maven Central for dependencies. If any of your library’s dependencies are hosted elsewhere, those maven repositories will need to be specified in your pom.xml. This includes any transitive dependencies.

Tip

You can specify maven repositories:

  • in a project.clj under :repositories, for example.

  • in a deps.edn under :mvn/repos, for example.

Docstrings

Docstrings are rendered from Markdown by default.

You can choose to override this behaviour in your doc/cljdoc.edn file via the :cljdoc/docstring-format option. Valid values are:

  • :markdown - the default, an option to view "raw docstring" as plaintext is available to the user.

  • :plaintext - presents only the raw docstring.

Consider these recommendations when writing your docstrings in markdown format:

  1. Backtick-Quote ` function arguments & special keywords to make them stand out more

  2. Link to other functions using [[wikilink]] syntax

  3. Include small examples using markdown fenced ```Clojure …​ ``` code blocks

  4. Use Markdown tables to describe complex options maps

  5. You can include images and links to articles, just be sure to use git repo root-relative links (links that start with a /):

    • ![my image](/dir1/dir2/image1.png)

    • [my article](/dir1/dir2/article.adoc)

Any HTML embedded within docstrings is escaped.

Article Documentation

Libraries often include additional guides and tutorials in markup files. Cljdoc calls these articles.

For cljdoc to find your articles:

This allows cljdoc to retrieve article files at the revision/commit of the release.

Cljdoc hosted articles will have their links rewritten to link back to cljdoc. All links that work on GitHub should also work on cljdoc.

Auto-discovered Articles

If your git repository does not contain a doc tree configuration, cljdoc will automatically include:

  • README.md else README.adoc - filename search is case insensitive

    • Title is Readme

  • CHANGELOG.md else CHANGELOG.adoc- filename search is case insensitive

    • Title is Changelog

  • Markup articles from your doc/ else docs/ folder

    • The title is read from the file’s first heading, and failing that, be based on the article filename. There will be no nesting and articles will be ordered alphabetically by filename.

Tip
Use filenames prefixed with digits like 01-intro.md to define the order of articles.

Configuring Articles

If you need more control, use a doc/cljdoc.edn file to specify a tree of articles.

Assuming you have a directory doc/ in your repository as follows:

doc/
  getting-started.md
  installation.md
  configuration.md

You can explicitly add these articles to your cljdoc build by with the following doc/cljdoc.edn file:

{:cljdoc.doc/tree [["Readme" {:file "README.md"}]
                   ["Getting Started" {:file "doc/getting-started.md"}
                    ["Installation" {:file "doc/installation.md"}]]
                   ["Configuration" {:file "doc/configuration.md"}]]}

Your articles will be presented with the following hierarchy and titles:

├── Readme
├── Getting Started
│   └── Installation
└── Configuration
Tip
Cljdoc will always present the readme and changelog articles first.
Important
The resulting URLs for those articles will be based on the title provided in the cljdoc.edn file and not on the filename or title within the article file.

See also: verifying articles

Article Slugs

Slugs for articles are currently based on the article title. Titles can be explicitly configured or discovered.

AsciiDoc Environment

Similar to env-github on GitHub, cljdoc will set an env-cljdoc attribute when rendering your AsciiDoc file. This allows you to hide or show sections of your document or set configuration parameters.

As an example, this AsciiDoctor snippet:

ifdef::env-cljdoc[]
THIS WILL BE SHOWN ON CLJDOC
endif::[]
ifndef::env-cljdoc[]
THIS WILL BE SHOWN EVERYWHERE ELSE
endif::[]

will render as so:

THIS WILL BE SHOWN EVERYWHERE ELSE

Testing & Verifying

Previewing Docs Locally

You can preview what your docs will look like before a Clojars release by running cljdoc locally.

Snapshot Builds

If you are already publishing -SNAPSHOT releases to Clojars, this can also be a useful way to experiment/preview on cljdoc.

We recommend that you populate <scm> <tag> in your pom.xml with the git commit sha of your snapshot release.

Cljdoc does not automatically build snapshot releases, but they will show up in the library search result. Upon selecting a snapshot release, cljdoc will offer to build its docset.

By its nature, a snapshot release will likely have many releases under the same version. You can choose to rebuild for against the current releases.

Article Verification Script

Sometimes people forget to update the paths after moving files around, we recommend you add the following to your CI setup:

curl -fsSL https://raw.githubusercontent.com/cljdoc/cljdoc/master/script/verify-cljdoc-edn | bash -s doc/cljdoc.edn

GitHub Action

We have a Cljdoc check action you can incorporate into your CI workflows.

Doc Build Triggers

  • Automatically

    • Every 60 seconds, cljdoc reaches out to clojars to discover new releases.

    • Every 10 minutes, it queues new releases to build

  • By request at cljdoc.org

    • If cljdoc has not already built a requested version of a library, you are given the option to build it from cljdoc.org.

  • By rebuild request at cljdoc.org

    • If your docs have already been built, you can request a rebuild via the barely visible rebuild hover link on the top right corner of your library docs page:

      cljdoc rebuild link
  • By REST request

Module Support

Some libraries are made up of submodule libraries. Cljdoc provides some support for these types of libraries.

Almagamating APIs

To include API documentation for some or all of an artifact’s submodule artifacts, specify their maven coordinates under :cljdoc/include-namespaces-from-dependencies:

{:cljdoc/include-namespaces-from-dependencies
 [metosin/reitit
  metosin/reitit-core
  metosin/reitit-ring
  metosin/reitit-spec
  metosin/reitit-schema
  metosin/reitit-swagger
  metosin/reitit-swagger-ui]}
Note
To be included, each dependency must also be specified as a maven dependency of the project itself (in the project’s deps.edn, project.clj, etc). The project’s resulting POM file will be used to load API information for the correct version.
Tip
Reitit is a great example reference for a project with submodules.
Warning
If analysis for a specified dependency has failed or hasn’t been run, its API documentation will not appear on cljdoc.

Distinctly Configuring

Sometimes a single git repository will be the source for multiple maven/clojars artifacts. Each of these artifacts will point back to the same single git repository and therefore the same cljdoc.edn.

Cljdoc allows for a distinct config for each of these artifacts. Specify cljdoc.edn config as normal for your primary library. For each submodule libary, include config under symbol submodule-group-id/submodule-artifact-id.

Here’s an example from clj-otel:

{:cljdoc/languages ["clj"]
 :cljdoc.doc/tree [["Introduction" {:file "README.adoc"}]
                   ["Tutorial" {:file "doc/tutorial.adoc"}]
                   ["Guides" {:file "doc/guides.adoc"}]
                   ["API & Reference" {:file "doc/reference.adoc"}]
                   ["Concepts" {:file "doc/concepts.adoc"}]
                   ["Examples" {:file "doc/examples.adoc"}]
                   ["Changelog" {:file "CHANGELOG.adoc"}]]
 com.github.steffan-westcott/clj-otel-contrib-aws-resources {:cljdoc.doc/tree [["README" {:file "clj-otel-contrib-aws-resources/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-contrib-aws-xray-propagator {:cljdoc.doc/tree [["README" {:file "clj-otel-contrib-aws-xray-propagator/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-jaeger-grpc {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-jaeger-grpc/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-jaeger-thrift {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-jaeger-thrift/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-logging {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-logging/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-logging-otlp {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-logging-otlp/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-otlp {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-otlp/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-prometheus {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-prometheus/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-exporter-zipkin {:cljdoc.doc/tree [["README" {:file "clj-otel-exporter-zipkin/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-extension-trace-propagators {:cljdoc.doc/tree [["README" {:file "clj-otel-extension-trace-propagators/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-instrumentation-resources {:cljdoc.doc/tree [["README" {:file "clj-otel-instrumentation-resources/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-instrumentation-runtime-telemetry-java8 {:cljdoc.doc/tree [["README" {:file "clj-otel-instrumentation-runtime-telemetry-java8/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-instrumentation-runtime-telemetry-java17 {:cljdoc.doc/tree [["README" {:file "clj-otel-instrumentation-runtime-telemetry-java17/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-sdk {:cljdoc.doc/tree [["README" {:file "clj-otel-sdk/README.adoc"}]]}
 com.github.steffan-westcott/clj-otel-sdk-extension-jaeger-remote-sampler {:cljdoc.doc/tree [["README" {:file "clj-otel-sdk-extension-jaeger-remote-sampler/README.adoc"}]]}}