Codebook

Codebook allows you to execute code that is also shown to the user embedded in an HTML page.

Usage

Codebook needs two parts to work: the code on the page that should be run, and the JavaScript to run it (see Methods).

The code on the page comes in blocks, which should have a class of js-codebook__block. These blocks can belong to sets, which can be run independently.

On this page, code blocks are marked by a 📖.

Sets

By default, a block belongs to a special set with the name "default". However, it can be added to a specific set by specifying a set name via a data-codebook-set attribute in one of two places:

  • On the block itself
  • On an ancestor of the code block, which also has the class js-codebook__set

If a set is specified in both of those places, the set specified on the block itself will take precedence. Code blocks can only belong to a single set.

Helper Methods

Code blocks in a set have two helper methods exposed that allow them to print output to the page:

log

log(...output: any[])

If a code block has a log output element, specified via a data-codebook-log attribute with a value matching the id attribute of the log output element, the log method will print output to that element when called.

Multiple values can be logged, with each value appended to the log after the last. A block's log is cleared before each time its set is run.

On this page, Codebook logs are marked by a 📝.

It will attempt to convert Date and Object to strings intelligently, otherwise it will attempt to coerce each value to a string.

html

html(output: string)

If a code block has an HTML output element, specified via a data-codebook-html attribute with a value matching the id attribute of the log output element, the html method will overwrite that element's inner HTML with whatever custom HTML is passed in as the output argument.

Asynchronous code

Particularly because the log and html methods take on different values at different times, any asynchronous code that runs within a Codebook block should finish before the main thread of the code block is finished.

This can be accomplished either by using the await keyword whenever calling asynchronous code, or by keeping track of all asynchronous code and using await Promise.all(promises); at the end of a block to wait for all asynchronous code to complete.

Codebook wraps each block in an async function, so await can be used. As a result, the methods used to run code sets each return a Promise, which resolves when all specified sets have finished running.

Methods

run

codebook.run(args?: Record<string, any>): Promise<void>;

The run method runs all sets of code on a page, in which they appear on the page.

Index

Code blocks within a set will run in the order in which they appear in the markup, unless they have a data-codebook-index attribute. This attribute should be a number, and code with a lower number here will run first.

Code blocks with an index will always run before code blocks without an index, and if code blocks have the same index then they will run in the order in which they appear in the markup.

Passing arguments

run takes a single optional argument, which is an Object containing named arguments to be passed to your code. This can be used to expose objects that don't exist on the global scope, such as packages that have been imported into a module.

codebook.run({ testArg: 'test', });

Codebook uses some variables to ensure the `log` and `html` methods are available, so these argument names are reserved and cannot be used:

'_log' '_$log' 'log' '_html' '_$html' 'html'

runSet

codebook.runSet(setName: string, args?: Record<string, any>): Promise<void>; codebook.runSet(args?: Record<string, any>): Promise<void>;

The runSet method allows you to run a specific set of code, instead of all of them. The setName argument should be a string matching the name of the set, or can be left blank to call the "default" set.

codebook.runSet('test', { testArg: 'test2' });

tidy

codebook.tidy(): void;

The tidy method looks for all Codebook code blocks and analyses their shared indentation based on how much the first line of code is indented, expecting indentation to be done with tabs. Then it strips all lines in that block by the same amount of indentation, so code appearing on the page using CSS rules like white-space: pre; won't appear hugely indented, but the source HTML code can still be tidily indented.

As well as code blocks, the tidy method will also tidy "inert" blocks by looking for elements with a class of js-codebook__inert. These blocks are otherwise ignored by Codebook, and it won't try to run any code they contain.

Examples

Default set

This code block belongs to the default set, and has log and html output elements defined.

Named set

This code belongs to a set with the name "test". You can edit the code in its only block, then re-run it by pressing a button.