@shogobg/markdown2confluence
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -16,6 +16,11 @@ #!/usr/bin/env node | ||
const commentEnd = buffer.indexOf('-->', 0); | ||
// Generate the confluence wiki markup | ||
const confluenceMarkup = md2conflu(buffer.slice(commentEnd + 3, buffer.length) + '') | ||
fs.writeFileSync(path.resolve(process.cwd(), outputFileName), confluenceMarkup) | ||
if(outputFileName){ | ||
fs.writeFileSync(path.resolve(process.cwd(), outputFileName), confluenceMarkup) | ||
} | ||
return confluenceMarkup; | ||
}) |
378
index.js
var marked = require('marked') | ||
var _ = require('min-util') | ||
var qs = require('min-qs') | ||
var inlineLexer = marked.inlineLexer | ||
module.exports = exports = markdown2confluence | ||
// https://roundcorner.atlassian.net/secure/WikiRendererHelpAction.jspa?section=all | ||
@@ -12,4 +9,2 @@ // https://confluence.atlassian.com/display/DOC/Confluence+Wiki+Markup | ||
var MAX_CODE_LINE = 20 | ||
function Renderer() {} | ||
@@ -19,86 +14,342 @@ | ||
var langArr = 'actionscript3 bash csharp coldfusion cpp css delphi diff erlang groovy java javafx javascript perl php none powershell python ruby scala sql vb html/xml'.split(/\s+/) | ||
var langMap = { | ||
shell: 'bash', | ||
html: 'html', | ||
xml: 'xml' | ||
} | ||
for (var i = 0, x; x = langArr[i++];) { | ||
langMap[x] = x | ||
} | ||
// https://confluence.atlassian.com/doc/code-block-macro-139390.html | ||
var defaultLanguageMap = require('./defaultLanguageMap.json') | ||
_.extend(Renderer.prototype, rawRenderer.prototype, { | ||
/** | ||
* Simple text. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
text: function (text){ | ||
return text; | ||
}, | ||
/** | ||
* A paragraph of text. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
paragraph: function(text) { | ||
return text + '\n\n' | ||
return `${text}\n\n`; | ||
} | ||
, html: function(html) { | ||
html = html.replace(/<([\w0-9]+)[\s\w="-_]*>([\w\s.!?\\-]*)<\/\1>/gi, '$1. $2') | ||
return html | ||
/** | ||
* Embedded HTML. | ||
* | ||
* <h1>My text</h1> | ||
* | ||
* turns into | ||
* | ||
* h1. My text | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, html: function(text) { | ||
return html.replace(/<([\w0-9]+)[\s\w="-_]*>([\w\s.!?\\-]*)<\/\1>/gi, '$1. $2') | ||
} | ||
, heading: function(text, level, raw) { | ||
return 'h' + level + '. ' + text + '\n\n' | ||
/** | ||
* Headings 1 through 6. | ||
* | ||
* Heading 1 | ||
* ========= | ||
* | ||
* # Heading 1 alternate | ||
* | ||
* ###### Heading 6 | ||
* | ||
* turns into | ||
* | ||
* h1. Heading 1 | ||
* | ||
* h1. Heading 1 alternate | ||
* | ||
* h6. Heading 6 | ||
* | ||
* @param {string} text | ||
* @param {number} level | ||
* @return {string} | ||
*/ | ||
, heading: function(text, level) { | ||
return `h${level}. ${text}\n\n`; | ||
} | ||
/** | ||
* Creates strong text. | ||
* | ||
* This is typically **bolded**. | ||
* | ||
* becomes | ||
* | ||
* This is typically *bolded*. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, strong: function(text) { | ||
return '*' + text + '*' | ||
return `*${text}*`; | ||
} | ||
/** | ||
* Emphasis. | ||
* | ||
* Typically this is *italicized* text. | ||
* | ||
* turns into | ||
* | ||
* Typically this is _italicized_ text. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, em: function(text) { | ||
return '_' + text + '_' | ||
return `_${text}_`; | ||
} | ||
/** | ||
* Strikethrough. | ||
* | ||
* Supported ~~everywhere~~ in GFM only. | ||
* | ||
* turns into | ||
* | ||
* Supported -everywhere- in GFM only. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, del: function(text) { | ||
return '-' + text + '-' | ||
return `-${text}-`; | ||
} | ||
/** | ||
* Inline code. | ||
* | ||
* Text that has statements, like `a = true` or similar. | ||
* | ||
* turns into | ||
* | ||
* Text that has statements, like {{a = true}} or similar. | ||
* | ||
* Be wary. This converts wrong: "Look at `~/file1` or `~/file2`" | ||
* Confluence thinks it is subscript and converts the markup into | ||
* "Look at <code><sub>/file1</code> or <code></sub>/file2</code>". | ||
* That's why some characters need to be escaped. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, codespan: function(text) { | ||
return '{{' + text + '}}' | ||
text = text.split(/(&[^;]*;)/).map((match, index) => { | ||
// These are the delimeters. | ||
if (index % 2) { | ||
return match; | ||
} | ||
return match.replace(/[^a-zA-Z0-9 ]/g, (badchar) => { | ||
return `&#${badchar[0].charCodeAt(0)};`; | ||
}); | ||
}); | ||
return `{{${text.join("")}}}`; | ||
} | ||
, blockquote: function(quote) { | ||
return '{quote}' + quote + '{quote}' | ||
/** | ||
* Blockquote. | ||
* | ||
* > This is a blockquote. | ||
* | ||
* is changed into | ||
* | ||
* {quote} | ||
* This is a blockquote. | ||
* {quote} | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, blockquote: function(text) { | ||
return `{quote}\n${text.trim()}\n{quote}\n\n`; | ||
} | ||
/** | ||
* A line break. Supposedly if you have a line with 2 or more spaces | ||
* followed by a line that doesn't have whitespace, then it turns into | ||
* this element. I'm failing to reproduce that scenario. | ||
* | ||
* @return {string} | ||
*/ | ||
, br: function() { | ||
return '\n' | ||
} | ||
/** | ||
* Horizontal rule. | ||
* | ||
* --- | ||
* | ||
* turns into | ||
* | ||
* ---- | ||
* | ||
* @return {string} | ||
*/ | ||
, hr: function() { | ||
return '----' | ||
return '----\n\n' | ||
} | ||
/** | ||
* Link to another resource. | ||
* | ||
* [Home](/) | ||
* [Home](/ "some title") | ||
* | ||
* turns into | ||
* | ||
* [Home|/] | ||
* [some title|/] | ||
* | ||
* @param {string} href | ||
* @param {string} title | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, link: function(href, title, text) { | ||
var arr = [href] | ||
if (text) { | ||
arr.unshift(text) | ||
} | ||
return '[' + arr.join('|') + ']' | ||
// Sadly, one must choose if the link's title should be displayed | ||
// or the linked text should be displayed. We picked the linked text. | ||
text = text || title; | ||
if (text) { | ||
text += "|"; | ||
} | ||
return `[${text}${href}]`; | ||
} | ||
, list: function(body, ordered) { | ||
var arr = _.filter(_.trim(body).split('\n'), function(line) { | ||
return line | ||
}) | ||
var type = ordered ? '#' : '*' | ||
return _.map(arr, function(line) { | ||
return type + ' ' + line | ||
}).join('\n') + '\n\n' | ||
/** | ||
* Converts a list. | ||
* | ||
* # ordered | ||
* * unordered | ||
* | ||
* becomes | ||
* | ||
* # ordered | ||
* #* unordered | ||
* | ||
* Note: This adds an extra "\r" before the list in order to cope | ||
* with nested lists better. When there's a "\r" in a nested list, it | ||
* is translated into a "\n". When the "\r" is left in the converted | ||
* result then it is removed. | ||
* | ||
* @param {string} text | ||
* @param {boolean} ordered | ||
* @return {string} | ||
*/ | ||
, list: function(text, ordered) { | ||
text = text.trim(); | ||
if (ordered) { | ||
text = text.replace(/^\*/gm, "#"); | ||
} | ||
return `\r${text}\n\n`; | ||
} | ||
, listitem: function(body, ordered) { | ||
return body + '\n' | ||
/** | ||
* Changes a list item. Always marks it as an unordered list, but | ||
* list() will change it back. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, listitem: function(text) { | ||
// If a list item has a nested list, it will have a "\r" in the | ||
// text. Turn that "\r" into "\n" but trim out other whitespace | ||
// from the list. | ||
text = text.replace(/\s*$/, "").replace(/\r/g, "\n"); | ||
// Convert newlines followed by a # or a * into sub-list items | ||
text = text.replace(/\n([*#])/g, "\n*$1"); | ||
return `* ${text}\n`; | ||
} | ||
, image: function(href, title, text) { | ||
return '!' + href + '!' | ||
/** | ||
* An embedded image. | ||
* | ||
* ![alt-text](image-url) | ||
* | ||
* is changed into | ||
* | ||
* !image-url! | ||
* | ||
* Markdown supports alt text and titles. Confluence does not. | ||
* | ||
* @param {string} href | ||
* @return {string} | ||
*/ | ||
, image: function(href) { | ||
return `!${href}!`; | ||
} | ||
/** | ||
* Renders a table. Most of the work is done in tablecell. | ||
* | ||
* @param {string} header | ||
* @param {string} body | ||
* @return {string} | ||
*/ | ||
, table: function(header, body) { | ||
return header + body + '\n' | ||
return `${header}${body}\n`; | ||
} | ||
, tablerow: function(content, flags) { | ||
return content + '\n' | ||
/** | ||
* Converts a table row. Most of the work is done in tablecell, however | ||
* that can't tell if the cell is at the end of a row or not. Get the | ||
* first cell's leading boundary and remove the double-boundary marks. | ||
* | ||
* @param {string} text | ||
* @return {string} | ||
*/ | ||
, tablerow: function(text) { | ||
var boundary; | ||
boundary = text.match(/^\|*/); | ||
if (boundary) { | ||
boundary = boundary[0]; | ||
} else { | ||
boundary = "|"; | ||
} | ||
return `${text}${boundary}\n`; | ||
} | ||
, tablecell: function(content, flags) { | ||
var type = flags.header ? '||' : '|' | ||
return type + content | ||
/** | ||
* Converts a table cell. When this is a header, the cell is prefixed | ||
* with two bars instead of one. | ||
* | ||
* @param {string} text | ||
* @param {Object} flags | ||
* @return {string} | ||
*/ | ||
, tablecell: function(text, flags) { | ||
var boundary; | ||
if (flags.header) { | ||
boundary = "||"; | ||
} else { | ||
boundary = "|"; | ||
} | ||
return `${boundary}${text}`; | ||
} | ||
, code: function(code, lang) { | ||
// {code:language=java|borderStyle=solid|theme=RDark|linenumbers=true|collapse=true} | ||
if(lang) { | ||
lang = lang.toLowerCase() | ||
} | ||
lang = langMap[lang] || 'none' | ||
/** | ||
* Code block. | ||
* | ||
* ```js | ||
* // JavaScript code | ||
* ``` | ||
* | ||
* is changed into | ||
* | ||
* {code:language=javascript|borderStyle=solid|theme=RDark|linenumbers=true|collapse=true} | ||
* // JavaScript code | ||
* {code} | ||
* | ||
* @param {string} text | ||
* @param {string} lang | ||
* @return {string} | ||
*/ | ||
, code: function(text, lang) { | ||
lang = defaultLanguageMap[(lang ?? "").toLowerCase()]; | ||
var param = { | ||
@@ -112,9 +363,6 @@ title: lang, | ||
} | ||
// var lineCount = _.split(code, '\n').length | ||
// if (lineCount > MAX_CODE_LINE) { | ||
// // code is too long | ||
// param.collapse = true | ||
// } | ||
param = qs.stringify(param, '|', '=') | ||
return '{code:' + param + '}\n' + code + '\n{code}\n\n' | ||
return `{code${param}}\n${text}\n{code}\n\n`; | ||
} | ||
@@ -125,4 +373,6 @@ }) | ||
function markdown2confluence(markdown) { | ||
markdown2confluence = (markdown, options) => { | ||
return marked(markdown, {renderer: renderer}) | ||
} | ||
}; | ||
module.exports = markdown2confluence; |
{ | ||
"name": "@shogobg/markdown2confluence", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Convert Markdown to Confluence markup", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "node test.js", | ||
"test": "node ./tests/test.js", | ||
"build": "npm run build-html && npm run build-js", | ||
@@ -9,0 +9,0 @@ "build-js": "webpack browser/app.js --output-filename=browser/bundle.js" |
@@ -1,23 +0,40 @@ | ||
This tool converts Markdown to Confluence markup | ||
# Markdown2Confluence | ||
This tool converts [Markdown] to [Confluence Wiki Markup]. | ||
[![NPM version][npm-image]][npm-url] | ||
[![Build status][travis-image]][travis-url] | ||
[![NPM version][npm-image]][npm-url] | ||
[![Downloads][downloads-image]][downloads-url] | ||
[![Dev Dependencies][devdependencies-badge]][devdependencies-link] | ||
[![Dependency Status][david-image]][david-url] | ||
[![codecov.io][codecov-badge]][codecov-link] | ||
[npm-image]: https://img.shields.io/npm/v/@shogobg/markdown2confluence.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/@shogobg/markdown2confluence | ||
[downloads-image]: http://img.shields.io/npm/dm/@shogobg/markdown2confluence.svg?style=flat-square | ||
[downloads-url]: https://www.npmjs.com/package/@shogobg/markdown2confluence | ||
[david-image]: http://img.shields.io/david/Shogobg/markdown2confluence.svg?style=flat-square | ||
[david-url]: https://david-dm.org/Shogobg/markdown2confluence | ||
## Overview | ||
Using [Markdown] is fast becoming a standard for open-source projects and their documentation. There are a few variants, such as [GitHub Flavored Markdown], which add additional features. | ||
Atlassian's Confluence has a different way of writing documentation, according to their [Confluence Wiki Markup] and [later pages](https://confluence.atlassian.com/display/DOC/Confluence+Wiki+Markup) and [references](https://roundcorner.atlassian.net/secure/WikiRendererHelpAction.jspa?section=all). | ||
This project contains a library and a command-line tool that bridges the gap and converts from Markdown to Confluence. | ||
## Installation | ||
```sh | ||
npm i @shogobg/markdown2confluence -g | ||
npm i -g @shogobg/markdown2confluence | ||
``` | ||
## Usage | ||
```sh | ||
npm i --save @shogobg/markdown2confluence | ||
``` | ||
## Command-Line Use | ||
Use it to convert a markdown file. | ||
markdown2confluence README.md | ||
Or pipe in a file. | ||
cat README.md | markdown2confluence | ||
```sh | ||
@@ -27,8 +44,38 @@ markdown2confluence <path/to/markdown.md> <path/to/output.txt> | ||
## Document | ||
## As library dependency | ||
[Confluence Wiki Markup](https://confluence.atlassian.com/display/CONF42/Confluence+Wiki+Markup) | ||
Or just edit your application `package.json` and add the following code to your `dependencies` object: | ||
![demo preview](./preview.png) | ||
{ | ||
... | ||
"dependencies": { | ||
... | ||
"@shogobg/markdown2confluence": "*" | ||
... | ||
} | ||
... | ||
} | ||
Now you write some JavaScript to load Markdown content and convert. | ||
markdown2confluence = require("@shogobg/markdown2confluence"); | ||
markdown = fs.readFileSync("README.md"); | ||
confluence = markdown2confluence(markdown); | ||
console.log(confluence); | ||
This uses the wonderful [marked](https://www.npmjs.com/package/marked) library to parse and reformat the Markdown text. | ||
## Supported Markdown | ||
The aim of this library is to convert as much Markdown to Confluence Wiki Markup. As such, most Markdown is supported but there are going to be rare cases that are not supported (such as code blocks within lists) or other scenarios that people find. | ||
If it is possible to convert the Markdown to Confluence Wiki Markup (without resorting to HTML), then this library should be able to do it. If you find anything wrong, it is likely a bug and should be reported. I would need a sample of Markdown, the incorrect translation and the correct way to represent that in Confluence. Please file an issue with this information in order to help replicate and fix the issue. | ||
A good demonstration chunk of markdown is available in [demo.md](demo.md). | ||
**What does not work?** | ||
- HTML. It is copied verbatim to the output text. | ||
- Did you find anything else? Please tell us about it by opening an issue. | ||
## License | ||
@@ -38,2 +85,20 @@ | ||
# About | ||
This tool was originally written by [chunpu](https://github.com/chunpu/markdown2confluence), but it was outdated with latest version from 2017. | ||
It didn't suit my needs to convert Markdown generated by widdershins, so i decided to update it and publish the changes. | ||
Shamelessly copied improvements from [fdian](https://github.com/connected-world-services/markdown2confluence-cws). | ||
[markdown]: http://daringfireball.net/projects/markdown/syntax | ||
[confluence wiki markup]: https://confluence.atlassian.com/display/CONF42/Confluence+Wiki+Markup | ||
[npm-image]: https://img.shields.io/npm/v/@shogobg/markdown2confluence.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/@shogobg/markdown2confluence | ||
[downloads-image]: http://img.shields.io/npm/dm/@shogobg/markdown2confluence.svg?style=flat-square | ||
[downloads-url]: https://www.npmjs.com/package/@shogobg/markdown2confluence | ||
[david-image]: http://img.shields.io/david/Shogobg/markdown2confluence.svg?style=flat-square | ||
[david-url]: https://david-dm.org/Shogobg/markdown2confluence | ||
[devdependencies-badge]: https://img.shields.io/david/dev/Shogobg/markdown2confluence.svg | ||
[devdependencies-link]: https://david-dm.org/Shogobg/markdown2confluence#info=devDependencies | ||
[codecov-badge]: https://img.shields.io/codecov/c/github/Shogobg/markdown2confluence/master.svg | ||
[codecov-link]: https://codecov.io/github/Shogobg/markdown2confluence?branch=master | ||
[travis-image]: https://img.shields.io/travis/@shogobg/markdown2confluence.svg?style=flat-square | ||
@@ -43,6 +108,1 @@ [travis-url]: https://travis-ci.org/Shogobg/markdown2confluence | ||
[license-url]: # | ||
# About | ||
This tools was originally written by chunpu, but it was outdated with latest version from 2017. | ||
It didn't suit my needs to convert Markdown generated by widdershins, so i decided to update it and publish the changes. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
23498
10
406
106
1