Skip to content

Commit 6eddf81

Browse files
committed
Initial commit.
1 parent 75cf800 commit 6eddf81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+8524
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,5 @@ dist
102102

103103
# TernJS port file
104104
.tern-port
105+
lib/
106+
.umbra-cache/

README.md

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,106 @@
1-
# mdbook-nodejs-preprocessor-util
1+
# mdbook-nodejs-preprocessor-builder
2+
> A framework for building [mdBook](https://github.com/rust-lang/mdBook) preprocessors with NodeJS. Supports MdBook 0.3.x
3+
4+
`mdbook-nodejs-preprocessor-builder` enables you to easily build macros and dynamically generated content into mdBook projects. Easily build macros, text replacement, table generation, or even dynamic content from the flexibility and comfort of NodeJS.
5+
6+
## What, Why, and How
7+
MdBook has the concept of a [Preprocessor](https://rust-lang.github.io/mdBook/for_developers/preprocessors.html), which can transform markdown after it's been read by MdBook but before it reaches the final Renderer. Preprocessors enable users to add advanced functionality without having to modify the MdBook source or rely solely on client-side JavaScript. At its simplest, a Preprocessor can replace any input text into any output text. For example, a username replacer preprocessor could replace all instances of `{{#username}}` with `Bob`.
8+
9+
As MdBook is written in Rust, Rust-based preprocessors are well supported with full interfaces and examples. One can relatively easy create a new preprocessor in Rust, but there are many other languages one might use with their own advantages and disadvantages. MdBook's preprocessor feature supports executing preprocessors in a separate process, leading to the ability to implement them in any way shape and form the user likes.
10+
11+
The `mdbook-nodejs-preproessor-builder`, as its name implies, is a framework for easily implementing said preprocessors using NodeJS. It abstracts away all the details of integrating with MdBook, message passing, and serializing/deserializing data. It also includes support for common macro patterns, enabling preprocessors to be easily implemented in TypeScript or JavaScript.
12+
13+
## Handler Types
14+
This builder comes with out-of-the-box support for three distinct types of handlers: `Raw`, `RegExp`, and `KeyValue`. Each one includes a increasing level of built-in macro processing: `Raw` supplies the full content for generic processing by the handler, `RegExp` allows you to supply your own custom Regular Expression for macro matching, and `KeyValue` makes it easy to add structured macros with well-defined key-value attributes.
15+
16+
#### Ordering of handlers
17+
Be aware that a preprocessor can contain any number of handlers, with any number of types. They are always executed as follows:
18+
1. Raw handlers, in the order of assignment to the builder.
19+
1. RegExp handlers, in the order of assignment to the builder.
20+
1. KeyValue handlers, in the order of assignment to the builder.
21+
22+
This means that if you attach RawA, RegExpA, KeyValueA, KeyValueB, RegExpB, RawB, the order of execution will be RawA, RawB, RegExpA, RegExpB, KeyValueA, KeyValueB.
23+
24+
### Raw
25+
Raw handlers are the most generic type of handler -- each chapter is passed in blindly for processing, and each handler can modify the Chapter as it wishes. Unlike the other handlers, there is no parsing or processing of the content whatsoever. It is solely the responsibility of the handler.
26+
27+
Note: There is no need to modify `sub_items` in each Chapter -- the handler will be called for each and every Chapter, including sub_items.
28+
29+
```typescript
30+
import {MdBookProcessorBuilder} from "mdbook-nodejs-preprocessor-builder";
31+
32+
MdBookPreprocessorBuilder.builder()
33+
.withRendererSupport("html")
34+
.withRawContentHandler((inputChapter: Chapter) => {
35+
chapter.content = chapter.content.replace("Kirk", "Picard");
36+
return chapter;
37+
})
38+
.ready();
39+
```
40+
41+
### RegExp
42+
RegExp handlers include integrated support for RegExp parsing and replacement of content, without the user needing to add their own boilerplate. Unlike raw handlers above, this requires the user to return the replacement string. The full `Chapter` is still included for reference, as well as if the Handler would like to modify it (not recommended). Do **not** modify `chapter.content`, as it will be overridden once all handlers have been evaluated.
43+
44+
In the following example, all instances of any string matching the RegExp of `{{RankMacro (\S*) (\S*)}}` will be matched and entirely replaced.
45+
46+
```typescript
47+
import {MdBookProcessorBuilder} from "mdbook-nodejs-preprocessor-builder";
48+
49+
MdBookPreprocessorBuilder.builder()
50+
.withRendererSupport("html")
51+
.withRegExpHandler(/{{RankMacro (\S*) (\S*)}}/g, (inputChapter: Chapter, fullyMatchedString: string, firstName: string, lastName: string) => {
52+
// fullyMatchedString: "{{#RankMacro Jean-Luc Picard}}"
53+
if(firstName === "Jean-Luc" && lastName === "Picard") {
54+
return "Captain Jean-Luc Picard";
55+
}
56+
57+
return `${firstName} ${lastName}`;
58+
})
59+
.ready();
60+
```
61+
62+
### KeyValue
63+
KeyValue handlers include integrated support for parsing macros in a common form: `{{#macroName key=value boolKey}}`. This is the easiest way to get started with your own macro.
64+
65+
#### Names
66+
KeyValue handlers only need a name to get started. This name is used to find any and all macros with the format: ``{{#macroName}}``. So, if your name is `Ship`, the matched macro would be of the form `{{#Ship}}`. The handler will *only* be executed for strings matching that structure, regardless of whether or not there are any attributes given.
67+
68+
#### Attributes
69+
Attributes are anything after the name of the macro (plus a space), and before the closing curly-braces. All attributes are expected to be in the format used by [node-logfmt](https://github.com/csquared/node-logfmt) -- these are very simple key-value pairs, with boolean values optionally requiring an explicit value. Strings may be wrapped in quotes, double quotes, or none at all if they don't contain whitespace. Numbers and boolean values are likewise parsed.
70+
71+
Attributes passed into handlers are directly parsed from the raw macro string. For full documentation of the schema, see [node-logfmt](https://github.com/csquared/node-logfmt).
72+
73+
For example, the following are equivalent:
74+
* `{{#Ship name="Enterprise" registryNumber=1701 active=true}}`
75+
* `{{#Ship name=Enterprise registryNumber=1701 active}}`
76+
77+
```typescript
78+
import {MdBookProcessorBuilder} from "mdbook-nodejs-preprocessor-builder";
79+
80+
MdBookPreprocessorBuilder.builder()
81+
.withRendererSupport("html")
82+
.withKeyValueHandler("Ship", (inputChapter: Chapter, matches: { name: string; registryNumber: number, active: boolean }) => {
83+
// fullyMatchedString: "{{#Ship name="Enterprise" registryNumber=1701 active}}"
84+
// Replaces it with: "USS Enterprise, NCC-1701, currently active"
85+
return `USS ${matches.name}, NCC-${matches.registryNumber}, currently ${matches.active ? "active" : "inactive"}`;
86+
})
87+
.ready();
88+
```
89+
90+
## Creating a Preprocessor
91+
92+
1. Create a new NodeJS package.
93+
1. Install `mdbook-nodejs-preprocessor`
94+
1. Create a TS or JS file that can be executed on the CLI. TypeScript definitions are included for your convenience.
95+
1. Create an instance of `MdBookPreprocessorBuilder` and declare one or more `renderer` types. These should match those in your `book.toml` file.
96+
1. Attach one or more content replacement handlers, either via `KeyValue`, `RawContent`, or `RegExp`.
97+
1. Call `.ready()`, optionally waiting for the promise to resolve.
98+
99+
## Adding the Preprocessor
100+
This is the easy part -- just add a toml table in the format of `[preprocessor.*]` and with `command` and `renderer` keys. The renderer types must match those declared in your preprocessor.
101+
102+
```toml
103+
[preprocessor.example]
104+
command = "node where/your/preprocessor-is.js"
105+
renderer = ["html"]
106+
```

examples/book-src/SUMMARY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Summary
2+
- [Overview](overview.md)
3+
- [SubChapter](subchapter.md)
4+

examples/book-src/overview.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Top-level Chapter
2+
3+
## Raw Example
4+
This is an example of processing with raw chapters. In this case, we're going to replace all instances of Kirk with Picard. So, if the preprocessor worked, you should see the Kirk Kirk Kirk be Picard Picard Picard.
5+
6+
## RegExp Example
7+
This is an example of processing with a RegExp handler. In this case, we're going to insert Picard's rank, but no one else's.
8+
9+
The Commanding Officer of the USS Enterprise is {{RankMacro Jean-Luc Picard}}. His First Officer is {{RankMacro Will Riker}}. If the processor worked, you should see Picard's rank, but not Riker's.
10+
11+
## KeyValue Example
12+
This is an example of processing with a KeyValue handler. In this case, we're going to generate Ship identifiers based on attributes.
13+
14+
The following is a list of active or inactive Federation Starships. if the processor worked, you should see the Enterprise, the Defiant, and Voyager. See the flexibility in the attribute parsing supplied by [node-logfmt](https://github.com/csquared/node-logfmt).
15+
* {{#Ship name=Enterprise registryNumber=1701}}
16+
* {{#Ship name=Defiant registryNumber=75633 active}}
17+
* {{#Ship name="Voyager" registryNumber=74656 active=false}}
18+
19+

examples/book-src/subchapter.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Sub Chapter Example
2+
3+
## Raw Example
4+
This is an example of processing with raw chapters. In this case, we're going to replace all instances of Kirk with Picard. So, if the preprocessor worked, you should see the Kirk Kirk Kirk be Picard Picard Picard.

examples/book.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[book]
2+
authors = ["pnann"]
3+
language = "en"
4+
multilingual = false
5+
src = "book-src"
6+
title = "Raw Processor Example"
7+
8+
[output.html]
9+
default-theme = "rust"
10+
11+
[preprocessor.spells]
12+
command = "node ./processor.js"
13+
renderer = ["html"]

examples/book/.nojekyll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file makes sure that Github Pages doesn't process mdBook's output.

examples/book/FontAwesome/css/font-awesome.css

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)