Repository: jamesramsay/hercule
Pull request #221 (2016-04-03 10:41:02)

Improved API for accessing sourceFilePaths
Accessing the pathList by providing an error is very hackish and fragile approach.

Implementation has been updated to emit `sources` event from the core transclusion stream which is then captured and provided as an argument to the callback.

codecov-io: ## [Current coverage][1] is `99.16%`
> Merging **#221** into **master** will increase coverage by **+0.03%** as of [`ddfc2ae`][3]


```diff
@@            master    #221   diff @@
======================================
  Files            9       9       
  Stmts          232     240     +8
  Branches        25      24     -1
  Methods          0       0       
======================================
+ Hit            230     238     +8
  Partial          0       0       
  Missed           2       2       
```

> Review entire [Coverage Diff][4] as of [`ddfc2ae`][3]


[1]: https://codecov.io/github/jamesramsay/hercule?ref=ddfc2ae0bf6179ab1796521cb2cf5a80720d1784
[2]: https://codecov.io/github/jamesramsay/hercule/features/suggestions?ref=ddfc2ae0bf6179ab1796521cb2cf5a80720d1784
[3]: https://codecov.io/github/jamesramsay/hercule/commit/ddfc2ae0bf6179ab1796521cb2cf5a80720d1784
[4]: https://codecov.io/github/jamesramsay/hercule/compare/1937c9454dedb61d4af106da4dd402808bbaaa0c...ddfc2ae0bf6179ab1796521cb2cf5a80720d1784

> Powered by [Codecov](https://codecov.io). Updated on successful CI builds.

Patch:
diff --git a/README.md b/README.md
index 13550a6..4a055a1 100644
--- a/README.md
+++ b/README.md
@@ -229,12 +229,11 @@ __Arguments__
 - `str` - A string to process.
 - `options` - An object of options to be applied when processing input.
   - `relativePath` - A path to which the transclusion links within input `str` are relative.
-- `pathList` - An array (typically empty) which the path of every transclusion will be appended to.
-  This is helpful for generating a watch list for live reloading.
-- `callback(err, [output])` - A callback which is called after the file at the provided `filepath`
-  has been processed. `callback` will be passed an error and the processed output.
+- `callback(err, [output], [sourcePaths])` - A callback which is called after the input `str` has been processed.
+  `callback` will be passed an error, processed output and array of source document file paths.
+
 
-Omit the `callback` and `pathList` if using `transcludeStringSync`.
+Omit the `callback` if using `transcludeStringSync`. Only `output` will be returned.
 
 __Examples__
 
@@ -276,12 +275,10 @@ __Arguments__
 - `filepath` - A path to a file to process.
 - `options` - An object of options to be applied when processing input.
   - `relativePath` - A path to which the input `filepath` is relative.
-- `pathList` - An array (typically empty) which the path of every transclusion will be appended to.
-  This is helpful for generating a watch list for live reloading.
-- `callback(err, [output])` - A callback which is called after the file at the provided `filepath`
-  has been processed. `callback` will be passed an error and the processed output.
+- `callback(err, [output], [sourcePaths])` - A callback which is called after the file at the provided `filepath`
+  has been processed. `callback` will be passed an error, processed output and array of source document file paths.
 
-Omit the `callback` and `pathList` if using `transcludeFileSync`.
+Omit the `callback` if using `transcludeFileSync`. Only `output` will be returned.
 
 __Examples__
 
diff --git a/src/hercule.js b/src/hercule.js
index 65fc93d..a272b53 100644
--- a/src/hercule.js
+++ b/src/hercule.js
@@ -11,10 +11,11 @@ export const TranscludeStream = Transcluder;
 export function transcludeString(...args) {
   const input = args.shift();
   const cb = args.pop();
-  const [options, linkPaths] = args;
+  const [options] = args;
 
-  const transclude = new Transcluder(options, linkPaths);
+  const transclude = new Transcluder(options);
   let outputString = '';
+  let sourcePaths;
   let cbErr = null;
 
   transclude
@@ -27,7 +28,8 @@ export function transcludeString(...args) {
     .on('error', (err) => {
       if (!cbErr) cbErr = err;
     })
-    .on('end', () => cb(cbErr, outputString));
+    .on('sources', (srcPaths) => (sourcePaths = srcPaths))
+    .on('end', () => cb(cbErr, outputString, sourcePaths));
 
   transclude.write(input, 'utf8');
   transclude.end();
@@ -37,11 +39,12 @@ export function transcludeString(...args) {
 export function transcludeFile(...args) {
   const input = args.shift();
   const cb = args.pop();
-  const [options, linkPaths] = args;
+  const [options] = args;
 
-  const transclude = new Transcluder(options, linkPaths);
+  const transclude = new Transcluder(options);
   const inputStream = fs.createReadStream(input, { encoding: 'utf8' });
   let outputString = '';
+  let sourcePaths;
   let cbErr = null;
 
   inputStream.on('error', (err) => cb(err));
@@ -56,7 +59,8 @@ export function transcludeFile(...args) {
     .on('error', (err) => {
       if (!cbErr) cbErr = err;
     })
-    .on('end', () => cb(cbErr, outputString));
+    .on('sources', (srcPaths) => (sourcePaths = srcPaths))
+    .on('end', () => cb(cbErr, outputString, sourcePaths));
 
   inputStream.pipe(transclude);
 }
diff --git a/src/inflate-stream.js b/src/inflate-stream.js
index e6876d3..f0ecf38 100644
--- a/src/inflate-stream.js
+++ b/src/inflate-stream.js
@@ -31,11 +31,11 @@ const DEFAULT_OPTIONS = {
   output: 'content',
 };
 
-export default function InflateStream(opt, linkPaths) {
+export default function InflateStream(opt) {
   const options = _.merge({}, DEFAULT_OPTIONS, opt);
 
   function inflateDuplex(chunk, link) {
-    const resolver = new ResolveStream(link.href, linkPaths);
+    const resolver = new ResolveStream(link.href);
     const inflater = new InflateStream();
     const trimmer = new TrimStream();
 
diff --git a/src/resolve-stream.js b/src/resolve-stream.js
index 4994929..e3f17cb 100644
--- a/src/resolve-stream.js
+++ b/src/resolve-stream.js
@@ -60,7 +60,7 @@ function parse(rawLink, relativePath) {
   return { parsedLink, parsedReferences };
 }
 
-export default function ResolveStream(sourceFile, sourcePaths = []) {
+export default function ResolveStream() {
   function transform(chunk, encoding, cb) {
     const rawLink = _.get(chunk, ['link', 'href']);
     const relativePath = _.get(chunk, 'relativePath') || '';
@@ -85,9 +85,7 @@ export default function ResolveStream(sourceFile, sourcePaths = []) {
     const references = _.uniq([...parsedReferences, ...parentRefs]);
     const link = resolve(parsedLink, parentRefs, relativePath);
 
-    // Add the resolved link path to the array of all source paths
-    sourcePaths.push(link.href);
-
+    this.emit('source', link.href);
     this.push(_.assign(chunk, { link, references }));
     return cb();
   }
diff --git a/src/transclude-stream.js b/src/transclude-stream.js
index dfecdfb..b326f3e 100644
--- a/src/transclude-stream.js
+++ b/src/transclude-stream.js
@@ -19,9 +19,10 @@ const DEFAULT_OPTIONS = {
   output: 'content',
 };
 
-export default function Transcluder(opt, linkPaths) {
+export default function Transcluder(opt) {
   const options = _.merge({}, DEFAULT_OPTIONS, opt);
   const source = options.source;
+  const sourcePaths = [];
 
   function token(match) {
     return defaultToken(match, options);
@@ -33,8 +34,8 @@ export default function Transcluder(opt, linkPaths) {
 
   const tokenizerOptions = { leaveBehind: `${WHITESPACE_GROUP}`, token, separator };
   const tokenizer = regexpTokenizer(tokenizerOptions, options.linkRegExp || linkRegExp);
-  const resolver = new ResolveStream(source, linkPaths);
-  const inflater = new InflateStream({ linkRegExp: options.linkRegExp, linkMatch: options.linkMatch }, linkPaths);
+  const resolver = new ResolveStream(source);
+  const inflater = new InflateStream({ linkRegExp: options.linkRegExp, linkMatch: options.linkMatch });
   const indenter = new IndentStream();
   const stringify = get('content');
 
@@ -56,5 +57,13 @@ export default function Transcluder(opt, linkPaths) {
     inflater.end();
   });
 
+  resolver.on('source', (filepath) => {
+    sourcePaths.push(filepath);
+  });
+
+  transcluder.on('end', () => {
+    transcluder.emit('sources', sourcePaths);
+  });
+
   return transcluder;
 }


Test patch:
diff --git a/test/units/transcludeFile.js b/test/units/transcludeFile.js
index 174d9ac..ba2cae3 100644
--- a/test/units/transcludeFile.js
+++ b/test/units/transcludeFile.js
@@ -58,17 +58,16 @@ test.cb('should return one error if invalid links found', (t) => {
   });
 });
 
-test.cb('should provide pathList if variable provided', (t) => {
+test.cb('should return sourceList', (t) => {
   const input = path.join(__dirname, '../fixtures/local-link/index.md');
   const options = { relativePath: path.join(__dirname, '../fixtures/local-link') };
   const expected = 'Jackdaws love my big sphinx of quartz.\n';
-  const pathList = [];
 
-  transcludeFile(input, options, pathList, (err, output) => {
+  transcludeFile(input, options, (err, output, sourceList) => {
     t.same(err, null);
     t.same(output, expected);
-    t.regex(pathList[0], /fixtures\/local-link\/size\.md/);
-    t.same(pathList.length, 1);
+    t.regex(sourceList[0], /fixtures\/local-link\/size\.md/);
+    t.same(sourceList.length, 1);
     t.end();
   });
 });
diff --git a/test/units/transcludeString.js b/test/units/transcludeString.js
index adbe93d..cc5c798 100644
--- a/test/units/transcludeString.js
+++ b/test/units/transcludeString.js
@@ -23,15 +23,15 @@ test.cb('should transclude with optional relativePath argument', (t) => {
   });
 });
 
-test.cb('should provide pathList if variable provided', (t) => {
-  const input = 'The quick brown fox jumps over the lazy dog.';
-  const expected = 'The quick brown fox jumps over the lazy dog.';
-  const pathList = [];
+test.cb('should return sourceList', (t) => {
+  const input = 'Jackdaws love my :[size link](size.md) sphinx of quartz.';
+  const options = { relativePath: path.join(__dirname, '../fixtures/local-link') };
+  const expected = 'Jackdaws love my big sphinx of quartz.';
 
-  transcludeString(input, null, pathList, (err, output) => {
+  transcludeString(input, options, (err, output, sourceList) => {
     t.same(err, null);
     t.same(output, expected);
-    t.same(pathList.length, 0);
+    t.same(sourceList.length, 1);
     t.end();
   });
 });