# `evalmd`

[![github actions](https://img.shields.io/github/check-runs/reggi/evalmd/master)](https://github.com/reggi/evalmd/actions) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard)

Write javascript in your markdown & execute it. I wanted a way of making sure the javscript that I write in markdown was valid and worked, not only for my own sake, but to ensure the examples and code provided was valid for others to reliably refer to.

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Usage](#usage)
- [Testing](#testing)
- [Install](#install)
- [Current Module Definition](#current-module-definition)
- [Preventing Eval](#preventing-eval)
- [Prepend Flag](#prepend-flag)
- [Inspiration](#inspiration)
- [Todo](#todo)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Usage

`evalmd` will scan a markdown file searching for a javascript code declaration, all of them are gathered then the string sent to [`eval`](https://github.com/pierrec/node-eval).

    ```javascript
    ```

or

    ```js
    ```

## Options

```
evalmd - Evaluate the javascript in markdown files

Options:
  -i, --include    Includes prevented blocks  [default: false]
  -P, --prevent    Prevent code from being evaluated  [default: false]
  -b, --block      Change the scope to block level  [default: false]
  -o, --output     Output js  [choices: false, true, "preserve", "concat", "preserveAlter", "concatAlter"] [default: false]
  -n, --nonstop    Runs all files regardless if error  [default: false]
  -s, --silent     Silence `evalmd` logging  [default: false]
  -u, --uniform    Does not use absolute urls when error logging  [default: false]
  -D, --debug      Debug Output  [default: false]
  -e, --eval       Comma-separated list of block kinds to evaluate (e.g. js, sh)  [default: "js"]
  --sloppy         Evaluate code in sloppy (non-strict) mode  [default: false]
  --eslint         Derive parser settings (ecmaVersion, sourceType, parser) from your eslint config per block  [default: false]
  -h, --help       Show help  [boolean]
  --path           Prefix local module with path  [default: "./"]
  --package        Th path of a package.json  [default: "./package.json"]
  --version        Show version number  [boolean]
  -d, --delimeter  [default: false]

Examples:
  evalmd <file(s)>        Evaluate file(s)
  evalmd <file(s)> -n     Evaluate file(s) uninterrupted
  evalmd <file(s)> -b     Evaluate block(s)
  evalmd <file(s)> -bn    Evaluate block(s) uninterrupted
  evalmd <file(s)> -Po    Outputs js file(s)
  evalmd <file(s)> -Pio   Outputs js file(s) with all block(s) (for linting)
  evalmd <file(s)> -Pob   Outputs block(s)
  evalmd <file(s)> -Piob  Outputs all blocks(s) (for linting)
  evalmd <file(s)> --eval=js,sh  Evaluate js and sh block(s)
```

## Block kinds

By default `evalmd` evaluates `js` (and `javascript`) blocks. Use `--eval` to pick which fenced block kinds are evaluated, given as a comma-separated list - or by repeating the flag. Each kind is dispatched to its own evaluator, so more kinds can be added over time.

    evalmd ./readme.md --eval=js,sh
    evalmd ./readme.md --eval=js --eval=sh

### `sh` blocks

An `sh` block is one or more prompts and their expected output.
A prompt line begins with `$`, `%`, or `>`;
every line after it, up to the next prompt, is the command's expected combined `stdout`/`stderr` output.
Each command must exit `0` and its output must match, otherwise `evalmd` exits non-zero.
Multiple prompts per block are supported.

    ```sh
    > echo hello
    hello
    > node -e "console.log('a'); console.error('b')"
    a
    b
    ```

## Sloppy mode

Blocks are evaluated in strict mode by default. Pass `--sloppy` to evaluate in sloppy (non-strict) mode instead - for example, to use `with`.
Sloppy blocks use CommonJS (`require`);
ES `import`/`export` syntax is only available in the default strict mode.

## eslint parser config

Dependency detection parses each block with a fixed `ecmaVersion` by default, so modern syntax (object spread, etc.) can fail to parse. Pass `--eslint` to instead derive the parser settings from your own eslint config, per block.

For each block, evalmd resolves the config for the virtual path `<file>.md/<n>.js` (where `<n>` is the block number), so eslint `overrides` (eslintrc) or `files` (flat config) that target `**/*.md/*.js` apply — the same convention as [`eslint-plugin-markdown`](https://github.com/eslint/eslint-plugin-markdown). This is how each block (or block kind) gets its own settings. eslint 8, 9, and 10 are supported, in both eslintrc and flat config.

`--eslint` honors `ecmaVersion`, `sourceType`, and a configured `parser` (such as `@typescript-eslint/parser`). It requires `eslint` to be installed — evalmd does not depend on it, so nothing changes for users who don't pass `--eslint`.

## Testing

Here is a bit of javascript that has an assertion at the end of it. The assertion will throw an error if the result of the `.equal` is invalid. This file is used as a test to see if `evalmd` is in working order.

```javascript
var assert = require('assert')
var helloWorld = 'foo'
assert.equal(helloWorld, 'foo')
```

Here's another one:

```js
var assert = require('assert')
var helloWorld = ['foo', 'bar']
assert.deepEqual(helloWorld, ['foo', 'bar'])
```

If you run this file using `test-markdown` it will exit with a status code of `0`, meaning no exceptions where thrown.

This overall provides a different way of sharing and explaining code, because it's much more formal then a complicated test file.

## Install

Try it yourself by executing the command:

```bash
npm install evalmd -g
evalmd ./readme.md
```

## Current Module Definition

If the command is ran within a node module with a `package.main` and a `package.name` then that reference will be replaced throughout your code. For instance the following passes.

```javascript
var evalmd = require('evalmd')
assert.equal(typeof evalmd, 'function')
```

## Prevent Eval Declaration

The `preventEval` declaration allows you to prevent a code block from being evaluated.  There are two different ways of declaring a code block. One is to use an `anchor`. Here's an example:

    [](#preventEval)
    ```js
    module.exports = 'alpha-path'
    ```

When adding a `preventEval` declaration in this way the name of the file is the `text` content of the anchor. Another way to declare a block as a file is using a comment. Here's an example:

    ```js
    // preventEval
    module.exports = 'alpha-path'
    ```

When the first line of a code block is a comment with the word `preventEval` in front the string after will be interpreted as the file.

> Note. The match patterns for `prevent eval` and `preventEval` are case-insensitive. So `pReVenTeVaL` works just as well.

# File Eval Declaration

The `fileEval` declaration allows you to define a code block as a file. There are two different ways of declaring a code block. One is to use an `anchor` tag with the `href` set to `#fileEval`. Here's an example:

    This is the file [./alpha.js](#fileEval).

    ```js
    module.exports = 'alpha-path'
    ```

When adding a `fileEval` declaration in this way the name of the file is the `text` content of the anchor. Another way to declare a block as a file is using a comment. Here's an example:

    ```js
    // fileEval ./alpha.js
    module.exports = 'alpha-path'
    ```

When the first line of a code block is a comment with the word `fileEval` in front the string after will be interpreted as the file.

> Note. If any of the code blocks in a file contain a `fileEval` declaration then the entire file will be run as `blockScope`.

> Note. The match patterns for `file eval` and `fileEval` are case-insensitive. So `fILeEvAl` works just as well.

## Prepend Flag

If you want to run code from `docs`, and your javscript files are in the root directory, you can use the `--prepend` flag to prepend every local module reference with the value.

Let's say you run the command:

```bash
evalmd ./docs/my-document.md --prepend='..'
```

And you have `my-document.md` with the conents:

    ```javascript
    var alpha = require('./alpha.js')
    ```

The prepend command will transform this code to this before it executes it.

    ```javascript
    var alpha = require('../alpha.js')
    ```

> Note: it's a prepend `path.join()` string, and not a concatenated prepend.

## Inspiration

I wanted a way of writing unit tests in markdown. I've been playing around with things like [`yamadapc/jsdoctest`](https://github.com/yamadapc/jsdoctest) which parses `JSDoc` declarations looking for `@example` keywords in source code and creates a test based on them.

## Todo

* Add ability for custom linting support (<3 [`standard`](https://github.com/feross/standard#standardlinttexttext-opts-callback))
