Skip to content
This repository has been archived by the owner on Nov 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #36 from jamesramsay/verbose-logging
Browse files Browse the repository at this point in the history
Verbose logging and optional function arguments
  • Loading branch information
James Ramsay committed Jul 11, 2015
2 parents cbe0660 + d7f1461 commit 4f23e22
Show file tree
Hide file tree
Showing 18 changed files with 212 additions and 180 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ node_modules
lib
.coveralls.yml
coverage.html
npm-debug.log
*.log
test/modules*
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Or use as a library:
```javascript
var hercule = require('hercule');

hercule.transcludeString("# Title\n\n:[abstract](abstract.md)", null, null, null, function(output) {
hercule.transcludeString("# Title\n\n:[abstract](abstract.md)", function(output) {
return console.log(output);
});
```
Expand Down
26 changes: 13 additions & 13 deletions src/grammar.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
# }

transcludeGrammar = """
start = f:link? ' '? o:reference* {
start = l:link? ' '? o:reference* {
return {
"link": f.value,
"type": f.type,
"href": l.href,
"hrefType": l.hrefType,
"references": o
};
}
reference = p:placeholder ':' l:link ' '? {
return {
"placeholder": p,
"type": l.type,
"value": l.value
"href": l.href,
"hrefType": l.hrefType
};
}
Expand All @@ -37,29 +37,29 @@ link = httpLink / fileLink / string / reset
fileLink = f:[^ ()\"]+ {
return {
"type": "file",
"value": f.join("")
"hrefType": "file",
"href": f.join("")
};
}
httpLink = left:("http://" / "https://") right:[^ ()]+ {
return {
"type": "http",
"value": left + right.join("")
"hrefType": "http",
"href": left + right.join("")
};
}
string = '\"' s:([^\"]+) '\"' {
return {
"type": "string",
"value": s.join("")
"hrefType": "string",
"href": s.join("")
};
}
reset = {
return {
"type": "string",
"value": ""
"hrefType": "string",
"href": ""
};
}
"""
Expand Down
136 changes: 86 additions & 50 deletions src/hercule.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,117 @@ _ = require 'lodash'
utils = require './utils'
async = require 'async'

#log = (message) -> console.error "#{message}"

processInput = (input, parents, dir) ->
rawLinks = utils.scan input
#log "Scan: #{rawLinks.length} links found"

transclude = (input, relativePath, parents, parentRefs, logger, cb) ->
rawLinks = utils.scan input, relativePath, parents
links = _.forEach rawLinks, (rawLink) ->
rawLink.relativePath = dir
rawLink.parents = parents[..]

parsedLink = utils.parse rawLink
#log "Parse: #{parsedLink.references.length} references found"

return parsedLink
utils.parse rawLink

return links


transclude = (input, relativePath, parents, parentRefs, cb) ->
# TODO: rename processInput...
# log "Scanning: #{input}"
links = processInput input, parents, relativePath
logger "Links found: #{links.length}"
if links.length < 1 then return cb input

async.eachSeries links, ({link, type, file, references, parents, whitespace, placeholder}, done) ->
linkType = type
async.eachSeries links, (link, done) ->
{href, hrefType, references, parents, whitespace, placeholder} = link

references = _.merge parentRefs, references
match = utils.find link, references
matchingReferences = parentRefs.filter (ref) -> "#{ref.placeholder}" is "#{href}"
if matchingReferences[0]?
overridingReference = matchingReferences[0]
logger "Overriding reference: #{JSON.stringify overridingReference}"

if match?
#log "Expanding: #{link} -> #{match.value}"
link = match.value
linkType = match.type
else if linkType is "file"
link = path.join relativePath, link
if not matchingReferences[0]? and hrefType is "file"
href = path.join relativePath, href

if _.contains parents, link
# #{link} is in parents:\n#{JSON.stringify parents}
throw new Error("Circular reference detected")
href = overridingReference?.href || href
hrefType = overridingReference?.hrefType || hrefType

#log "Transclude: #{link} into #{parents[-1..][0]}"
parents.push link
dir = path.dirname link
if _.contains parents, href
logger "#{href} is in parents:\n#{JSON.stringify parents}"
throw new Error("Circular reference detected")

utils.inflate link, linkType, (content) ->
parents.push href
dir = path.dirname href
references = _.merge parentRefs, references

transclude content, dir, parents, references, (output) ->
utils.inflate href, hrefType, (content) ->
logger "Transcluding: #{href} (#{hrefType}) into #{parents[-1..][0]}"
transclude content, dir, parents, references, logger, (output) ->
if output?
# Preserve indentation if transclude is not preceded by content
# Remove new lines at EOF which cause unexpected paragraphs and breaks
# Preserve leading whitespace and trim excess new lines at EOF
output = output
.replace /\n/g, "\n#{whitespace}"
.replace /\n$/, ""

input = input.replace "#{placeholder}", output
#log "Replaced: \"#{placeholder}\"\n with: #{JSON.stringify output}"
return done()
, ->
return cb input


transcludeString = (input, relativePath = "", parents = [], parentRefs = [], cb) ->
transclude input, relativePath, parents, parentRefs, (output) ->
return cb output
validateOptionalArgs = (args) ->
OPTION_LOGGER = 0
OPTION_OPTIONS = 1

defaultOptions =
relativePath: ""
parents: []
parentRefs: []

logger = (message) -> return true

[input, optionalArgs..., cb] = args

if not (typeof cb is 'function')
throw new Error("Argument error: 'callback' should be a function")

if not (typeof input is 'string')
throw new Error("Argument error: 'input' should be a string")

if typeof optionalArgs[OPTION_LOGGER] is 'function'
logger = optionalArgs[OPTION_LOGGER]

{relativePath, parents, parentRefs} = _.merge defaultOptions, optionalArgs[OPTION_OPTIONS]

return {input, relativePath, parents, parentRefs, logger, cb}


# transcludeString(input, [logger], [options], callback)
#
# Arguments:
# 1. input (string): The input string which will be processed for transclusion links
# 2. [log (function)]: Logging function accepting a string as the input
# 3. [options (Object)]: todo
# 4. callback (function): Function returns through the callback
#
# Returns: (string): Transcluded string
transcludeString = (args...) ->
{input, relativePath, parents, parentRefs, logger, cb} = validateOptionalArgs args

logger "Transcluding string..."
transclude input, relativePath, parents, parentRefs, logger, (output) ->
return cb output


# transcludeFile(input, [logger], [options], callback)
#
# Arguments:
# 1. filepath (string): The location of the local file which will be processed for transclusion links
# 2. [log (function)]: Logging function accepting a string as the input
# 3. [options (Object)]: todo
# 4. callback (function): Function returns through the callback
#
# Returns: (string): Transcluded string
transcludeFile = (args...) ->
{input, relativePath, parents, parentRefs, logger, cb} = validateOptionalArgs args

transcludeFile = (file, relativePath = "", parents = [], parentRefs = [], cb) ->
fullFilePath = path.join relativePath, file
fullRelativePath = path.dirname fullFilePath
logger "Transcluding file... #{input}"
fullFilePath = path.join relativePath, input
fullRelativePath = path.dirname fullFilePath

parents.push fullFilePath
content = utils.readFile fullFilePath
parents.push fullFilePath
content = utils.readFile fullFilePath

transclude content, fullRelativePath, parents, parentRefs, (output) ->
return cb output
transclude content, fullRelativePath, parents, parentRefs, logger, (output) ->
return cb output


module.exports = {transcludeString, transcludeFile}
11 changes: 9 additions & 2 deletions src/main.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ fs = require 'fs'
path = require 'path'
async = require 'async'

_VERBOSE = false
_DEBUG = false

parser = dashdash.createParser options: [
names: ['help', 'h']
type: 'bool'
Expand Down Expand Up @@ -35,16 +38,20 @@ if opts.help
console.log "usage: hercule [OPTIONS]\noptions:\n#{help}"
process.exit()

logger = (message) ->
if _VERBOSE then console.error "#{message}"

main = ->
transcludedOutput = ""

if opts.verbose then _VERBOSE = true

async.eachSeries opts._args, (input, done) ->
hercule.transcludeFile input, null, null, null, (output) ->
hercule.transcludeFile input, logger, (output) ->
transcludedOutput += output
return done()

, ->
, ->
if opts.output
fs.writeFile opts.output, transcludedOutput, (err) ->
throw err if err
Expand Down
56 changes: 20 additions & 36 deletions src/utils.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,72 +18,57 @@ linkParser = peg.buildParser grammar.transcludeGrammar


# Scan a document string for links
scan = (input) ->
scan = (input, relativePath = "", parents = []) ->
links = []

while (match = linkRegExp.exec input)
links.push
placeholder: match[PLACEHOLDER_GROUP]
link: match[LINK_GROUP]
href: match[LINK_GROUP]
whitespace: if match[WHITESPACE_GROUP] then match[WHITESPACE_GROUP] else ""

relativePath: relativePath
parents: parents[..]
return links


# Parse a transclude link
parse = (rawLink) ->
parsedLink = linkParser.parse rawLink.link

# References are relative to the document where declared
parse = (link) ->
parsedLink = linkParser.parse link.href
parsedLink.references.forEach (ref) ->
if ref.type is "file"
ref.value = path.join rawLink.relativePath, ref.value

return _.merge rawLink, parsedLink
if ref.hrefType is "file"
ref.href = path.join link.relativePath, ref.href
return _.merge link, parsedLink


# Read file to string
readFile = (filename) ->
try
content = (fs.readFileSync filename).toString()
return content

catch err
if err.code = 'ENOENT'
console.error "File (#{filename}) not found."

console.error "Error: File (#{filename}) not found."
return null


# Read URI contents to string
readUri = (link, cb) ->
request link, (err, res, body) ->
readUri = (uri, cb) ->
request uri, (err, res, body) ->
if (err) or not (res.statusCode is 200)
console.error "Remote file (#{link}) could not be retrieved."
console.error "Error: Remote file (#{uri}) could not be retrieved."
return cb null

return cb body


find = (link, references) ->
matches = _.filter references, (ref) ->
return ref?.placeholder is link

if matches.length > 1
console.error "Multiple matching references found for #{link}."
#log.error "Multiple matching references found for #{link}.", {matches}

return matches[0]


inflate = (link, linkType, cb) ->
switch linkType
inflate = (href, hrefType, cb) ->
switch hrefType
when "string"
return cb link
return cb href
when "file"
return cb (readFile link)
content = readFile href
return cb content
when "http"
readUri link, (content) -> return cb content
readUri href, (content) ->
return cb content
else
return cb ""

Expand All @@ -93,6 +78,5 @@ module.exports = {
parse
readFile
readUri
find
inflate
}
1 change: 1 addition & 0 deletions test/fixtures/test-parent-leakage/fox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The quick brown fox jumps over the lazy dog.
3 changes: 3 additions & 0 deletions test/fixtures/test-parent-leakage/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:[fox 1](fox.md)

:[fox 2](fox.md)
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:[jumpy thing](activity) over the :[state](state) :[animal](animal)
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/canine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dog
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/color.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
brown
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/disinterest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lazy
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/fox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The :[vulpis](fox fox:color.md) :[jumping activity](activity activity:jump.md animal:canine.md state:disinterest.md).
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:[starts here...](fox.md fox:vulpis.md activity:activity.md)
1 change: 1 addition & 0 deletions test/fixtures/test-reference-collision/jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jumps
Loading

0 comments on commit 4f23e22

Please sign in to comment.