Files
atom/src/fs-utils.coffee
2013-09-20 10:02:20 -07:00

306 lines
8.6 KiB
CoffeeScript

_ = require './underscore-extensions'
fs = require 'fs'
mkdirp = require 'mkdirp'
Module = require 'module'
async = require 'async'
rimraf = require 'rimraf'
path = require 'path'
fsExtensions =
# Make the given path absolute by resolving it against the
# current working directory.
absolute: (relativePath) ->
return null unless relativePath?
if relativePath is '~'
relativePath = process.env.HOME
else if relativePath.indexOf('~/') is 0
relativePath = "#{process.env.HOME}#{relativePath.substring(1)}"
try
fs.realpathSync(relativePath)
catch e
relativePath
# Returns true if a file or folder at the specified path exists.
exists: (pathToCheck) ->
pathToCheck? and fs.existsSync(pathToCheck)
# Returns true if the specified path is a directory that exists.
isDirectorySync: (directoryPath) ->
return false unless directoryPath?.length > 0
try
fs.statSync(directoryPath).isDirectory()
catch e
false
isDirectory: (directoryPath, done) ->
return done(false) unless directoryPath?.length > 0
fs.exists directoryPath, (exists) ->
if exists
fs.stat directoryPath, (error, stat) ->
if error?
done(false)
else
done(stat.isDirectory())
else
done(false)
# Returns true if the specified path is a regular file that exists.
isFileSync: (filePath) ->
return false unless filePath?.length > 0
try
fs.statSync(filePath).isFile()
catch e
false
# Returns true if the specified path is executable.
isExecutableSync: (pathToCheck) ->
return false unless pathToCheck?.length > 0
try
(fs.statSync(pathToCheck).mode & 0o777 & 1) isnt 0
catch e
false
# Returns an array with the paths of the files and folders
# contained in the directory path.
listSync: (rootPath, extensions) ->
return [] unless @isDirectorySync(rootPath)
paths = fs.readdirSync(rootPath)
paths = @filterExtensions(paths, extensions) if extensions
paths = paths.map (childPath) -> path.join(rootPath, childPath)
paths
list: (rootPath, rest...) ->
extensions = rest.shift() if rest.length > 1
done = rest.shift()
fs.readdir rootPath, (error, paths) =>
if error?
done(error)
else
paths = @filterExtensions(paths, extensions) if extensions
paths = paths.map (childPath) -> path.join(rootPath, childPath)
done(null, paths)
filterExtensions: (paths, extensions) ->
extensions = extensions.map (ext) ->
if ext is ''
ext
else
'.' + ext.replace(/^\./, '')
paths.filter (pathToCheck) -> _.include(extensions, path.extname(pathToCheck))
listTreeSync: (rootPath) ->
paths = []
onPath = (childPath) ->
paths.push(childPath)
true
@traverseTreeSync(rootPath, onPath, onPath)
paths
move: (source, target) ->
fs.renameSync(source, target)
# Remove the file or directory at the given path.
remove: (pathToRemove) ->
rimraf.sync(pathToRemove)
# Open, read, and close a file, returning the file's contents.
read: (filePath) ->
fs.readFileSync(filePath, 'utf8')
# Open, write, flush, and close a file, writing the given content.
writeSync: (filePath, content) ->
mkdirp.sync(path.dirname(filePath))
fs.writeFileSync(filePath, content)
write: (filePath, content, callback) ->
mkdirp path.dirname(filePath), (error) ->
if error?
callback?(error)
else
fs.writeFile(filePath, content, callback)
copy: (sourcePath, destinationPath, done) ->
mkdirp path.dirname(destinationPath), (error) ->
if error?
done?(error)
return
sourceStream = fs.createReadStream(sourcePath)
sourceStream.on 'error', (error) ->
done?(error)
done = null
destinationStream = fs.createWriteStream(destinationPath)
destinationStream.on 'error', (error) ->
done?(error)
done = null
destinationStream.on 'close', ->
done?()
done = null
sourceStream.pipe(destinationStream)
# Create a directory at the specified path including any missing parent
# directories.
makeTree: (directoryPath) ->
mkdirp.sync(directoryPath) if directoryPath and not @exists(directoryPath)
traverseTreeSync: (rootPath, onFile, onDirectory=onFile) ->
return unless @isDirectorySync(rootPath)
traverse = (directoryPath, onFile, onDirectory) ->
for file in fs.readdirSync(directoryPath)
childPath = path.join(directoryPath, file)
stats = fs.lstatSync(childPath)
if stats.isSymbolicLink()
try
stats = fs.statSync(childPath)
if stats.isDirectory()
traverse(childPath, onFile, onDirectory) if onDirectory(childPath)
else if stats.isFile()
onFile(childPath)
traverse(rootPath, onFile, onDirectory)
traverseTree: (rootPath, onFile, onDirectory, onDone) ->
fs.readdir rootPath, (error, files) ->
if error
onDone?()
else
queue = async.queue (childPath, callback) ->
fs.stat childPath, (error, stats) ->
if error
callback(error)
else if stats.isFile()
onFile(childPath)
callback()
else if stats.isDirectory()
if onDirectory(childPath)
fs.readdir childPath, (error, files) ->
if error
callback(error)
else
for file in files
queue.unshift(path.join(childPath, file))
callback()
else
callback()
queue.concurrency = 1
queue.drain = onDone
queue.push(path.join(rootPath, file)) for file in files
md5ForPath: (pathToDigest) ->
contents = fs.readFileSync(pathToDigest)
require('crypto').createHash('md5').update(contents).digest('hex')
resolve: (args...) ->
extensions = args.pop() if _.isArray(_.last(args))
pathToResolve = args.pop()
loadPaths = args
if pathToResolve[0] is '/'
if extensions and resolvedPath = @resolveExtension(pathToResolve, extensions)
return resolvedPath
else
return pathToResolve if @exists(pathToResolve)
for loadPath in loadPaths
candidatePath = path.join(loadPath, pathToResolve)
if extensions
if resolvedPath = @resolveExtension(candidatePath, extensions)
return resolvedPath
else
return @absolute(candidatePath) if @exists(candidatePath)
undefined
resolveOnLoadPath: (args...) ->
loadPaths = Module.globalPaths.concat(module.paths)
@resolve(loadPaths..., args...)
resolveExtension: (pathToResolve, extensions) ->
for extension in extensions
if extension == ""
return @absolute(pathToResolve) if @exists(pathToResolve)
else
pathWithExtension = pathToResolve + "." + extension.replace(/^\./, "")
return @absolute(pathWithExtension) if @exists(pathWithExtension)
undefined
isCompressedExtension: (ext) ->
_.indexOf([
'.gz'
'.jar'
'.tar'
'.tgz'
'.zip'
], ext, true) >= 0
isImageExtension: (ext) ->
_.indexOf([
'.gif'
'.jpeg'
'.jpg'
'.png'
'.tiff'
], ext, true) >= 0
isPdfExtension: (ext) ->
ext is '.pdf'
isMarkdownExtension: (ext) ->
_.indexOf([
'.markdown'
'.md'
'.mdown'
'.mkd'
'.mkdown'
'.ron'
], ext, true) >= 0
isBinaryExtension: (ext) ->
_.indexOf([
'.DS_Store'
'.a'
'.o'
'.so'
'.woff'
], ext, true) >= 0
isReadmePath: (readmePath) ->
extension = path.extname(readmePath)
base = path.basename(readmePath, extension).toLowerCase()
base is 'readme' and (extension is '' or @isMarkdownExtension(extension))
readPlistSync: (plistPath) ->
plist = require 'plist'
plist.parseStringSync(@read(plistPath))
readPlist: (plistPath, done) ->
plist = require 'plist'
fs.readFile plistPath, 'utf8', (error, contents) ->
if error?
done(error)
else
try
done(null, plist.parseStringSync(contents))
catch parseError
done(parseError)
readObjectSync: (objectPath) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFileSync(objectPath)
else
@readPlistSync(objectPath)
readObject: (objectPath, done) ->
CSON = require 'season'
if CSON.isObjectPath(objectPath)
CSON.readFile(objectPath, done)
else
@readPlist(objectPath, done)
module.exports = _.extend({}, fs, fsExtensions)