JavaScript Examples

remark plugin to run JavaScript examples.

Usage

var run = require( '@stdlib/_tools/remark/plugins/remark-run-javascript-examples' );

run( options )

Attaches a remark plugin which, when provided a Markdown abstract syntax tree, runs JavaScript examples.

var remark = require( 'remark' );

var str = [
    '<section class="examples">',
    '',
    '## Examples',
    '',
    '```javascript',
    'console.log( "HELLO WORLD!" );',
    '```',
    '',
    '</section>',
    '',
    '<!-- /.examples -->'
];
str = str.join( '\n' );

remark().use( run ).process( str, done );

function done( error ) {
    if ( error ) {
        throw error;
    }
}

The plugin accepts the following options:

  • cwd: the current working directory when executing an example. If not specified,

    • when provided a virtual file having a directory path, the plugin sets the current working directory of the child process to the directory of the virtual file.
    • when provided a virtual file missing a directory path (e.g., when processing a Markdown string), the plugin sets the current working directory of the child process to the current working directory of the calling process.
  • quiet: boolean indicating whether to silence examples writing to stdout, but allow writing to stderr. Default: false.

  • silent: boolean indicating whether to silence examples writing to both stdout and stderr. Default: false.

  • verbose: boolean indicating whether to print plugin meta information, such as which code block is executing. Default: true.

  • maxBuffer: maximum buffer size (in bytes) for stdout and stderr. If exceeded, the plugin terminates code block execution.

  • timeout: maximum number of milliseconds allotted for the execution of each code block. If exceeded, the plugin terminates code block execution. When the timeout is 0, the plugin allows code blocks to run indefinitely. Default: 0.

To specify plugin options, provide an options object.

var remark = require( 'remark' );
var cwd = require( '@stdlib/process/cwd' );

var str = [
    '<section class="examples">',
    '',
    '## Examples',
    '',
    '```javascript',
    'console.log( "HELLO WORLD!" );',
    '```',
    '',
    '</section>',
    '',
    '<!-- /.examples -->'
];
str = str.join( '\n' );

var opts = {
    'cwd': cwd(),
    'silent': true,
    'verbose': false
};

remark().use( run, opts ).process( str, done );

function done( error ) {
    if ( error ) {
        throw error;
    }
}

Notes

  • When processing a Markdown abstract syntax tree (AST), the plugin relies on the presence of HTML section elements and their associated class attributes to determine how to process code blocks. Consider the following example:

    <section class="usage">
    
    ## Usage
    
    ```javascript
    var path = require( "path" );
    ```
    
    #### path.posix.join( [...paths] )
    
    ```javascript
    var p = path.posix.join( "foo", "bar" );
    ```
    
    </section>
    
    <!-- /.usage -->
    
    <section class="examples">
    
    ## Examples
    
    ```javascript
    console.log( 'HELLO WORLD' );
    ```
    
    </section>
    
    <!-- /.examples -->
    

    Upon finding a usage section, the plugin scans for JavaScript code blocks. The plugin assumes that the intent of the first encountered code block is to declare the imported package and further assumes that the import statement is implicit in all subsequent JavaScript code blocks in a usage section. Accordingly, the plugin prepends the first code block to each subsequent usage section code block prior to code block execution. Hence, for the example above, prior to execution, the plugin transforms the second code block into the following:

    var path = require( "path" );
    var p = path.posix.join( "foo", "bar" );
    

    In contrast to usage code blocks, the plugin assumes that JavaScript code blocks in examples sections are self-contained and performs no such transformations.

  • The plugin supports configuration comments, which are HTML comments containing configuration settings located immediately above a fenced code block.

    <section class="examples">
    
    ## Examples
    
    <!-- run timeout: 1000 -->
    
    ```javascript
    console.log( 'HELLO WORLD' );
    ```
    
    </section>
    
    <!-- /.examples -->
    

    The plugin supports multiple consecutive comments, including interleaved configuration comments for other plugins.

    <section class="examples">
    
    ## Examples
    
    <!-- run timeout: 1000 -->
    
    <!-- eslint-disable no-console -->
    
    <!-- run maxBuffer: 204800 -->
    
    ```javascript
    console.log( 'HELLO WORLD' );
    ```
    
    </section>
    
    <!-- /.examples -->
    
  • Configuration comments use the following JSON-like format:

    <!-- run key1: true, key2: false, key3: null, key4: "foo", key5: 3.14 -->
    

    Each key-value pair is separated by a comma. Keys should be unquoted and correspond to the names of configuration settings. Key values should be parseable as JSON.

    To assign multiple setting values, use array literal syntax.

    <!-- run key: [ "value", 3.14, true, false, null ] -->
    
  • The plugin supports the following configuration comment settings:

    • maxBuffer: maximum buffer size (in bytes) for stdout and stderr. If exceeded, the plugin terminates the configured code block. Default: options.maxBuffer.
    • timeout: maximum number of milliseconds allotted for code block execution. If exceeded, the plugin terminates the configured code block. When the timeout is 0, the plugin allows the code block to run indefinitely. Default: options.timeout.
    • cwd: the current working directory when executing the code block. Default: options.cwd.
    • throws: boolean indicating whether the code block intentionally throws an error. If set to true and the code block fails to throw, the plugin will return an error. Default: false.
  • Configuration comments only apply to a code block which follows immediately after. Hence, the plugin does not apply the following configuration comment to a subsequent code block.

    <section class="examples">
    
    <!-- run timeout: 1000 -->
    
    ## Examples
    
    ```javascript
    console.log( 'HELLO WORLD' );
    ```
    
    </section>
    
    <!-- /.examples -->
    
  • Configuration comments are not shared between code blocks. Thus, one must repeat configuration comments for each code block.

    <section class="usage">
    
    ## Usage
    
    <!-- run-disable -->
    
    ```javascript
    var path = require( 'path' );
    ```
    
    #### path
    
    <!-- run-disable -->
    
    ```javascript
    console.log( path );
    ```
    
    </section>
    
    <!-- /.usage -->
    
  • The plugin executes each code block in a separate child process. Prior to execution, the plugin wraps each code block in a module wrapper similar to the following:

    (function(exports, require, module, __filename, __dirname) {
        {{code block}}
    })(exports, require, module, __filename, __dirname);
    

    The exports, require, and module variables are the same as those provided by the Node.js runtime prior to a module's execution.

    If provided a virtual file having path information, the plugin sets the __filename and __dirname variables to the absolute file and directory paths, respectively, of the virtual file. If provided a virtual file missing path information, the plugin generates random paths based on options.cwd.

  • To disable execution for a particular code block, use the comment <!-- run-disable -->.

Examples

var join = require( 'path' ).join;
var remark = require( 'remark' );
var readSync = require( 'to-vfile' ).readSync;
var run = require( '@stdlib/_tools/remark/plugins/remark-run-javascript-examples' );

// Load a Markdown file:
var fpath = join( __dirname, 'examples', 'fixtures', 'file.txt' );
var file = readSync( fpath, 'utf8' );

// Run examples:
remark().use( run ).process( file, done );

function done( error ) {
    if ( error ) {
        throw error;
    }
}