{"id":19607889,"url":"https://github.com/mkdoc/mkpi","last_synced_at":"2025-09-15T23:30:43.179Z","repository":{"id":57298934,"uuid":"53833601","full_name":"mkdoc/mkpi","owner":"mkdoc","description":"Processing instruction tool","archived":true,"fork":false,"pushed_at":"2017-02-07T04:58:44.000Z","size":109,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-23T07:59:27.088Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkdoc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-14T06:52:07.000Z","updated_at":"2024-12-11T22:13:08.000Z","dependencies_parsed_at":"2022-08-26T18:12:41.660Z","dependency_job_id":null,"html_url":"https://github.com/mkdoc/mkpi","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/mkdoc/mkpi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkdoc%2Fmkpi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkdoc%2Fmkpi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkdoc%2Fmkpi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkdoc%2Fmkpi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkdoc","download_url":"https://codeload.github.com/mkdoc/mkpi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkdoc%2Fmkpi/sbom","scorecard":{"id":652302,"data":{"date":"2025-08-11","repo":{"name":"github.com/mkdoc/mkpi","commit":"5f341e80316df748e2a8280a0f28a360f5f4387b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T13:48:01.799Z","repository_id":57298934,"created_at":"2025-08-21T13:48:01.799Z","updated_at":"2025-08-21T13:48:01.799Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275336590,"owners_count":25446818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-15T02:00:09.272Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T10:12:52.274Z","updated_at":"2025-09-15T23:30:42.757Z","avatar_url":"https://github.com/mkdoc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Processing Instructions\n\n[![Build Status](https://travis-ci.org/mkdoc/mkpi.svg?v=3)](https://travis-ci.org/mkdoc/mkpi)\n[![npm version](http://img.shields.io/npm/v/mkpi.svg?v=3)](https://npmjs.org/package/mkpi)\n[![Coverage Status](https://coveralls.io/repos/mkdoc/mkpi/badge.svg?branch=master\u0026service=github\u0026v=3)](https://coveralls.io/github/mkdoc/mkpi?branch=master)\n\n\u003e Script markdown documents\n\nUses the [mkparse][] library to form a DSL based on tags declared in processing instructions `\u003c? ... ?\u003e`.\n\nParses 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.\n\n## Install\n\n```\nnpm i mkpi --save\n```\n\nFor the command line interface install [mkdoc][] globally (`npm i -g mkdoc`).\n\n---\n\n- [Install](#install)\n- [Security](#security)\n- [Usage](#usage)\n- [Example](#example)\n- [Sample](#sample)\n- [Macros](#macros)\n  - [Grammar](#grammar)\n  - [Custom Macros](#custom-macros)\n  - [Macro Functions](#macro-functions)\n- [Help](#help)\n- [API](#api)\n  - [pi](#pi)\n  - [Grammar](#grammar-1)\n- [License](#license)\n\n---\n\n## Security\n\nBy 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.\n\n## Usage\n\n```javascript\nvar pi = require('mkpi')\n  , ast = require('mkast');\nast.src('\u003c? @exec {shell} pwd ?\u003e')\n  .pipe(pi())\n  .pipe(ast.stringify({indent: 2}))\n  .pipe(process.stdout);\n```\n\n## Example\n\nExecute processing instructions in a file:\n\n```shell\nmkcat README.md | mkpi | mkout\n```\n\nDisable unsafe macros for untrusted input:\n\n```shell\nmkcat README.md | mkpi --safe | mkout\n```\n\n## Sample\n\nThis [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:\n\n```markdown\n# Processing Instructions\n\n\u003c? @include readme/badges.md ?\u003e\n\n\u003e Script markdown documents\n\n\u003c? @include {=readme} introduction.md install.md ?\u003e\n\n***\n\u003c!-- @toc --\u003e\n***\n\n\u003c? @include {=readme}\n    security.md \n    usage.md \n    example.md \n    sample.md \n    macros.md \n    help.md ?\u003e\n\n\u003c? @exec ./sbin/apidocs ?\u003e\n\u003c? @include {=readme} license.md links.md ?\u003e\n```\n\nUsing the command:\n\n```shell\nmkcat doc/readme.md | mkpi | mkmsg | mkref | mkabs | mkout \u003e README.md\n```\n\n## Macros\n\nThe default processing instruction grammar includes functions for including markdown documents, executing commands and more.\n\n### Grammar\n\nThese macros are defined by the default grammar.\n\n#### @include\n\nInclude one or more markdown documents into the AST stream:\n\n```xml\n\u003c? @include intro.md install.md license.md ?\u003e\n```\n\nProcessing instructions in included files are also executed, paths are resolved relative to the owner document when a file is available.\n\nYou can specify a path to include from using the tag value:\n\n```xml\n\u003c? @include {=path/to/folder} intro.md install.md license.md ?\u003e\n```\n\nUse this as shorthand when all the files to include are in the same directory.\n\nIf you specify a directory to include this implementation will look for `index.md` within the directory:\n\n```xml\n\u003c? @include path/to/folder ?\u003e\n```\n\nResolves to `path/to/folder/index.md` relative to the file containing the instruction.\n\nNote that file paths passed to this macro cannot include whitespace.\n\n#### @exec\n\nExecute a command and parse the result into the AST stream:\n\n```xml\n\u003c? @exec pwd ?\u003e\n```\n\nTo capture the stderr stream use the `stderr` keyword before the command:\n\n```xml\n\u003c? @exec stderr pwd ?\u003e\n```\n\nAn error is reported when a command fails, to include the output of a command with a non-zero exit code use the `@exec!` tag:\n\n```xml\n\u003c? @exec! stderr pwd ?\u003e\n```\n\nCommands may contain newlines they are removed before execution:\n\n```xml\n\u003c?\n  @exec ls -la\n          lib\n          dist\n          test\n?\u003e\n```\n\nTo wrap the output in a fenced code block use a type:\n\n```xml\n\u003c? @exec {javascript} cat index.js ?\u003e\n```\n\n#### @source\n\nLoad a file and parse it as markdown or wrap it in a fenced code block, unlike the @include macro processing instructions **are not executed**.\n\nParse a markdown file into the AST stream but do not execute processing instructions:\n\n```xml\n\u003c? @source file.md ?\u003e\n```\n\nLoad a file into a fenced code block:\n\n```xml\n\u003c? @source {javascript} index.js ?\u003e\n```\n\nSometimes 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.\n\nTo do so specify a string substitution in the form `s/{regexp}/{replace}/gimy` as a value, for example:\n\n```xml\n\u003c? @source {javascript=s/\\.\\.\\/index/mkpi/gm} usage.js ?\u003e\n```\n\nWill replace all occurences of `../index` with `mkpi` in `usage.js` before the file is parsed into the stream.\n\n#### @macro\n\nDefines a macro function body; use this for application specific logic.\n\nReturn a value to inject some information into the stream:\n\n```xml\n\u003c?\n  @macro return require('./package.json').name;\n?\u003e\n```\n\nOr wrap the result in a fenced code block:\n\n```xml\n\u003c?\n  @macro {shell} return require('./package.json').scripts.test;\n?\u003e\n```\n\nFor asynchronous operations you can callback with a string to write to the stream:\n\n```xml\n\u003c?\n  @macro cb(null, '*emph*');\n?\u003e\n```\n\nSee the [macro api docs](#macro-1) for more detail.\n\n### Custom Macros\n\nCreate a vanilla object if you wish to discard the default grammar macros:\n\n```javascript\nvar mkpi = require('mkpi')\n  , grammar = {}\n  , id = 'custom-macro';\n\ngrammar[id] = function(cb) {\n  // implement macro logic\n  cb();\n}\n\nmkpi({grammar: grammar});\n```\n\nYou macro function will then be executed when the `\u003c? @custom-macro ?\u003e` processing instruction is encountered.\n\nTo extend the existing grammar with a custom macro function use:\n\n```javascript\nvar mkpi = require('mkpi')\n  , grammar = require('mkpi/lib/grammar')\n  , id = 'custom-macro';\n\ngrammar[id] = function(cb) {\n  // implement macro logic\n  cb();\n}\n\nmkpi({grammar: grammar});\n```\n\n### Macro Functions\n\nA macro function accepts a single argument `cb` which must be invoked when processing is complete, an `Error` may be passed to the callback.\n\nThey access all pertinent information via `this`, for example:\n\n```javascript\nfunction(cb) {\n  var tag = this.tag;\n  console.error(tag.name);\n  cb();\n}\n```\n\nNote the exception that `@macro` function body definitions that return a value other than `undefined` should not call the callback.\n\nSee the [grammar api docs](#grammar-1) for more information.\n\n## Help\n\n```\nUsage: mkpi [options]\n\n  Processing instruction macros.\n\nOptions\n  -s, --safe              Disable the @exec and @macro directives\n  -p, --preserve          Do not remove processing instructions\n  -h, --help              Display help and exit\n  --version               Print the version and exit\n\nmkpi@1.1.5\n```\n\n## API\n\n### pi\n\n```javascript\npi([opts][, cb])\n```\n\nExecute processing instructions found in the AST.\n\nInstructions are removed from the AST by default, use `preserve` to keep\nthem in the output.\n\nWhen no `input` and no `output` are specified the parser stream\nis returned and `cb` is ignored.\n\nReturns an output stream.\n\n* `opts` Object processing options.\n* `cb` Function callback function.\n\n#### Options\n\n* `input` Readable input stream.\n* `output` Writable output stream.\n* `grammar` Object grammar macro functions.\n* `preserve` Boolean=false keep processing instructions in the AST.\n* `safe` Boolean=false disable command and code execution.\n\n### Grammar\n\nDefault map of tag names to grammar macro functions.\n\nA macro function has the signature `function(cb)`, it should always invoke\nthe callback and may pass an error.\n\nIt can access the following fields via `this`:\n\n- `writer`: output stream, write AST nodes to this stream.\n- `comment`: parsed processing instruction comment.\n- `tag`: the tag that fired the macro function.\n- `parser`: markdown parser (`parser.parse()` to convert strings to nodes).\n- `grammar`: grammar document, map of tag identifiers to macro functions.\n- `preserve`: whether to preserve the processing instructions.\n\n#### exec\n\n```javascript\nexec(cb)\n```\n\nRun an external command, newlines are removed from the command so it\nmay span multiple lines.\n\n```xml\n\u003c? @exec pwd ?\u003e\n```\n\nTo capture the stderr stream set `stderr` before the command:\n\n```xml\n\u003c? @exec stderr pwd ?\u003e\n```\n\nBy default an error is reported if the command fails, to include the\noutput when a command returns a non-zero exit code use the `@exec!` tag:\n\n```xml\n\u003c? @exec! pwd ?\u003e\n```\n\nTo wrap a result in a fenced code block specify a `type`:\n\n```xml\n\u003c? @exec {javascript} cat index.js ?\u003e\n```\n\nIf you want the result in a fenced code block with no info string use:\n\n```xml\n\u003c? @exec {} cat index.js ?\u003e\n```\n\n* `cb` Function callback function.\n\n#### include\n\n```javascript\ninclude(cb)\n```\n\nInclude one or more markdown files into the AST, processing instructions\nin included files are executed.\n\n```html\n\u003c? @include intro.md install.md ?\u003e\n```\n\nIf a type is given it is a relative or absolute path to include from:\n\n```html\n\u003c? @include {path/to/folder} intro.md install.md ?\u003e\n```\n\nInclude files are resolved relative to the including file when file\ndata is available (`mkcat file.md`), but when no file data is available,\nfor example from stdin (`cat file.md | mkcat`), then files are resolved\nrelative to the current working directory.\n\n* `cb` Function callback function.\n\n#### macro\n\n```javascript\nmacro(cb)\n```\n\nAccepts a function body, compiles it and executes the function.\n\nUse this for inline application-specific logic.\n\nThe function is assumed to be a standard macro function implementation\nthat accepts the arguments:\n\n- `cb`: callback function to invoke when not returning a value.\n\nIt is also passed an additional non-standard argument:\n\n- `require`: alias to require files relative to the cwd.\n\nIf the function returns a value other than `undefined` the result is\nparsed as markdown and written to the stream and and control flow\nis returned (as if `cb` was invoked automatically).\n\n```xml\n\u003c? @macro return require('package.json').name; ?\u003e\n```\n\nOtherwise the macro **must** invoke the callback function and should\npass an optional error and result string to the callback:\n\n```xml\n\u003c? @macro cb(null, '*emph*'); ?\u003e\n```\n\n* `cb` Function callback function.\n\n#### Parser\n\n```javascript\nnew Parser([opts])\n```\n\nFinds processing instructions in the stream, parses those found with\n[mkparse][] and invokes a macro function if it exists for a tag in\nthe parsed processing instruction.\n\n* `opts` Object processing options.\n\n##### Options\n\n* `preserve` Boolean=false keep processing instructions.\n* `safe` Boolean=false disable command and code execution.\n\n#### replace\n\n```javascript\nreplace(str)\n```\n\nParse a substitution definition in the form `s/{regexp}/{string}/gimy`.\n\nWhen the `str` can be parsed the returned object includes:\n\n- `regexp`: RegExp compiled pattern.\n- `replace`: String replacement string.\n- `flags`: String regexp flags.\n\nIf it cannot be parsed null is returned.\n\nReturns replacement object or null.\n\n* `str` String substitution definition.\n\n##### Throws\n\n* `SyntaxError` if the regexp pattern is malformed.\n\n#### source\n\n```javascript\nsource(cb)\n```\n\nLoad and parse a file as markdown without executing processing instructions\nor wrap the file in a fenced code block.\n\n```html\n\u003c? @source file.md ?\u003e\n\u003c? @source {javascript} index.js ?\u003e\n```\n\n* `cb` Function callback function.\n\n## License\n\nMIT\n\n---\n\nCreated by [mkdoc](https://github.com/mkdoc/mkdoc) on April 18, 2016\n\n[mkdoc]: https://github.com/mkdoc/mkdoc\n[mkparse]: https://github.com/mkdoc/mkparse\n[node]: http://nodejs.org\n[npm]: http://www.npmjs.org\n[commonmark]: http://commonmark.org\n[jshint]: http://jshint.com\n[jscs]: http://jscs.info\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkdoc%2Fmkpi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkdoc%2Fmkpi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkdoc%2Fmkpi/lists"}