Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move regular expressions #13

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[ignore]
.*node_modules/babel.*
.*node_modules/fbjs.*
.*node_modules/json5.*

[include]

Expand Down
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ISC License

Copyright (c) 2016, Simon Sturmer <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ This project is still under development. If you want to help out, please open an

## License

This software is [BSD Licensed](/LICENSE).
This software is [ISC Licensed](/LICENSE).
49 changes: 32 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
{
"name": "draft-js-export-markdown",
"version": "0.1.2",
"version": "0.2.0",
"description": "DraftJS: Export ContentState to Markdown",
"main": "lib/main.js",
"scripts": {
"build": "babel src --ignore '_*' --out-dir lib",
"lint": "eslint --max-warnings 0 .",
"typecheck": "flow",
"prepublish": "npm run build",
"test": "npm run lint && npm run test-src",
"test-src": "mocha"
"test": "npm run lint && npm run typecheck && npm run test-src",
"test-src": "mocha src/__tests__/*.js src/**/__tests__/*.js"
},
"dependencies": {
"draft-js-tools": "^0.1.2"
"draft-js-utils": "^0.1.5"
},
"peerDependencies": {
"draft-js": ">=0.5.0",
"immutable": "3.x.x"
},
"devDependencies": {
"babel-core": "^6.7.2",
"babel-eslint": "^5.0.0",
"babel-plugin-transform-class-properties": "^6.6.0",
"babel-preset-es2015": "^6.6.0",
"babel-cli": "^6.9.0",
"babel-core": "^6.9.0",
"babel-eslint": "^6.0.4",
"babel-plugin-transform-class-properties": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-2": "^6.5.0",
"draft-js": "^0.2.2",
"eslint": "2.2.0",
"eslint-plugin-babel": "^3.1.0",
"eslint-plugin-flow-vars": "^0.2.1",
"eslint-plugin-react": "^4.2.1",
"expect": "^1.15.2",
"immutable": "^3.7.6",
"eslint": "^2.10.2",
"eslint-plugin-babel": "^3.2.0",
"eslint-plugin-flow-vars": "^0.4.0",
"eslint-plugin-react": "^5.1.1",
"expect": "^1.20.1",
"flow-bin": "^0.25.0",
"mocha": "^2.4.5",
"react": "^0.14.7",
"react-dom": "^0.14.7"
"react": "^15.0.2",
"react-dom": "^15.0.2"
},
"repository": {
"type": "git",
Expand All @@ -40,6 +45,16 @@
"export-markdown"
],
"author": "[email protected]",
"contributors": [
{
"name": "Freddy Harris",
"url": "https://github.com/Freddy03h"
},
{
"name": "Simon Sturmer",
"url": "https://github.com/sstur"
}
],
"license": "ISC",
"bugs": {
"url": "https://github.com/sstur/draft-js-export-markdown/issues"
Expand Down
46 changes: 46 additions & 0 deletions src/__tests__/utilities-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* @flow */
const {describe, it} = global;
import expect from 'expect';
import {
encodeContent,
encodeURL,
escapeTitle,
} from '../utilities';

describe('utilities', () => {
describe('encodeContent', () => {
it('should escape the * character', () => {
var result = encodeContent('Test * String ** Testing*');
expect(result).toEqual('Test \\* String \\*\\* Testing\\*');
});

it('should escape the _ character', () => {
var result = encodeContent('Test _ String __ Testing_');
expect(result).toEqual('Test \\_ String \\_\\_ Testing\\_');
});

it('should escape the ` character', () => {
var result = encodeContent('Test ` String `` Testing`');
expect(result).toEqual('Test \\` String \\`\\` Testing\\`');
});

it('should escape *, _, and ` characters', () => {
var result = encodeContent('Test ` String ** Testing_');
expect(result).toEqual('Test \\` String \\*\\* Testing\\_');
});
});

describe('encodeURL', () => {
it('should escape the ) character', () => {
var result = encodeURL('https://google.com/hello)');
expect(result).toEqual('https://google.com/hello%29');
});
});

describe('escapeTitle', () => {
it('should escape the " character', () => {
var result = escapeTitle('Test "Hello" Test');
expect(result).toEqual('Test \\"Hello\\" Test');
});
});
});
42 changes: 26 additions & 16 deletions src/stateToMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import {
BLOCK_TYPE,
ENTITY_TYPE,
INLINE_STYLE,
} from 'draft-js-tools';
} from 'draft-js-utils';
import {Entity} from 'draft-js';
import {
encodeContent,
encodeURL,
escapeTitle,
} from './utilities';

import type {ContentState, ContentBlock} from 'draft-js';

Expand Down Expand Up @@ -63,6 +68,21 @@ class MarkupGenerator {
this.output.push('### ' + this.renderBlockContent(block) + '\n');
break;
}
case BLOCK_TYPE.HEADER_FOUR: {
this.insertLineBreaks(1);
this.output.push('#### ' + this.renderBlockContent(block) + '\n');
break;
}
case BLOCK_TYPE.HEADER_FIVE: {
this.insertLineBreaks(1);
this.output.push('##### ' + this.renderBlockContent(block) + '\n');
break;
}
case BLOCK_TYPE.HEADER_SIX: {
this.insertLineBreaks(1);
this.output.push('###### ' + this.renderBlockContent(block) + '\n');
break;
}
case BLOCK_TYPE.UNORDERED_LIST_ITEM: {
let blockDepth = block.getDepth();
let lastBlock = this.getLastBlock();
Expand Down Expand Up @@ -210,6 +230,11 @@ class MarkupGenerator {
let url = data.url || '';
let title = data.title ? ` "${escapeTitle(data.title)}"` : '';
return `[${content}](${encodeURL(url)}${title})`;
} else if (entity != null && entity.getType() === ENTITY_TYPE.IMAGE) {
let data = entity.getData();
let src = data.src || '';
let alt = data.alt ? ` "${escapeTitle(data.alt)}"` : '';
return `![${alt}](${encodeURL(src)})`;
} else {
return content;
}
Expand All @@ -227,21 +252,6 @@ function canHaveDepth(blockType: any): boolean {
}
}

function encodeContent(text) {
return text.replace(/[*_`]/g, '\\$&');
}

// Encode chars that would normally be allowed in a URL but would conflict with
// our markdown syntax: `[foo](http://foo/)`
function encodeURL(url) {
return url.replace(/\)/g, '%29');
}

// Escape quotes using backslash.
function escapeTitle(text) {
return text.replace(/"/g, '\\"');
}

export default function stateToMarkdown(content: ContentState): string {
return new MarkupGenerator(content).generate();
}
32 changes: 32 additions & 0 deletions src/utilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* @flow */
const BASIC_MARKDOWN_CHARS = /[*_`]/g;
const URL_MARKDOWN_CHARS = /\)/g;
const QUOTATIONS = /"/g;

/**
* Escape chars (*, _) that have meaning in markdown so that they aren't interpreted as part of the markdown
* @param {string} text - the text to be replaced.
* @returns {string}
*/
export function encodeContent(text: string) {
return text.replace(BASIC_MARKDOWN_CHARS, '\\$&');
}

/**
* Encode chars that would normally be allowed in a URL but would conflict with
* our markdown syntax: `[foo](http://foo/)`
* @param {string} url The url to be encoded.
* @returns {string}
*/
export function encodeURL(url: string) {
return url.replace(URL_MARKDOWN_CHARS, '%29');
}

/**
* Escape quotes using backslash
* @param {string} text The string to be escaped
* @returns {string}
*/
export function escapeTitle(text: string) {
return text.replace(QUOTATIONS, '\\"');
}
1 change: 0 additions & 1 deletion test/mocha.opts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
--compilers js:babel-core/register
src/__tests__/*.js src/**/__tests__/*.js
8 changes: 8 additions & 0 deletions test/test-cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ _**BoldItalic**_
{"entityMap":{},"blocks":[{"key":"9nc73","text":"BoldItalic","type":"unstyled","depth":0,"inlineStyleRanges":[{"offset":4,"length":6,"style":"BOLD"},{"offset":0,"length":4,"style":"ITALIC"}],"entityRanges":[]}]}
_Bold_**Italic**

>> Image with alt
{"entityMap":{"0":{"type":"IMAGE","mutability":"MUTABLE","data":{"src":"/a.jpg","alt":"x"}}},"blocks":[{"key":"f131g","text":"Hello World.","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[{"offset":5,"length":1,"key":0}]}]}
Hello![ "x"](/a.jpg)World.

>> Image with empty alt
{"entityMap":{"0":{"type":"IMAGE","mutability":"MUTABLE","data":{"src":"/a.jpg","alt":""}}},"blocks":[{"key":"f131g","text":"Hello World.","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[{"offset":5,"length":1,"key":0}]}]}
Hello![](/a.jpg)World.

>> Link without title
{"entityMap":{"0":{"type":"LINK","mutability":"MUTABLE","data":{"url":"/a","foo":"x"}}},"blocks":[{"key":"f131g","text":"Hello World.","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[{"offset":6,"length":5,"key":0}]}]}
Hello [World](/a).
Expand Down