Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:showdownjs/showdown into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
tivie committed Mar 31, 2022
2 parents e098ec6 + 010f94f commit a9ee330
Show file tree
Hide file tree
Showing 10 changed files with 1,372 additions and 1 deletion.
773 changes: 773 additions & 0 deletions docs/available-options.md

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
Showdown comes bundled with a Command-line interface (CLI) tool that allows you to run Showdown converter from the command line.

## Requirements

* [Node.js](https://nodejs.org/en/)

## Quick start guide

1. Check that Showdown CLI is accessible.

* If you installed Showdown globally via `npm install showdown -g`, you can access the CLI tool help by typing `showdown -h` in the command line:

=== "input"

```sh
showdown -h
```

=== "output"

```
Usage: showdown <command> [options]

CLI to Showdownjs markdown parser v3.0.0-alpha

Options:
-V, --version output the version number
-q, --quiet Quiet mode. Only print errors
-m, --mute Mute mode. Does not print anything
-h, --help display help for command

Commands:
makehtml [options] Converts markdown into html
help [command] display help for command
```

* If you installed Showdown locally via `npm install showdown`, open the folder where Showdown is installed, and type `node ./bin/showdown.js -h` in the command line:

=== "input"

```sh
node ./bin/showdown.js -h
```

=== "output"

```
Usage: showdown <command> [options]

CLI to Showdownjs markdown parser v3.0.0-alpha

Options:
-V, --version output the version number
-q, --quiet Quiet mode. Only print errors
-m, --mute Mute mode. Does not print anything
-h, --help display help for command

Commands:
makehtml [options] Converts markdown into html
help [command] display help for command
```

1. Use `makehtml` command to convert your document to HTML. For example:

!!! example "Convert `foo.md` into `bar.html`"

```sh
showdown makehtml -i foo.md -o bar.html
```

## Commands

### `makehtml`

Convert a Markdown input into HTML.

**Usage**

```sh
showdown makehtml [options]
```

#### Options

###### `-i / --input`

* Short format: `-i`
* Alias: `--input`
* Description: Input source. Usually a `.md` file. If omitted or empty, reads from `stdin`.
* Examples:

!!! example ""

```sh
// Read from stdin and output to stdout
showdown makehtml -i

// Read from the foo.md file and output to stdout
showdown makehtml --input foo.md
```

###### `-o/--output`

* Short format: `-o`
* Alias: `--output`
* Description: Output target. Usually a `.html` file. If omitted or empty, writes to `stdout`.
* Example:

!!! example ""

```sh
// Read from the foo.md file and output to bar.html
showdown makehtml -i foo.md -o bar.html
```

###### `-a/--append`

* Short format: `-a`
* Alias: `--append`
* Description: Append data to output instead of overwriting.
* Example:

!!! example ""

```sh
showdown makehtml -a
```

###### `-u/--encoding`

* Short format: `-u`
* Alias: `--encoding`
* Description: Specify the input encoding.
* Example:

!!! example ""

```sh
showdown makehtml -u UTF8
```

###### `-e/--extensions`

* Short format: `-e`
* Alias: `--extension`
* Description: Load the specified extension(s). Should be valid path(s) to Node-compatible extensions.
* Example:

!!! example ""

```sh
showdown makehtml -e ~/twitter.js -e ~/youtube.js
```
60 changes: 60 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
You can change Showdown's default behavior via options.

## Set option

### Globally

Setting an option globally affects all Showdown instances.

```js
showdown.setOption('optionKey', 'value');
```

### Locally

Setting an option locally affects the specified Converter object only. You can set local options via:

=== "Constructor"

```js
var converter = new showdown.Converter({optionKey: 'value'});
```

=== "setOption() method"

```js
var converter = new showdown.Converter();
converter.setOption('optionKey', 'value');
```

## Get option

Showdown provides both local and global methods to retrieve previously set options:

=== "getOption()"

```js
// Global
var myOption = showdown.getOption('optionKey');

//Local
var myOption = converter.getOption('optionKey');
```

=== "getOptions()"

```js
// Global
var showdownGlobalOptions = showdown.getOptions();

//Local
var thisConverterSpecificOptions = converter.getOptions();
```

### Get default options

You can get Showdown's default options with:

```js
var defaultOptions = showdown.getDefaultOptions();
```
182 changes: 182 additions & 0 deletions docs/create-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
A Showdown extension is a function that returns an array of language or outputs extensions (henceforth called "sub-extensions").

```js
var myext = function () {
var myext1 = {
type: 'lang',
regex: /markdown/g,
replace: 'showdown'
};
var myext2 = {
/* extension code */
};
return [myext1, myext2];
}
```

Each sub-extension (`myext1` and `myext2` in the example above) should be an object that defines the behavior of the corresponding sub-extension.

## Sub-extension object properties

A sub-extension object should have a [`type` property](#type) that defines the type of the sub-extension, and either [`regex` and `replace` properties](#regex-and-replace) or a [`filter` property](#filter).

### Type

**Type** is a **required** property that defines the nature of the corresponding sub-extensions. It takes one of the two values:

* **`lang`**: language extension to add new Markdown syntax to Showdown.

`lang` extensions have the **highest priority** in the subparser order, so they are called after [escaping and normalizing](#escape-and-normalization) the input text and before calling any other subparser (or extension).

!!! example "When to use `lang` type"

For example, if you want the `^^youtube http://www.youtube.com/watch?v=oHg5SJYRHA0` syntax to automatically be rendered as an embedded YouTube video.

* **`output`**: output extension (or modifier) to alter the HTML output generated by Showdown.

`output` extensions have the **lowest priority** in the subparser order, so they are called right before the cleanup step and after calling all other subparsers.

!!! example "When to use `output` type"

For example, if you want the `<div class="header">` to become `<header>`.

### Regex and replace

`regex`/`replace` properties are similar to the Javascript's `string.replace` function and work the same way:

* `regex`: a `string` or a `RegExp` object.

If `regex` is a `string`, it will automatically be assigned a `g` (global) modifier, that is, all matches of that string will be replaced.

* `replace` a `string` or a `function`.

If `replace` is a `string`, you can use the `$1` syntax for group substitution, exactly as if it were making use of `string.replace`.

!!! example "Regex and replace example"

In this example, all the occurrences of `markdown` will be replaced with `showndown`.

```js
var myext = {
type: 'lang',
regex: /markdown/g,
replace: 'showdown'
};
```

### Filter

Alternately, if you'd like to have more control over the modification process, you can use `filter` property.

This property should be used as a function that acts as a callback. The callback should receive the following parameters:

1. `text`: the source text within the Showdown's engine.
1. `converter`: the full instance of the current Showdown's converter object.
1. `options`: the options used to initialize the converter

!!! warning ""
The filter function **should return the transformed text**. If it doesn't, it will fail **silently** and return an empty output.

!!! example "Filter example"

```js
var myext = {
type: 'lang',
filter: function (text, converter, options) {
// ... do stuff to text ...
return text;
}
};
```

!!! warning "Use `filter` with care"

Although Filter extensions are more powerful, they have a few pitfalls that you should keep in mind before using them, especially regarding the `converter` parameter.

Since the `converter` parameter passed to the filter function is the fully initialized instance, any change made to it will be propagated outside the scope of the filter function and will remain there until a new converter instance is created. So, **it is not recommended to make ANY change to the converter object**.

Another aspect is that if you call the `converter` recursively, it will call your extension itself at some point. It may lead to infinite recursion in some circumstances, and it's up to you to prevent this. A simple solution is to place a kind of safeguard to disable your extension if it's called more than x times:

```js
var x = 0;
var myext = {
type: 'lang',
filter: function (text, converter) {
if (x < 3) {
++x;
someSubText = converter.makeHtml(someSubText);
}
}
};
```

## Register an extension


To let Showdown know what extensions are available, you need to register them in the Showdown global object.

To register an extension, call the `showdown.extension` function with two parameters: the first one is the extension name; the second one is the actual extension.

```js
showdown.extension('myext', myext);
```

## Test an extension

The Showdown test runner is configured to automatically test cases for extensions.

To add test cases for an extension:

1. Create a new folder under `./test/extensions` that matches with the name of the `.js` file in `./src/extensions`.
1. Place any test cases into the filter using the `md/html` format. These cases will automatically be executed when running tests.

## Additional information

### Escape and normalization

Showdown performs the following escape/normalization:

* Replaces `¨` (trema) with `¨T`
* Replaces `$` (dollar sign) with `¨D`
* Normalizes line endings (`\r`, `\r\n` are converted into `\n`)
* Uses `\r` as a char placeholder

!!! note ""
This only applies to **language extensions** since these chars are unescaped before output extensions are run.

!!! warning ""

Keep in mind that these modifications happen **before language extensions** are run, so if your extension relies on any of those chars, you have to make the appropriate adjustments.


### Implementation concerns

One of the concerns is maintaining both client-side and server-side compatibility. You can do this with a few lines of boilerplate code.:

```js
(function (extension) {
if (typeof showdown !== 'undefined') {
// global (browser or node.js global)
extension(showdown);
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['showdown'], extension);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = extension(require('showdown'));
} else {
// showdown was not found so an error is thrown
throw Error('Could not find showdown library');
}
}(function (showdown) {
// loading extension into showdown
showdown.extension('myext', function () {
var myext = { /* ... actual extension code ... */ };
return [myext];
});
}));
```

In the code above, the extension definition is wrapped in a self-executing function to prevent pollution of the global scope. It has another benefit of creating several scope layers that can be useful for interaction between sub-extensions global-wise or local-wise.

It is also loaded conditionally to make it compatible with different loading mechanisms (such as browser, CommonJS, or AMD).
Loading

0 comments on commit a9ee330

Please sign in to comment.