Skip to content

reggi/evalmd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

evalmd

github actions js-standard-style

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.

Usage

evalmd will scan a markdown file searching for a javascript code declaration, all of them are gathered then the string sent to 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. 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.

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

Here's another one:

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:

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.

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:

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 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)

About

🎣 Evaluates javascript code blocks from markdown files.

Resources

License

Stars

47 stars

Watchers

4 watching

Forks

Packages

 
 
 

Contributors