Socket
Socket
Sign inDemoInstall

file-utils

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

file-utils - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

9

Gruntfile.js

@@ -17,3 +17,3 @@ /*

nodeunit: {
all: ['tests/**.js', '!tests/fixtures/**', '!tests/helpers/**']
all: [ 'tests/**.js', '!tests/fixtures/**', '!tests/helpers/**' ]
},

@@ -36,2 +36,8 @@ jshint: {

}
},
watch: {
tests: {
files: [ '**/*' ],
tasks: [ 'test' ]
}
}

@@ -43,2 +49,3 @@ });

grunt.loadNpmTasks('grunt-contrib-nodeunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-release');

@@ -45,0 +52,0 @@

@@ -0,0 +0,0 @@ var _ = require('lodash');

@@ -12,6 +12,7 @@ var path = require('path');

this._base = opt.base || '';
this._destBase = opt.dest || '';
this._writeFilters = {};
this._validationFilters = {};
var methodsToPrefix = [ 'mkdir', 'recurse', 'read', 'readJSON', 'write', 'copy',
'delete' ];
var methodsToPrefix = [ 'mkdir', 'recurse', 'read', 'readJSON', 'write', 'delete' ];

@@ -27,6 +28,12 @@ // Prefix path arguments with this environment root dir

var actualWrite = this.write;
this.copy = function() {
var args = _.toArray(arguments);
args[0] = this.fromBase(args[0]);
args[1] = this.fromDestBase(args[1]);
return File.prototype.copy.apply(this, args);
};
this._actualWrite = this.write;
this.write = function(filepath, contents, options) {
var file = this.applyWriteFilters({ path: filepath, contents: contents });
return actualWrite.call(this, file.path, file.contents, options);
return this.applyWriteFilters({ path: filepath, contents: contents }, this.applyValidationFilters, options);
};

@@ -45,2 +52,10 @@ }

// Return a path prefixed by the destination base (if not absolute)
Env.prototype.fromDestBase = function( filepath ) {
if (this.isPathAbsolute(filepath)) {
return filepath;
}
return path.join(this._destBase, filepath);
};
Env.prototype.registerWriteFilter = function(name, filter) {

@@ -54,7 +69,87 @@ this._writeFilters[name] = filter;

Env.prototype.applyWriteFilters = function(file) {
_.each(this._writeFilters, function(filter) {
file = filter(file);
});
return file;
Env.prototype.registerValidationFilter = function(name, filter) {
this._validationFilters[name] = filter;
};
Env.prototype.removeValidationFilter = function(name) {
delete this._validationFilters[name];
};
Env.prototype.applyWriteFilters = function(file, validate, options) {
var writeFilters = _.reduce(this._writeFilters, function(m, v) { m.push(v); return m; }, []);
if (!writeFilters.length) {
return validate.call(this, file, options);
}
var i = 0;
var output;
var recurse = function(file) {
i++;
if (writeFilters[i]) {
runAsync(writeFilters[i], recurse, file);
} else {
output = validate.call(this, file, options);
}
}.bind(this);
runAsync(writeFilters[i], recurse, file);
return output;
};
Env.prototype.applyValidationFilters = function( file, options ) {
var validationFilters = _.reduce(this._validationFilters, function(m, v) { m.push(v); return m; }, []);
if (!validationFilters.length) {
return this._actualWrite.call(this, file.path, file.contents, options);
}
var i = 0;
var output;
var recurse = function(validated) {
if ( validated !== true ) {
return this.log.write(validated || 'Not actually writing to '+ file.path +' haven\'t pass validation' );
}
i++;
if (validationFilters[i]) {
runAsync(validationFilters[i], recurse, file);
} else {
output = this._actualWrite.call(this, file.path, file.contents, options);
}
}.bind(this);
runAsync(validationFilters[i], recurse, file);
return output;
};
/**
* Allow a function to be run async by using `this.async()`. If not, then the function is
* runned synchronously.
* @param {Function} func The function to run
* @param {Function} cb Callback to be provided even if the function is run sync
* @param {...rest} Arguments passed to `func`
* @return {null}
*/
function runAsync( func, cb ) {
var rest = [];
var len = 1;
while ( len++ < arguments.length ) {
rest.push( arguments[len] );
}
var async = false;
var returnValue = func.apply({
async: function() {
async = true;
return _.once(cb);
}
}, rest );
// Note: Call the callback synchronously to keep the sync flow by default
if ( !async ) {
cb(returnValue);
}
}

15

lib/file.js

@@ -251,6 +251,4 @@ /*

}
this.log.ok();
return contents;
} catch(e) {
this.log.error();
throw new Error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);

@@ -267,6 +265,4 @@ }

result = JSON.parse(src);
this.log.ok();
return result;
} catch(e) {
this.log.error();
throw new Error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);

@@ -293,6 +289,4 @@ }

}
this.log.ok();
return true;
} catch(e) {
this.log.error();
throw new Error('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);

@@ -318,5 +312,3 @@ }

contents = options.process(contents, srcpath);
this.log.ok();
} catch(e) {
this.log.error();
throw new Error('Error while processing "' + srcpath + '" file.', e);

@@ -327,3 +319,3 @@ }

if (contents === false) {
this.log.writeln('Write aborted.');
this.log.write('Write aborted.');
} else {

@@ -344,3 +336,2 @@ this.write(destpath, contents, readWriteOptions);

if (!this.exists(filepath)) {
this.log.error();
this.log.warn('Cannot delete nonexistent file.');

@@ -354,7 +345,5 @@ return false;

if (fquery.isPathCwd(filepath)) {
this.log.error();
this.log.warn('Cannot delete the current working directory.');
return false;
} else if (!fquery.doesPathContain(base, filepath)) {
this.log.error();
this.log.warn('Cannot delete files outside the current working directory.');

@@ -370,6 +359,4 @@ return false;

}
this.log.ok();
return true;
} catch(e) {
this.log.error();
throw new Error('Unable to delete "' + filepath + '" file (' + e.message + ').', e);

@@ -376,0 +363,0 @@ }

module.exports = {
warn: function() {},
error: function() {},
write: function() {},
writeln: function() {},
ok: function() {}
warn: function(msg) {
console.log('WARN: ' + msg);
},
write: function(msg) {
console.log(msg);
}
};

@@ -0,0 +0,0 @@ /**

{
"name": "file-utils",
"version": "0.0.1",
"version": "0.1.0",
"description": "Sync file utility for Node.js command line tools",

@@ -25,2 +25,3 @@ "main": "index.js",

"grunt-contrib-nodeunit": "~0.2.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-release": "~0.3.3"

@@ -27,0 +28,0 @@ },

@@ -9,2 +9,24 @@ file-utils [![](https://travis-ci.org/SBoudrias/file-utils.png)](https://travis-ci.org/SBoudrias/file-utils)

File API
=========
Upcoming. Meanwhile, check [Grunt.file documentation](http://gruntjs.com/api/grunt.file) as the same methods are available.
#### Setting options - `file.option( name, [ value ])`
```
// Set option
file.option('write', false);
// Get option
file.option('write');
```
**Available Options**
- `write` (Boolean): If write is set to `false`, then no file will be written or deleted. Useful for test run without side effets.
- `logger` (Logger object): Used internally to log information to the console. **API still work in progress**
- `encoding` (String): Defaults `utf8`. Set the default encoding used for reading/writing. Note most methods allow you to overwridde it for a single run.
- `force` (Boolean): `force: true` Force the deletion of folders and file outside the utility scope (or CWD if no scope).
ENV scope and filters

@@ -19,8 +41,13 @@ =========

var env = file.createEnv({
base: 'my/scoped/path'
base: 'my/scoped/path',
dest: 'destination/path' // optionnal
});
```
`file-utils` root module options are inherited by the `Env` instance if not overwritten in the option hash.
The `base` directory will prefix any paths passed to `mkdir`, `recurse`, `read`, `readJSON`, `write`, `delete` methods.
The `dest` directory will prefix the `destination` path provided in the `copy` method. Note that this option is optionnal and will default to the current working directory.
If [options (`logger`, `write`, etc)](#setting-options---fileoption-name--value-) are not passed, each `Env` instance inherit those of its parent.
Write Filters

@@ -31,2 +58,4 @@ ---------

They're used to modifiy the content or the filepath of a file.
#### Add a write filter - `env.registerWriteFilter( name, filter )`

@@ -57,23 +86,53 @@

#### Async filter
File API
=========
The filter can also be asynchronous. This is done by calling `this.async()` and passing the return value to the callback provided.
Upcoming. Meanwhile, check [Grunt.file documentation](http://gruntjs.com/api/grunt.file).
```javascript
env.registerWriteFilter( 'coffee', function( file ) {
var done = this.async();
#### Setting options - `file.option( name, [ value ])`
// some process
setTimeout(function() {
done({ path: '/newfile', contents: 'filtered content' });
}, 1000);
});
```
**Caution:** Using an asynchronous filter will change the way write and copy method are called to. This will make both of those method to run asynchronously too.
Validation Filters
----------
Validation filters are applied on `env.write` and `env.copy`.
They're used to allow or disallow the write action.
#### Add a validation filter - `env.registerValidationFilter( name, filter )`
**options**
- `name` (String): The name under which registering the filter
- `filter` (Function): The filter function
The filter function take a file object as parameter. This file object is a hash containing a `path` and a `contents` property.
Return `true` to allow the file to be written. Return `false` or an error message `String` to disallow the write action.
```javascript
env.registerWriteFilter( 'checkConflicts', function( toOutput ) {
if ( file.exist(toOutput.path) ) {
return 'file is already present';
}
return true;
});
```
// Set option
file.option('write', false);
// Get option
file.option('write');
Just like the write filters, [this filter can be asynchronous](#async-filter).
#### Remove a write filter - `env.removeValidationFilter( name )`
```javascript
env.removeValidationFilter('checkConflicts');
```
**Available Options**
- `write` (Boolean): If write is set to `false`, then no file will be written or deleted. Useful for test run without side effets.
- `logger` (Logger object): Used internally to log information to the console. **API still work in progress**
- `encoding` (String): Defaults `utf8`. Set the default encoding used for reading/writing. Note most methods allow you to overwridde it for a single run.
- `force` (Boolean): `force: true` Force the deletion of folders and file outside the utility scope (or CWD if no scope).

@@ -84,3 +143,1 @@ Todos

- Real Logging system
- Scoping the destination when copying
- Async filtering (?)

@@ -64,2 +64,17 @@ 'use strict';

},
'copy with dest': function(test) {
test.expect(1);
var dest = path.join(tmpdir.path, '/copy');
var env = file.createEnv({
base: path.join(__dirname, 'fixtures'),
dest: dest
});
env.copy('utf8.txt', 'utf-8.txt');
var initial = this.fixtures.read('utf8.txt');
var copied = fs.readFileSync(dest + '/utf-8.txt', 'utf8');
test.strictEqual(initial, copied, 'File should be copied from the root dir to the dest dir');
test.done();
},
'delete': function(test) {

@@ -108,11 +123,10 @@ test.expect(3);

exports['Env() write filters'] = {
exports['Env() filters'] = {
'setUp': function(done) {
this.env = file.createEnv();
this.env = file.createEnv({ base: tmpdir.path });
done();
},
'.registerWriteFilter() and apply output': function(test) {
'.registerWriteFilter() synchronous and apply output': function(test) {
test.expect(3);
var env = file.createEnv({ base: tmpdir.path });
env.registerWriteFilter('tmp', function(file) {
this.env.registerWriteFilter('tmp', function(file) {
test.equal(file.path, 'foo');

@@ -122,4 +136,4 @@ test.equal(file.contents, 'bar');

});
env.write('foo', 'bar');
var written = env.read('simple-filter');
this.env.write('foo', 'bar');
var written = this.env.read('simple-filter');
test.equal(written, 'test', 'should have written the filtered file and path');

@@ -130,4 +144,3 @@ test.done();

test.expect(4);
var env = file.createEnv({ base: tmpdir.path });
env.registerWriteFilter('1', function(file) {
this.env.registerWriteFilter('1', function(file) {
test.equal(file.path, 'foo');

@@ -137,3 +150,3 @@ test.equal(file.contents, 'bar');

});
env.registerWriteFilter('2', function(file) {
this.env.registerWriteFilter('2', function(file) {
test.equal(file.path, 'piped-filter');

@@ -143,3 +156,3 @@ test.equal(file.contents, 'test');

});
env.write('foo', 'bar');
this.env.write('foo', 'bar');
test.done();

@@ -149,13 +162,92 @@ },

test.expect(1);
var env = file.createEnv({ base: tmpdir.path });
env.registerWriteFilter('broke', function(file) {
this.env.registerWriteFilter('broke', function(file) {
test.ok(false);
return { path: 'broke', contents: 'broke' };
});
env.removeWriteFilter('broke');
env.write('no-filter', 'bar');
var written = env.read('no-filter');
this.env.removeWriteFilter('broke');
this.env.write('no-filter', 'bar');
var written = this.env.read('no-filter');
test.equal(written, 'bar', 'should have removed the filter');
test.done();
},
'Async write filter': function(test) {
test.expect(2);
this.env._actualWrite = function(filepath, contents) {
test.equal(filepath, 'async-write');
test.equal(contents, 'puts async');
test.done();
};
this.env.registerWriteFilter('async', function() {
var done = this.async();
setTimeout(function() {
done({ path: 'async-write', contents: 'puts async' });
}, 10);
});
this.env.write('foo', 'bar');
},
'.registerValidationFilter': {
'setUp': function(done) {
var self = this;
this.env.option('logger', _.extend({}, require('../lib/logger'), {
write: function(msg) {
self.errMsg = msg;
}
}));
done();
},
'passing validation': function(test) {
test.expect(3);
this.env.registerValidationFilter('tmp', function(file) {
test.equal(file.path, 'foo');
test.equal(file.contents, 'bar');
return true;
});
this.env.write('foo', 'bar');
var written = this.env.read('simple-filter');
test.equal(written, 'test', 'should have written the filtered file and path');
test.done();
},
'failing validation': function(test) {
test.expect(2);
this.env.registerValidationFilter('tmp', function(file) { return false; });
this.env.write('failing-filter', 'bar');
test.ok(!file.exists(this.env.fromBase('failing-filter')), 'should have written the filtered file and path');
test.equal(this.errMsg, 'Not actually writing to failing-filter haven\'t pass validation', 'default error message is log');
test.done();
},
'failing validation and custom error message': function(test) {
test.expect(2);
this.env.registerValidationFilter('tmp', function(file) { return 'a bad error'; });
this.env.write('failing-filter', 'bar');
test.ok(!file.exists(this.env.fromBase('failing-filter')), 'should not have written');
test.equal(this.errMsg, 'a bad error', 'custom error message is log');
test.done();
},
'async validator': function(test) {
var self = this;
test.expect(1);
this.env._actualWrite = function() { test.ok(false, 'should not be call') };
this.env.registerValidationFilter('tmp', function(file) {
var done = this.async();
setTimeout(function() {
done('a bad error');
test.equal(self.errMsg, 'a bad error', 'custom error message is log');
test.done();
}, 10);
});
this.env.write('async-failing-filter', 'bar');
}
},
'.removeValidationFilter': function(test) {
test.expect(1);
this.env.registerValidationFilter('no-run', function() {
test.ok(false, 'shouldn\'t run the filter');
});
this.env.removeValidationFilter('no-run');
this.env.write('removed-validation', 'bar');
test.ok(true);
test.done();
}
};

@@ -0,0 +0,0 @@ 'use strict';

@@ -0,0 +0,0 @@

@@ -0,0 +0,0 @@

@@ -0,0 +0,0 @@

@@ -0,0 +0,0 @@ module.exports = function(grunt) {

A��o � isso a�

@@ -0,0 +0,0 @@ // This is a test fixture for a case where spawn receives incomplete

@@ -0,0 +0,0 @@

@@ -0,0 +0,0 @@ {

@@ -0,0 +0,0 @@ {

Ação é isso aí

@@ -0,0 +0,0 @@ exports.assertTextEqual = function (value, expected, test, msg) {

@@ -0,0 +0,0 @@ 'use strict';

@@ -0,0 +0,0 @@ 'use strict';

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc