Repository: jamesramsay/hercule (js)

Issue:
SyntaxError: Unexpected token {
When using transcludeStringSync and there are multiple parse errors the Error handling errors...

```
SyntaxError: Unexpected token {
    at Object.parse (native)
    at Object.transcludeStringSync (.../node_modules/hercule/lib/hercule.js:127:33)
```

When logging the error before it's rethrown it contains:
```
{"errno":-2,"code":"ENOENT","syscall":"open","path":"somepath","msg":"Could not read file"}{"errno":-2,"code":"ENOENT","syscall":"open","path":"someotherpatht","msg":"Could not read file"}
```

Pull request:
Fix: sync API exception when multiple errors thrown (fixes #219)
`process.exit()` wasn't being called on first error when being executed through sync API.

Patch:
diff --git a/src/hercule.js b/src/hercule.js
index a2ce5af..65fc93d 100644
--- a/src/hercule.js
+++ b/src/hercule.js
@@ -24,7 +24,9 @@ export function transcludeString(...args) {
         outputString += content.toString('utf8');
       }
     })
-    .on('error', (err) => (cbErr = err))
+    .on('error', (err) => {
+      if (!cbErr) cbErr = err;
+    })
     .on('end', () => cb(cbErr, outputString));
 
   transclude.write(input, 'utf8');
@@ -51,7 +53,9 @@ export function transcludeFile(...args) {
         outputString += content;
       }
     })
-    .on('error', (err) => (cbErr = err))
+    .on('error', (err) => {
+      if (!cbErr) cbErr = err;
+    })
     .on('end', () => cb(cbErr, outputString));
 
   inputStream.pipe(transclude);
diff --git a/src/main.js b/src/main.js
index 49eab8d..0f6d43a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -87,8 +87,8 @@ function main() {
       process.stderr.write(JSON.stringify(err));
     } else {
       process.stdout.write(`\n\nERROR: ${err.msg} (${err.path})\n`);
-      process.exit(1);
     }
+    process.exit(1);
   });
 
   inputStream.pipe(transclude).pipe(outputStream);


Test patch:
diff --git a/test/fixtures/invalid-link/_expect.json b/test/fixtures/invalid-link/_expect.json
index fac0ae9..35b933a 100644
--- a/test/fixtures/invalid-link/_expect.json
+++ b/test/fixtures/invalid-link/_expect.json
@@ -1,7 +1,7 @@
 {
-  "plan": 3,
+  "plan": 5,
   "error": {
     "msg": "Could not read file",
-    "path": "fixtures/invalid-link/i-dont-exist.md"
+    "path": "fixtures/invalid-link/(i-dont-exist.md|mineral.md)"
   }
 }
diff --git a/test/fixtures/invalid-link/_expect.md b/test/fixtures/invalid-link/_expect.md
index 3af3a6a..d36086e 100644
--- a/test/fixtures/invalid-link/_expect.md
+++ b/test/fixtures/invalid-link/_expect.md
@@ -1,1 +1,1 @@
-Jackdaws love my :[missing](i-dont-exist.md)
\ No newline at end of file
+Jackdaws love my :[missing](i-dont-exist.md) sphinx of :[missing](mineral.md)
\ No newline at end of file
diff --git a/test/fixtures/invalid-link/index.md b/test/fixtures/invalid-link/index.md
index bcba4e7..0c0bb41 100644
--- a/test/fixtures/invalid-link/index.md
+++ b/test/fixtures/invalid-link/index.md
@@ -1,1 +1,1 @@
-Jackdaws love my :[missing](i-dont-exist.md) sphinx of quartz.
+Jackdaws love my :[missing](i-dont-exist.md) sphinx of :[missing](mineral.md).
diff --git a/test/integration/transcludeFile.js b/test/integration/transcludeFile.js
index ca8d754..484b037 100644
--- a/test/integration/transcludeFile.js
+++ b/test/integration/transcludeFile.js
@@ -11,9 +11,6 @@ _.forEach((fixtures.fixtures), (fixture) => {
     const options = { relativePath: path.resolve(__dirname, '../fixtures', fixture.name) };
     const config = fixture.expectedConfig;
 
-    // TODO: ava plan
-    // if (fixture.expectedConfig.plan > 0) t.plan(fixture.expectedConfig.plan);
-
     transcludeFile(fixture.inputFile, options, (err, output) => {
       if (err) {
         t.same(err.msg, config.error.msg);
diff --git a/test/integration/transcludeString.js b/test/integration/transcludeString.js
index 09efb19..b041ee5 100644
--- a/test/integration/transcludeString.js
+++ b/test/integration/transcludeString.js
@@ -12,9 +12,6 @@ _.forEach((fixtures.fixtures), (fixture) => {
     const options = { relativePath: path.resolve(__dirname, '../fixtures', fixture.name) };
     const config = fixture.expectedConfig;
 
-    // TODO: ava plan
-    // if (fixture.expectedConfig.plan > 0) t.plan(fixture.expectedConfig.plan);
-
     transcludeString(fixture.input, options, (err, output) => {
       if (err) {
         t.same(err.msg, config.error.msg);
diff --git a/test/units/transcludeFile.js b/test/units/transcludeFile.js
index 6fdc2b1..174d9ac 100644
--- a/test/units/transcludeFile.js
+++ b/test/units/transcludeFile.js
@@ -34,7 +34,7 @@ test.cb('should return error if file doesn\'t exist', (t) => {
   });
 });
 
-test.cb('should return error if circular dependency found', (t) => {
+test.cb('should return one error if circular dependency found', (t) => {
   const input = path.join(__dirname, '../fixtures/circular-references/index.md');
   const options = { relativePath: path.join(__dirname, '../fixtures/circular-references') };
   const expected = 'The quick brown :[fox](fox.md) jumps over the lazy dog.\n';
@@ -46,6 +46,18 @@ test.cb('should return error if circular dependency found', (t) => {
   });
 });
 
+test.cb('should return one error if invalid links found', (t) => {
+  const input = path.join(__dirname, '../fixtures/invalid-link/index.md');
+  const options = { relativePath: path.join(__dirname, '../fixtures/invalid-link') };
+  const expected = 'Jackdaws love my :[missing](i-dont-exist.md) sphinx of :[missing](mineral.md)';
+  transcludeFile(input, options, (err, output) => {
+    t.same(err.msg, 'Could not read file');
+    t.regex(err.path, /fixtures\/invalid-link\/i-dont-exist.md/);
+    t.same(output, expected);
+    t.end();
+  });
+});
+
 test.cb('should provide pathList if variable provided', (t) => {
   const input = path.join(__dirname, '../fixtures/local-link/index.md');
   const options = { relativePath: path.join(__dirname, '../fixtures/local-link') };
diff --git a/test/units/transcludeString.js b/test/units/transcludeString.js
index 0b08c34..adbe93d 100644
--- a/test/units/transcludeString.js
+++ b/test/units/transcludeString.js
@@ -1,4 +1,5 @@
 import test from 'ava';
+import path from 'path';
 
 import { transcludeString } from '../../lib/hercule';
 
@@ -35,6 +36,17 @@ test.cb('should provide pathList if variable provided', (t) => {
   });
 });
 
+test.cb('should return one error if invalid links found', (t) => {
+  const input = 'Jackdaws love my :[missing](i-dont-exist.md) sphinx of :[missing](mineral.md)';
+  const options = { relativePath: path.join(__dirname, '../fixtures/invalid-link') };
+  transcludeString(input, options, (err, output) => {
+    t.same(err.msg, 'Could not read file');
+    t.regex(err.path, /fixtures\/invalid-link\/i-dont-exist.md/);
+    t.same(output, input);
+    t.end();
+  });
+});
+
 test.cb('should return errors with tokenizer options', (t) => {
   const input = '# Title\n<!-- include(test1.apib) -->\nSome content...\n';
   const options = {