= Jupyter Notebook to HTML
:npm-name: ipynb2html
:gh-name: jirutka/{npm-name}
:gh-branch: master
:version: 0.3.0
:ansiup-version: 5.0.1
:hljs-version: 10.7.3
:katex-version: 0.13.11
:marked-version: 2.0.7
:vs-marketplace-uri: https://marketplace.visualstudio.com/items?itemName=

ifdef::env-github[]
image:https://github.com/{gh-name}/workflows/CI/badge.svg[CI Status, link=https://github.com/{gh-name}/actions?query=workflow%3A%22CI%22]
endif::env-github[]

{npm-name} is a converter (renderer) of the https://nbformat.readthedocs.io/en/stable/[Jupyter Notebook Format] 4.0+ to static HTML.
It works both in Node.js and browser environment.


== Packages

This repository contains the following packages, all published on https://www.npmjs.com/[npm].


=== {npm-name}-core

ifdef::env-github[]
image:https://img.shields.io/npm/v/{npm-name}-core.svg[Version on npm, link="https://www.npmjs.org/package/{npm-name}-core"]
image:https://img.shields.io/bundlephobia/min/{npm-name}-core.svg[Minified bundle size, link="https://bundlephobia.com/result?p={npm-name}-core"]
endif::env-github[]

This package provides the converter itself and some utilities with *no dependencies*.
You have to provide your own syntax highlighter and Markdown, math and ANSI sequences renderer; or not, if you don’t need them.


=== {npm-name}

ifdef::env-github[]
image:https://img.shields.io/npm/v/{npm-name}.svg[Version on npm, link="https://www.npmjs.org/package/{npm-name}"]
image:https://img.shields.io/bundlephobia/min/{npm-name}.svg[Minified bundle size, link="https://bundlephobia.com/result?p={npm-name}"]
endif::env-github[]

This package builds on the {npm-name}-core and provides a complete, ready-to-go renderer configured with:

* https://github.com/markedjs/marked[marked] as Markdown renderer,
* https://github.com/KaTeX/KaTeX[KaTeX] as math renderer,
* https://github.com/IonicaBizau/anser[anser] as ANSI sequences renderer,
* https://github.com/highlightjs/highlight.js[highlight.js] as syntax highlighter.

It also provides a reference stylesheet which you can find in `dist/notebook.min.css` (or non-minified link:packages/{npm-name}/styles/notebook.css[`styles/notebook.css`]).


=== {npm-name}-cli

ifdef::env-github[]
image:https://img.shields.io/npm/v/{npm-name}-cli.svg[Version on npm, link="https://www.npmjs.org/package/{npm-name}-cli"]
image:https://img.shields.io/bundlephobia/min/{npm-name}-cli.svg[Minified bundle size, link="https://bundlephobia.com/result?p={npm-name}-cli"]
endif::env-github[]

This package provides a CLI interface for {npm-name}.


ifndef::npm-readme[]

== Installation

All the <<Packages, packages>> can be installed using `npm` or `yarn` from https://www.npmjs.com/[npmjs.com].


=== Standalone CLI Tool

{npm-name}-cli is also provided as a single minified JavaScript with all the external dependencies bundled in.
It requires only Node.js (version 10 or newer) to be installed on the system.

* https://github.com/{gh-name}/releases/download/v{version}/{npm-name}-cli-v{version}.tar.gz[{npm-name}-cli-v{version}.tar.gz]
* https://github.com/{gh-name}/releases/download/v{version}/{npm-name}-cli-v{version}.zip[{npm-name}-cli-v{version}.zip]

The archive also contains source maps (useful for debugging).

endif::[]


== Usage

=== CLI

[source, subs="+attributes"]
{npm-name} notebook.ipynb notebook.html

Run `{npm-name} --help` for more information.


=== Node.js (server-side)

To render HTML in Node.js (server-side rendering), you need some (fake) DOM implementation.
The recommended one is https://github.com/redom/nodom/[nodom] -- it’s lightweight, https://bundlephobia.com/result?p=nodom[small], doesn’t have any external dependencies and {npm-name} is tested against it.
However, you can choose any other if you like.

[source, subs="+attributes"]
npm install {npm-name} nodom

[source, js, subs="+attributes"]
----
import * as fs from 'fs'
import * as ipynb from '{npm-name}'
import { Document } from 'nodom'

const renderNotebook = ipynb.createRenderer(new Document())

const notebook = JSON.parse(fs.readFileSync('./example.ipynb', 'utf8'))

console.log(renderNotebook(notebook).outerHTML)
----


=== Browser (client-side)

You have basically two options how to use {npm-name} in the browser: use the browser bundles provided in the {npm-name} package, or build your own bundle (using e.g. https://rollupjs.org[Rollup] or https://webpack.js.org/[webpack]).

The provided bundles are in UMD format (AMD, CommonJS and IIFE in one file), so they should work in all environments (old and modern browsers, Node.js).
They are transpiled and have injected https://github.com/zloirock/core-js/[core-js] polyfills to be compatible with browsers that have https://browserl.ist/?q=%3E0.5%25%2C+Firefox+ESR%2C+not+dead[>0.5% global coverage, Firefox ESR, and not dead browsers].


==== Full Bundle

`{npm-name}-full.min.js` is a self-contained bundle with all the external dependencies included (marked, KaTeX, Anser and Highlight.js).

You can link it from https://www.jsdelivr.com/[jsDelivr CDN], for example:

[source, html, subs="+attributes"]
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/{npm-name}@{version}/dist/notebook.min.css" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@{katex-version}/dist/katex.min.css" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@{hljs-version}/build/styles/default.min.css" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/{npm-name}@{version}/dist/{npm-name}-full.min.js" crossorigin="anonymous"></script>
  </head>
  ...
</html>

The bundle exposes global variable `{npm-name}`:

[source, js, subs="+attributes"]
const element = {npm-name}.render(notebook)
document.body.appendChild(element)

{npm-name} also provides function `autoRender` that renders each notebook on the page embedded (as JSON) inside `<script type="application/x-ipynb+json">\...</script>`.footnote:[Don’t forget to escape HTML special characters: `<`, `>`, and `&`.]

[source, html, subs="+attributes"]
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/{npm-name}@{version}/dist/notebook.min.css" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@{katex-version}/dist/katex.min.css" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@{hljs-version}/build/styles/default.min.css" crossorigin="anonymous">
    <script defer src="https://cdn.jsdelivr.net/npm/{npm-name}@{version}/dist/{npm-name}-full.min.js" crossorigin="anonymous"
        onload="{npm-name}.autoRender();"></script>
  </head>
  <body>
    <main>
      <script type="application/x-ipynb+json">
        {
          "cells": [ ... ],
          "metadata": { ... },
          "nbformat": 4,
          "nbformat_minor": 3
        }
      </script>
    </main>
  </body>
<html>


==== Slim Bundle

`{npm-name}.min.js` contains only {npm-name} and {npm-name}-core code (plus polyfills).
If you load marked, KaTeX, AnsiUp, and Highlight.js in the page, you will get the same functionality as with `{npm-name}-full.min.js`:

[source, html, subs="+attributes"]
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@{katex-version}/dist/katex.min.css" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@{hljs-version}/build/styles/default.min.css" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/marked@{marked-version}/marked.min.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/ansi_up@{ansiup-version}/ansi_up.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@{hljs-version}/build/highlight.min.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/katex@{katex-version}/dist/katex.min.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/{npm-name}@{version}/dist/{npm-name}.min.js" crossorigin="anonymous"></script>
  </head>
  ...
</html>

Or you may use any other implementations and provide them to the `{npm-name}.createRenderer` function.
All of them are optional, but you usually need at least a Markdown renderer.

ifndef::npm-readme[]


== Development

=== System Requirements

* https://nodejs.org[NodeJS] 10.13+
* https://pandoc.org[Pandoc] and https://asciidoctor.org[Asciidoctor] (used only for converting README.adoc to Markdown for npmjs)


=== Used Tools

* https://www.typescriptlang.org[TypeScript] the language
* https://github.com/cevek/ttypescript[ttypescript] wrapper for `tsc` allowing to use custom AST transformers
* https://yarnpkg.com[yarn] for dependencies management and building
* https://eslint.org[ESLint] for linting JS/TypeScript code
* https://jestjs.io[Jest] for testing
* https://postcss.org[PostCSS] for transforming stylesheets to plain CSS
* https://rollupjs.org[Rollup] for building single-file bundles


=== How to Start

. Clone this repository:
[source, subs="+attributes"]
git clone https://github.com/{gh-name}.git

. Install Yarn (if you don’t have it already):
[source]
npm install -g yarn

. Install all JS dependencies:
[source]
yarn install

. Build the project:
[source]
yarn build

. Run tests and generate code coverage:
[source]
yarn test

. Run linter:
[source]
yarn lint

IMPORTANT: Keep in mind that JS sources are located in the `src` directories; `lib` directories contains transpiled code (created after running `yarn build`)!


=== Visual Studio Code

If you use Visual Studio Code, you should install the following extensions:

* link:{vs-marketplace-uri}ryanluker.vscode-coverage-gutters[Coverage Gutters]
* link:{vs-marketplace-uri}EditorConfig.EditorConfig[EditorConfig for VS Code]
* link:{vs-marketplace-uri}dbaeumer.vscode-eslint[ESLint]
* link:{vs-marketplace-uri}cpylua.language-postcss[language-postcss]
* link:{vs-marketplace-uri}Orta.vscode-jest[Jest] (and link:{vs-marketplace-uri}shtian.jest-snippets-standard[Jest Snippets Standard Style])
* link:{vs-marketplace-uri}gamunu.vscode-yarn[yarn]

endif::[]


== Credits

* The renderer module is originally based on https://github.com/jsvine/notebookjs[notebookjs] 0.4.2 developed by https://github.com/jsvine[Jeremy Singer-Vine] and distributed under the http://opensource.org/licenses/MIT/[MIT License].
* The mathExtractor module is based on https://github.com/jupyter/notebook/blob/6.0.1/notebook/static/notebook/js/mathjaxutils.js[mathjaxutils.js] from the https://github.com/jupyter/notebook[Jupyter Notebook] 6.0.1 distributed under the https://github.com/jupyter/notebook/blob/6.0.1/COPYING.md[Modified BSD License].


== License

This project is licensed under http://opensource.org/licenses/MIT/[MIT License].
For the full text of the license, see the link:LICENSE[LICENSE] file.
