Browse Source

initial commit

master
Guillermo Rauch 9 years ago
commit
f2693e19d7
  1. 11
      .eslintrc.json
  2. 1
      .gitignore
  3. 43
      Readme.md
  4. 45
      bin/now
  5. 56
      build/bin/now
  6. 347
      build/lib/get-files.js
  7. 100
      build/lib/hash.js
  8. 6
      build/lib/ignored.js
  9. 88
      build/lib/index.js
  10. 22
      build/lib/test.js
  11. 57
      gulpfile.js
  12. 138
      lib/get-files.js
  13. 30
      lib/hash.js
  14. 24
      lib/ignored.js
  15. 27
      lib/index.js
  16. 20
      lib/test.js
  17. 63
      package.json
  18. 0
      test/_fixtures/files-in-package-ignore/.a.swp
  19. 2
      test/_fixtures/files-in-package-ignore/.npmignore
  20. 0
      test/_fixtures/files-in-package-ignore/README
  21. 0
      test/_fixtures/files-in-package-ignore/build/a/b/c/d.js
  22. 0
      test/_fixtures/files-in-package-ignore/build/a/e.js
  23. 0
      test/_fixtures/files-in-package-ignore/build/a/should-be-excluded.js
  24. 5
      test/_fixtures/files-in-package-ignore/package.json
  25. 0
      test/_fixtures/files-in-package-ignore/should-be-excluded.js
  26. 0
      test/_fixtures/files-in-package/.DS_Store
  27. 0
      test/_fixtures/files-in-package/build/a/b/c/d.js
  28. 0
      test/_fixtures/files-in-package/build/a/e.js
  29. 5
      test/_fixtures/files-in-package/package.json
  30. 0
      test/_fixtures/files-in-package/should-be-excluded.js
  31. BIN
      test/_fixtures/hashes/dei.png
  32. 1
      test/_fixtures/hashes/index.js
  33. 6
      test/_fixtures/hashes/package.json
  34. 0
      test/_fixtures/simple-main/build/a.js
  35. 0
      test/_fixtures/simple-main/index.js
  36. 8
      test/_fixtures/simple-main/package.json
  37. 0
      test/_fixtures/simple/bin/test
  38. 0
      test/_fixtures/simple/index.js
  39. 0
      test/_fixtures/simple/lib/woot
  40. 0
      test/_fixtures/simple/lib/woot.jsx
  41. 3
      test/_fixtures/simple/package.json
  42. 56
      test/index.js

11
.eslintrc.json

@ -0,0 +1,11 @@
{
"extends": "standard",
"parser": "babel-eslint",
"rules": {
"yoda": 0,
"semi": [2, "always"],
"no-extra-semi": 2,
"semi-spacing": [2, { "before": false, "after": true }],
"no-shadow": [2, {"builtinGlobals": true, "hoist": "functions"}]
}
}

1
.gitignore

@ -0,0 +1 @@
node_modules

43
Readme.md

@ -0,0 +1,43 @@
# now
The fastest way to deploy a Node.JS service.
## How it works
In any directory with a `package.json`, run:
```bash
$ now
> https://some-code-nd23n2.now.sh
```
every time you run it, you get a new URL (unless nothing's changed).
### Conventions
- Only files that would be included in `npm publish` are synchronized.
- `package.json` `files`, `.npmignore`, `.gitignore` supported
- `package.json` must contain a `start` task inside `scripts`.
If a `now` script is defined, that's used instead.
- Your HTTP server is expected to run in port `3000`.
## Installing
```bash
$ npm install -g now
> Enter your email: rauchg@gmail.com.
> We sent an email. Click to log in.
```
## Features
- No reliance on Git.
- File de-duping.
- Respects NPM conventions.
## Options
```
-d Debug mode. Lists all files to be uploaded.
-f Force. Creates a new URL even if nothing has changed.
```

45
bin/now

@ -0,0 +1,45 @@
#!/usr/bin/env node
import program from 'commander';
import { resolve } from 'path';
import { copy } from 'copy-paste';
import now from '../lib';
import ms from 'ms';
program
.option('-d, --debug', 'Debug mode [off]', false)
.option('-C, --no-clipboard', 'Do not attempt to copy URL to clipboard')
.parse(process.argv);
let path = program.args[program.args.length - 1];
if (path) {
if ('/' !== path[0]) {
path = resolve(process.cwd(), path);
}
} else {
path = process.cwd();
}
const debug = !!program.debug;
const clipboard = !program.noClipboard;
const start = Date.now();
console.log(`> Deploying ${path}`);
now(path, { debug }).then((url) => {
const elapsed = ms(new Date() - start);
if (clipboard) {
copy(url, (err) => {
console.log(`> ${url} ${!err ? '(copied to clipboard)' : ''} [${elapsed}]`);
});
} else {
console.log(`> ${url} [${elapsed}]`);
}
}, (err) => {
error(err.message);
process.exit(1);
});
function error (err) {
console.error(`> \u001b[31mError!\u001b[39m ${err}.`);
}

56
build/bin/now

@ -0,0 +1,56 @@
#!/usr/bin/env node
'use strict';
var _commander = require('commander');
var _commander2 = _interopRequireDefault(_commander);
var _path = require('path');
var _copyPaste = require('copy-paste');
var _lib = require('../lib');
var _lib2 = _interopRequireDefault(_lib);
var _ms = require('ms');
var _ms2 = _interopRequireDefault(_ms);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_commander2.default.option('-d, --debug', 'Debug mode [off]', false).option('-C, --no-clipboard', 'Do not attempt to copy URL to clipboard').parse(process.argv);
var path = _commander2.default.args[_commander2.default.args.length - 1];
if (path) {
if ('/' !== path[0]) {
path = (0, _path.resolve)(process.cwd(), path);
}
} else {
path = process.cwd();
}
var debug = !!_commander2.default.debug;
var clipboard = !_commander2.default.noClipboard;
var start = Date.now();
console.log('> Deploying ' + path);
(0, _lib2.default)(path, { debug: debug }).then(function (url) {
var elapsed = (0, _ms2.default)(new Date() - start);
if (clipboard) {
(0, _copyPaste.copy)(url, function (err) {
console.log('> ' + url + ' ' + (!err ? '(copied to clipboard)' : '') + ' [' + elapsed + ']');
});
} else {
console.log('> ' + url + ' [' + elapsed + ']');
}
}, function (err) {
error(err.message);
process.exit(1);
});
function error(err) {
console.error('> \u001b[31mError!\u001b[39m ' + err + '.');
}

347
build/lib/get-files.js

@ -0,0 +1,347 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _fsPromise = require('fs-promise');
var _fsPromise2 = _interopRequireDefault(_fsPromise);
var _path = require('path');
var _arrFlatten = require('arr-flatten');
var _arrFlatten2 = _interopRequireDefault(_arrFlatten);
var _arrayUnique = require('array-unique');
var _arrayUnique2 = _interopRequireDefault(_arrayUnique);
var _minimatch = require('minimatch');
var _minimatch2 = _interopRequireDefault(_minimatch);
var _ignored = require('./ignored');
var _ignored2 = _interopRequireDefault(_ignored);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Returns a list of files in the given
* directory that are subject to be
* synchronized.
*
* @param {String} full path to directory
* @return {Array} comprehensive list of paths to sync
*/
exports.default = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(path) {
var pkgData, pkg, search, found, npmIgnore, gitIgnore, ignored;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return (0, _fsPromise.readFile)((0, _path.resolve)(path, 'package.json'), 'utf8');
case 2:
pkgData = _context.sent;
pkg = JSON.parse(pkgData);
search = (pkg.files || ['.']).concat('package.json');
if (pkg.main) search = search.concat(pkg.main);
search = search.map(function (file) {
return asAbsolute(file, path);
});
_context.next = 9;
return explode(search);
case 9:
_context.t0 = _context.sent;
found = (0, _arrayUnique2.default)(_context.t0);
_context.next = 13;
return maybeRead((0, _path.resolve)(path, '.npmignore'));
case 13:
npmIgnore = _context.sent;
if (!npmIgnore) {
_context.next = 18;
break;
}
_context.t1 = '';
_context.next = 21;
break;
case 18:
_context.next = 20;
return maybeRead((0, _path.resolve)(path, '.gitignore'));
case 20:
_context.t1 = _context.sent;
case 21:
gitIgnore = _context.t1;
ignored = (0, _arrayUnique2.default)(_ignored2.default.concat(gitIgnore.split('\n').filter(invalidFilter)).concat(npmIgnore.split('\n').filter(invalidFilter))).map(function (file) {
return (0, _path.resolve)(path, file);
});
return _context.abrupt('return', found.filter(ignoredFilter(ignored)));
case 24:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
return function getFiles(_x) {
return ref.apply(this, arguments);
};
}();
/**
* Returns a filter function that
* excludes ignored files in the path.
*
* @param {String} path
* @return {Function} filter fn
*/
var ignoredFilter = function ignoredFilter(ignored) {
return function (file) {
return !ignored.some(function (test) {
return (0, _minimatch2.default)(file, test);
});
};
};
/**
* Returns a filter function that
* excludes invalid rules for .*ignore files
*
* @param {String} path
* @return {Function} filter fn
*/
var invalidFilter = function invalidFilter(path) {
return !(
/* commments */
'#' === path[0] ||
/* empty lines or newlines */
!path.trim().length);
};
/**
* Transform relative paths into absolutes,
* and maintains absolutes as such.
*
* @param {String} maybe relative path
* @param {String} parent full path
*/
var asAbsolute = function asAbsolute(path, parent) {
if ('/' === path[0]) return path;
return (0, _path.resolve)(parent, path);
};
/**
* Explodes directories into a full list of files.
* Eg:
* in: ['/a.js', '/b']
* out: ['/a.js', '/b/c.js', '/b/d.js']
*
* @param {Array} of {String}s representing paths
* @return {Array} of {String}s of full paths
*/
var explode = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee5(paths) {
var _this2 = this;
var many, list;
return _regenerator2.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
many = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee3(all) {
return _regenerator2.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
_context3.next = 2;
return _promise2.default.all(all.map(function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(file) {
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return list(file);
case 2:
return _context2.abrupt('return', _context2.sent);
case 3:
case 'end':
return _context2.stop();
}
}
}, _callee2, _this2);
})),
_this = _this2;
return function (_x4) {
return ref.apply(_this, arguments);
};
}()));
case 2:
return _context3.abrupt('return', _context3.sent);
case 3:
case 'end':
return _context3.stop();
}
}
}, _callee3, _this2);
})),
_this = _this2;
return function many(_x3) {
return ref.apply(_this, arguments);
};
}();
list = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee4(file) {
var path, stat, all;
return _regenerator2.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
path = file;
stat = undefined;
_context4.prev = 2;
_context4.next = 5;
return _fsPromise2.default.stat(path);
case 5:
stat = _context4.sent;
_context4.next = 14;
break;
case 8:
_context4.prev = 8;
_context4.t0 = _context4['catch'](2);
// in case the file comes from `files` or `main`
// and it wasn't specified with `.js` by the user
path = file + '.js';
_context4.next = 13;
return _fsPromise2.default.stat(path);
case 13:
stat = _context4.sent;
case 14:
if (!stat.isDirectory()) {
_context4.next = 21;
break;
}
_context4.next = 17;
return _fsPromise2.default.readdir(file);
case 17:
all = _context4.sent;
return _context4.abrupt('return', many(all.map(function (subdir) {
return asAbsolute(subdir, file);
})));
case 21:
return _context4.abrupt('return', path);
case 22:
case 'end':
return _context4.stop();
}
}
}, _callee4, _this2, [[2, 8]]);
})),
_this = _this2;
return function list(_x5) {
return ref.apply(_this, arguments);
};
}();
_context5.next = 4;
return many(paths);
case 4:
_context5.t0 = _context5.sent;
return _context5.abrupt('return', (0, _arrFlatten2.default)(_context5.t0));
case 6:
case 'end':
return _context5.stop();
}
}
}, _callee5, this);
}));
return function explode(_x2) {
return ref.apply(this, arguments);
};
}();
/**
* Returns the contents of a file if it exists.
*
* @return {String} results or `''`
*/
var maybeRead = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee6(path) {
return _regenerator2.default.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
_context6.prev = 0;
_context6.next = 3;
return _fsPromise2.default.readFile(path, 'utf8');
case 3:
return _context6.abrupt('return', _context6.sent);
case 6:
_context6.prev = 6;
_context6.t0 = _context6['catch'](0);
return _context6.abrupt('return', '');
case 9:
case 'end':
return _context6.stop();
}
}
}, _callee6, this, [[0, 6]]);
}));
return function maybeRead(_x6) {
return ref.apply(this, arguments);
};
}();

100
build/lib/hash.js

@ -0,0 +1,100 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _map = require('babel-runtime/core-js/map');
var _map2 = _interopRequireDefault(_map);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _fsPromise = require('fs-promise');
var _fsPromise2 = _interopRequireDefault(_fsPromise);
var _crypto = require('crypto');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Computes hashes for the contents of each file given.
*
* @param {Array} of {String} full paths
* @return {Map}
*/
exports.default = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(files) {
var _this2 = this;
var entries;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return _promise2.default.all(files.map(function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(file) {
var data;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _fsPromise2.default.readFile(file);
case 2:
data = _context.sent;
return _context.abrupt('return', [file, hash(data)]);
case 4:
case 'end':
return _context.stop();
}
}
}, _callee, _this2);
})),
_this = _this2;
return function (_x2) {
return ref.apply(_this, arguments);
};
}()));
case 2:
entries = _context2.sent;
return _context2.abrupt('return', new _map2.default(entries));
case 4:
case 'end':
return _context2.stop();
}
}
}, _callee2, this);
}));
return function hashes(_x) {
return ref.apply(this, arguments);
};
}();
/**
* Computes a hash for the given buf.
*
* @param {Buffer} file data
* @return {String} hex digest
*/
function hash(buf) {
return (0, _crypto.createHash)('sha1').update(buf).digest('hex');
}

6
build/lib/ignored.js

@ -0,0 +1,6 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = ['._*', '.hg', '.git', '.svn', '.npmrc', '.*.swp', '.DS_Store', '.wafpickle-*', '.lock-wscript', 'npm-debug.log', 'config.gypi', 'node_modules', 'CVS', 'README', 'README.*', 'CHANGELOG', 'History.md', 'LICENSE', 'Readme', 'Readme.*', 'test', 'tests'];

88
build/lib/index.js

@ -0,0 +1,88 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _fsPromise = require('fs-promise');
var _fsPromise2 = _interopRequireDefault(_fsPromise);
var _getFiles = require('./get-files');
var _getFiles2 = _interopRequireDefault(_getFiles);
var _hash = require('./hash');
var _hash2 = _interopRequireDefault(_hash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = function () {
var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(path, _ref) {
var debug = _ref.debug;
var files, hashes;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return _fsPromise2.default.stat(path);
case 3:
_context.next = 8;
break;
case 5:
_context.prev = 5;
_context.t0 = _context['catch'](0);
throw new Error('Could not read directory ' + path + '.');
case 8:
if (debug) console.time('> [debug] Getting files');
_context.next = 11;
return (0, _getFiles2.default)(path);
case 11:
files = _context.sent;
if (debug) console.timeEnd('> [debug] Getting files');
if (debug) console.time('> [debug] Computing hashes');
_context.next = 16;
return (0, _hash2.default)(files);
case 16:
hashes = _context.sent;
if (debug) console.timeEnd('> [debug] Computing hashes');
if (debug) {
hashes.forEach(function (val, key) {
console.log('> [debug] Found "' + key + '" [' + val + ']');
});
}
return _context.abrupt('return', 'https://test.now.run');
case 20:
case 'end':
return _context.stop();
}
}
}, _callee, this, [[0, 5]]);
}));
return function now(_x, _x2) {
return ref.apply(this, arguments);
};
}();

22
build/lib/test.js

@ -0,0 +1,22 @@
'use strict';
var _getFiles = require('./get-files');
var _getFiles2 = _interopRequireDefault(_getFiles);
var _path = require('path');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(0, _getFiles2.default)((0, _path.resolve)('../mng-test/files-in-package')).then(function (files) {
console.log(files);
(0, _getFiles2.default)((0, _path.resolve)('../mng-test/files-in-package-ignore')).then(function (files2) {
console.log('ignored: ');
console.log(files2);
}).catch(function (err) {
console.log(err.stack);
});
}).catch(function (err) {
console.log(err.stack);
});

57
gulpfile.js

@ -0,0 +1,57 @@
const gulp = require('gulp');
const del = require('del');
const ext = require('gulp-ext');
const babel = require('gulp-babel');
const eslint = require('gulp-eslint');
const help = require('gulp-task-listing');
gulp.task('help', help);
gulp.task('compile', [
'compile-lib',
'compile-bin'
]);
gulp.task('compile-lib', function () {
return gulp.src('lib/**/*.js')
.pipe(babel({
presets: ['es2015'],
plugins: [
'syntax-async-functions',
'transform-async-to-generator',
'transform-runtime'
]
}))
.pipe(gulp.dest('build/lib'));
});
gulp.task('compile-bin', function () {
return gulp.src('bin/*')
.pipe(babel({
presets: ['es2015'],
plugins: [
'syntax-async-functions',
'transform-async-to-generator',
'transform-runtime'
]
}))
.pipe(ext.crop())
.pipe(gulp.dest('build/bin'));
});
gulp.task('lint', function () {
return gulp.src([
'gulpfile.js',
'lib/**/*.js',
'bin/*'
])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});
gulp.task('clean', function () {
return del(['build']);
});
gulp.task('default', ['lint', 'compile']);

138
lib/get-files.js

@ -0,0 +1,138 @@
import fs, { readFile as read } from 'fs-promise';
import { resolve } from 'path';
import flatten from 'arr-flatten';
import unique from 'array-unique';
import minimatch from 'minimatch';
import IGNORED from './ignored';
/**
* Returns a list of files in the given
* directory that are subject to be
* synchronized.
*
* @param {String} full path to directory
* @return {Array} comprehensive list of paths to sync
*/
export default async function getFiles (path) {
const pkgData = await read(resolve(path, 'package.json'), 'utf8');
const pkg = JSON.parse(pkgData);
let search = (pkg.files || ['.']).concat('package.json');
if (pkg.main) search = search.concat(pkg.main);
search = search.map((file) => asAbsolute(file, path));
const found = unique((await explode(search)));
const npmIgnore = await maybeRead(resolve(path, '.npmignore'));
const gitIgnore = npmIgnore
? ''
: (await maybeRead(resolve(path, '.gitignore')));
const ignored = unique(IGNORED
.concat(gitIgnore.split('\n').filter(invalidFilter))
.concat(npmIgnore.split('\n').filter(invalidFilter)))
.map(file => resolve(path, file));
return found.filter(ignoredFilter(ignored));
}
/**
* Returns a filter function that
* excludes ignored files in the path.
*
* @param {String} path
* @return {Function} filter fn
*/
const ignoredFilter = (ignored) => (file) => {
return !ignored.some((test) => {
return minimatch(file, test);
});
};
/**
* Returns a filter function that
* excludes invalid rules for .*ignore files
*
* @param {String} path
* @return {Function} filter fn
*/
const invalidFilter = (path) => {
return !(
/* commments */
'#' === path[0] ||
/* empty lines or newlines */
!path.trim().length
);
};
/**
* Transform relative paths into absolutes,
* and maintains absolutes as such.
*
* @param {String} maybe relative path
* @param {String} parent full path
*/
const asAbsolute = function (path, parent) {
if ('/' === path[0]) return path;
return resolve(parent, path);
};
/**
* Explodes directories into a full list of files.
* Eg:
* in: ['/a.js', '/b']
* out: ['/a.js', '/b/c.js', '/b/d.js']
*
* @param {Array} of {String}s representing paths
* @return {Array} of {String}s of full paths
*/
const explode = async function (paths) {
const many = async (all) => {
return await Promise.all(all.map(async (file) => {
return await list(file);
}));
};
const list = async (file) => {
let path = file;
let stat;
try {
stat = await fs.stat(path);
} catch (e) {
// in case the file comes from `files` or `main`
// and it wasn't specified with `.js` by the user
path = file + '.js';
stat = await fs.stat(path);
}
if (stat.isDirectory()) {
const all = await fs.readdir(file);
return many(all.map(subdir => asAbsolute(subdir, file)));
} else {
return path;
}
};
return flatten((await many(paths)));
};
/**
* Returns the contents of a file if it exists.
*
* @return {String} results or `''`
*/
const maybeRead = async function (path) {
try {
return (await fs.readFile(path, 'utf8'));
} catch (e) {
return '';
}
};

30
lib/hash.js

@ -0,0 +1,30 @@
import fs from 'fs-promise';
import { createHash } from 'crypto';
/**
* Computes hashes for the contents of each file given.
*
* @param {Array} of {String} full paths
* @return {Map}
*/
export default async function hashes (files) {
const entries = await Promise.all(files.map(async (file) => {
const data = await fs.readFile(file);
return [file, hash(data)];
}));
return new Map(entries);
}
/**
* Computes a hash for the given buf.
*
* @param {Buffer} file data
* @return {String} hex digest
*/
function hash (buf) {
return createHash('sha1')
.update(buf)
.digest('hex');
}

24
lib/ignored.js

@ -0,0 +1,24 @@
export default [
'._*',
'.hg',
'.git',
'.svn',
'.npmrc',
'.*.swp',
'.DS_Store',
'.wafpickle-*',
'.lock-wscript',
'npm-debug.log',
'config.gypi',
'node_modules',
'CVS',
'README',
'README.*',
'CHANGELOG',
'History.md',
'LICENSE',
'Readme',
'Readme.*',
'test',
'tests'
];

27
lib/index.js

@ -0,0 +1,27 @@
import fs from 'fs-promise';
import getFiles from './get-files';
import hash from './hash';
export default async function now (path, { debug }) {
try {
await fs.stat(path);
} catch (err) {
throw new Error(`Could not read directory ${path}.`);
}
if (debug) console.time('> [debug] Getting files');
const files = await getFiles(path);
if (debug) console.timeEnd('> [debug] Getting files');
if (debug) console.time('> [debug] Computing hashes');
const hashes = await hash(files);
if (debug) console.timeEnd('> [debug] Computing hashes');
if (debug) {
hashes.forEach((val, key) => {
console.log(`> [debug] Found "${key}" [${val}]`);
});
}
return 'https://test.now.run';
}

20
lib/test.js

@ -0,0 +1,20 @@
import getFiles from './get-files';
import { resolve } from 'path';
getFiles(resolve('../mng-test/files-in-package'))
.then(files => {
console.log(files);
getFiles(resolve('../mng-test/files-in-package-ignore'))
.then(files2 => {
console.log('ignored: ');
console.log(files2);
})
.catch(err => {
console.log(err.stack);
});
})
.catch(err => {
console.log(err.stack);
});

63
package.json

@ -0,0 +1,63 @@
{
"name": "now",
"private": true,
"version": "0.0.1",
"description": "",
"main": "./build/lib/index",
"files": [
"build/lib",
"build/bin"
],
"bin": {
"now": "./build/bin/now"
},
"dependencies": {
"arr-flatten": "1.0.1",
"array-unique": "0.2.1",
"commander": "2.9.0",
"copy-paste": "1.1.4",
"fs-promise": "0.4.1",
"http2": "3.3.2",
"ms": "0.7.1"
},
"devDependencies": {
"alpha-sort": "1.0.2",
"ava": "0.12.0",
"babel-eslint": "4.1.8",
"babel-plugin-syntax-async-functions": "6.3.13",
"babel-plugin-transform-async-to-generator": "6.4.6",
"babel-preset-es2015": "6.3.13",
"babel-register": "6.5.2",
"del": "2.2.0",
"eslint-config-standard": "4.4.0",
"eslint-plugin-standard": "1.3.1",
"gulp": "3.9.0",
"gulp-babel": "6.1.2",
"gulp-eslint": "1.1.1",
"gulp-ext": "1.0.0",
"gulp-task-listing": "1.0.1"
},
"scripts": {
"gulp": "gulp",
"test": "ava"
},
"babel": {
"presets": [
"es2015"
],
"plugins": [
"transform-runtime",
"syntax-async-functions",
"transform-async-to-generator"
]
},
"ava": {
"failFast": true,
"files": [
"test/*.js"
],
"require": [
"babel-register"
]
}
}

0
test/_fixtures/files-in-package-ignore/.a.swp

2
test/_fixtures/files-in-package-ignore/.npmignore

@ -0,0 +1,2 @@
should-be-excluded.js
./build/a/should-be-excluded.js

0
test/_fixtures/files-in-package-ignore/README

0
test/_fixtures/files-in-package-ignore/build/a/b/c/d.js

0
test/_fixtures/files-in-package-ignore/build/a/e.js

0
test/_fixtures/files-in-package-ignore/build/a/should-be-excluded.js

5
test/_fixtures/files-in-package-ignore/package.json

@ -0,0 +1,5 @@
{
"files": [
"build"
]
}

0
test/_fixtures/files-in-package-ignore/should-be-excluded.js

0
test/_fixtures/files-in-package/.DS_Store

0
test/_fixtures/files-in-package/build/a/b/c/d.js

0
test/_fixtures/files-in-package/build/a/e.js

5
test/_fixtures/files-in-package/package.json

@ -0,0 +1,5 @@
{
"files": [
"build"
]
}

0
test/_fixtures/files-in-package/should-be-excluded.js

BIN
test/_fixtures/hashes/dei.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

1
test/_fixtures/hashes/index.js

@ -0,0 +1 @@
woot

6
test/_fixtures/hashes/package.json

@ -0,0 +1,6 @@
{
"name": "hashes",
"version": "0.0.1",
"description": "",
"dependencies": {}
}

0
test/_fixtures/simple-main/build/a.js

0
test/_fixtures/simple-main/index.js

8
test/_fixtures/simple-main/package.json

@ -0,0 +1,8 @@
{
"name": "simple-with-main",
"version": "0.0.1",
"description": "",
"main": "./index.js",
"files": ["build/a.js"],
"dependencies": {}
}

0
test/_fixtures/simple/bin/test

0
test/_fixtures/simple/index.js

0
test/_fixtures/simple/lib/woot

0
test/_fixtures/simple/lib/woot.jsx

3
test/_fixtures/simple/package.json

@ -0,0 +1,3 @@
{
"name": "simple"
}

56
test/index.js

@ -0,0 +1,56 @@
import test from 'ava';
import { join, resolve } from 'path';
import getFiles from '../lib/get-files';
import hash from '../lib/hash';
import { asc as alpha } from 'alpha-sort';
const prefix = join(__dirname, '_fixtures') + '/';
const base = (path) => path.replace(prefix, '');
const fixture = (name) => resolve(`./_fixtures/${name}`);
test('`files` + README', async t => {
let files = await getFiles(fixture('files-in-package'));
t.same(files.length, 3);
files = files.sort(alpha);
t.same(base(files[0]), 'files-in-package/build/a/b/c/d.js');
t.same(base(files[1]), 'files-in-package/build/a/e.js');
t.same(base(files[2]), 'files-in-package/package.json');
});
test('`files` + README + `.*.swp` + `.npmignore`', async t => {
let files = await getFiles(fixture('files-in-package-ignore'));
t.same(files.length, 3);
files = files.sort(alpha);
t.same(base(files[0]), 'files-in-package-ignore/build/a/b/c/d.js');
t.same(base(files[1]), 'files-in-package-ignore/build/a/e.js');
t.same(base(files[2]), 'files-in-package-ignore/package.json');
});
test('simple', async t => {
let files = await getFiles(fixture('simple'));
t.same(files.length, 5);
files = files.sort(alpha);
t.same(base(files[0]), 'simple/bin/test');
t.same(base(files[1]), 'simple/index.js');
t.same(base(files[2]), 'simple/lib/woot');
t.same(base(files[3]), 'simple/lib/woot.jsx');
t.same(base(files[4]), 'simple/package.json');
});
test('simple with main', async t => {
let files = await getFiles(fixture('simple-main'));
t.same(files.length, 3);
files = files.sort(alpha);
t.same(base(files[0]), 'simple-main/build/a.js');
t.same(base(files[1]), 'simple-main/index.js');
t.same(base(files[2]), 'simple-main/package.json');
});
test('hashes', async t => {
const files = await getFiles(fixture('hashes'));
const hashes = await hash(files);
t.same(hashes.size, 3);
t.same(hashes.get(prefix + 'hashes/dei.png'), '277c55a2042910b9fe706ad00859e008c1b7d172');
t.same(hashes.get(prefix + 'hashes/index.js'), '56c00d0466fc6bdd41b13dac5fc920cc30a63b45');
t.same(hashes.get(prefix + 'hashes/package.json'), '706214f42ae940a01d2aa60c5e32408f4d2127dd');
});
Loading…
Cancel
Save