-
Notifications
You must be signed in to change notification settings - Fork 1
/
cjs-task.js
185 lines (140 loc) · 4.97 KB
/
cjs-task.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
var _yield = require('cjs-yield'),
create_hooks_instance = require('cjs-sync-hooks');
module.exports = create_task;
function create_task( callback ){
var hook = create_hooks_instance(),
step_order = [],
store = {},
log = [],
api = {},
current_step = 0,
insertions = 0,
steps_run = 0,
steps_deleted = 0,
started;
if( ! callback ) callback = default_task_callback;
if( typeof callback !== 'function' ) throw new Error( 'TASK CALLBACK MUST BE A FUNCTION' );
// control flow
api.start = start_task;
api.step = create_task_step;
api.next = start_next_task_step;
api.end = end_task;
api.subtask = create_subtask;
// configuration
api.get = get_task_variable;
api.set = set_task_variable;
api.unset = delete_task_varable;
api.callback = set_task_callback;
// pluggable
api.hook = hook;
// stats
api.stats = {
steps_run: function(){ return steps_run },
steps_deleted: function(){ return steps_deleted }
};
// logging
api.log = function( entry ){
if( entry ) create_task_log_entry( entry );
else return get_task_log();
};
return api;
function set_task_callback( end_callback ){
if( typeof end_callback !== 'function' ) throw new Error( 'TASK CALLBACK MUST BE A FUNCTION' );
else callback = end_callback;
}
function create_task_step( name, step ){
if( ! name ) throw new Error( 'TASKS CAN\'T HAVE UNNAMED STEPS' );
if( typeof name !== 'string' ) throw new Error( 'STEP NAMES MUST BE STRINGS' );
if( ! step || typeof step !== 'function' ) throw new Error( 'TASK STEPS MUST BE FUNCTIONS' );
if( ! started ) step_order.push({ name: name, step: step });
else {
step_order.splice( current_step + 1 + insertions, 0, { name: name, step: step });
insertions += 1;
}
hook.run( 'step-created', name );
}
function start_task(){
if( started ) throw new Error( 'TASK HAS ALREADY STARTED' );
if( step_order.length < 1 ) throw new Error( 'TASK HAS NO STEPS TO RUN' );
started = true;
hook.run( 'task-start' );
run_step();
}
function start_next_task_step(){
if( ! started ) throw new Error( 'CAN\'T CALL NEXT STEP BEFORE TASK STARTS' );
steps_run += 1;
// remove completed step
var completed_step = step_order.shift();
steps_deleted += 1;
// reset step insertion tracker
insertions = 0;
hook.run( 'step-end', completed_step.name );
// end task if no more steps remain
var should_end_task = step_order.length === 0;
if( should_end_task ) return end_task();
// else run next step
run_step();
}
function run_step(){
_yield( function(){
hook.run( 'step-start', step_order[ current_step ].name );
try {
step_order[ current_step ].step();
}
catch( error ){
end_task( error );
}
});
}
function end_task(){
if( ! started ) throw new Error( 'CAN\'T CALL NEXT STEP BEFORE TASK STARTS' );
hook.run( 'task-end' );
callback.apply( callback, arguments );
store = log = api = null;
}
function create_subtask( name, handler ){
if( ! name ) throw new Error( 'SUBTASKS CAN\'T BE UNNAMED' );
if( typeof name !== 'string' ) throw new Error( 'SUBTASK NAMES MUST BE STRINGS' );
if( ! handler || typeof handler !== 'function' ) throw new Error( 'SUBTASK HANDLERS MUST BE FUNCTIONS' );
create_task_step( name, function(){
var subtask = create_task();
// copy store from task to subtask
for( var key in store ){
if( ! store.hasOwnProperty( key ) ) continue;
else subtask.set( key, store[ key ] );
}
// copy store changes in subtask to task
subtask.hook.add( 'value-updated', 'copy-changes-to-main-task', function( details ){
set_task_variable( details.key, details.value );
});
handler( subtask );
});
}
function delete_task_varable( key ){
if( ! key ) throw new Error( 'NEED A KEY TO DELETE DATA' );
if( typeof key !== 'string' ) throw new Error( 'KEY MUST BE A STRING' );
delete store[ key ];
hook.run( 'value-updated', { key: key, value: null });
}
function set_task_variable( key, value ){
if( ! key ) throw new Error( 'NEED A KEY TO STORE DATA' );
if( typeof key !== 'string' ) throw new Error( 'KEY MUST BE A STRING' );
if( typeof value === 'undefined' ) throw new Error( 'NEED A VALUE TO STORE' );
store[ key ] = value;
hook.run( 'value-updated', { key: key, value: value });
}
function get_task_variable( key ){
if( ! key ) throw new Error( 'NEED A KEY TO RETRIEVE DATA' );
if( typeof key !== 'string' ) throw new Error( 'KEY MUST BE A STRING' );
return ( typeof store[ key ] !== 'undefined' ? store[ key ] : null );
}
function create_task_log_entry( entry ){
log.push( entry );
}
function get_task_log(){
return log;
}
function default_task_callback( error ){
if( error && error instanceof Error ) throw error;
}
}