Repository: jamesramsay/hercule
Pull request #43 (2015-07-22 05:18:10)

Escaped quotes and precompiled pegjs grammar
- Support for escaped quotes (Reference: https://github.com/pegjs/pegjs/blob/master/examples/json.pegjs#L101)
- Precompiled pegjs grammar
- Removed pegjs dependency


Patch:
diff --git a/package.json b/package.json
index 3d7cb6e..2ddbf3a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "hercule",
-  "version": "1.2.0",
+  "version": "1.2.1",
   "description": "Markdown transcluder",
   "main": "./lib/hercule",
   "scripts": {
@@ -9,8 +9,8 @@
     "integration-tests": "npm run compile && ./test/modules/bin/bats test/bats",
     "coverage": "NODE_ENV=test COVERAGE=1 ./node_modules/.bin/mocha --compilers coffee:coffee-script/register --require blanket --reporter html-cov > coverage.html && open coverage.html",
     "coveralls": "NODE_ENV=test COVERAGE=1 ./node_modules/.bin/mocha --compilers coffee:coffee-script/register --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js",
-    "compile": "coffee --output lib -c src/*",
-    "watch": "coffee --output lib -cw src/*",
+    "compile": "coffee --output lib -c src/*.coffee && ./node_modules/pegjs/bin/pegjs src/transclude.pegjs lib/transclude-parser.js",
+    "watch": "coffee --output lib -cw src/*.coffee",
     "install-bats": "./scripts/install-bats"
   },
   "config": {
@@ -46,7 +46,6 @@
     "async": "^1.3.0",
     "dashdash": "^1.7.0",
     "lodash": "^3.0.0",
-    "pegjs": "^0.8.0",
     "request": "^2.58.0"
   },
   "devDependencies": {
@@ -56,6 +55,7 @@
     "coveralls": "^2.11.2",
     "mocha": "^2.2.5",
     "mocha-lcov-reporter": "^0.0.2",
-    "nock": "^2.7.0"
+    "nock": "^2.7.0",
+    "pegjs": "^0.8.0"
   }
 }
diff --git a/src/grammar.coffee b/src/grammar.coffee
deleted file mode 100644
index 5efc522..0000000
--- a/src/grammar.coffee
+++ /dev/null
@@ -1,76 +0,0 @@
-# Parses transclusion links
-#
-# :[example transclude link](file.md || "default value" common:file.md)
-#
-# {
-#  href: file.md
-#  hrefType: file
-#  references: [
-#     placeholder: common
-#     type: file
-#     value: file.md
-#   ]
-#  default: {
-#   type: string
-#   value: default value
-#  }
-# }
-
-transcludeGrammar = """
-start = l:link? ' || '? d:default? ' '? o:reference* {
-  return {
-    "href": l.href,
-    "hrefType": l.hrefType,
-    "references": o,
-    "default": d
-  };
-}
-
-reference = p:placeholder ':' l:link ' '? {
-  return {
-    "placeholder": p,
-    "href": l.href,
-    "hrefType": l.hrefType
-  };
-}
-
-placeholder = p:[a-zA-Z0-9]+ {
-  return p.join("");
-}
-
-default = httpLink / fileLink / string
-
-link = httpLink / fileLink / string / reset
-
-fileLink = f:[^ ()\"]+ {
-  return {
-    "hrefType": "file",
-    "href": f.join("")
-  };
-}
-
-httpLink = left:("http://" / "https://") right:[^ ()]+ {
-  return {
-    "hrefType": "http",
-    "href": left + right.join("")
-  };
-}
-
-string = '\"' s:([^\"]*) '\"' {
-  return {
-    "hrefType": "string",
-    "href": s.join("")
-  };
-}
-
-reset = {
-  return {
-    "hrefType": "string",
-    "href": ""
-  };
-}
-"""
-
-module.exports = {
-  transcludeGrammar
-}
diff --git a/src/transclude.pegjs b/src/transclude.pegjs
new file mode 100644
index 0000000..066894c
--- /dev/null
+++ b/src/transclude.pegjs
@@ -0,0 +1,76 @@
+start = l:link? " || "? d:default? " "? o:reference* {
+  return {
+    "href": l.href,
+    "hrefType": l.hrefType,
+    "references": o,
+    "default": d
+  };
+}
+
+reference = p:placeholder ":" l:link " "? {
+  return {
+    "placeholder": p,
+    "href": l.href,
+    "hrefType": l.hrefType
+  };
+}
+
+placeholder = p:[a-zA-Z0-9]+ {
+  return p.join("");
+}
+
+default = httpLink / fileLink / stringLink
+
+link = httpLink / fileLink / stringLink / reset
+
+fileLink = f:[^ ()\"]+ {
+  return {
+    "hrefType": "file",
+    "href": f.join("")
+  };
+}
+
+httpLink = left:("http://" / "https://") right:[^ ()]+ {
+  return {
+    "hrefType": "http",
+    "href": left + right.join("")
+  };
+}
+
+stringLink = s:string {
+  return {
+    "hrefType": "string",
+    "href": s
+  };
+}
+
+reset = {
+  return {
+    "hrefType": "string",
+    "href": ""
+  };
+}
+
+string = QUOTATION_MARK chars:char* QUOTATION_MARK {
+  return chars.join("");
+}
+
+char = UNESCAPED / ESCAPE sequence:(
+        "\""
+      / "\\"
+      / "/"
+      / "b" { return "\b"; }
+      / "f" { return "\f"; }
+      / "n" { return "\n"; }
+      / "r" { return "\r"; }
+      / "t" { return "\t"; }
+      / "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG) {
+          return String.fromCharCode(parseInt(digits, 16));
+        }
+    )
+    { return sequence; }
+
+ESCAPE         = "\\"
+QUOTATION_MARK = "\""
+UNESCAPED      = [\x20-\x21\x23-\x5B\x5D-\u10FFFF]
+HEXDIG         = [0-9a-f]i
diff --git a/src/utils.coffee b/src/utils.coffee
index b642e94..741a573 100644
--- a/src/utils.coffee
+++ b/src/utils.coffee
@@ -1,9 +1,14 @@
 fs = require 'fs'
 path = require 'path'
-peg = require 'pegjs'
 _ = require 'lodash'
 request = require 'request'
 
+try
+  linkParser = require './transclude-parser'
+catch ex
+  peg = require 'pegjs'
+  linkParser = peg.buildParser (fs.readFileSync './src/transclude.pegjs', {encoding: 'utf8'})
+
 
 # Link detection (including leading whitespace)
 linkRegExp = new RegExp(/(^[\t ]*)?(\:\[.*?\]\((.*?)\))/gm)
@@ -12,11 +17,6 @@ PLACEHOLDER_GROUP = 2
 LINK_GROUP = 3
 
 
-# Build the link parser once
-grammar = require './grammar'
-linkParser = peg.buildParser grammar.transcludeGrammar
-
-
 # Scan a document string for links
 scan = (input, relativePath = "", parents = []) ->
   links = []


Test patch:
diff --git a/test/fixtures/test-quotes/foo.json b/test/fixtures/test-quotes/foo.json
new file mode 100644
index 0000000..11f0c11
--- /dev/null
+++ b/test/fixtures/test-quotes/foo.json
@@ -0,0 +1,3 @@
+{
+  "bar": :[test](barValue || "null")
+}
diff --git a/test/fixtures/test-quotes/main.md b/test/fixtures/test-quotes/main.md
new file mode 100644
index 0000000..1e2546c
--- /dev/null
+++ b/test/fixtures/test-quotes/main.md
@@ -0,0 +1,4 @@
+```
+:[example](foo.json),
+:[example](foo.json barValue:"\"green\"")
+```
diff --git a/test/hercule.coffee b/test/hercule.coffee
index 340919a..d351613 100644
--- a/test/hercule.coffee
+++ b/test/hercule.coffee
@@ -171,3 +171,19 @@ describe 'hercule', ->
         assert.equal output, 'Jackdaws love my imagined sphinx of quartz.\n'
         done()
 
+    it 'should transclude files with escaped quotes within strings', (done) ->
+      inputFile = __dirname + "/fixtures/test-quotes/main.md"
+
+      hercule.transcludeFile inputFile, (output) ->
+        assert.equal output, """
+        ```
+        {
+          "bar": null
+        },
+        {
+          "bar": "green"
+        }
+        ```
+
+        """
+        done()