machinepack-fs
Advanced tools
Comparing version 2.3.0 to 3.0.0
module.exports = { | ||
friendlyName: 'Copy file or directory', | ||
friendlyName: 'Copy (cp)', | ||
description: 'Copy file or directory located at source path to the destination path.', | ||
extendedDescription: 'Includes all of its descendant files and directories (i.e. `cp -r`)', | ||
inputs: { | ||
source: { | ||
@@ -12,2 +20,3 @@ example: '/Users/mikermcneil/.tmp/foo', | ||
}, | ||
destination: { | ||
@@ -18,10 +27,12 @@ example: '/Users/mikermcneil/.tmp/bar', | ||
} | ||
}, | ||
defaultExit: 'success', | ||
catchallExit: 'error', | ||
exits: { | ||
error: {}, | ||
success: {} | ||
success: { | ||
description: 'Done.' | ||
} | ||
}, | ||
@@ -28,0 +39,0 @@ |
module.exports = { | ||
friendlyName: 'Ensure directory exists', | ||
friendlyName: 'Ensure directory', | ||
@@ -10,3 +10,3 @@ | ||
extendedDescription: '', | ||
idempotent: true, | ||
@@ -26,5 +26,2 @@ | ||
defaultExit: 'success', | ||
exits: { | ||
@@ -31,0 +28,0 @@ |
@@ -13,3 +13,3 @@ module.exports = { | ||
cacheable: true, | ||
idempotent: true, | ||
@@ -16,0 +16,0 @@ |
module.exports = { | ||
friendlyName: 'Check existence', | ||
friendlyName: 'Exists?', | ||
@@ -5,0 +5,0 @@ |
module.exports = { | ||
friendlyName: 'List directory contents', | ||
friendlyName: 'List contents (ls)', | ||
description: 'List contents of a directory on the local filesystem.', | ||
cacheable: true, | ||
inputs: { | ||
dir: { | ||
friendlyName: 'Directory path', | ||
example: '/Users/mikermcneil/.tmp/foo', | ||
description: 'Absolute path to the directory whose contents should be listed (if relative path provided, will be resolved from cwd).', | ||
description: 'Path to the directory whose contents should be listed.', | ||
extendedDescription: 'If a relative path is provided, it will be resolved to an absolute path from the context of the current working directory.', | ||
required: true | ||
}, | ||
depth: { | ||
description: 'The maximum number of "hops" (i.e. directories deep) to include directory contents from. For instance, if you are running `ls` on "foo/" which has a subdirectory "foo/bar/baz/", if `depth` is set to 2, the results will include "foo/bar/baz/", but none of the files/folders contained within.', | ||
example: 2 | ||
friendlyName: 'Depth', | ||
description: 'The maximum number of "hops" (i.e. directories deep) to include directory contents from.', | ||
extendedDescription: 'For instance, if you are running `ls` on "foo/" which has a subdirectory "foo/bar/baz/", if `depth` is set to 2, the results will include "foo/bar/baz/", but none of the files/folders contained within.', | ||
example: 1, | ||
defaultsTo: 1 | ||
}, | ||
types: { | ||
description: 'The types of directory entries to return (defaults to ["all"])', | ||
example: ['file'], | ||
includeFiles: { | ||
friendlyName: 'Files?', | ||
description: 'Whether or not to include files', | ||
example: true, | ||
defaultsTo: true | ||
}, | ||
includeDirs: { | ||
friendlyName: 'Directories?', | ||
description: 'Whether or not to include directories', | ||
example: true, | ||
defaultsTo: true | ||
}, | ||
includeSymlinks: { | ||
friendlyName: 'Symlinks?', | ||
description: 'Whether or not to include symbolic links', | ||
example: true, | ||
defaultsTo: true | ||
}, | ||
includeHidden: { | ||
friendlyName: 'Hidden entries?', | ||
description: 'Whether or not to include hidden files/directories/symlinks', | ||
example: false, | ||
defaultsTo: false | ||
} | ||
}, | ||
defaultExit: 'success', | ||
catchallExit: 'error', | ||
exits: { | ||
error: { | ||
description: 'Triggered when a filesystem error occurs' | ||
}, | ||
success: { | ||
variableName: 'dirContents', | ||
example: [ | ||
@@ -34,77 +70,89 @@ '/Users/mikermcneil/.tmp/foo/.gitignore' | ||
} | ||
}, | ||
fn: function (inputs, exits) { | ||
var path = require('path'); | ||
var Walker = require('walker'); | ||
try { | ||
// Ensure we've got an absolute path. | ||
inputs.dir = path.resolve(inputs.dir); | ||
// Ensure we've got an absolute path | ||
inputs.dir = path.resolve(inputs.dir); | ||
// Determine the depth of the top-level directory we're walking, | ||
// for comparison later on. | ||
var topLvlDirDepth = inputs.dir.split(path.sep).length; | ||
var spinlock; | ||
var results = []; | ||
// Initialize the walker and teach it to skip walking directories | ||
// that are: | ||
// • deeper than requested, or | ||
// • hidden (if the `includeHidden` input is set to false) | ||
var walker = Walker(inputs.dir); | ||
walker.filterDir(function(dir, stat) { | ||
// Too deep | ||
if (dir.split(path.sep).length > (topLvlDirDepth + inputs.depth)) { | ||
return false; | ||
} | ||
// Too hidden | ||
if (path.basename(dir).match(/^\./) && !inputs.includeHidden) { | ||
return false; | ||
} | ||
return true; | ||
}); | ||
// Default depth to 0 (infinite recursion) | ||
var depth = inputs.depth || 0; | ||
// Get the depth of the directory we're walking, for comparison | ||
var dirDepth = inputs.dir.split(path.sep).length; | ||
// Default types to "all" | ||
var types = inputs.types || 'all'; | ||
// Initialize the walker | ||
var walker = require('walker')(inputs.dir) | ||
// Skip directories that are deeper than requested | ||
.filterDir(function(dir, stat) { | ||
if (depth && dir.split(path.sep).length > (dirDepth + depth)) { | ||
return false; | ||
// Accumulate results array by listing file, directory, and/or symlink | ||
// entries from the specified directory. | ||
var results = []; | ||
if (inputs.includeFiles) { | ||
walker.on('file', function (entry, stat) { | ||
// Add the new entry to our result list unless it is: | ||
// • hidden (and the `includeHidden` input is set to false), or | ||
// • too deep | ||
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden; | ||
var tooDeep = entry.split(path.sep).length > (topLvlDirDepth + inputs.depth); | ||
if ( !tooHidden && !tooDeep ) { | ||
results.push(entry); | ||
} | ||
return true; | ||
}) | ||
// Handle errors | ||
.on('error', function (err){ | ||
if (spinlock) return; | ||
spinlock = true; | ||
return exits.error(err); | ||
}) | ||
// When walking is done, return the results | ||
.on('end', function (){ | ||
if (spinlock) return; | ||
spinlock = true; | ||
return exits.success(results); | ||
}); | ||
// Include file entries if requested | ||
if (types == 'all' || types.indexOf('file') > -1) { | ||
walker.on('file', function (entry, stat) { | ||
// Filter out files that are deeper than requested | ||
if (!depth || (entry.split(path.sep).length <= (dirDepth + depth))) { | ||
results.push(entry); | ||
} | ||
}); | ||
} | ||
// Include directory entries if requested | ||
if (types == 'all' || types.indexOf('dir') > -1) { | ||
walker.on('dir', function (entry, stat) { | ||
if (entry===inputs.dir) return; | ||
} | ||
if (inputs.includeDirs) { | ||
walker.on('dir', function (entry, stat) { | ||
// If this is the top-level directory, exclude it. | ||
if (entry === inputs.dir) return; | ||
// Add the new entry to our result list unless it is: | ||
// • hidden (and the `includeHidden` input is set to false) | ||
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden; | ||
if ( !tooHidden ) { | ||
results.push(entry); | ||
}); | ||
} | ||
// Include symlink entries if requested | ||
if (types == 'all' || types.indexOf('symlink') > -1) { | ||
walker.on('symlink', function (entry, stat) { | ||
if (entry===inputs.dir) return; | ||
} | ||
results.push(entry); | ||
}); | ||
} | ||
if (inputs.includeSymlinks) { | ||
walker.on('symlink', function (entry, stat) { | ||
// If this is the top-level directory, exclude it. | ||
if (entry===inputs.dir) return; | ||
// Add the new entry to our result list unless it is: | ||
// • hidden (and the `includeHidden` input is set to false) | ||
var tooHidden = path.basename(entry).match(/^\./) && !inputs.includeHidden; | ||
if ( !tooHidden ) { | ||
results.push(entry); | ||
}); | ||
} | ||
} | ||
results.push(entry); | ||
}); | ||
} | ||
catch(e){ | ||
return exits.error(e); | ||
} | ||
// When walking is done, because of an error or otherwise, | ||
// return the results. | ||
var spinlock; | ||
walker.on('error', function (err){ | ||
if (spinlock) return; | ||
spinlock = true; | ||
return exits.error(err); | ||
}); | ||
walker.on('end', function (){ | ||
if (spinlock) return; | ||
spinlock = true; | ||
return exits.success(results); | ||
}); | ||
} | ||
@@ -111,0 +159,0 @@ }; |
module.exports = { | ||
friendlyName: 'Create directory', | ||
friendlyName: 'Make directory (mkdir)', | ||
description: 'Create a new directory.', | ||
description: 'Make a new directory.', | ||
@@ -9,0 +9,0 @@ |
module.exports = { | ||
friendlyName: 'Move file or directory', | ||
friendlyName: 'Move (mv)', | ||
description: 'Move file or directory located at source path to the destination path.', | ||
extendedDescription: '', | ||
inputs: { | ||
source: { | ||
@@ -10,2 +16,3 @@ example: '/Users/mikermcneil/.tmp/foo', | ||
}, | ||
destination: { | ||
@@ -15,12 +22,15 @@ example: '/Users/mikermcneil/.tmp/bar', | ||
} | ||
}, | ||
defaultExit: 'success', | ||
exits: { | ||
error: { | ||
description: 'Unexpected error occurred.' | ||
}, | ||
success: { | ||
description: 'Done.' | ||
} | ||
}, | ||
fn: function(inputs, exits) { | ||
@@ -27,0 +37,0 @@ var fsx = require('fs-extra'); |
module.exports = { | ||
friendlyName: 'Read JSON file', | ||
description: 'Read and parse JSON file located at source path on disk into usable data.', | ||
extendedDescription: 'Assumes file is encoded using utf8.', | ||
cacheable: true, | ||
idempotent: true, | ||
inputs: { | ||
source: { | ||
@@ -14,2 +23,3 @@ description: 'Absolute path to the source file (if relative path is provided, will resolve path from current working directory)', | ||
}, | ||
schema: { | ||
@@ -20,16 +30,20 @@ description: 'An example of what the parsed data should look like (used for type-coercion)', | ||
} | ||
}, | ||
defaultExit: 'success', | ||
exits: { | ||
error: {}, | ||
doesNotExist: { | ||
description: 'No file exists at the provided `source` path' | ||
}, | ||
couldNotParse: { | ||
description: 'Could not parse file as JSON.' | ||
}, | ||
// TODO: not a file (and return what it ACTUALLY is, e.g. dir or symlink) | ||
success: { | ||
description: 'Returns the data stored in file at `source` path', | ||
description: 'Returns parsed JSON data from the source file.', | ||
getExample: function (inputs){ | ||
@@ -39,11 +53,12 @@ return inputs.schema; | ||
} | ||
}, | ||
fn: function (inputs, exits) { | ||
var Util = require('machinepack-util'); | ||
var MPJSON = require('machinepack-json'); | ||
var thisPack = require('../'); | ||
var readFile = require('machine').build(require('./read')); | ||
readFile({ | ||
thisPack.read({ | ||
source: inputs.source | ||
@@ -54,3 +69,3 @@ }).exec({ | ||
success: function (contents){ | ||
Util.parseJson({ | ||
MPJSON.parse({ | ||
json: contents, | ||
@@ -68,2 +83,4 @@ schema: inputs.schema | ||
} | ||
}; |
@@ -16,5 +16,2 @@ module.exports = { | ||
idempotent: true, | ||
inputs: { | ||
@@ -21,0 +18,0 @@ |
module.exports = { | ||
friendlyName: 'Delete file or directory', | ||
friendlyName: 'Remove (rm)', | ||
description: 'Completely remove a file or directory (like rm -rf).', | ||
extendedDescription: 'If the provided path is a directory, all contents will be removed recursively. If nothing exists at the provided path, the success exit will be triggered, but nothing will be deleted.', | ||
idempotent: true, | ||
inputs: { | ||
dir: { | ||
@@ -13,9 +23,12 @@ example: '/Users/mikermcneil/.tmp/foo', | ||
} | ||
}, | ||
defaultExit: 'success', | ||
exits: { | ||
error: {}, | ||
success: {} | ||
success: { | ||
description: 'Done.' | ||
} | ||
}, | ||
@@ -22,0 +35,0 @@ |
module.exports = { | ||
friendlyName: 'Template file', | ||
description: 'Read file at source path, replace substrings with provided data, then write to destination path.', | ||
extendedDescription: 'Uses lodash template syntax (e.g. `"Hi there, <%=firstName%>!"`)', | ||
friendlyName: 'Template', | ||
description: 'Read file at source path as a template, render with provided data, then write to destination path.', | ||
extendedDescription: 'Uses Lodash template syntax (e.g. `<%= %>`, `<%- %>`, `<% %>`) Also provides access to the Node.js core utility module (as `util`), as well as Lodash itself (as `_`).', | ||
idempotent: true, | ||
inputs: { | ||
source: { | ||
example: '/Users/mikermcneil/.tmp/foo', | ||
description: 'The path on disk to the template file.', | ||
example: '/Users/mikermcneil/.tmp/foo.tpl', | ||
required: true | ||
}, | ||
destination: { | ||
example: '/Users/mikermcneil/.tmp/bar', | ||
description: 'The path on disk where the resulting file should be written', | ||
example: '/Users/mikermcneil/.tmp/bar.md', | ||
required: true | ||
}, | ||
data: { | ||
friendlyName: 'Template data', | ||
description: 'The data which will be accessible from the template', | ||
extendedDescription: 'Each key will be a variable accessible in the template. For instance, if you supply an array `[{name:"Chandra"}, {name:"Mary"}]` as the key "friends", then you will be able to access `friends` from the template; i.e. `<ul><% _.each(friends, function (friend){ %><li><%= friend.name %></li> <%}); %></ul>` Use `<%= %>` to inject the contents of a variable as-is, `<%- %>` to HTML-escape them first, or `<% %>` to execute some JavaScript code.', | ||
typeclass: 'dictionary' | ||
@@ -26,93 +43,78 @@ // e.g. { | ||
}, | ||
force: { | ||
description: 'overwrite existing file(s)?', | ||
example: false | ||
}, | ||
options: { | ||
description: 'template options (see http://lodash.com/docs#template)', | ||
typeclass: 'dictionary' | ||
// example: { | ||
// interpolate: /{{([\s\S]+?)}}/g | ||
// } | ||
friendlyName: 'Force?', | ||
description: 'Whether or not to overwrite existing file(s).', | ||
example: false, | ||
defaultsTo: false | ||
} | ||
}, | ||
defaultExit: 'success', | ||
exits: { | ||
error: { | ||
description: 'Unexpected error occurred.' | ||
}, | ||
success: { | ||
description: 'File written successfully.' | ||
}, | ||
noTemplate: { | ||
description: 'Source template file not found' | ||
description: 'Source template file not found.' | ||
}, | ||
missingData: { | ||
friendlyName: 'missing data', | ||
description: 'One or more variables used in the template were not provided in the template data.', | ||
variableName: 'info', | ||
example: { | ||
status: 500, | ||
exit: 'missingData', | ||
name: 'ReferenceError', | ||
message: '`appname` is used in template "/Users/mikermcneil/.tmp/foo", but no value for `appname` was provided as template data.' | ||
message: 'Some variables (`me`,`friends`) were used in template "/code/machine/docs/.type-table.tpl", but not provided in the template data dictionary.', | ||
missingVariables: ['me', 'friends'] | ||
} | ||
} | ||
}, | ||
}, | ||
fn: function (inputs, exits) { | ||
couldNotRender: { | ||
description: 'Could not render the template due to invalid or unparseable syntax.' | ||
}, | ||
var util = require('util'); | ||
var fsx = require('fs-extra'); | ||
var _ = require('lodash'); | ||
var writeFileFromStr = require('machine').build(require('./write')); | ||
alreadyExists: { | ||
description: 'Something already exists at the specified path (overwrite by enabling the `force` input)' | ||
} | ||
fsx.readFile(inputs.source, 'utf8', function(err, contents) { | ||
if (err) { | ||
err = (typeof err === 'object' && err instanceof Error) ? err : new Error(err); | ||
err.message = 'Template error: ' + err.message; | ||
err.path = inputs.source; | ||
if (err.code === 'ENOENT') { | ||
return (exits.noTemplate||exits.error)(err); | ||
} | ||
return exits.error(err); | ||
} | ||
try { | ||
var options = inputs.options || {}; | ||
contents = _.template(contents, inputs.data||{}, options); | ||
}, | ||
// With lodash teplates, HTML entities are escaped by default. | ||
// Default assumption is we DON'T want that, so we'll reverse it. | ||
if (!inputs.escapeHTMLEntities) { | ||
contents = _.unescape(contents); | ||
} | ||
} catch (e) { | ||
var err = e; | ||
if (!(typeof err === 'object' && err instanceof Error)){ | ||
err = new Error(err); | ||
} | ||
// Recognize lodash template error (scope variable not defined) | ||
if (err.name === 'ReferenceError' || err.type === 'not_defined') { | ||
var undefinedScopeVar = err.arguments && err.arguments[0]; | ||
err = { | ||
status: 500, | ||
exit: 'missingData', | ||
name: 'ReferenceError', | ||
message: util.format('`%s` is used in template "%s", but no value for `%s` was provided as template data.', undefinedScopeVar, inputs.source, undefinedScopeVar) | ||
}; | ||
return (exits.missingData||exits.error)(err); | ||
} | ||
fn: function (inputs, exits) { | ||
var thisPack = require('../'); | ||
var MPStrings = require('machinepack-strings'); | ||
return exits.error(e); | ||
// Read template from disk | ||
thisPack.read({ | ||
source: inputs.source | ||
}).exec({ | ||
error: exits.error, | ||
doesNotExist: exits.noTemplate, | ||
success: function (contents) { | ||
MPStrings.template({ | ||
templateStr: contents, | ||
data: inputs.data | ||
}).exec({ | ||
error: exits.error, | ||
missingData: exits.missingData, | ||
couldNotRender: exits.couldNotRender, | ||
success: function (rendered) { | ||
thisPack.write({ | ||
destination: inputs.destination, | ||
string: rendered, | ||
force: inputs.force | ||
}).exec({ | ||
error: exits.error, | ||
alreadyExists: exits.alreadyExists, | ||
success: exits.success | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
// Finally, write templated string to disk | ||
writeFileFromStr({ | ||
force: inputs.force, | ||
string: contents, | ||
destination: inputs.destination | ||
}) | ||
.exec(exits); | ||
}); | ||
} | ||
}; |
module.exports = { | ||
friendlyName: 'Write JSON file', | ||
description: 'Write some data to the specified destination path on disk.', | ||
extendedDescription: 'Assumes file is encoded using utf8.', | ||
idempotent: true, | ||
inputs: { | ||
json: { | ||
@@ -12,2 +23,3 @@ typeclass: '*', | ||
}, | ||
destination: { | ||
@@ -18,22 +30,25 @@ example: '/Users/mikermcneil/.tmp/bar.json', | ||
}, | ||
force: { | ||
description: 'Overwrite existing file(s)?', | ||
example: false | ||
example: false, | ||
defaultsTo: false | ||
} | ||
}, | ||
defaultExit: 'success', | ||
exits: { | ||
error: { | ||
description: 'Unexpected error occurred' | ||
}, | ||
alreadyExists: { | ||
description: 'A file or folder already exists at the specified `destination`' | ||
description: 'A file or folder already exists at the specified destination' | ||
}, | ||
success: { | ||
description: 'JSON file written successfully.' | ||
} | ||
}, | ||
fn: function (inputs, exits) { | ||
@@ -56,5 +71,2 @@ | ||
// Don't actually write the file if this is a dry run. | ||
if (inputs.dry) return exits.success(); | ||
async.series([ | ||
@@ -75,2 +87,4 @@ function deleteExistingFileIfNecessary(next) { | ||
} | ||
}; |
module.exports = { | ||
friendlyName: 'Write file from string', | ||
friendlyName: 'Write file', | ||
description: 'Generate a file on the local filesystem using the specified utf8 string as its contents.', | ||
idempotent: true, | ||
inputs: { | ||
string: { | ||
description: 'Text to write to the file', | ||
description: 'Text to write to the file (if omitted, will create an empty file)', | ||
example: 'lots of words, utf8 things you know', | ||
}, | ||
destination: { | ||
@@ -16,19 +25,25 @@ description: 'Path (relative or absolute) to the file to write.', | ||
}, | ||
force: { | ||
description: 'overwrite existing file(s)?', | ||
example: false | ||
description: 'Whether to overwrite existing file(s) which might exist at the destination path.', | ||
example: false, | ||
defaultsTo: false | ||
} | ||
}, | ||
defaultExit: 'success', | ||
catchallExit: 'error', | ||
exits: { | ||
error: {}, | ||
success: {}, | ||
alreadyExists: { | ||
description: 'Something already exists at the specified path (overwrite by enabling the `force` input)' | ||
}, | ||
success: { | ||
description: 'OK.' | ||
} | ||
}, | ||
fn: function (inputs, exits) { | ||
@@ -41,7 +56,2 @@ | ||
inputs = _.defaults(inputs, { | ||
force: false, | ||
dry: false | ||
}); | ||
// Coerce `string` input into an actual string | ||
@@ -60,5 +70,2 @@ inputs.string = inputs.string || ''; | ||
// Don't actually write the file if this is a dry run. | ||
if (inputs.dry) return exits.success(); | ||
async.series([ | ||
@@ -80,2 +87,4 @@ function deleteExistingFileIfNecessary(exits) { | ||
} | ||
}; |
{ | ||
"name": "machinepack-fs", | ||
"version": "2.3.0", | ||
"version": "3.0.0", | ||
"description": "Work with the local filesystem; list files, write files, etc.", | ||
@@ -20,8 +20,10 @@ "scripts": { | ||
"dependencies": { | ||
"async": "^0.9.0", | ||
"fs-extra": "~0.10.0", | ||
"lodash": "^2.4.1", | ||
"fs-extra": "~0.10.0", | ||
"walker": "~1.0.6", | ||
"machine": "^4.0.0", | ||
"async": "^0.9.0", | ||
"machinepack-util": "^0.7.0" | ||
"machine": "^4.1.1", | ||
"machinepack-json": "^1.0.0", | ||
"machinepack-strings": "^3.1.0", | ||
"machinepack-util": "^0.7.0", | ||
"walker": "~1.0.6" | ||
}, | ||
@@ -48,3 +50,2 @@ "repository": { | ||
"ensure-dir", | ||
"get-home-dirpath", | ||
"ensure-json" | ||
@@ -51,0 +52,0 @@ ], |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27346
9
8
18
695
+ Addedmachinepack-json@^1.0.0
+ Addedmachinepack-strings@^3.1.0
+ Added@sailshq/lodash@3.10.6(transitive)
+ Addedlodash@3.10.1(transitive)
+ Addedmachine@11.0.3(transitive)
+ Addedmachinepack-json@1.2.1(transitive)
+ Addedmachinepack-strings@3.1.1(transitive)
+ Addedrttc@8.1.7(transitive)
+ Addedswitchback@2.0.5(transitive)
Updatedmachine@^4.1.1