react 0.0.0-fa1e8df11 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,4 @@
1
- MIT License
2
-
3
- Copyright (c) Facebook, Inc. and its affiliates.
1
+ Copyright (c) 2011 Jeff Barczewski
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
4
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
7
  copies of the Software, and to permit persons to whom the Software is
10
8
  furnished to do so, subject to the following conditions:
11
9
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
14
12
 
15
13
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
14
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
15
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
16
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
17
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,13 +1,138 @@
1
- # `react`
1
+ # React.js
2
2
 
3
- React is a JavaScript library for creating user interfaces.
3
+ React is a javascript module to make it easier to work with asynchronous code,
4
+ by reducing boilerplate code and improving error and exception handling while
5
+ allowing variable and task dependencies when defining flow.
4
6
 
5
- The `react` package contains only the functionality necessary to define React components. It is typically used together with a React renderer like `react-dom` for the web, or `react-native` for the native environments.
7
+ This async flow control module is initially designed to work with Node.js but
8
+ is planned to be extended to browser and other environments.
6
9
 
7
- **Note:** by default, React will be in development mode. The development version includes extra warnings about common mistakes, whereas the production version includes extra performance optimizations and strips all error messages. Don't forget to use the [production build](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build) when deploying your application.
10
+ It takes inspiration from several projects including:
8
11
 
9
- ## Example Usage
12
+ - Tim Caswell and Elijah Insua's [conductor](https://github.com/creationix/conductor) - [Article](http://howtonode.org/step-of-conductor)
13
+ - Caolan McMahon's [async](https://github.com/caolan/async)
10
14
 
11
- ```js
12
- var React = require('react');
13
- ```
15
+ ## Goals
16
+
17
+ - Improved error and exception handling
18
+ - Provide useful stack traces and context information for easier debugging
19
+ - Minimize boilerplate code needed for working with asynchronous functions
20
+ - Make code more readable and easier to understand which should translate to less defects
21
+ - Provide the right level of abstraction to make it easier to refactor code, without being too magical
22
+ - Allow the mixing of pure functions, method calls, and callback style functions in the flow
23
+ - Minimize the need to customize your code simply to use async flow control. The use of a flow control module ideally should not affect the way you write your code, it should only help take over some of the burden.
24
+
25
+ ## Concept
26
+
27
+ Borrowing heavily from Tim and Elijah's ideas for conductor, this async flow control module provides a way to construct a flow from a collection of functions or methods (referred to as _tasks_ in this module). It allows dependencies to be defined between the tasks so they can run in parallel as their dependencies are satisfied. React can us both variable dependencies and task dependencies.
28
+
29
+ As tasks complete, React watches the dependencies and kicks off additional tasks that have all their dependencies met and are ready to execute. This allows the flow to run at maximum speed without needing to arbitrarily block tasks into groups of parallel and serial flow.
30
+
31
+ To reduce the boilerplate code needed and improve error handling, React automatically provides callback functions for your asynchronous code. These React-provided callback functions perform these steps:
32
+
33
+ 1. check for error and handle by calling outer callback function with this error after augmenting it with additional context information for easier debugging
34
+ 2. save the callback variables into a context for future reference
35
+ 3. call back into React (and it will kick off additional tasks that are now ready to go)
36
+
37
+ ## Design
38
+
39
+ - Optional parse step to create flow AST (TODO allow pluggable parsers to allow many interfaces)
40
+ - Validate the flow AST - determine if dependencies can all be met as defined such that flow will complete (TODO)
41
+ - Execute the flow AST
42
+
43
+ ## Examples
44
+
45
+ 1. [Direct AST](#directAST)
46
+ 2. [Using Simple DSL](#simpleDSL)
47
+
48
+ <a name="directAST"/>
49
+ ### Example directly using AST
50
+
51
+ var react = require('react').react;
52
+
53
+ function loadUser(uid, cb){ setTimeout(cb, 100, null, "User"+uid); }
54
+ function loadFile(filename, cb){ setTimeout(cb, 100, null, 'Filedata'+filename); }
55
+ function markdown(filedata) { return 'html'+filedata; }
56
+ function prepareDirectory(outDirname, cb){ setTimeout(cb, 200, null, 'dircreated-'+outDirname); }
57
+ function writeOutput(html, user, cb){ setTimeout(cb, 300, null, html+'_bytesWritten'); }
58
+ function loadEmailTemplate(cb) { setTimeout(cb, 50, null, 'emailmd'); }
59
+ function customizeEmail(user, emailHtml, cb) { return 'cust-'+user+emailHtml; }
60
+ function deliverEmail(custEmailHtml, cb) { setTimeout(cb, 100, null, 'delivered-'+custEmailHtml); }
61
+
62
+ function useHtml(err, html, user, bytesWritten) {
63
+ if(err) {
64
+ console.log('***Error: %s', err);
65
+ return;
66
+ }
67
+ console.log('final result: %s, user: %s, written:%s', html, user, bytesWritten);
68
+ }
69
+
70
+ var r = react();
71
+ r.ast.inputNames = ['filename', 'uid', 'outDirname', 'cb'];
72
+ r.ast.taskDefs = [
73
+ { f:loadUser, a:['uid'], cb:['user'] },
74
+ { f:loadFile, a:['filename'], cb:['filedata'] },
75
+ { f:markdown, a:['filedata'], ret:['html'] },
76
+ { f:prepareDirectory, a:['outDirname'], cb:['dircreated'] },
77
+ { f:writeOutput, a:['html', 'user'], cb:['bytesWritten'], after:['prepareDirectory'] },
78
+ { f:loadEmailTemplate, a:[], cb:['emailmd'] },
79
+ { f:markdown, a:['emailmd'], ret:['emailHtml'] },
80
+ { f:customizeEmail, a:['user', 'emailHtml'], ret:['custEmailHtml'] },
81
+ { f:deliverEmail, a:['custEmailHtml'], cb:['deliveredEmail'], after:['writeOutput'] }
82
+ ];
83
+ r.ast.finalOutputNames = ['html', 'user', 'bytesWritten'];
84
+
85
+ r.exec("hello.txt", 100, 'outHello', useHtml);
86
+ r.exec("small.txt", 200, 'outSmall', useHtml);
87
+
88
+ <a name="simpleDSL"/>
89
+ ### Example using simple DSL interface
90
+
91
+ var react = require('react').react;
92
+
93
+ function loadUser(uid, cb){ setTimeout(cb, 100, null, "User"+uid); }
94
+ function loadFile(filename, cb){ setTimeout(cb, 100, null, 'Filedata'+filename); }
95
+ function markdown(filedata) { return 'html'+filedata; }
96
+ function prepareDirectory(outDirname, cb){ setTimeout(cb, 200, null, 'dircreated-'+outDirname); }
97
+ function writeOutput(html, user, cb){ setTimeout(cb, 300, null, html+'_bytesWritten'); }
98
+ function loadEmailTemplate(cb) { setTimeout(cb, 50, null, 'emailmd'); }
99
+ function customizeEmail(user, emailHtml, cb) { return 'cust-'+user+emailHtml; }
100
+ function deliverEmail(custEmailHtml, cb) { setTimeout(cb, 100, null, 'delivered-'+custEmailHtml); }
101
+
102
+ function useHtml(err, html, user, bytesWritten) {
103
+ if(err) {
104
+ console.log('***Error: %s', err);
105
+ return;
106
+ }
107
+ console.log('final result: %s, user: %s, written:%s', html, user, bytesWritten);
108
+ }
109
+
110
+ var r = react('filename, uid, outDirname, cb').define(
111
+ loadUser, 'uid -> err, user',
112
+ loadFile, 'filename -> err, filedata',
113
+ markdown, 'filedata -> returns html',
114
+ prepareDirectory, 'outDirname -> err, dircreated',
115
+ writeOutput, 'html, user -> err, bytesWritten', { after:prepareDirectory },
116
+ loadEmailTemplate,' -> err, emailmd',
117
+ markdown, 'emailmd -> returns emailHtml',
118
+ customizeEmail, 'user, emailHtml -> returns custEmailHtml',
119
+ deliverEmail, 'custEmailHtml -> err, deliveredEmail', { after: writeOutput }
120
+ ).callbackDef('err, html, user, bytesWritten');
121
+
122
+ ## Status
123
+
124
+ - 2011-10-26 - React is in active development and interface may change frequently in these early stages. Current code is functional but does not perform validation yet. Additional interfaces are planned to make it easy to define flows in a variety of ways. Documentation and examples forthcoming.
125
+
126
+ ## License
127
+
128
+ - [MIT license](http://github.com/jeffbski/react/raw/master/LICENSE)
129
+
130
+ ## Contributors
131
+
132
+ - Author: Jeff Barczewski (@jeffbski)
133
+
134
+ ## Contributing
135
+
136
+ - Source code repository: http://github.com/jeffbski/react
137
+ - Ideas and pull requests are encouraged - http://github.com/jeffbski/react/issues
138
+ - You may contact me at @jeffbski or through github at http://github.com/jeffbski
package/lib/react.js ADDED
@@ -0,0 +1,228 @@
1
+ 'use strict';
2
+
3
+ var util = require('util');
4
+
5
+ var reactOptions = {
6
+ debugOutput: false
7
+ };
8
+
9
+ /**
10
+ @example
11
+ var r = react('filename, uid, cb').define(
12
+ loadUser, 'uid -> err, user',
13
+ loadFile, 'filename -> err, filedata',
14
+ markdown, 'filedata -> returns html',
15
+ prepareDirectory, 'outDirname -> err, dircreated',
16
+ sendOutput, 'html, user -> err, html, bytesWritten', {after:prepareDirectory}
17
+ ).callbackDef('err, html, user, bytesWritten');
18
+
19
+ r.exec(filename, uid, function(err, html, user, bytesWritten){
20
+ //use html
21
+ });
22
+
23
+ */
24
+
25
+ function fName(fn){
26
+ return (typeof(fn) === 'string') ? fn : fn.name;
27
+ }
28
+
29
+ function formatErrorMeta(err){
30
+ if (!err.meta) return;
31
+ var vcon = err.meta.vcon;
32
+ var task = err.meta.task;
33
+ return '\n\n' +
34
+ 'Error occurs in Task function: ' + fName(task.f) + '(' + task.a.join(',') + ')\n\n' +
35
+ 'Variable Context: \n' +
36
+ util.inspect(vcon) + '\n\n' +
37
+ 'Task Source:\n\n' +
38
+ task.f.toString() + '\n\n'; //TODO need to pretty print function, gets collapsed
39
+ }
40
+
41
+ function augmentError(err, meta){
42
+ if (typeof(err) === 'string' ) { err = new Error(err); } //props will be lost on non-objects
43
+ var origMsg = err.toString();
44
+ err.meta = meta;
45
+ err.toString = function() { return origMsg + formatErrorMeta(err); };
46
+ return err;
47
+ }
48
+
49
+ function splitTrimFilterArgs(commaSepArgs){ //parse 'one, two' into ['one', 'two']
50
+ return commaSepArgs.split( ',' ) //split on commas
51
+ .map(function(s){ return s.trim(); }) //trim
52
+ .filter(function(s){ return (s); }); //filter out empty strings
53
+ }
54
+
55
+ function parseInOutDef(inOutDef){ //'a, b -> err, c, d' into { inDef: ['a','b'], outDef: ['c', 'd'] }
56
+ var match = /^([^-]*)(->)?\s*(er{0,2}\s*,)?(.*)$/.exec(inOutDef);
57
+ if (match) {
58
+ return { inDef: splitTrimFilterArgs(match[1]),
59
+ outDef: splitTrimFilterArgs(match[4]) };
60
+ }
61
+ throw "error parsing in/out def: "+inOutDef;
62
+ }
63
+
64
+ function nameTasks(tasks){ //name tasks that are not already named, validation done elsewhere, ret map
65
+ var namesMap = tasks.reduce(function(map, t){
66
+ if (t.name) { map[t.name] = t; }
67
+ return map; }, {});
68
+ tasks.forEach(function(t, idx){
69
+ if (!t.name) { //not already named
70
+ var name = fName(t.f);
71
+ if (!name || namesMap[name]) name = ''+name+'_'+idx; //if empty or already used, postfix with _idx
72
+ t.name = name;
73
+ namesMap[name] = t;
74
+ }
75
+ });
76
+ return namesMap;
77
+ }
78
+
79
+ function parseTaskDefs(arrArgs){ // [fun, strArgsCbArgs, optObj]
80
+ var taskDefs = [];
81
+ var CHECK_RETURNS_RE = /^returns?\s+(\w+)\s*;?$/;
82
+ while(arrArgs.length){
83
+ var fn = arrArgs.shift();
84
+ var strArgsCbArgs = arrArgs.shift();
85
+ var optObj = (typeof(arrArgs[0]) === 'object') ? arrArgs.shift() : { };
86
+ if (typeof(strArgsCbArgs) !== 'string') throw "eror parsing taskdef, expected str, got:"+strArgsCbArgs;
87
+ var inOutDef = parseInOutDef(strArgsCbArgs);
88
+ var taskDef = { f:fn, a:inOutDef.inDef, cb:inOutDef.outDef };
89
+ Object.keys(optObj).forEach(function(k){ taskDef[k] = optObj[k]; })
90
+ if (taskDef.after) {
91
+ if (!Array.isArray(taskDef.after)) { taskDef.after = [taskDef.after]; } //ensure arr
92
+ taskDef.after = taskDef.after.map(function(a){ return fName(a); });
93
+ }
94
+ var matchReturn = CHECK_RETURNS_RE.exec(taskDef.cb[0]);
95
+ if (matchReturn) { // found return(s) varname, so change this to return type fn
96
+ delete taskDef.cb; taskDef.ret = matchReturn[1]; } // del cb, add ret:varname
97
+ taskDefs.push( taskDef );
98
+ }
99
+ return taskDefs;
100
+ }
101
+
102
+
103
+ function react(inputDef){
104
+ inputDef = Array.prototype.slice.call(arguments).join(', '); //convert 'a', 'b', 'c' into 'a, b, c'
105
+ var reactObj;
106
+ var inOutDef = parseInOutDef(inputDef);
107
+ var ast = { inputNames: inOutDef.inDef,
108
+ finalOutputNames: inOutDef.outDef, //this might be set later
109
+ taskDefs: [] }; //set in define()
110
+ var STATUS = { READY: 'ready', RUNNING: 'running', ERRORED: 'errored', COMPLETE: 'complete' };
111
+
112
+ function define(arg1, arg2, argN){
113
+ ast.taskDefs = ast.taskDefs.concat(parseTaskDefs(Array.prototype.slice.call(arguments)));
114
+ nameTasks(ast.taskDefs); //set names in ast.taskDefs so that ast can be inspected before exec
115
+ return reactObj;
116
+ }
117
+
118
+ function callbackDef(argDef){ //define the callback output names
119
+ argDef = Array.prototype.slice.call(arguments).join(', '); //convert 'a', 'b', 'c' into 'a, b, c'
120
+ argDef = ( argDef && /^\s*->/.test(argDef)) ? argDef : '-> '+argDef; //prefix with -> before parse
121
+ var inOutDef = parseInOutDef(argDef); //should be '-> a,b,c'
122
+ ast.finalOutputNames = inOutDef.outDef;
123
+ return reactObj;
124
+ }
125
+
126
+ function exec(arg1, arg2, argN){
127
+ var args = Array.prototype.slice.call(arguments);
128
+ var cbFinal = args.pop(); //pop off final callback from end
129
+ var vCon = { }; //create variable context
130
+ args.forEach(function(x, idx){ vCon[ast.inputNames[idx]] = x; });
131
+ var firstError; //will be set to the err of first task that errors
132
+ var contExec; //function defined later
133
+ var tasksByName = {}; //set later, by calling nameTasks
134
+
135
+ function handleTaskError(task, err){
136
+ task.status = STATUS.ERRORED;
137
+ if (!firstError) { //no prev error, only calling final callback with error once
138
+ var errWithMeta = augmentError(err, {task:task, vcon:vCon});
139
+ firstError = errWithMeta; //save this, stop other tasks from being launched
140
+ cbFinal.call(null, errWithMeta); //call the final callback with the first error hit
141
+ }
142
+ }
143
+
144
+ function createCallback(task) {
145
+ return function(err, arg0, arg1, argn){
146
+ var args = Array.prototype.slice.call(arguments,1);
147
+ if(err){ handleTaskError(task, err); return; } //handle error and return, we are done
148
+
149
+ //no error, save callback args to vCon context, then continue execution
150
+ task.cb.forEach(function(k, idx){ //save cb args to v context
151
+ vCon[k] = (args[idx] !== undefined) ? args[idx] : null; //upgrade any undefined to null
152
+ });
153
+ task.status = STATUS.COMPLETE;
154
+ if (reactOptions.debugOutput) console.log('in callback: %s cb:', fName(task.f), args, vCon);
155
+ contExec();
156
+ };
157
+ }
158
+
159
+ var tasks = ast.taskDefs.map(function(ot){ //create working task copies
160
+ var t = Object.create(ot);
161
+ if(t.cb) t.cbFun = createCallback(t); //if is callback type fn, create callback
162
+ return t;
163
+ });
164
+ tasksByName = nameTasks(tasks); //remap names to exec task copies instead of taskDefs
165
+
166
+
167
+ function execTask(t){
168
+ t.status = STATUS.RUNNING;
169
+ var args = t.a.map(function(k){ return vCon[k]; }); //get args from vCon
170
+ if (t.cbFun) args.push(t.cbFun); //push custom callback to back if fn uses cb
171
+ if (reactOptions.debugOutput) console.log('starting task: %s', fName(t.f), args);
172
+ try {
173
+ var func;
174
+ var bindObj = null; //start as global object
175
+ if (typeof(t.f) === 'string') { //object method call
176
+ var match = /(\w+)\.(\w+)/.exec(t.f);
177
+ if (match) {
178
+ var objName = match[1];
179
+ var methName = match[2];
180
+ bindObj = vCon[objName];
181
+ func = bindObj[methName];
182
+ }
183
+ if (!func) throw new Error('Object or method not found: '+t.f);
184
+ } else { //function call
185
+ func = t.f;
186
+ }
187
+ var ret = func.apply(bindObj, args);
188
+ if (t.ret) { //if non-cb fn/method,
189
+ vCon[t.ret] = ret; // save retval
190
+ t.status = STATUS.COMPLETE; // mark complete
191
+ contExec(); // continue since no callback to run this
192
+ }
193
+ } catch (e) { handleTaskError(t, e); } //catch and handle the task error, calling final cb
194
+ }
195
+
196
+ contExec = function contExec(){
197
+ if (firstError) { return; } //stop execution, we already hit an error
198
+ if (tasks.every(function(t){ return (t.status === STATUS.COMPLETE); })) { //all completed
199
+ //we are done, call final callback
200
+ var finalArgs = ast.finalOutputNames.map(function(k){ return vCon[k]; });
201
+ finalArgs.unshift(null); //unshift err=null to front
202
+ cbFinal.apply(null, finalArgs);
203
+ return;
204
+ }
205
+ var tasksReady = tasks.filter(function(t, idx, arr){ //if we are here then we stil have tasks to run
206
+ return !t.status && // filter for not started AND
207
+ t.a.every(function(k){ return (vCon[k] !== undefined); }) && // all dep vars defined AND
208
+ (!t.after || // (no dep tasks OR
209
+ t.after.every( function(n){ return tasksByName[n].status === STATUS.COMPLETE; })); //alldone
210
+ });
211
+ tasksReady.forEach(function(t){ t.status = STATUS.READY; }); //set ready before call, no double exec
212
+ tasksReady.forEach(function(t){ execTask(t); });
213
+ };
214
+ contExec(); //now kick off the execution for exec()
215
+ }
216
+
217
+ reactObj = {
218
+ define: define,
219
+ callbackDef: callbackDef,
220
+ exec: exec,
221
+ ast: ast,
222
+ };
223
+
224
+ return reactObj;
225
+ }
226
+
227
+ module.exports.react = react;
228
+ module.exports.reactOptions = reactOptions;
package/package.json CHANGED
@@ -1,38 +1,13 @@
1
1
  {
2
2
  "name": "react",
3
- "description": "React is a JavaScript library for building user interfaces.",
4
- "keywords": [
5
- "react"
6
- ],
7
- "version": "0.0.0-fa1e8df11",
8
- "homepage": "https://reactjs.org/",
9
- "bugs": "https://github.com/facebook/react/issues",
10
- "license": "MIT",
11
- "files": [
12
- "LICENSE",
13
- "README.md",
14
- "build-info.json",
15
- "index.js",
16
- "cjs/",
17
- "umd/"
18
- ],
19
- "main": "index.js",
20
- "repository": {
21
- "type": "git",
22
- "url": "https://github.com/facebook/react.git",
23
- "directory": "packages/react"
24
- },
25
- "engines": {
26
- "node": ">=0.10.0"
27
- },
28
- "dependencies": {
29
- "loose-envify": "^1.1.0",
30
- "object-assign": "^4.1.1",
31
- "prop-types": "^15.6.2"
32
- },
33
- "browserify": {
34
- "transform": [
35
- "loose-envify"
36
- ]
37
- }
3
+ "description": "React is a javascript module to make it easier to work with asynchronous code, by reducing boilerplate code and improving error and exception handling while allowing variable and task dependencies when defining flow.",
4
+ "version": "0.0.1",
5
+ "author": "Jeff Barczewski <jeff.barczewski@gmail.com>",
6
+ "repository": { "type": "git", "url": "http://github.com/jeffbski/react.git" },
7
+ "bugs" : { "url": "http://github.com/jeffbski/react/issues" },
8
+ "licenses": [{ "type": "MIT", "url" : "http://github.com/jeffbski/react/raw/master/LICENSE" }],
9
+ "main": "react",
10
+ "engines": { "node": "~v0.4.12" },
11
+ "dependencies": {},
12
+ "devDependencies": {}
38
13
  }