New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@october/slate-md-serializer

Package Overview
Dependencies
Maintainers
10
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@october/slate-md-serializer - npm Package Compare versions

Comparing version 1.0.26 to 1.0.27

163

lib/parser.js

@@ -58,10 +58,10 @@ "use strict";

fences: noop,
hr: /^( *[-*_]){3,} *(?:\n+|$)/,
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
hr: /^( *[-*_]){3,} *(?:\n|$)/,
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n|$)/,
nptable: noop,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|def))+)\n*/,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n|$)/,
blockquote: /^( *>[^\n]+(\n(?!def)[^\n])*(?:\n|$))+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n(?! )(?!\1bull )\n|\s*$)/,
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n|$)/,
paragraph: /^((?:[^\n]+(?!hr|heading|lheading|blockquote|def))+)(?:\n|$)/,
text: /^[^\n]+/

@@ -91,5 +91,5 @@ };

block.gfm = assign({}, block.normal, {
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n|$)/,
paragraph: /^/,
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n{1,2}|$)/
});

@@ -104,4 +104,4 @@

block.tables = assign({}, block.gfm, {
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)/
});

@@ -169,2 +169,3 @@

src = src.replace(/^ +$/gm, "");
src = src.replace(/^\n/, "");

@@ -175,6 +176,11 @@ while (src) {

src = src.substring(cap[0].length);
if (cap[0].length > 1) {
this.tokens.push({
type: "space"
});
var newlines = cap[0].length;
if (top) {
for (var _i = 0; _i < newlines; _i++) {
this.tokens.push({
type: "paragraph",
text: ""
});
}
}

@@ -208,2 +214,8 @@ }

src = src.substring(cap[0].length);
var last = this.tokens[this.tokens.length - 1];
if (last && last.type === "paragraph" && last.text === "") {
this.tokens.splice(-1, 1);
}
this.tokens.push({

@@ -412,6 +424,13 @@ type: "heading",

src = src.substring(cap[0].length);
var endsWithNewline = cap[1].charAt(cap[1].length - 1) === "\n";
this.tokens.push({
type: "paragraph",
text: cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1]
text: endsWithNewline ? cap[1].slice(0, -1) : cap[1]
});
if (endsWithNewline) {
this.tokens.push({
type: "paragraph",
text: ""
});
}
continue;

@@ -554,3 +573,3 @@ }

out.push({
kind: "text",
object: "text",
leaves: [{

@@ -580,3 +599,3 @@ text: cap[1]

out.push({
kind: "text",
object: "text",
leaves: [{

@@ -678,13 +697,13 @@ text: cap[0].charAt(0)

var accLast = acc.length - 1;
var lastIsText = accLast >= 0 && acc[accLast] && acc[accLast]["kind"] === "text";
var lastIsText = accLast >= 0 && acc[accLast] && acc[accLast]["object"] === "text";
if (current instanceof TextNode) {
if (current.text) {
if (lastIsText) {
// If the previous item was a text kind, push the current text to it's range
// If the previous item was a text object, push the current text to it's range
acc[accLast].leaves.push(current);
return acc;
} else {
// Else, create a new text kind
// Else, create a new text object
acc.push({
kind: "text",
object: "text",
leaves: [current]

@@ -711,3 +730,3 @@ });

return {
kind: "block",
object: "block",
type: "code",

@@ -721,3 +740,3 @@ data: data,

return {
kind: "block",
object: "block",
type: "block-quote",

@@ -730,3 +749,3 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "heading" + level,

@@ -739,10 +758,4 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "horizontal-rule",
nodes: [{
kind: "text",
leaves: [{
text: ""
}]
}],
isVoid: true

@@ -754,3 +767,3 @@ };

return {
kind: "block",
object: "block",
type: style + "-list",

@@ -770,3 +783,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
type: "list-item",

@@ -780,3 +793,3 @@ data: data,

return {
kind: "block",
object: "block",
type: "paragraph",

@@ -789,3 +802,3 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "table",

@@ -798,3 +811,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
type: "table-row",

@@ -809,3 +822,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
data: { align: align },

@@ -841,7 +854,12 @@ type: flags.header ? "table-head" : "table-cell",

Renderer.prototype.codespan = function (text) {
return new TextNode(text, { type: "code" });
return {
text: text,
marks: [{ type: "code" }]
};
};
Renderer.prototype.br = function () {
return new TextNode("");
return {
text: " "
};
};

@@ -879,3 +897,3 @@

return {
kind: "inline",
object: "inline",
type: "link",

@@ -900,6 +918,6 @@ nodes: this.groupTextInLeaves(childNode),

return {
kind: "block",
object: "block",
type: "image",
nodes: [{
kind: "text",
object: "text",
leaves: [{

@@ -915,13 +933,7 @@ text: ""

Renderer.prototype.text = function (childNode) {
return new TextNode(childNode);
return {
text: childNode
};
};
// Auxiliary object constructors:
function TextNode(text, marks) {
this.text = text;
if (marks) {
this.marks = [marks];
}
}
/**

@@ -1004,3 +1016,3 @@ * Parsing & Compiling

return {
kind: "text",
object: "text",
leaves: [{

@@ -1121,15 +1133,40 @@ text: ""

options = assign({}, defaults, options);
var fragment = void 0;
try {
var fragment = Parser.parse(Lexer.parse(src, options), options);
} catch (e) {
fragment = Parser.parse(Lexer.parse(src, options), options);
if (!fragment.length) {
fragment = [{
object: "block",
type: "paragraph",
isVoid: false,
data: {},
nodes: [{
object: "text",
leaves: [{
object: "leaf",
text: "",
marks: []
}]
}]
}];
}
} catch (err) {
if (options.silent) {
fragment = [{
kind: "block",
object: "block",
type: "paragraph",
isVoid: false,
data: {},
nodes: [{
kind: "text",
object: "text",
leaves: [{
text: "An error occured:"
object: "leaf",
text: "An error occured:",
marks: []
}, {
text: e.message
object: "leaf",
text: e.message,
marks: []
}]

@@ -1139,7 +1176,7 @@ }]

} else {
throw e;
throw err;
}
}
var mainNode = { nodes: fragment };
return mainNode;
return { nodes: fragment };
}

@@ -1146,0 +1183,0 @@ };

@@ -26,3 +26,3 @@ "use strict";

var String = new _immutable.Record({
kind: "string",
object: "string",
text: ""

@@ -32,11 +32,7 @@ });

function formatLinkBar(img, url, title, desc, domain) {
return "%%%\n" + (img ? url + "\n" + img : url) + "\n" + title + "\n" + desc.replace(/\[(.*?)\]/g, "") + "\n" + domain + "\n%%%\n";
return "\n\n%%%\n" + (img ? url + "\n" + img : url) + "\n" + title + "\n" + desc.replace(/\[(.*?)\]/g, "") + "\n" + domain + "\n%%%\n";
}
function formatSoftBreak(children) {
return children.replace(/\n/g, " \n");
}
/**
* Rules to (de)serialize nodes.git pu
* Rules to (de)serialize nodes.
*

@@ -50,4 +46,4 @@ * @type {Object}

serialize: function serialize(obj, children) {
if (obj.kind === "string") {
return ("" + children).replace(/\\/g, "\\\\").replace(/!/g, "\\!").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/%/g, "\\%");
if (obj.object === "string") {
return children;
}

@@ -57,3 +53,3 @@ }

serialize: function serialize(obj, children, document) {
if (obj.kind !== "block") return;
if (obj.object !== "block") return;
var parent = document.getParent(obj.key);

@@ -64,3 +60,5 @@

tableHeader = "";
return children;
// trim removes trailing newline
return children.trim();
case "table-head":

@@ -93,18 +91,21 @@ {

case "paragraph":
if (parent.type === "list-item") {
return formatSoftBreak(children);
} else {
return "\n" + formatSoftBreak(children) + "\n";
}
return children;
case "code":
return "```\n" + children + "\n```\n";
return "```\n" + children + "\n```";
case "code-line":
return children + "\n";
case "block-quote":
return "> " + children + "\n";
return "> " + children;
case "todo-list":
case "bulleted-list":
case "ordered-list":
if (parent === document) {
return "\n" + children;
{
// root list
if (parent === document) {
return children;
}
// nested list
return "\n" + children.replace(/\n+$/gm, "").replace(/^/gm, " ");
}
return "\n" + children.replace(/^/gm, " ");
case "list-item":

@@ -114,32 +115,30 @@ {

case "ordered-list":
return "1. " + formatSoftBreak(children) + "\n";
return "1. " + children + "\n";
case "todo-list":
var checked = obj.getIn(["data", "checked"]);
var box = checked ? "[x]" : "[ ]";
return box + " " + formatSoftBreak(children) + "\n";
return box + " " + children + "\n";
default:
case "bulleted-list":
return "* " + formatSoftBreak(children) + "\n";
return "* " + children + "\n";
}
}
case "heading1":
return "# " + formatSoftBreak(children);
return "# " + children + "\n";
case "heading2":
return "## " + children;
return "\n## " + children + "\n";
case "heading3":
return "### " + children;
return "\n### " + children + "\n";
case "heading4":
return "#### " + children;
return "\n#### " + children + "\n";
case "heading5":
return "##### " + children;
return "\n##### " + children + "\n";
case "heading6":
return "###### " + children;
case "heading6":
return "###### " + children;
return "\n###### " + children + "\n";
case "horizontal-rule":
return "---\n";
return "---";
case "image":
var alt = obj.getIn(["data", "alt"]);
var alt = obj.getIn(["data", "alt"]) || "";
var src = (0, _urls.encode)(obj.getIn(["data", "src"]) || "");
return "![" + alt + "](" + src + ")\n";
return "![" + alt + "](" + src + ")";
case "linkbar":

@@ -157,9 +156,7 @@ var img = (0, _urls.encode)(obj.getIn(["data", "image"]) || "");

serialize: function serialize(obj, children) {
if (obj.kind !== "inline") return;
if (obj.object !== "inline") return;
switch (obj.type) {
case "link":
var href = (0, _urls.encode)(obj.getIn(["data", "href"]) || "");
return "[" + children.trim() + "](" + href + ")";
case "code-line":
return "`" + children + "`";
return href ? "[" + children.trim() + "](" + href + ")" : children.trim();
case "mention":

@@ -175,3 +172,3 @@ var username = obj.getIn(["data", "username"]) || "";

serialize: function serialize(obj, children) {
if (obj.kind !== "mark") return;
if (obj.object !== "mark") return;
switch (obj.type) {

@@ -181,7 +178,7 @@ case "bold":

case "italic":
return "*" + children + "*";
return "_" + children + "_";
case "code":
return "`" + children + "`";
case "inserted":
return "__" + children + "__";
return "++" + children + "++";
case "deleted":

@@ -256,5 +253,14 @@ return "~~" + children + "~~";

if (node.kind == "text") {
if (node.object == "text") {
var leaves = node.getLeaves();
return leaves.map(this.serializeLeaves);
var inCodeBlock = !!document.getClosest(node.key, function (n) {
return n.type === "code";
});
return leaves.map(function (leave) {
var inCodeMark = !!leave.marks.filter(function (mark) {
return mark.type === "code";
}).size;
return _this2.serializeLeaves(leave, !inCodeBlock && !inCodeMark);
});
}

@@ -307,3 +313,10 @@

var string = new String({ text: leaves.text });
var escape = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var leavesText = leaves.text;
if (escape) {
// escape markdown characters
leavesText = leavesText.replace(/([\\`*{}\[\]()#+\-.!_>]@%)/gi, "\\$1").replace(/\n/g, " \n"); // format softBreaks
}
var string = new String({ text: leavesText });
var text = this.serializeString(string);

@@ -310,0 +323,0 @@

@@ -10,7 +10,24 @@ "use strict";

function encode(href) {
return href.trim().replace(/ /g, "%20").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29");
return decodeSafe(href).trim().replace(/ /g, "%20").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29");
}
function decode(href) {
return decodeURI(href);
try {
return decodeURI(href);
} catch (e) {
return decodeSafe(href);
}
}
// convert hanging % characters into percentage encoded %25 as decodeURI cannot
// handle this scenario but users may input 'invalid' urls.
function decodeSafe(uri) {
var components = uri.split(/(%(?:d0|d1)%.{2})/);
return components.map(function (component) {
try {
return decodeURIComponent(component);
} catch (e) {
return component.replace(/%(?!\d+)/g, "%25");
}
}).join("");
}
{
"name": "@october/slate-md-serializer",
"version": "1.0.26",
"version": "1.0.27",
"description": "",

@@ -20,3 +20,3 @@ "main": "lib/renderer.js",

"immutable": ">=3.0.0",
"slate": ">=0.31.5"
"slate": "~0.34.0"
},

@@ -37,3 +37,3 @@ "devDependencies": {

"react-dom": "^15.5.4",
"slate": "0.31.5"
"slate": "^0.34.5"
},

@@ -40,0 +40,0 @@ "jest": {

import MarkdownRenderer from "../renderer";
const Markdown = new MarkdownRenderer();
// By parsing, rendering and reparsing we can test both sides of the serializer
// at the same time and ensure that parsing / rendering is compatible.
function getNodes(text) {
const parsed = Markdown.deserialize(text);
const rendered = Markdown.serialize(parsed);
const reparsed = Markdown.deserialize(rendered);
return reparsed.document.nodes;
}
test("parses paragraph", () => {
const output = Markdown.deserialize("This is just a sentance");
expect(output.document.nodes).toMatchSnapshot();
const text = "This is just a sentance";
expect(getNodes(text)).toMatchSnapshot();
});
test("parses two paragraphs", () => {
const text = `
This is the first sentance
This is the second sentance
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses two paragraphs", () => {
const text = `
This is the first sentance
This is the second sentance
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("maintains multiple empty paragraphs", () => {
const text = `
This is the first sentance
Two empty paragraphs above
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses heading1", () => {

@@ -39,2 +75,59 @@ const output = Markdown.deserialize("# Heading");

test("headings are not greedy about newlines", () => {
const text = `
a paragraph
## Heading
another paragraph
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses horizontal rule", () => {
const text = `
---
a paragraph
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("bold mark", () => {
const text = `**this is bold**`;
expect(getNodes(text)).toMatchSnapshot();
});
test("italic mark", () => {
const text = `*this is italic* _this is italic too_`;
expect(getNodes(text)).toMatchSnapshot();
});
test("deleted mark", () => {
const text = `~~this is strikethrough~~`;
expect(getNodes(text)).toMatchSnapshot();
});
test("inserted mark", () => {
const text = `++inserted text++`;
expect(getNodes(text)).toMatchSnapshot();
});
test("code mark", () => {
const text = "`const foo = 123;`";
expect(getNodes(text)).toMatchSnapshot();
});
test("code mark with escaped characters", () => {
const text = "`<script>alert('foo')</script>`";
expect(getNodes(text)).toMatchSnapshot();
});
test("does not escape characters inside of code marks", () => {
const text = "`<script>alert('foo')</script>`";
const parsed = Markdown.deserialize(text);
const rendered = Markdown.serialize(parsed);
expect(rendered).toMatchSnapshot();
});
test("parses quote", () => {

@@ -44,14 +137,31 @@ const text = `

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("parses quote with marks", () => {
test("parses quote followed by list with quote (outline/#723)", () => {
const text = `
> **bold** in a quote
> this is a quote
1. > this is a list item
`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("quotes do not get combined", () => {
const text = `
> this is a quote
> this is a different quote
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("quote is not greedy about newlines", () => {
const text = `
> this is a quote
this is a paragraph
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses list items", () => {

@@ -62,14 +172,24 @@ const text = `

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("parses list with trailing item", () => {
test("parses nested list items", () => {
const text = `
- one
- two
-
* one
* two
* nested
next para`;
expect(getNodes(text)).toMatchSnapshot();
});
test("does not add extra paragraphs around lists", () => {
const text = `
first paragraph
- list
second paragraph
`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -82,4 +202,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -92,4 +211,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -102,4 +220,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -112,4 +229,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -123,4 +239,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -136,4 +251,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -149,7 +263,14 @@

`;
expect(getNodes(text)).toMatchSnapshot();
});
const output = Markdown.deserialize(text);
const result = Markdown.serialize(output);
const output2 = Markdown.deserialize(result);
expect(output2.document.nodes).toMatchSnapshot();
test("tables are not greedy about newlines", () => {
const text = `
| Tables | Are | Cool |
|----------|:-------------:|------:|
| col 1 is | left-aligned | $1600 |
a new paragraph
`;
expect(getNodes(text)).toMatchSnapshot();
});

@@ -162,4 +283,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -173,4 +293,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -186,4 +305,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -196,4 +314,3 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -210,6 +327,35 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("does not escape characters inside of code blocks", () => {
const text = `
\`\`\`
const hello = 'world';
function() {
return hello;
}
\`\`\`
`;
const parsed = Markdown.deserialize(text);
const rendered = Markdown.serialize(parsed);
expect(rendered).toMatchSnapshot();
});
test("code is not greedy about newlines", () => {
const text = `
one sentance
\`\`\`
const hello = 'world';
function() {
return hello;
}
\`\`\`
two sentance
`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses ~~~ code fences", () => {

@@ -224,4 +370,3 @@ const text = `

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -236,10 +381,13 @@

`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("parses image", () => {
const text = `![example](http://example.com/logo.png)`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses link", () => {
const text = `[google](http://google.com)`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -249,4 +397,3 @@

const text = `**[google](http://google.com)**`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});

@@ -256,26 +403,34 @@

const text = `[kibana](https://example.com/app/kibana#/discover?_g=%28refreshInterval:%28%27$$hashKey%27:%27object:1596%27,display:%2710%20seconds%27,pause:!f,section:1,value:10000%29,time:%28from:now-15m,mode:quick,to:now%29%29&_a=%28columns:!%28metadata.step,message,metadata.attempt_f,metadata.tries_f,metadata.error_class,metadata.url%29,index:%27logs-%27,interval:auto,query:%28query_string:%28analyze_wildcard:!t,query:%27metadata.at:%20Stepper*%27%29%29,sort:!%28time,desc%29%29)`;
const output = Markdown.deserialize(text);
const result = Markdown.serialize(output);
const output2 = Markdown.deserialize(result);
expect(output2.document.nodes).toMatchSnapshot();
expect(getNodes(text)).toMatchSnapshot();
});
test("parses interesting nesting", () => {
const text = `
* List item that contains a blockquote with inline mark
test("parses link with percent symbol", () => {
const text = `[kibana](https://example.com/app/kibana#/visualize/edit/Requests-%)`;
expect(getNodes(text)).toMatchSnapshot();
});
>Blockquote with code \`mapStateToProps()\`
`;
const output = Markdown.deserialize(text);
expect(output.document.nodes).toMatchSnapshot();
test("ignores empty link", () => {
const text = `[empty]()`;
expect(getNodes(text)).toMatchSnapshot();
});
test("parses empty string", () => {
const output = Markdown.deserialize("");
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes("")).toMatchSnapshot();
});
test("parses whitespace string", () => {
const output = Markdown.deserialize(" ");
expect(output.document.nodes).toMatchSnapshot();
expect(getNodes(" ")).toMatchSnapshot();
});
test("handles escaped blocks", () => {
expect(getNodes("\\# text")).toMatchSnapshot();
expect(getNodes("\\- text")).toMatchSnapshot();
expect(getNodes("\\* text")).toMatchSnapshot();
});
test("handles escaped marks", () => {
expect(getNodes("this is \\*\\*not bold\\*\\*")).toMatchSnapshot();
expect(getNodes("this is \\*not italic\\*")).toMatchSnapshot();
expect(getNodes("this is \\[not\\]\\(a link\\)")).toMatchSnapshot();
expect(getNodes("this is \\!\\[not\\]\\(an image\\)")).toMatchSnapshot();
});

@@ -54,10 +54,10 @@ import { decode } from "./urls";

fences: noop,
hr: /^( *[-*_]){3,} *(?:\n+|$)/,
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
hr: /^( *[-*_]){3,} *(?:\n|$)/,
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n|$)/,
nptable: noop,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|def))+)\n*/,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n|$)/,
blockquote: /^( *>[^\n]+(\n(?!def)[^\n])*(?:\n|$))+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n(?! )(?!\1bull )\n|\s*$)/,
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n|$)/,
paragraph: /^((?:[^\n]+(?!hr|heading|lheading|blockquote|def))+)(?:\n|$)/,
text: /^[^\n]+/

@@ -96,5 +96,5 @@ };

block.gfm = assign({}, block.normal, {
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n|$)/,
paragraph: /^/,
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n{1,2}|$)/
});

@@ -116,4 +116,4 @@

block.tables = assign({}, block.gfm, {
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)/
});

@@ -185,2 +185,3 @@

src = src.replace(/^ +$/gm, "");
src = src.replace(/^\n/, "");

@@ -191,6 +192,11 @@ while (src) {

src = src.substring(cap[0].length);
if (cap[0].length > 1) {
this.tokens.push({
type: "space"
});
const newlines = cap[0].length;
if (top) {
for (let i = 0; i < newlines; i++) {
this.tokens.push({
type: "paragraph",
text: ""
});
}
}

@@ -224,2 +230,8 @@ }

src = src.substring(cap[0].length);
const last = this.tokens[this.tokens.length - 1];
if (last && last.type === "paragraph" && last.text === "") {
this.tokens.splice(-1, 1);
}
this.tokens.push({

@@ -432,8 +444,13 @@ type: "heading",

src = src.substring(cap[0].length);
const endsWithNewline = cap[1].charAt(cap[1].length - 1) === "\n";
this.tokens.push({
type: "paragraph",
text: cap[1].charAt(cap[1].length - 1) === "\n"
? cap[1].slice(0, -1)
: cap[1]
text: endsWithNewline ? cap[1].slice(0, -1) : cap[1]
});
if (endsWithNewline) {
this.tokens.push({
type: "paragraph",
text: ""
});
}
continue;

@@ -579,3 +596,3 @@ }

out.push({
kind: "text",
object: "text",
leaves: [

@@ -610,3 +627,3 @@ {

out.push({
kind: "text",
object: "text",
leaves: [

@@ -711,13 +728,13 @@ {

let lastIsText =
accLast >= 0 && acc[accLast] && acc[accLast]["kind"] === "text";
accLast >= 0 && acc[accLast] && acc[accLast]["object"] === "text";
if (current instanceof TextNode) {
if (current.text) {
if (lastIsText) {
// If the previous item was a text kind, push the current text to it's range
// If the previous item was a text object, push the current text to it's range
acc[accLast].leaves.push(current);
return acc;
} else {
// Else, create a new text kind
// Else, create a new text object
acc.push({
kind: "text",
object: "text",
leaves: [current]

@@ -744,3 +761,3 @@ });

return {
kind: "block",
object: "block",
type: "code",

@@ -754,3 +771,3 @@ data,

return {
kind: "block",
object: "block",
type: "block-quote",

@@ -763,3 +780,3 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "heading" + level,

@@ -772,14 +789,4 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "horizontal-rule",
nodes: [
{
kind: "text",
leaves: [
{
text: ""
}
]
}
],
isVoid: true

@@ -791,3 +798,3 @@ };

return {
kind: "block",
object: "block",
type: `${style}-list`,

@@ -805,3 +812,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
type: "list-item",

@@ -815,3 +822,3 @@ data,

return {
kind: "block",
object: "block",
type: "paragraph",

@@ -824,3 +831,3 @@ nodes: this.groupTextInLeaves(childNode)

return {
kind: "block",
object: "block",
type: "table",

@@ -833,3 +840,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
type: "table-row",

@@ -844,3 +851,3 @@ nodes: childNode

return {
kind: "block",
object: "block",
data: { align },

@@ -876,7 +883,12 @@ type: flags.header ? "table-head" : "table-cell",

Renderer.prototype.codespan = function(text) {
return new TextNode(text, { type: "code" });
return {
text,
marks: [{ type: "code" }]
};
};
Renderer.prototype.br = function() {
return new TextNode("");
return {
text: " "
};
};

@@ -914,3 +926,3 @@

return {
kind: "inline",
object: "inline",
type: "link",

@@ -935,7 +947,7 @@ nodes: this.groupTextInLeaves(childNode),

return {
kind: "block",
object: "block",
type: "image",
nodes: [
{
kind: "text",
object: "text",
leaves: [

@@ -954,13 +966,7 @@ {

Renderer.prototype.text = function(childNode) {
return new TextNode(childNode);
return {
text: childNode
};
};
// Auxiliary object constructors:
function TextNode(text, marks) {
this.text = text;
if (marks) {
this.marks = [marks];
}
}
/**

@@ -1042,3 +1048,3 @@ * Parsing & Compiling

return {
kind: "text",
object: "text",
leaves: [

@@ -1163,19 +1169,50 @@ {

options = assign({}, defaults, options);
let fragment;
try {
var fragment = Parser.parse(Lexer.parse(src, options), options);
} catch (e) {
fragment = Parser.parse(Lexer.parse(src, options), options);
if (!fragment.length) {
fragment = [
{
object: "block",
type: "paragraph",
isVoid: false,
data: {},
nodes: [
{
object: "text",
leaves: [
{
object: "leaf",
text: "",
marks: []
}
]
}
]
}
];
}
} catch (err) {
if (options.silent) {
fragment = [
{
kind: "block",
object: "block",
type: "paragraph",
isVoid: false,
data: {},
nodes: [
{
kind: "text",
object: "text",
leaves: [
{
text: "An error occured:"
object: "leaf",
text: "An error occured:",
marks: []
},
{
text: e.message
object: "leaf",
text: e.message,
marks: []
}

@@ -1188,7 +1225,7 @@ ]

} else {
throw e;
throw err;
}
}
let mainNode = { nodes: fragment };
return mainNode;
return { nodes: fragment };
}

@@ -1195,0 +1232,0 @@ };

@@ -7,3 +7,3 @@ import parser from "./parser";

const String = new Record({
kind: "string",
object: "string",
text: ""

@@ -13,3 +13,5 @@ });

function formatLinkBar(img, url, title, desc, domain) {
return `%%%
return `
%%%
${img ? `${url}\n${img}` : url}

@@ -23,8 +25,4 @@ ${title}

function formatSoftBreak(children) {
return children.replace(/\n/g, " \n");
}
/**
* Rules to (de)serialize nodes.git pu
* Rules to (de)serialize nodes.
*

@@ -39,9 +37,4 @@ * @type {Object}

serialize(obj, children) {
if (obj.kind === "string") {
return `${children}`
.replace(/\\/g, "\\\\")
.replace(/!/g, "\\!")
.replace(/\[/g, "\\[")
.replace(/\]/g, "\\]")
.replace(/%/g, "\\%");
if (obj.object === "string") {
return children;
}

@@ -52,3 +45,3 @@ }

serialize(obj, children, document) {
if (obj.kind !== "block") return;
if (obj.object !== "block") return;
let parent = document.getParent(obj.key);

@@ -59,3 +52,5 @@

tableHeader = "";
return children;
// trim removes trailing newline
return children.trim();
case "table-head": {

@@ -87,51 +82,51 @@ switch (obj.getIn(["data", "align"])) {

case "paragraph":
if (parent.type === "list-item") {
return formatSoftBreak(children);
} else {
return `\n${formatSoftBreak(children)}\n`;
}
return children;
case "code":
return `\`\`\`\n${children}\n\`\`\`\n`;
return `\`\`\`\n${children}\n\`\`\``;
case "code-line":
return `${children}\n`;
case "block-quote":
return `> ${children}\n`;
return `> ${children}`;
case "todo-list":
case "bulleted-list":
case "ordered-list":
case "ordered-list": {
// root list
if (parent === document) {
return `\n${children}`;
return children;
}
return `\n${children.replace(/^/gm, " ")}`;
// nested list
return `\n${children.replace(/\n+$/gm, "").replace(/^/gm, " ")}`;
}
case "list-item": {
switch (parent.type) {
case "ordered-list":
return `1. ${formatSoftBreak(children)}\n`;
return `1. ${children}\n`;
case "todo-list":
let checked = obj.getIn(["data", "checked"]);
let box = checked ? "[x]" : "[ ]";
return `${box} ${formatSoftBreak(children)}\n`;
return `${box} ${children}\n`;
default:
case "bulleted-list":
return `* ${formatSoftBreak(children)}\n`;
return `* ${children}\n`;
}
}
case "heading1":
return `# ${formatSoftBreak(children)}`;
return `# ${children}\n`;
case "heading2":
return `## ${children}`;
return `\n## ${children}\n`;
case "heading3":
return `### ${children}`;
return `\n### ${children}\n`;
case "heading4":
return `#### ${children}`;
return `\n#### ${children}\n`;
case "heading5":
return `##### ${children}`;
return `\n##### ${children}\n`;
case "heading6":
return `###### ${children}`;
case "heading6":
return `###### ${children}`;
return `\n###### ${children}\n`;
case "horizontal-rule":
return `---\n`;
return `---`;
case "image":
const alt = obj.getIn(["data", "alt"]);
const alt = obj.getIn(["data", "alt"]) || "";
const src = encode(obj.getIn(["data", "src"]) || "");
return `![${alt}](${src})\n`;
return `![${alt}](${src})`;
case "linkbar":

@@ -150,9 +145,7 @@ const img = encode(obj.getIn(["data", "image"]) || "");

serialize(obj, children) {
if (obj.kind !== "inline") return;
if (obj.object !== "inline") return;
switch (obj.type) {
case "link":
const href = encode(obj.getIn(["data", "href"]) || "");
return `[${children.trim()}](${href})`;
case "code-line":
return `\`${children}\``;
return href ? `[${children.trim()}](${href})` : children.trim();
case "mention":

@@ -168,3 +161,3 @@ const username = obj.getIn(["data", "username"]) || "";

serialize(obj, children) {
if (obj.kind !== "mark") return;
if (obj.object !== "mark") return;
switch (obj.type) {

@@ -174,7 +167,7 @@ case "bold":

case "italic":
return `*${children}*`;
return `_${children}_`;
case "code":
return `\`${children}\``;
case "inserted":
return `__${children}__`;
return `++${children}++`;
case "deleted":

@@ -237,5 +230,14 @@ return `~~${children}~~`;

serializeNode(node, document) {
if (node.kind == "text") {
if (node.object == "text") {
const leaves = node.getLeaves();
return leaves.map(this.serializeLeaves);
const inCodeBlock = !!document.getClosest(
node.key,
n => n.type === "code"
);
return leaves.map(leave => {
const inCodeMark = !!leave.marks.filter(mark => mark.type === "code")
.size;
return this.serializeLeaves(leave, !inCodeBlock && !inCodeMark);
});
}

@@ -262,4 +264,11 @@

serializeLeaves(leaves) {
const string = new String({ text: leaves.text });
serializeLeaves(leaves, escape = true) {
let leavesText = leaves.text;
if (escape) {
// escape markdown characters
leavesText = leavesText
.replace(/([\\`*{}\[\]()#+\-.!_>]@%)/gi, "\\$1")
.replace(/\n/g, " \n"); // format softBreaks
}
const string = new String({ text: leavesText });
const text = this.serializeString(string);

@@ -266,0 +275,0 @@

// to ensure markdown compatability we need to specifically encode some characters
export function encode(href: string) {
return href
export function encode(href) {
return decodeSafe(href)
.trim()

@@ -12,3 +12,22 @@ .replace(/ /g, "%20")

export function decode(href: string) {
return decodeURI(href);
try {
return decodeURI(href);
} catch (e) {
return decodeSafe(href);
}
}
// convert hanging % characters into percentage encoded %25 as decodeURI cannot
// handle this scenario but users may input 'invalid' urls.
function decodeSafe(uri) {
const components = uri.split(/(%(?:d0|d1)%.{2})/);
return components
.map(component => {
try {
return decodeURIComponent(component);
} catch (e) {
return component.replace(/%(?!\d+)/g, "%25");
}
})
.join("");
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc