A light weight set of handy tools for real world program.
Reduce the gap between different systems. Such as watch directory changes on a network file system, operate spawn
on Windows and Linux, handle async IO api with promise, etc.
Rather than help you decide what to do, it is designed to create possibilities. Even this document is generated by nokit itself.
- All async functions will return promise.
- All functions are highly lazy designed, minimum boot time.
- Light weight and self-reference.
As a lib dependency, install it locally: npm i nokit
.
Goto changelog
-
- Overview
- _
- browserHelper(opts)
- brush
- depsCache(info)
- daemonize(opts)
- decrypt(data, password, algorithm)
- drives
- encrypt(data, password, algorithm)
- err(msg, opts)
- errs(args)
- exec(cmd, shell)
- formatComment(comments, opts)
- fs
- fuzzySearch(keys, list, opts)
- genModulePaths(moduleName, dir, modDir)
- indent(text, num, char, reg)
- isDevelopment()
- isProduction()
- jhash
- log(msg, action, opts)
- logs(args)
- monitorApp(opts)
- nodeVersion()
- defaultArgs(args, defaults)
- parseComment(code, opts)
- parseDependency(entryPaths, opts)
- path
- Promise
- proxy
- regexReduce(reg, str, iter, init)
- regexMap(reg, str, iter)
- replace(str, pattern, iter)
- replaceSync(str, pattern, iter)
- require(moduleName, dir, loaded)
- requireOptional(name, dir, semver)
- request(opts)
- semver
- spawn(cmd, args, opts)
- sse
- task(name, opts, fn)
- treeKill(pid, signal, callback)
- url
- warp(from, opts)
- which(name)
- whichSync
- xinspect(obj, opts)
- xopen(cmds, opts)
-
- Overview
- body()
- connect(opts)
- debugJs(opts)
- etag()
- file(opts)
- fileRequest(opts)
- flow
- flowToMid(fn)
- match(pattern, opts)
- midToFlow(h)
- parseUrl(parseQueryString, slashesDenoteHost)
- relayConnect(opts)
- relayClient(opts)
- select(sel, middleware)
- serverHelper(opts)
- static(opts)
- tcpFrame(socket, opts)
- url(opts)
- van(ctx)
noe
is a dev tool to run / watch / reload program automatically. Run noe -h
to see what you
can do with it.
For more help, run: noe -h
.
nos
is a tool to statically serve a folder. Run nos -h
to see what you
can do with it.
For more help, run: nos -h
.
nor
is a cross platform remote tty tool.
For more help, run: nor -h
.
-
Nokit extends all the functions of nofs and
yaku/lib/utils
. You can use it as same as nofs. For more info, see the doc:-
example:
kit.readFile('test.txt', 'utf8').then((str) => console.log(str) ); kit.outputFile('a.txt', 'test') .then(() => kit.log('done')); kit.writeJSON('b.json', { a: 10 }) .then(() => kit.log('done')) kit.mkdirs('b.json', { a: 10 }) .then(() => kit.log('done'));
-
-
The lodash lib.
-
type: { Object }
-
example:
kit._.map([1, 2, 3]);
-
-
The browser helper. It helps you to live reload the page and log remotely.
-
static:
-
param:
opts
{ Object }The options of the client, defaults:
{ host: '', // The host of the event source. useJs: false // By default the function will return html string }
-
return: { String }
The code of client helper.
-
example:
When the client code is loaded on the browser, you can use the
nb.log
to log anything to server's terminal. The server will auto-format and log the information to the terminal. It's convinient for mobile development when remote debug is not possible.// The nb is assigned to the "window" object. nb.log({ a: 10 }); nb.log(10); nb.es.addEventListener('fileModified', () => console.log('file changed') );
-
-
Generate styled string for terminal. It's disabled when
process.env.NODE_ENV == 'production'
.-
example:
let br = kit.require('brush'); kit.log(br.red('error info')); // Disable color globally. br.isEnabled = false; // To see all the available brushes. kit.log(Object.keys(br));
-
-
A fast file cache helper. It uses hard link to cache files.
-
param:
info
{ Object }Not optional.
{ // The first item is the key path, others are // its dependencies. deps: Array, // The path of the output file. // If it's undefined, depsCache will try to get cache. dests: Array, cacheDir: '.nokit' }
-
return: { Promise }
Resolve a info object.
{ isNewer: Boolean, // { path: mtime } deps: Object, // { destPath: cachePath } dests: Object, cacheError: undefined || Error }
-
example:
// Set cache kit.depsCache({ dests: ['index.css'], deps: ['index.less', 'b.less', 'c.less'] }); // Get cache // You don't have to sepecify 'b.less', 'c.less'. kit.depsCache({ deps: ['index.less'] }) .then((cache) => { if (cache.isNewer) { kit.log('cache is newer'); kit.log(cache.dests); } });
-
-
Daemonize a program. Just a shortcut usage of
kit.spawn
.-
param:
opts
{ Object }Defaults:
{ bin: 'node', args: ['app.js'], stdout: 'stdout.log', // Can also be a fd stderr: 'stderr.log' // Can also be a fd }
-
return: { Porcess }
The daemonized process.
-
-
A simple decrypt helper. Cross-version of node.
-
param:
data
{ Any } -
param:
password
{ String | Buffer } -
param:
algorithm
{ String }Default is 'aes128'.
-
return: { Buffer }
-
-
The warp drives. You must
kit.require 'drives'
before using it. For more information goto theDrives
section.- type: { Object }
-
A simple encrypt helper. Cross-version of node.
-
param:
data
{ Any } -
param:
password
{ String | Buffer } -
param:
algorithm
{ String }Default is 'aes128'.
-
return: { Buffer }
-
-
A error log shortcut for
kit.log(msg, 'error', opts)
-
param:
msg
{ Any } -
param:
opts
{ Object }
-
-
Shortcut for logging multiple error infos.
-
param:
args
{ Any }...
-
example:
kit.errs('test1', 'test2', 'test3'); // => [2015-02-07 08:31:49] test1 test2 test3
-
-
A better
child_process.exec
. Supports multi-line shell script. For supporting old version of node, it will create 3 temp files, the temp files will be removed after the execution.-
param:
cmd
{ String }Shell commands.
-
param:
shell
{ String }Shell name. Such as
bash
,zsh
. Optinal. -
return: { Promise }
Resolves when the process's stdio is drained. The resolve value is like:
{ code: 0, signal: null, stdout: 'hello world', stderr: '' }
-
example:
kit.exec(` a='hello world' echo $a `).then(({code, stdout}) => { kit.log code // output => 0 kit.log stdout // output => "hello world" }); // Bash doesn't support "**" recusive match pattern. let p = kit.exec(` echo **/*.css `, 'zsh'); // Get the child process object. p.process.then((proc) => kit.log(proc.pid) );
-
-
Format the parsed comments array to a markdown string.
-
param:
comments
{ Array } -
param:
opts
{ Object }Defaults:
{ indent: 0, name: ({ name }) => String, tag: ({ tagName, name, type }) => String }
-
return: { String }
-
-
See my project nofs.
-
Fuzzy search a string list by a key word.
-
param:
keys
{ String }The key word.
-
param:
list
{ Array }The list of string to search.
-
param:
opts
{ Object }Defaults:
{ result: (wrappedList) => wrappedList.minBy('distance').words, threshold: (cOffset, keyLen, cIndex) => Infinity, notFound: (cOffset, keyLen, cIndex) => Infinity, span: (cOffset, keyLen, cIndex) => cOffset, found: (cOffset, keyLen, cIndex) => (Math.exp(cOffset + 1) - 1) * (keyLen - cIndex), tail: (cOffset, keyLen, cIndex, tailLen) => tailLen }
-
return: { String }
The best matched one. If not found, return undefined.
-
example:
kit.fuzzySearch('hw', ['test', 'hello world', 'hey world']) // output => 'hey world' // To get a sortable weighted list. kit.fuzzySearch('hw', ['test', 'hello world', 'hey world'], { result: (wrappedList) => wrappedList.value() }); // output => [ // { distance: Infinity } // { words: 'hello world', distance: 1110.069 } // { words: 'hey world', distance: 159.849 } // ]
-
-
Generate a list of module paths from a name and a directory.
-
param:
moduleName
{ String }The module name.
-
param:
dir
{ String }The root path. Default is current working dir.
-
param:
modDir
{ String }Default is 'node_modules'.
-
return: { Array }
Paths
-
example:
// Suppose current working directory is '/home/a' kit.genModulePaths('test') // output => ['/home/a/node_modules/test', '/home/node_modules/test', '/node_modules/test']
-
-
Indent a text block.
-
param:
text
{ String } -
param:
num
{ Int } -
param:
char
{ String } -
param:
reg
{ RegExp }Default is
/^/mg
. -
return: { String }
The indented text block.
-
example:
// Increase kit.indent("one\ntwo", 2) // => " one\n two" // Decrease kit.indent("--one\n--two", 0, '', /^--/mg) // => "one\ntwo"
-
-
Nokit use it to check the running mode of the app. Overwrite it if you want to control the check logic. By default it returns the
rocess.env.NODE_ENV == 'development'
.- return: { Boolean }
-
Nokit use it to check the running mode of the app. Overwrite it if you want to control the check logic. By default it returns the
rocess.env.NODE_ENV == 'production'
.- return: { Boolean }
-
A fast helper to hash string or binary file. See my jhash project. You must
kit.require 'jhash'
before using it.-
example:
kit.require('jhash'); kit.jhash.hash('test'); // output => '349o' jhash.hash(kit.readFileSync('a.jpg')); // Control the hash char set. kit.jhash.setSymbols('abcdef'); kit.jhash.hash('test'); // output => 'decfddfe' // Control the max length of the result hash value. Unit is bit. jhash.setMaskLen(10); jhash.hash('test'); // output => 'ede'
-
-
A better log for debugging, it uses the
kit.xinspect
to log.Use terminal command like
logReg='pattern' node app.js
to filter the log info.Use
logTrace='on' node app.js
to force each log end with a stack trace.-
param:
msg
{ Any }Your log message.
-
param:
action
{ String }'log', 'error', 'warn'.
-
param:
opts
{ Object }Default is same with
kit.xinspect
, but with some extra options:{ isShowTime: true, logReg: process.env.logReg && new RegExp(process.env.logReg), logTrace: process.env.logTrace === 'on', // Custom log method log: (str, action) => console[action](str) }
-
example:
kit.log('test'); // => '[2015-02-07 08:31:49] test' kit.log('test', { isShowTime: false }); // => 'test' kit.log('test', { logReg: /a/ }); // => '' kit.log('%s %s %d', ['a', 'b', 10]); // => '[2015-02-07 08:31:49] a b 10'
-
-
Shortcut for logging multiple infos.
-
param:
args
{ Any }...
-
example:
kit.logs('test1', 'test2', 'test3'); // => [2015-02-07 08:31:49] test1 test2 test3
-
-
Monitor an application and automatically restart it when file changed. Even when the monitored app exit with error, the monitor will still wait for your file change to restart the application. Not only nodejs, but also other programs like ruby or python. It will print useful infomation when it application unexceptedly.
-
param:
opts
{ Object }Defaults:
{ bin: 'node', args: ['index.js'], prefix: 'string', // see the `kit.spawn` for details watchList: [], // By default, the same with the "args". isNodeDeps: true, opts: {}, // Same as the opts of 'kit.spawn'. // The option of `kit.parseDependency` parseDependency: {}, // A hook for restarting the program, run the function "start" to // restart. retry: (start) => {}, onStart: => kit.log("Monitor: " + opts.watchList), onRestart: (path) => kit.log("Reload app, modified: " + path), onWatchFiles: (paths) => kit.log('Watching:' + paths.join(', ')), onNormalExit: ({ code, signal }) => kit.log('EXIT' + ` code: ${code} signal: ${signal}`), onErrorExit: ({ code, signal }) => kit.err('EXIT' + ` code: ${code} signal: ${signal}\n` + 'Process closed. Edit and save the watched file to restart.'), }
-
return: { Object }
Properties:
{ // Call it to stop monitor. stop: => {}, // Resolve a list of watch handlers. watchPromise: Promise }
-
example:
kit.monitorApp({ bin: 'coffee', args: ['main.coffee'] }); kit.monitorApp({ bin: 'ruby' args: ['app.rb', 'lib/**/*.rb'] isNodeDeps: false });
-
-
Node version. Such as
v0.10.23
is0.1023
,v0.10.1
is0.1001
.- return: { Float }
-
A helper for arguments type based function override.
-
param:
args
{ Array | Object }The arguments to set.
-
param:
defaults
{ Object }The default argument settings. The key value of the setting is the argument name, the value is an object, and the key is the type of the argument, the value is the default value of the argument.
-
return: { Object }
-
example:
let foo = () => { kit.defaultArgs(arguments, { name: { String: 'A' }, brush: { Array: [] }, family: { String: null }, isReal: { Boolean: false }, fn: { Function: => 'callback' } }); }; kit.log(foo('test', false, ['red'], -> 'nothing')); // Here the logged value will deeply equal: { name: 'test', brush: ['red'], family: null, fn: => 'nothing' }
-
-
A comments parser for javascript and coffee-script. Used to generate documentation from source code automatically. It will traverse through all the comments of a coffee file.
-
param:
code
{ String }Coffee source code.
-
param:
opts
{ Object }Parser options:
{ commentReg: RegExp, splitReg: RegExp, tagNameReg: RegExp, typeReg: RegExp, nameReg: RegExp, nameTags: ['param', 'property'], descriptionReg: RegExp }
-
return: { Array }
The parsed comments. Each item is something like:
{ name: 'parseComment', description: 'A comments parser for coffee-script.', tags: [ { tagName: 'param', type: 'string', name: 'code', description: 'The name of the module it belongs to.', index: 256, // The target char index in the file. line: 32 // The line number of the target in the file. } ] }
-
-
Parse dependency tree by regex. The dependency relationships is not a tree, but a graph. To avoid dependency cycle, this function only return an linear array of the dependencies, from which you won't get the detail relationshops between files.
-
param:
entryPaths
{ String | Array }The file to begin with.
-
param:
opts
{ Object }Defaults:
{ // It will match `require`, `import` statements. depReg: RegExp, // It will handle all the matched paths. // Return false value if you don't want this match. handle: (path) => path }
-
return: { Promise }
It resolves the dependency path array.
-
example:
kit.parseDependency('main.', { depReg: /require\s*\(?['"](.+)['"]\)?/gm, handle: (path) => { if (path.match(/^(?:\.|/|[a-z]:)/i)) return path; } }) .then((markdownStr) => kit.log(markdownStr) );
-
-
io.js native module
path
. Seenofs
for more information. -
The promise lib. Now, it uses Yaku as ES5 polyfill. In the future, the Yaku will be replaced with native ES6 Promise. Please don't use any API other than the ES6 spec.
- type: { Object }
-
The
proxy
module. You mustkit.require 'proxy'
before using it. For more information goto theProxy
section. -
Reduce a string via a regex.
-
param:
reg
{ RegExp } -
param:
str
{ String } -
param:
iter
{ Function }(init, matchGroup) -> init
, default is_.iteratee
. -
param:
init
{ Any } -
return: { Any }
-
example:
let out = kit.regexReduce(/\w(\d+)/g, 'a1, b10, c3', (ret, ms) => { ret.push(ms[1]); return ret; }, []); kit.log(out); // => [1, 10, 3]
-
-
Map a string via a regex.
-
param:
reg
{ RegExp } -
param:
str
{ String } -
param:
iter
{ Function }(matchGroup) ->
, default is_.iteratee
. -
return: { Array }
-
example:
let out = kit.regexMap(/\w(\d+)/g, 'a1, b10, c3', 1); kit.log(out) // => [1, 10, 3]
-
-
An async string replace function.
-
param:
str
{ String }The string to replace
-
param:
pattern
{ String | Regex } -
param:
iter
{ Function }It can return a promise
-
return: { Promise }
-
-
An async string replace function, each replacement process will run in line.
-
param:
str
{ String }The string to replace
-
param:
pattern
{ String | Regex } -
param:
iter
{ Function }It can return a promise
-
return: { Promise }
-
-
Much faster than the native require of node, but you should follow some rules to use it safely. Use it to load nokit's internal module.
-
param:
moduleName
{ String }The module path or name.
-
param:
dir
{ String }Current absolute file path. Not optional, expect when requiring nokit's internal modules. On most times, just pass
__dirname
to it is enough. -
param:
loaded
{ Function }Run only the first time after the module loaded.
-
return: { Module }
The module that you require.
-
example:
Use it to load nokit's internal module.
kit.require('jhash'); // Then you can use the module, or it will be null. kit.jhash.hash('test');
To load a relative path, or you own module, the second parameter 'dir' is required.
let mod = kit.require('./mod', __dirname); // Or load your own 'jhash', rather than nokit's. let jhash = kit.require('jhash', __dirname);
-
-
Require an optional package. If not found, it will warn the user to npm install it, and exit the process. When
kit.requireOptional.autoInstall
is set totrue
, the package will be auto installed if it's missed.-
param:
name
{ String }Package name
-
param:
dir
{ String }Current absolute file path. Not optional. On most times, just pass
__dirname
to it is enough. -
param:
semver
{ String }Specify what version you need, such as
^0.3.1
or>=1.2.3
, ect. -
return: { Any }
The required package.
-
-
A handy extended combination of
http.request
andhttps.request
.-
param:
opts
{ Object }The same as the http.request, but with some extra options:
{ // String or Url Object. url: String | Object, // Other than return `res` with `res.body`,return `body` directly. body: true, // Max times of auto redirect. If 0, no auto redirect. redirect: 0, // Timeout of the socket of the http connection. // If timeout happens, the promise will reject. // Zero means no timeout. timeout: 0, // The key of headers should be lowercased. headers: {}, protocol: 'http:' or 'https:', agent: null, // Auto set "transfer-encoding" header to 'chunked' if the `reqData` is // stream and the 'Content-Length' header is not set. autoTE: true, // Set null to use buffer, optional. // It supports GBK, ShiftJIS etc. // For more info, see https://github.com/ashtuchkin/iconv-lite resEncoding: 'auto', // Whether to unzip gzip / deflate. autoUnzip: true, // It's string, object, stream or buffer, it's optional. When it's an object, // The request will be 'application/x-www-form-urlencoded'. reqData: null, // auto end the request. autoEndReq: true, // Writable stream. resPipe: null, // Handle resPipe before it's piped. // Its returned value will be assigned to `opts.resPipe`. So you can return // null to make the request resolve the `body`. handleResPipe: (res, resPipe) => resPipe, /// The progress of the request. reqProgress: (complete, total) => {}, // The progress of the response. resProgress: (complete, total) => {}, resPipeError: (res) => res.end() }
And if set opts as string, it will be treated as the url.
-
return: { Promise }
Contains the http response object, it has an extra
body
property. You can also get the request object by usingPromise.req
. -
example:
let p = kit.request('http://test.com'); p.req.on('response', (res) => kit.log res.headers['content-length'] ); p.then((body) => kit.log(body); // html or buffer ); kit.request({ url: { protocol: 'https', hostname: 'test.com', port: 8123, path: '/a.mp3?s=1' }, body: false, resProgress: (complete, total) => kit.log(`Progress: ${complete} / ${total}`) }) .then((res) => { kit.log(res.body.length); kit.log(res.headers); }); // Send form-data. let form = new require('form-data'); form.append('image', Buffer.alloc(0), { filename: 'a.jpg', contentType: 'image/jpg' }); form.append('key', 'value'); kit.request({ url: 'a.com', method: 'POST', headers: form.getHeaders(), reqData: form }) .then((body) => kit.log(body) );
-
-
The semantic versioner for npm, known as semver. You must
kit.require 'semver'
before using it.- type: { Object }
-
A safer version of
child_process.spawn
to cross-platform run a process. In some conditions, it may be more convenient to use thekit.exec
. It will automatically addnode_modules/.bin
to thePATH
environment variable.-
param:
cmd
{ String }Path or name of an executable program.
-
param:
args
{ Array }CLI arguments. If any of the item is an object, it will be converted to string by
JSON.stringify
. -
param:
opts
{ Object }Process options. Almost the same with the Node.js official documentation. It will inherit the parent's stdio by default. An extra
prefix
option, if it's enabled, all stdout and stderr will be prefix with the specified string, you can also specify the color likeweb:red
,web:blue
, if no color found, a random color will be used. -
return: { Promise }
The
promise.process
is the spawned child process object. Resolves when the process's stdio is drained and the exit code is either0
or130
. The resolve value is like:{ code: 0, signal: null }
-
example:
kit.spawn('git', ['commit', '-m', '42 is the answer to everything']) .then(({code}) => kit.log code);
-
-
The
sse
module. You mustkit.require 'sse'
before using it. For more information goto thesse
section. -
Sequencing and executing tasks and dependencies concurrently.
-
param:
name
{ String }The task name.
-
param:
opts
{ Object }Optional. Defaults:
{ deps: String | Array, description: String, logStart: () => (), logEnd: () => (), // Whether to run dependency in a row. isSequential: false }
-
param:
fn
{ Function }(val) -> Promise | Any
The task function. If it is a async task, it should return a promise. It will get its dependency tasks' resolved values. -
property:
run
{ Function }Use it to start tasks. Each task will only run once.
(names = 'default', opts) ->
. Thenames
can be a string or array. The default opts:{ isSequential: false, // Will be passed as the first task's argument. init: undefined, // To stop the run currently in process. Set the `$stop` // reference to true. It will reject a "runStopped" error. warp: { $stop: false } }
-
property:
list
{ Object }The defined task functions.
-
return: { Promise }
Resolve with the last task's resolved value. When
isSequential == true
, it resolves a value, else it resolves an array. -
example:
kit.task('default', { deps: 'build' }, () => kit.log('run defaults...') ); kit.task('build', { deps: ['clean'] }, (isFull) => isFull ? 'do something' : 'do something else' ); kit.task('clean', (opts) => opts.isForce ? kit.remove('dist/**', { isForce: true }) : kit.remove('dist/**') ); kit.task.run() .then(() => kit.log('All Done!') );
-
-
Cross-platform kill process tree by root process id.
-
param:
pid
{ Number } -
param:
signal
{ String | Number }Such as 'SIGINT'
-
param:
callback
{ Function }
-
-
The
url
module of node. You mustkit.require 'url'
before using it. -
Works much like
gulp.src
, but with Promise instead. The warp control and error handling is more pleasant.-
param:
from
{ String }Glob pattern string.
-
param:
opts
{ Object }It extends the options of
nofs.glob
, but with some extra proptereis. Defaults:{ // The base directory of the pattern. baseDir: String }
-
return: { Object }
The returned warp object has these members:
{ // The drive can also be a promise that will resolve a drive. load: (drive) => fileInfo | null, run: (path) => Promise }
Each piped drive will recieve a object that extends
nofs
's fileInfo object:{ // Set the contents and return self. set: (String | Buffer) => fileInfo, // The src file path. path: String, // The dest root path. to: String, baseDir: String, // The destination path. // Alter it if you want to change the output file's location. // You can set it to string, warp will auto-convert it to object. // It's "valueOf" will return "kit.path.join dir, name + ext". dest: { root, dir, base, ext, name }, // The file content. contents: String | Buffer, isDir: Boolean, stats: fs.Stats, // Alter it to control the left drives dynamically. drives: [Function], // All the globbed files. list: Array, driveList: Array, // The opts you passed to "kit.warp", it will be extended. opts: Object }
Each drive can have a
onEnd: (fileInfo) -> Any | Promise
function, which will be called after a file's whole warp is ended.The drive can have a
isReader
property, which will make the drive override the default file reader.The drive can have a
isWriter
property, which will make the drive override the default file writer.If a drive overrides another, it can call
fileInfo.super()
to use it again. -
example:
// Define a simple workflow. kit.warp('src/**/*.js') .load((fileInfo) => fileInfo.set('/* Lisence Info */' + fileInfo.contents) ) .load(jslint()) .load(minify()) .run('build/minified'); // Override warp's file reader with a custom one. let myReader = kit._.extend((f) => kit.readFile(f.path, 'hex').then(f.path) ), { // This will tell warp you want use your own reader. isReader: true }); // Override writer. let myWriter = kit._.extend((f) => { if (f.dest === 'a.js') return; // Call the overrided writer. f.super(); }, { isWriter: true, onEnd: () => { super(); kit.log(this.list); }); kit.warp('src/**/*.js') .load(myWriter) .run('dist'); // Use nokit's built-in warp drives. let drives = kit.require('drives'); kit.warp('src/**/*.coffee') .load(drives.coffee()); .run('dist');
-
-
Same as the unix
which
command. You mustkit.require 'which'
before using it.-
param:
name
{ String }The command.
-
return: { Promise }
-
-
Sync version of
which
. You mustkit.require 'whichSync'
before using it.- type: { Function }
-
For debugging. Dump a colorful object.
-
param:
obj
{ Object }Your target object.
-
param:
opts
{ Object }Options. Default:
{ colors: true, depth: 7 }
-
return: { String }
-
-
Open a thing that your system can recognize. Now only support Windows, OSX or system that installed 'xdg-open'.
-
param:
cmds
{ String | Array }The thing you want to open.
-
param:
opts
{ Object }The options of the node native
child_process.exec
. -
return: { Promise }
When the child process exists.
-
example:
Open a webpage with the default browser.
kit.open('http://ysmood.org');
-
-
For test, page injection development. A cross-platform programmable Fiddler alternative. You can even replace express.js with it's
flow
function. -
A simple request body middleware. It will append a property
reqBody
toctx
. It will append a propertybody
toctx.req
.-
params:
opts {Object} Defaults:
{ limit: Infinity, memoryLimit: 100 * 1024 // 100KB }
-
return: { Function }
(ctx) -> Promise
-
example:
let kit = require('nokit'); let proxy = kit.require('proxy'); let app = proxy.flow(); app.push(proxy.body()); app.push(($) => { kit.logs($.reqBody); }); app.listen(8123);
-
-
Http CONNECT method tunneling proxy helper. Most times it is used to proxy https and websocket.
-
param:
opts
{ Object }Defaults:
{ // If it returns false, the proxy will be ignored. filter: (req) => true, handleReqHeaders: (headers) => headers, host: null, // Optional. The target host force to. port: null, // Optional. The target port force to. onError: (err, socket) => {} }
-
return: { Function }
The connect request handler.
-
example:
let kit = require('nokit'); let proxy = kit.require('proxy'); let app = proxy.flow(); // Directly connect to the original site. app.server.on('connect', kit.proxy.connect()); app.listen(8123);
-
-
Proxy and replace a single js file with a local one.
-
param:
opts
{ Object }{ url: Regex, // The url pattern to match file: String // The local js file path }
-
return: { Function }
noflow middleware
-
example:
let kit = require('nokit'); let http = require('http'); let proxy = kit.require('proxy'); let app = proxy.flow(); app.push(proxy.debugJs({ url: /main.js$/, file: './main.js' })); app.listen(8123);
-
-
Create a etag middleware.
- return: { Function }
-
A simple protocol to read, write, chmod, delete file via http. The protocol is very simple
POST / HTTP/1.1 file-action: ${action} ${body}
The
action
is somethine like{ type: 'create', path: '/home/u/a/b.js', mode: 0o777 }
Thebody
is the binary of the file content. Both theaction
and thebody
are encrypt with the password and algorithm specified in the opts.-
param:
opts
{ Object }defaults
{ password: 'nokit', algorithm: 'aes128', rootAllowed: '/', actionKey: 'file-action' }
-
return: { Function }
noflow middleware
-
-
Make a file create request to
proxy.file
.-
param:
opts
{ Object }Defaults
{ action: 'read', url: '127.0.0.1', path: String, data: Any, password: 'nokit', algorithm: 'aes128', actionKey: 'file-action', typeKey: 'file-type' }
-
return: { Promise }
-
-
A minimal middleware composer for the future. https://github.com/ysmood/noflow
-
Convert noflow middleware express middleware.
-
param:
fn
{ Function }noflow middleware
-
return: { FUnction }
express middleware
-
-
Generate an express like unix path selector. See the example of
proxy.flow
.-
param:
pattern
{ String } -
param:
opts
{ Object }Same as the path-to-regexp's options.
-
return: { Function }
(String) -> Object
. -
example:
let proxy = kit.require('proxy'); let match = proxy.match('/items/:id'); kit.log(match('/items/10')) // output => { id: '10' }
-
-
Convert a Express-like middleware to
proxy.flow
middleware.-
param:
h
{ Function }(req, res, next) ->
-
return: { Function }
(ctx) -> Promise
let proxy = kit.require('proxy'); let http = require('http'); let bodyParser = require('body-parser'); let middlewares = [ proxy.midToFlow(bodyParser.json()), (ctx) => ctx.body = ctx.req.body ]; http.createServer(proxy.flow(middlewares)).listen(8123);
-
-
A simple url parser middleware. It will append a
url
object toctx
-
param:
parseQueryString
{ boolean } -
param:
slashesDenoteHost
{ boolean } -
return: { Function }
(ctx) -> Promise
-
example:
let kit = require('nokit'); let proxy = kit.require('proxy'); let app = proxy.flow(); app.push(proxy.parseUrl(true)); app.push(($) => { kit.logs($.reqUrl.path); }); app.listen(8123);
-
-
A helper for http server port tunneling.
-
param:
opts
{ Object }{ allowedHosts: [], onSocketError: () => {}, onRelayError: () => {} }
-
return: { Function }
A http connect method helper.
-
-
A helper for http server port tunneling.
-
param:
opts
{ Object }{ host: '0.0.0.0:9970', relayHost: '127.0.0.1:9971', hostTo: '127.0.0.1:8080', onSocketError: () => {}, onRelayError: () => {} }
-
return: { Promise }
Resolve a tcp server object.
-
-
Create a conditional middleware that only works when the pattern matches.
-
param:
sel
{ Object }The selector. Members:
{ url: String | Regex | Function, method: String | Regex | Function, headers: Object }
When it's not an object, it will be convert via
sel = { url: sel }
. Theurl
,method
andheaders
are act as selectors. If current request matches the selector, themiddleware
will be called with the captured result. If the selector is a function, it should return anon-undefined, non-null
value when matches, it will be assigned to thectx
. When theurl
is a string, ifreq.url
starts with theurl
, the rest of the string will be captured. -
param:
middleware
{ Function } -
return: { Function }
-
-
Create a http request middleware.
-
param:
opts
{ Object }Same as the sse.
-
return: { Function }
(req, res, next) ->
. It has some extra properties:{ ssePrefix: '/nokit-sse', logPrefix: '/nokit-log', sse: kit.sse, watch: (filePath, reqUrl) => {}, host: '', // The host of the event source. useJs: false // By default the browserHelper will be a html string }
-
example:
Visit 'http://127.0.0.1:80123', every 3 sec, the page will be reloaded. If the
./static/default.css
is modified, the pagea.html
will also be reloaded.let kit = require('nokit'); let http = require('http'); let proxy = kit.require('proxy'); let handler = proxy.serverHelper(); let app = proxy.flow(); handler.watch('./static/default.css', '/st/default.css'); app.push(handler); app.push(proxy.select(/a\.html$/, proxy.url({ handleResBody: (body) => body + handler.browserHelper }))); app.listen(8123); setInterval(() => handler.sse.emit('fileModified', 'changed-file-path.js') ), 3000);
You can also use the
nokit.log
on the browser to log to the remote server.nokit.log({ any: 'thing' });
-
-
Create a static file middleware for
proxy.flow
.-
param:
opts
{ String | Object }Same as the send's. It has an extra option
{ onFile: (path, stats, ctx) => void }
. -
return: { Function }
The middleware handler of
porxy.flow
.let proxy = kit.require('proxy'); let http = require('http'); let middlewares = [proxy.select({ url: '/st' }, proxy.static('static'))] http.createServer(proxy.flow(middlewares)).listen(8123);
-
-
Send or receive any size of package over a socket. Add a
writeFrame
method and aframe
event tonet.Socket
object. ThewriteFrame
's signature is same with thenet.Socket.write
. Theframe
event is the same with the native stream'sdata
event.-
param:
socket
{ net.Socket }The nodejs native
net.Socket
. -
param:
opts
{ Object }Defaults
{ // The extra first chunk to be used as part of a frame head: Buffer }
-
-
Use it to proxy one url to another.
-
param:
opts
{ Object | String }Other options, if it is a string, it will be converted to
{ url: opts }
. Default:{ // The target url forced to. Optional. // Such as proxy 'http://test.com/a' to 'http://test.com/b', // proxy 'http://test.com/a' to 'http://other.com/a', // proxy 'http://test.com' to 'other.com'. // It can also be an url object. Such as // `{ protocol: 'http:', host: 'test.com:8123', pathname: '/a/b', query: 's=1' }`. url: null, // Mutate the url before the proxy take charge of it. handleUrl: (url) => url, agent: customHttpAgent, // Force the header's host same as the url's. isForceHeaderHost: false, // The request data to use. The return value should be stream, buffer or string. handleReqData: (req) => req.body || req // You can hack the headers before the proxy send it. handleReqHeaders: (headers, req) => headers handleResHeaders: (headers, req, proxyRes) => headers, // Same option as the `kit.request`'s `handleResPipe`. handleResPipe: (res, stream) => stream, // Manipulate the response body content of the response here, // such as inject script into it. Its return type is same as the `ctx.body`. handleResBody: (body, req, proxyRes) => body, // Only when the `content-type` matches, handleResBody will work handleResBodyMIME: /text|json|javascript|css|xml/ resPipeError: (res) => void, rejectUnauthorized: true, // It will log some basic error info. error: (e, req) => {} }
-
return: { Function }
(req, res) => Promise
A middleware. -
example:
let kit = require('nokit'); let proxy = kit.require('proxy'); let http = require('http'); http.createServer(proxy.flow( // Transparent proxy proxy.select({ url: '/a' }, proxy.url()), // Porxy to `a.com` proxy.select({ url: '/b' }, proxy.url({ url: 'a.com' })), // Porxy to a file proxy.select({ url: '/c' }, proxy.url({ url: 'c.com/s.js' })), proxy.select( { url: /$/, method: 'GET' }, proxy.url({ url: 'd.com', // Inject script to html page. handleResBody: (body, req, res) => { if (res.headers['content-type'].indexOf('text/html') > -1) return body + '<script>alert("test")</script>'; else return body; } }) ) ).listen(8123);
-
-
Add a
van
method to flow context object. It's a helper to set and get the context body.- param:
ctx
{ FlowContext }
- param:
-
A Server-Sent Event Manager. For more info see Using server-sent events. It is used to implement the live-reload of web assets.
-
param:
opts
{ Object }Defaults:
{ // The reconnection time to use when attempting to send the event, unit is ms. retry: 1000 }
-
example:
Your server side code may look like this:
let http = require('http'); let kit = require('nokit'); let sse = kit.require('sse'); let sseHandler = sse(); sseHandler.onConnect = ({ req }) => { console.log('client connected: ', req.url) } http.createServer((req, res) => { if (req.url === '/sse') sseHandler(req, res); else res.end(); }).listen(8080, () => setTimeout(() => sseHandler.emit('test', { test: 'ok' }) ); );
You browser code should be something like this:
let es = new EventSource('/sse'); es.addEventListener('test', (e) => { let msg = JSON.parse(e.data); console.log(msg); // => { test: 'ok' } });
-
-
The sse middleware for http handler.
-
param:
req
{ http.IncomingMessage }Also supports Express.js.
-
param:
res
{ http.ServerResponse }Also supports Express.js.
-
-
The sessions of connected clients.
- type: { Array }
-
Broadcast a event to all clients.
-
param:
event
{ String }The event name.
-
param:
msg
{ Object | String }The data you want to emit to session.
-
param:
[path]
{ String }The namespace of target sessions. If not set, broadcast to all clients.
-
-
Create a sse session.
-
param:
req
{ http.IncomingMessage }Also supports Express.js.
-
param:
res
{ http.ServerResponse }Also supports Express.js.
-
return: { SSESession }
-
-
A session object is something like:
{ req, // The http req object. res // The http res object. }
-
Emit message to client.
-
param:
event
{ String }The event name.
-
param:
msg
{ Object | String }The message to send to the client.
-
Here it will automatically lint, compile, compress and cache files by their extensions. You can goto Drives section to see what extensions are supported, or write your own.
let kit = require('nokit');
let drives = kit.require('drives');
kit.warp('src/**/*.@(jade|less|coffee|ls)')
// // To disable cache.
// .load drives.reader isCache: false
.load(drives.auto('lint'))
.load(drives.auto('compile', { '.coffee': { bare: false } }))
.load(drives.auto('compress'))
.load(concat('main.js'))
.run('dist/path');
Nokit has already provided some handy example drives, you can check them in the Drives section. It's fairly easy to write your own.
let kit = require('nokit');
let coffee = require('coffee-script');
// A drive for coffee, a simple curried function.
let compiler = (opts) => function () {
// Change extension from '.coffee' to '.js'.
this.dest.ext = '.js';
this.set(coffee.compile(this.contents, opts));
};
// A drive to prepend lisence to each file.
// Here "fileInfo.set" is the same with the "@set".
let lisencer = (lisence) => function (fileInfo) {
this.set(lisence + '\n' + this.contents)
}
// A drive to concat all files. It will override the default writer.
let concat = (outputFile) => {
let all = '';
// Object.assign
return kit._.assign(function () {
all += this.contents;
}, { isWriter: true, onEnd: function () {
// This will enable the auto-cache.
this.deps = kit._.pluck(this.list, 'path');
this.dest = this.to + '/' + outputFile;
this.set(all);
// Call the overrided writer.
// Call two times and create two output files.
this.super().then(() => {
this.dest = this.dest + '.info';
this.set = '/* info */\n' + all;
this.super();
});
} });
};
kit.warp('src/**/*.coffee')
.load(compiler(bare: true))
.load(lisencer('/* MIT lisence */'))
.load(concat('bundle.js'))
.run('dist')
.then(() => {
kit.log('Build Done');
});
-
The built-in plguins for warp. It's more like examples to show how to use nokit efficiently.
-
clean-css
-
param:
opts
{ Object } -
return: { Function }
-
-
coffee-script compiler
-
param:
opts
{ Object }Default is
{ bare: true }
. -
return: { Function }
/ }), coffee: _.extend(function (opts) { if (opts == null) { opts = {}; } _.defaults(opts, { bare: true });
const coffee = kit.requireOptional('coffee-script', __dirname, '>=1.8.0'); return function () { opts.filename = this.path; this.deps = [this.path]; this.dest.ext = '.js'; try { this.set(coffee.compile(this.contents + '', opts)); return kit.log(br.cyan('coffee: ') + this.path); } catch (err) { kit.err(br.red(err.stack)); return Promise.reject('coffeescriptCompileError'); } }; }, { compile: ['.coffee'] /**
coffeelint processor
-
param:
opts
{ Object }It extends the default config of coffeelint, properties:
{ colorize: true, reporter: 'default', // The json of the "coffeelint.json". // If it's null, coffeelint will try to find // "coffeelint.json" as its content. config: null | JSON | JsonFilePath }
-
return: { Function }
/ }), coffeelint: _.extend(function (opts) { if (opts == null) { opts = {}; } _.defaults(opts, { colorize: true, reporter: 'default' });
const coffeelint = kit.requireOptional('coffeelint', __dirname); if (!opts.config) { const configfinder = require('coffeelint/lib/configfinder'); opts.config = configfinder.getConfig(); } if (_.isString(opts.config)) { opts.config = kit.readJsonSync(opts.config); } const Reporter = require(`coffeelint/lib/reporters/${opts.reporter}`); return function () { this.deps = [this.path]; const errorReport = new coffeelint.getErrorReport(); errorReport.lint(this.path, this.contents, opts.config); const reporter = new Reporter(errorReport, opts); for (let path in errorReport.paths) { const errors = errorReport.paths[path]; kit.log(br.cyan('coffeelint: ') + _.trim(reporter.reportPath(path, errors))); if (errors.length > 0) { return Promise.reject(errors[0]); } } }; }, { lint: ['.coffee'] /**
Parse commment from a js, coffee, or livescript file, and output a markdown string.
-
param:
path
{ String } -
param:
opts
{ Object }Defaults:
{ // Output doc path. out: 'readme.md', // jst template path. tpl: 'readme.jst.md', // Init doc info. doc: {}, // Header size. h: 3, parseComment: () => {}, formatComment: () => {} }
-
return: { Function }
-
example:
The nofile of nokit shows how to use it. / }), comment2md(opts) { if (opts == null) { opts = {}; } _.defaults(opts, { out: 'readme.md', tpl: 'readme.jst.md', doc: {}, h: 3, parseComment: {}, formatComment: {} });
return _.extend(function (file) { const toc = []; opts.formatComment.name = function ({ name, line }) { name = name.replace('self.', ''); const tocName = name.toLowerCase() .replace(/\s/g, '-') .replace(/[^\w-]/g, ''); toc.push(` - [${name}](#${tocName})`); const link = `${file.path}?source#L${line}`; return `- ${_.repeat('#', opts.h)} **[${name}](${link})**\n\n`; }; const comments = kit.parseComment(this.contents + '', opts.parseComment); opts.doc[this.path] = kit.formatComment(comments, opts.formatComment); return opts.doc[this.path + '-toc'] = toc.join('\n'); } , { isWriter: true, onEnd(file) { if (_.keys(opts.doc).length < this.list.length) { return; } this.deps = _.map(this.list, 'path'); this.deps.push(opts.tpl); this.dest = kit.path.join(this.to, opts.out); return kit.readFile(opts.tpl, 'utf8') .then(tpl => file.set(_.template(tpl)({ doc: opts.doc }))) .then(function () { kit.log(br.cyan('comment2md: ') + kit.path.join(file.to, opts.out) ); return file.super(); }); } } ); }, /**
Auto-compiler file by extension. It will search through
kit.drives
, and find proper drive to run the task. You can extendkit.drives
to let it support more. For example:kit.drives.myCompiler = kit._.extend(() => { // your compile logic }), { compiler: ['.jsx'] })
-
param:
action
{ String }By default, it can be 'compile' or 'compress' or 'lint'
-
param:
opts
{ Object }{ // If no compiler match. onNotFound: (fileInfo) => {} }
-
return: { Function }
-
-
Change dest path with a filter.
-
param:
dir
{ String } -
param:
filter
{ Function }(fileInfo, dir) -> Boolean
-
return: { Function }
-
-
a batch file concat helper
-
param:
name
{ String }The output file path.
-
param:
dir
{ String }Optional. Override the dest of warp's.
-
return: { Function }
-
-
Suffix file name with the hash value of file content.
-
param:
hashMapPath
{ String }The output file name hash map.
-
return: { Function }
-
-
Lint js via
jshint
.-
param:
opts
{ Object }Properties:
{ global: null, config: null | JSON | JsonFilePath }
-
return: { Function }
-
-
Compile less.
-
param: { Object }
-
return: { Function }
/ } ), less: _.extend(function (opts) { if (opts == null) { opts = {}; } const less = kit.requireOptional('less', __dirname, '>=2.5.1');
return function (file) { this.dest.ext = '.css'; opts.filename = this.path; return less.render(this.contents + '', opts) .then(function (output) { file.deps = [file.path].concat(output.imports); file.set(output.css); return kit.log(br.cyan('less: ') + file.path); }, function (err) { if ((err.line == null)) { return Promise.reject(err); } // The error message of less is the worst. err.message = err.filename + `:${err.line}:${err.column}\n` + (err.extract != null ? err.extract.join('\n') : undefined) + '\n--------\n' + err.message; return Promise.reject(err); }); }; }, { compile: ['.less'] /**
LiveScript compiler.
-
param:
opts
{ Object }Default is
{ bare: true }
. -
return: { Function }
/ }), livescript: _.extend(function (opts) { if (opts == null) { opts = {}; } _.defaults(opts, { bare: true });
const LiveScript = kit.requireOptional('LiveScript', __dirname, '>=1.2.0'); return function () { this.deps = [this.path]; opts.filename = this.path; this.dest.ext = '.js'; try { this.set(LiveScript.compile(this.contents + '', opts)); return kit.log(br.cyan('livescript: ') + this.path); } catch (err) { kit.err(br.red(err)); return Promise.reject('livescriptCompileError'); } }; }, { compile: ['.ls'] /**
read file and set
contents
-
param:
opts
{ Object }Defaults:
{ isCache: false, encoding: 'utf8', cacheDir: '.nokit/warp' }
-
return: { Function }
/ }), reader(opts) { if (opts == null) { opts = {}; } _.defaults(opts, { isCache: false, encoding: 'utf8', cacheDir: '.nokit/warp' });
if (jhash == null) { jhash = new(kit.require('jhash').constructor); } // Create a unique id for each workflow. const hashDrives = function (ds) { const str = _.map(ds, d => d.toString()).join(); return jhash.hash(str, true) + ''; }; const read = function () { return kit.readFile(this.path, opts.encoding) .then(this.set); }; return _.extend(function (file) { if (!this.list.cacheDir) { this.list.isCache = opts.isCache; this.list.cacheDir = kit.path.join(opts.cacheDir, hashDrives(this.driveList)); } if (this.isDir) { return; } if (opts.isCache) { return kit.depsCache({ deps: [this.path], cacheDir: this.list.cacheDir }).then(function (cache) { file.deps = cache.deps; if (cache.isNewer) { kit.log(br.green('reader cache: ') + file.deps.join(br.grey(', ')) ); file.drives.length = 0; return Promise.all(_.map(cache.dests, (cachePath, dest) => kit.mkdirs(kit.path.dirname(dest)) .then(() => kit.link(cachePath, dest) .catch(function (err) { if (err.code !== 'EEXIST') { return Promise.reject(err); } }) ) )); } else { return read.call(file); } }); } else { return read.call(file); } }, { isReader: true }); }, /**
Compile stylus.
-
param:
opts
{ Object }It will use
stylus.set
to iterateopts
and set the key-value, is the value is not a function.{ config: (styl) => {} }
-
return: { Function }
-
example:
kit.drives.stylus({ compress: true, config: (styl) => styl.define('jack', 'a persion') });
-
npm test
or npm run no -- test
Run npm run no -- -h
for all command you can use.
Such as run npm run no -- build
to build this project.
Edit the templete of the readme at doc/readme.jst.md
.
MIT