https://github.com/mkdoc/mkpi
Processing instruction tool
https://github.com/mkdoc/mkpi
Last synced: 9 months ago
JSON representation
Processing instruction tool
- Host: GitHub
- URL: https://github.com/mkdoc/mkpi
- Owner: mkdoc
- License: other
- Archived: true
- Created: 2016-03-14T06:52:07.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2017-02-07T04:58:44.000Z (over 9 years ago)
- Last Synced: 2025-08-23T07:59:27.088Z (10 months ago)
- Language: JavaScript
- Size: 106 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Processing Instructions
[](https://travis-ci.org/mkdoc/mkpi)
[](https://npmjs.org/package/mkpi)
[](https://coveralls.io/github/mkdoc/mkpi?branch=master)
> Script markdown documents
Uses the [mkparse][] library to form a DSL based on tags declared in processing instructions ` ... ?>`.
Parses and executes processing instructions according to macro functions defined by a grammar; reads newline-delimited JSON records from an input stream, interprets and executes the instructions and writes the modified AST to an output stream.
## Install
```
npm i mkpi --save
```
For the command line interface install [mkdoc][] globally (`npm i -g mkdoc`).
---
- [Install](#install)
- [Security](#security)
- [Usage](#usage)
- [Example](#example)
- [Sample](#sample)
- [Macros](#macros)
- [Grammar](#grammar)
- [Custom Macros](#custom-macros)
- [Macro Functions](#macro-functions)
- [Help](#help)
- [API](#api)
- [pi](#pi)
- [Grammar](#grammar-1)
- [License](#license)
---
## Security
By default his library assumes you have trusted input. The [exec](#exec) and [macro](#macro) directives can run arbitrary commands and execute arbitrary javascript if your input is untrusted set the `safe` option and these directives are no longer recognised.
## Usage
```javascript
var pi = require('mkpi')
, ast = require('mkast');
ast.src(' @exec {shell} pwd ?>')
.pipe(pi())
.pipe(ast.stringify({indent: 2}))
.pipe(process.stdout);
```
## Example
Execute processing instructions in a file:
```shell
mkcat README.md | mkpi | mkout
```
Disable unsafe macros for untrusted input:
```shell
mkcat README.md | mkpi --safe | mkout
```
## Sample
This [readme document](https://github.com/mkdoc/mkpi/blob/master/README.md) ([raw version](https://raw.githubusercontent.com/mkdoc/mkpi/master/README.md)) was built from the source file [doc/readme.md](https://github.com/mkdoc/mkpi/blob/master/doc/readme.md) shown below:
```markdown
# Processing Instructions
@include readme/badges.md ?>
> Script markdown documents
@include {=readme} introduction.md install.md ?>
***
***
@include {=readme}
security.md
usage.md
example.md
sample.md
macros.md
help.md ?>
@exec ./sbin/apidocs ?>
@include {=readme} license.md links.md ?>
```
Using the command:
```shell
mkcat doc/readme.md | mkpi | mkmsg | mkref | mkabs | mkout > README.md
```
## Macros
The default processing instruction grammar includes functions for including markdown documents, executing commands and more.
### Grammar
These macros are defined by the default grammar.
#### @include
Include one or more markdown documents into the AST stream:
```xml
@include intro.md install.md license.md ?>
```
Processing instructions in included files are also executed, paths are resolved relative to the owner document when a file is available.
You can specify a path to include from using the tag value:
```xml
@include {=path/to/folder} intro.md install.md license.md ?>
```
Use this as shorthand when all the files to include are in the same directory.
If you specify a directory to include this implementation will look for `index.md` within the directory:
```xml
@include path/to/folder ?>
```
Resolves to `path/to/folder/index.md` relative to the file containing the instruction.
Note that file paths passed to this macro cannot include whitespace.
#### @exec
Execute a command and parse the result into the AST stream:
```xml
@exec pwd ?>
```
To capture the stderr stream use the `stderr` keyword before the command:
```xml
@exec stderr pwd ?>
```
An error is reported when a command fails, to include the output of a command with a non-zero exit code use the `@exec!` tag:
```xml
@exec! stderr pwd ?>
```
Commands may contain newlines they are removed before execution:
```xml
@exec ls -la
lib
dist
test
?>
```
To wrap the output in a fenced code block use a type:
```xml
@exec {javascript} cat index.js ?>
```
#### @source
Load a file and parse it as markdown or wrap it in a fenced code block, unlike the @include macro processing instructions **are not executed**.
Parse a markdown file into the AST stream but do not execute processing instructions:
```xml
@source file.md ?>
```
Load a file into a fenced code block:
```xml
@source {javascript} index.js ?>
```
Sometimes it is useful to perform a string replacement on the sourced file, this is particularly helpful when you want to include a usage example that can be run directly but show the final package name.
To do so specify a string substitution in the form `s/{regexp}/{replace}/gimy` as a value, for example:
```xml
@source {javascript=s/\.\.\/index/mkpi/gm} usage.js ?>
```
Will replace all occurences of `../index` with `mkpi` in `usage.js` before the file is parsed into the stream.
#### @macro
Defines a macro function body; use this for application specific logic.
Return a value to inject some information into the stream:
```xml
@macro return require('./package.json').name;
?>
```
Or wrap the result in a fenced code block:
```xml
@macro {shell} return require('./package.json').scripts.test;
?>
```
For asynchronous operations you can callback with a string to write to the stream:
```xml
@macro cb(null, '*emph*');
?>
```
See the [macro api docs](#macro-1) for more detail.
### Custom Macros
Create a vanilla object if you wish to discard the default grammar macros:
```javascript
var mkpi = require('mkpi')
, grammar = {}
, id = 'custom-macro';
grammar[id] = function(cb) {
// implement macro logic
cb();
}
mkpi({grammar: grammar});
```
You macro function will then be executed when the ` @custom-macro ?>` processing instruction is encountered.
To extend the existing grammar with a custom macro function use:
```javascript
var mkpi = require('mkpi')
, grammar = require('mkpi/lib/grammar')
, id = 'custom-macro';
grammar[id] = function(cb) {
// implement macro logic
cb();
}
mkpi({grammar: grammar});
```
### Macro Functions
A macro function accepts a single argument `cb` which must be invoked when processing is complete, an `Error` may be passed to the callback.
They access all pertinent information via `this`, for example:
```javascript
function(cb) {
var tag = this.tag;
console.error(tag.name);
cb();
}
```
Note the exception that `@macro` function body definitions that return a value other than `undefined` should not call the callback.
See the [grammar api docs](#grammar-1) for more information.
## Help
```
Usage: mkpi [options]
Processing instruction macros.
Options
-s, --safe Disable the @exec and @macro directives
-p, --preserve Do not remove processing instructions
-h, --help Display help and exit
--version Print the version and exit
mkpi@1.1.5
```
## API
### pi
```javascript
pi([opts][, cb])
```
Execute processing instructions found in the AST.
Instructions are removed from the AST by default, use `preserve` to keep
them in the output.
When no `input` and no `output` are specified the parser stream
is returned and `cb` is ignored.
Returns an output stream.
* `opts` Object processing options.
* `cb` Function callback function.
#### Options
* `input` Readable input stream.
* `output` Writable output stream.
* `grammar` Object grammar macro functions.
* `preserve` Boolean=false keep processing instructions in the AST.
* `safe` Boolean=false disable command and code execution.
### Grammar
Default map of tag names to grammar macro functions.
A macro function has the signature `function(cb)`, it should always invoke
the callback and may pass an error.
It can access the following fields via `this`:
- `writer`: output stream, write AST nodes to this stream.
- `comment`: parsed processing instruction comment.
- `tag`: the tag that fired the macro function.
- `parser`: markdown parser (`parser.parse()` to convert strings to nodes).
- `grammar`: grammar document, map of tag identifiers to macro functions.
- `preserve`: whether to preserve the processing instructions.
#### exec
```javascript
exec(cb)
```
Run an external command, newlines are removed from the command so it
may span multiple lines.
```xml
@exec pwd ?>
```
To capture the stderr stream set `stderr` before the command:
```xml
@exec stderr pwd ?>
```
By default an error is reported if the command fails, to include the
output when a command returns a non-zero exit code use the `@exec!` tag:
```xml
@exec! pwd ?>
```
To wrap a result in a fenced code block specify a `type`:
```xml
@exec {javascript} cat index.js ?>
```
If you want the result in a fenced code block with no info string use:
```xml
@exec {} cat index.js ?>
```
* `cb` Function callback function.
#### include
```javascript
include(cb)
```
Include one or more markdown files into the AST, processing instructions
in included files are executed.
```html
@include intro.md install.md ?>
```
If a type is given it is a relative or absolute path to include from:
```html
@include {path/to/folder} intro.md install.md ?>
```
Include files are resolved relative to the including file when file
data is available (`mkcat file.md`), but when no file data is available,
for example from stdin (`cat file.md | mkcat`), then files are resolved
relative to the current working directory.
* `cb` Function callback function.
#### macro
```javascript
macro(cb)
```
Accepts a function body, compiles it and executes the function.
Use this for inline application-specific logic.
The function is assumed to be a standard macro function implementation
that accepts the arguments:
- `cb`: callback function to invoke when not returning a value.
It is also passed an additional non-standard argument:
- `require`: alias to require files relative to the cwd.
If the function returns a value other than `undefined` the result is
parsed as markdown and written to the stream and and control flow
is returned (as if `cb` was invoked automatically).
```xml
@macro return require('package.json').name; ?>
```
Otherwise the macro **must** invoke the callback function and should
pass an optional error and result string to the callback:
```xml
@macro cb(null, '*emph*'); ?>
```
* `cb` Function callback function.
#### Parser
```javascript
new Parser([opts])
```
Finds processing instructions in the stream, parses those found with
[mkparse][] and invokes a macro function if it exists for a tag in
the parsed processing instruction.
* `opts` Object processing options.
##### Options
* `preserve` Boolean=false keep processing instructions.
* `safe` Boolean=false disable command and code execution.
#### replace
```javascript
replace(str)
```
Parse a substitution definition in the form `s/{regexp}/{string}/gimy`.
When the `str` can be parsed the returned object includes:
- `regexp`: RegExp compiled pattern.
- `replace`: String replacement string.
- `flags`: String regexp flags.
If it cannot be parsed null is returned.
Returns replacement object or null.
* `str` String substitution definition.
##### Throws
* `SyntaxError` if the regexp pattern is malformed.
#### source
```javascript
source(cb)
```
Load and parse a file as markdown without executing processing instructions
or wrap the file in a fenced code block.
```html
@source file.md ?>
@source {javascript} index.js ?>
```
* `cb` Function callback function.
## License
MIT
---
Created by [mkdoc](https://github.com/mkdoc/mkdoc) on April 18, 2016
[mkdoc]: https://github.com/mkdoc/mkdoc
[mkparse]: https://github.com/mkdoc/mkparse
[node]: http://nodejs.org
[npm]: http://www.npmjs.org
[commonmark]: http://commonmark.org
[jshint]: http://jshint.com
[jscs]: http://jscs.info