leaner 0.2.0 → 0.3.0

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/dist/core.js CHANGED
@@ -92,6 +92,9 @@ function withScope( scope, callback ) {
92
92
  }
93
93
 
94
94
  function createWatcher( deps, callback, async ) {
95
+ if ( async )
96
+ callback = createSafeCallback( callback );
97
+
95
98
  const watcher = {
96
99
  deps,
97
100
  callback,
@@ -99,7 +102,7 @@ function createWatcher( deps, callback, async ) {
99
102
  };
100
103
 
101
104
  if ( current != null )
102
- current.push( watcher );
105
+ current.watchers.push( watcher );
103
106
 
104
107
  if ( deps != null ) {
105
108
  for ( const dep of deps ) {
@@ -110,6 +113,24 @@ function createWatcher( deps, callback, async ) {
110
113
  return watcher;
111
114
  }
112
115
 
116
+ function createSafeCallback( callback ) {
117
+ if ( current != null && current.errorHandler != null ) {
118
+ const handler = current.errorHandler;
119
+
120
+ return safeCallback;
121
+
122
+ function safeCallback() {
123
+ try {
124
+ callback();
125
+ } catch ( err ) {
126
+ handler( err );
127
+ }
128
+ }
129
+ }
130
+
131
+ return callback;
132
+ }
133
+
113
134
  function updateWatcher( watcher, deps ) {
114
135
  for ( const dep of deps ) {
115
136
  dep.add( watcher );
@@ -126,7 +147,7 @@ function updateWatcher( watcher, deps ) {
126
147
  }
127
148
 
128
149
  function destroyScope( scope ) {
129
- for ( const watcher of scope ) {
150
+ for ( const watcher of scope.watchers ) {
130
151
  watcher.callback = null;
131
152
 
132
153
  if ( watcher.deps != null ) {
package/dist/web.js CHANGED
@@ -37,15 +37,21 @@ function createComponent( template ) {
37
37
  }
38
38
 
39
39
  function createContext( parent ) {
40
- return {
40
+ const context = {
41
41
  parent,
42
42
  children: null,
43
- scope: [],
43
+ scope: {
44
+ watchers: [],
45
+ errorHandler: err => handleError( context, err ),
46
+ },
44
47
  mount: null,
45
48
  destroy: null,
46
49
  refs: null,
47
50
  services: null,
51
+ errorHandler: null,
48
52
  };
53
+
54
+ return context;
49
55
  }
50
56
 
51
57
  function createChildContext() {
@@ -141,9 +147,9 @@ function destroyContext( context ) {
141
147
 
142
148
  context.mount = null;
143
149
 
144
- if ( context.scope.length > 0 ) {
150
+ if ( context.scope.watchers.length > 0 ) {
145
151
  destroyScope( context.scope );
146
- context.scope = [];
152
+ context.scope.watchers = [];
147
153
  }
148
154
 
149
155
  if ( context.children != null ) {
@@ -155,6 +161,25 @@ function destroyContext( context ) {
155
161
  context.services = null;
156
162
  }
157
163
 
164
+ function cleanUpContext( context ) {
165
+ context.destroy = null;
166
+ context.refs = null;
167
+ context.mount = null;
168
+
169
+ if ( context.scope.watchers.length > 0 ) {
170
+ destroyScope( context.scope );
171
+ context.scope.watchers = [];
172
+ }
173
+
174
+ if ( context.children != null ) {
175
+ for ( const child of context.children )
176
+ cleanUpContext( child );
177
+ context.children = null;
178
+ }
179
+
180
+ context.services = null;
181
+ }
182
+
158
183
  function provide( key, value ) {
159
184
  if ( current == null )
160
185
  throw new Error( 'provide() cannot be called outside of a component' );
@@ -175,6 +200,35 @@ function inject( key ) {
175
200
  }
176
201
  }
177
202
 
203
+ function handleError( context, err ) {
204
+ while ( context != null) {
205
+ if ( context.errorHandler != null ) {
206
+ context.errorHandler( err );
207
+ return;
208
+ }
209
+ context = context.parent;
210
+ }
211
+ throw err;
212
+ }
213
+
214
+ function createSafeHandler( callback ) {
215
+ if ( current != null ) {
216
+ const context = current;
217
+
218
+ return safeHandler;
219
+
220
+ function safeHandler( e ) {
221
+ try {
222
+ callback( e );
223
+ } catch ( err ) {
224
+ handleError( context, err );
225
+ }
226
+ }
227
+ }
228
+
229
+ return callback;
230
+ }
231
+
178
232
  function setClasses( element, classes ) {
179
233
  if ( typeof classes == 'function' ) {
180
234
  reactive( classes, value => {
@@ -643,7 +697,7 @@ function createForDirective( template ) {
643
697
 
644
698
  if ( containsMatchedByValue && !watchingContent ) {
645
699
  destroyScope( context.scope );
646
- context.scope = [];
700
+ context.scope.watchers = [];
647
701
  withScope( context.scope, () => {
648
702
  // clone the array to watch all elements
649
703
  reactive( () => [ ...itemsGetter() ], updateContent );
@@ -844,6 +898,41 @@ function createSwitchDirective( template ) {
844
898
  }
845
899
  }
846
900
 
901
+ function createTryDirective( template ) {
902
+ if ( template.length != 3 || typeof template[ 2 ] != 'function' )
903
+ throw new TypeError( 'Invalid try template' );
904
+
905
+ const result = new DynamicNode( null );
906
+
907
+ const context = createChildContext();
908
+
909
+ try {
910
+ updateDynamicNode( result, context, template[ 1 ] );
911
+ } catch ( err ) {
912
+ updateErrorState( err );
913
+ return result;
914
+ }
915
+
916
+ context.errorHandler = asyncErrorHandler;
917
+
918
+ return result;
919
+
920
+ function asyncErrorHandler( err ) {
921
+ try {
922
+ updateErrorState( err );
923
+ } catch ( err ) {
924
+ handleError( context, err );
925
+ }
926
+ }
927
+
928
+ function updateErrorState( err ) {
929
+ context.errorHandler = null;
930
+ if ( result.content == null )
931
+ cleanUpContext( context );
932
+ updateDynamicNode( result, context, template[ 2 ]( err ) );
933
+ }
934
+ }
935
+
847
936
  const Directives = {
848
937
  dynamic: createDynamicDirective,
849
938
  else: unexpectedDirective,
@@ -851,6 +940,7 @@ const Directives = {
851
940
  if: createIfDirective,
852
941
  repeat: createRepeatDirective,
853
942
  switch: createSwitchDirective,
943
+ try: createTryDirective,
854
944
  };
855
945
 
856
946
  const Properties = new Set( [ 'value', 'checked', 'textContent', 'innerHTML' ] );
@@ -943,7 +1033,7 @@ function setElementProperties( element, properties ) {
943
1033
 
944
1034
  function setElementProperty( element, key, value ) {
945
1035
  if ( key.startsWith( 'on' ) ) {
946
- element.addEventListener( key.substring( 2 ), value );
1036
+ element.addEventListener( key.substring( 2 ), createSafeHandler( value ) );
947
1037
  } else if ( Properties.has( key ) ) {
948
1038
  if ( typeof value == 'function' )
949
1039
  reactive( value, value => element[ key ] = value != null ? value : '' );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leaner",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Thin, reactive wrapper for DOM",
5
5
  "type": "module",
6
6
  "files": [