Skip to content

Commit

Permalink
finished first draft of CLI #2
Browse files Browse the repository at this point in the history
  • Loading branch information
neocotic committed Dec 7, 2012
1 parent ea3f5d0 commit 4862df6
Showing 1 changed file with 145 additions and 41 deletions.
186 changes: 145 additions & 41 deletions bin/md
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,157 @@
#
# Convert HTML into valid Markdown

# Module dependencies
# -------------------

{exec} = require 'child_process'
fs = require 'fs'
md = require '../lib/md'
optparse = require 'optparse'
path = require 'path'

R_HYPHEN_CAMEL = /-([a-z])/gi
SWITCHES = [
['-d', '--debug', 'print additional debug information']
['-e', '--eval', 'pass a string from the command line as input']
['-h', '--help', 'display this help information']
['-l', '--long-ext', 'use long extension for Markdown files']
['-o', '--output [DIR]', 'set the output directory for converted Markdown']
['-p', '--print', 'print out the converted Markdown']
['-v', '--version', 'display the version number']
# Constants
# ---------

# Error code used when a file/directory could not be found.
NOT_FOUND = 'ENOENT'
# Regular expression used to identify hidden files by their name.
R_HIDDEN = /^\.|~$/
# Regular expression used to identify HTML files using common extensions.
R_HTML_EXT = /\.s?html?$/i
#
SWITCHES = [
['-d', '--debug', 'print additional debug information']
['-e', '--eval', 'pass a string from the command line as input']
['-h', '--help', 'display this help information']
['-l', '--long-ext', 'use long extension for Markdown files']
['-o', '--output DIR', 'set the output directory for converted Markdown']
['-p', '--print', 'print out the converted Markdown']
['-v', '--version', 'display the version number']
]

exit = (code = 0) ->
# Variables
# ---------

# File extension to be applied to any Markdown files created.
extension = '.md'
# Options, including their defaults, possibly changed by flags passed in at
# runtime.
opts =
arguments: []
debug: no
eval: no
longExt: no
output: null
print: no
# List of HTML sources passed in at runtime.
# These can either be paths or a single HTML input (if `eval` option was
# enabled).
sources = []

# Functions
# ---------

# Copy exists for backwards compatibility.
exists = fs.exists or path.exists

# Exit the process accordingly, optionally displaying `message` before doing
# so.
exit = (code = 0, message) ->
console[if code then 'error' else 'log'] message if message
process.exit code

toCamelCase = (str) ->
str.replace R_HYPHEN_CAMEL, (match, char) -> char.toUpperCase()

options =
debug: no
eval: no
longExt: no
output: process.cwd()
print: no

parser = new optparse.OptionParser SWITCHES
parser.banner = 'md [options] [ -e html | file.html ] [arguments]'
parser.on '*', (opt, val) ->
opt = toCamelCase opt
if typeof options[opt] is 'boolean'
options[opt] = not options[opt]
else if val?
options[opt] = val
parser.on 'help', ->
console.log parser.toString()
exit()
parser.on 'version', ->
console.log "html.md version #{md.VERSION}"
exit()

parser.parse process.argv

unless process.platform is 'win32'
process.on 'SIGTERM', -> exit()

# TODO: Complete script
# Resolve the output path for `source`.
outputPath = (source, base) ->
fileName = path.basename(source, path.extname(source)) + extension
srcDir = path.dirname source
baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
path.join dir, fileName

# Either attempt to parse the contents of path or, if it's a directory, the
# contents of its children HTML files.
parsePath = (source, topLevel, base) ->
fs.stat source, (err, stats) ->
throw err if err and err.code isnt NOT_FOUND
if err?.code is NOT_FOUND
if topLevel and not R_HTML_EXT.test source
source = sources[sources.indexOf(source)] = "#{source}.html"
return parsePath source, topLevel, base
if topLevel
exit 1, "File not found: #{source}"
return
if stats.isDirectory()
fs.readdir source, (err, files) ->
throw err if err and err.code isnt NOT_FOUND
return if err?.code is NOT_FOUND
index = sources.indexOf source
files = files.filter (file) -> not R_HIDDEN.test file
sources[index..index] = (path.join source, file for file in files)
files.forEach (file) ->
parsePath (path.join source, file), no, base
else if topLevel or R_HTML_EXT.test source
fs.readFile source, (err, html) ->
throw err if err and err.code isnt NOT_FOUND
return if err?.code is NOT_FOUND
parseHtml source, html.toString(), base
else
sources.splice sources.indexOf(source), 1

# Parse `input` and write it to a Markdown file, where appropriate.
parseHtml = (file, input, base) ->
try
output = md input, debug: opts.debug
if opts.print
console.log output
else
writeMarkdown file, output, base
catch err
exit 1, err instanceof Error and err.stack or "ERROR: #{err}"

# Parse the options provided at runtime.
parseOptions = ->
parser = new optparse.OptionParser SWITCHES
parser.banner = 'md [options] [ -e html | file.html ] [arguments]'
rCamel = /-([a-z])/gi
toCamelCase = (str) ->
str.replace rCamel, (match, char) -> char.toUpperCase()
parser.on '*', (opt, val) ->
opt = toCamelCase opt
if typeof opts[opt] is 'boolean' then opts[opt] = not opts[opt]
else if val? then opts[opt] = val
parser.on 'help', ->
exit null, parser.toString()
parser.on 'version', ->
exit null, "html.md version #{md.VERSION}"
opts.arguments = parser.parse(process.argv)[2..]
extension = '.markdown' if opts.longExt
sources = opts.arguments
exit null, parser.toString() unless sources.length
opts

# Handle the runtime arguments accordingly.
run = ->
unless process.platform is 'win32'
process.on 'SIGTERM', -> exit()
parseOptions()
return parseHtml null, sources[0], opts if opts.eval
for source in sources
parsePath source, yes, path.normalize source

# Write the `markdown` converted from `source` to its relative output file.
writeMarkdown = (source, markdown, base) ->
mdPath = outputPath source, base
mdDir = path.dirname mdPath
parse = ->
markdown = ' ' if markdown.length <= 0
fs.writeFile mdPath, markdown, (err) ->
console.log err.message if err
exists mdDir, (result) ->
if result then parse() else exec "mkdir -p #{mdDir}", parse

# Process
# -------

# Make the magic happen!
run()

0 comments on commit 4862df6

Please sign in to comment.