leanweb 1.2.9 → 1.3.2

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/commands/init.js CHANGED
@@ -103,7 +103,8 @@ const require = createRequire(import.meta.url);
103
103
  });
104
104
  console.log('\nSome useful commands:');
105
105
  console.log('"lw s" to start the dev server.');
106
- console.log('"lw d" to build for production. The output will be in dist/ directory.');
106
+ console.log('"lw di" to build for production. The output will be in dist/ directory.');
107
107
  console.log('"lw g my-new-component" to generate a new standard web component.');
108
+ console.log('"lw h" to get more help information.');
108
109
  }
109
110
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leanweb",
3
- "version": "1.2.9",
3
+ "version": "1.3.2",
4
4
  "description": "Builds framework agnostic web components.",
5
5
  "bin": {
6
6
  "leanweb": "leanweb.js",
@@ -143,7 +143,6 @@ export default class LWElement extends HTMLElement {
143
143
  this._bindModels(rootNode);
144
144
  this._bindEvents(rootNode);
145
145
  this._bindInputs(rootNode);
146
- rootNode.removeAttribute('lw-elem-bind');
147
146
  }
148
147
  if (rootNode.hasAttribute('lw-if')) {
149
148
  this.updateIf(rootNode);
@@ -162,11 +161,17 @@ export default class LWElement extends HTMLElement {
162
161
  const treeWalker = document.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT, {
163
162
  acceptNode: node => {
164
163
  if (node.hasAttribute('lw-elem')) {
164
+ if (node.hasAttribute('lw-for')) {
165
+ this.updateFor(node);
166
+ return NodeFilter.FILTER_REJECT;
167
+ }
168
+ if (node.hasAttribute('lw-for-parent')) {
169
+ return NodeFilter.FILTER_REJECT;
170
+ }
165
171
  if (node.hasAttribute('lw-elem-bind')) {
166
172
  this._bindModels(node);
167
173
  this._bindEvents(node);
168
174
  this._bindInputs(node);
169
- node.removeAttribute('lw-elem-bind');
170
175
  }
171
176
  if (node.hasAttribute('lw-if')) {
172
177
  this.updateIf(node);
@@ -174,13 +179,6 @@ export default class LWElement extends HTMLElement {
174
179
  if (node.hasAttribute('lw-false')) {
175
180
  return NodeFilter.FILTER_REJECT;
176
181
  }
177
- if (node.hasAttribute('lw-for-parent')) {
178
- return NodeFilter.FILTER_REJECT;
179
- }
180
- if (node.hasAttribute('lw-for')) {
181
- this.updateFor(node);
182
- return NodeFilter.FILTER_REJECT;
183
- }
184
182
  this.updateEval(node);
185
183
  this.updateClass(node);
186
184
  this.updateBind(node);
@@ -202,7 +200,13 @@ export default class LWElement extends HTMLElement {
202
200
  });
203
201
  }
204
202
 
203
+ // properties:
204
+ // lw_input_bound: boolean
205
205
  _bindInputs(inputNode) {
206
+ if (inputNode['lw_input_bound']) {
207
+ return;
208
+ }
209
+ inputNode['lw_input_bound'] = true;
206
210
  for (const attr of inputNode.attributes) {
207
211
  const attrName = attr.name;
208
212
  const attrValue = attr.value;
@@ -218,31 +222,32 @@ export default class LWElement extends HTMLElement {
218
222
  }
219
223
 
220
224
  // properties:
221
- // lw-on:click: true
225
+ // lw_event_bound: boolean
222
226
  _bindEvents(eventNode) {
227
+ if (eventNode['lw_event_bound']) {
228
+ return;
229
+ }
230
+ eventNode['lw_event_bound'] = true;
223
231
  const me = this;
224
232
  for (const attr of eventNode.attributes) {
225
233
  const attrName = attr.name;
226
234
  const attrValue = attr.value;
227
235
  if (attrName.startsWith('lw-on:')) {
228
- if (eventNode[attrName]) {
229
- continue;
230
- }
231
- eventNode[attrName] = true;
232
236
  const interpolation = this.ast[attrValue];
233
237
  interpolation.lwValue.split(',').forEach(eventType => {
234
238
  eventNode.addEventListener(eventType.trim(), (event => {
235
239
  const context = this._getNodeContext(eventNode);
236
240
  const eventContext = { '$event': event, '$node': eventNode };
237
241
  const parsed = parser.evaluate(interpolation.ast, [eventContext, ...context], interpolation.loc);
238
-
239
- const promises = parsed.filter(p => typeof p?.then === 'function');
242
+ const promises = parsed.filter(p => typeof p?.then === 'function' && typeof p?.finally === 'function');
240
243
  if (parsed.length > promises.length) {
241
244
  me.update();
242
245
  }
243
- if (promises.length > 0) {
244
- Promise.allSettled(promises).then(_ => me.update());
245
- }
246
+ promises.forEach(p => {
247
+ p?.finally(() => {
248
+ me.update();
249
+ });
250
+ });
246
251
  }).bind(me));
247
252
  });
248
253
  }
@@ -250,16 +255,16 @@ export default class LWElement extends HTMLElement {
250
255
  }
251
256
 
252
257
  // properties:
253
- // model_event_bound: boolean
258
+ // lw_model_bound: boolean
254
259
  _bindModels(modelNode) {
255
260
  const key = modelNode.getAttribute('lw-model');
256
261
  if (!key) {
257
262
  return;
258
263
  }
259
- if (modelNode['model_event_bound']) {
264
+ if (modelNode['lw_model_bound']) {
260
265
  return;
261
266
  }
262
- modelNode['model_event_bound'] = true;
267
+ modelNode['lw_model_bound'] = true;
263
268
  const interpolation = this.ast[key];
264
269
  modelNode.addEventListener('input', (event => {
265
270
  const context = this._getNodeContext(modelNode);
@@ -24,6 +24,25 @@ const binaryOperations = {
24
24
  // '|>': (a, b) => a |> b,
25
25
  };
26
26
 
27
+ const assignmentOperations = {
28
+ '=': (c, a, b) => { c[a] = b; },
29
+ '+=': (c, a, b) => { c[a] += b; },
30
+ '-=': (c, a, b) => { c[a] -= b; },
31
+ '*=': (c, a, b) => { c[a] *= b; },
32
+ '/=': (c, a, b) => { c[a] /= b; },
33
+ '%=': (c, a, b) => { c[a] %= b; },
34
+ '**=': (c, a, b) => { c[a] **= b; },
35
+ '&&=': (c, a, b) => { c[a] &&= b; },
36
+ '??=': (c, a, b) => { c[a] ??= b; },
37
+ '||=': (c, a, b) => { c[a] ||= b; },
38
+ '>>=': (c, a, b) => { c[a] >>= b; },
39
+ '>>>=': (c, a, b) => { c[a] >>>= b; },
40
+ '<<=': (c, a, b) => { c[a] <<= b; },
41
+ '&=': (c, a, b) => { c[a] &= b; },
42
+ '|=': (c, a, b) => { c[a] |= b; },
43
+ '^=': (c, a, b) => { c[a] ^= b; },
44
+ };
45
+
27
46
  const logicalOperators = {
28
47
  '||': (a, b) => a || b,
29
48
  '&&': (a, b) => a && b,
@@ -43,9 +62,9 @@ const unaryOperators = {
43
62
 
44
63
  const updateOperators = (operator, prefix) => {
45
64
  if (operator === '++') {
46
- return prefix ? a => ++a : a => a++;
65
+ return (c, a) => prefix ? ++c[a] : c[a]++;
47
66
  } else if (operator === '--') {
48
- return prefix ? a => --a : a => a--;
67
+ return (c, a) => prefix ? --c[a] : c[a]--;
49
68
  }
50
69
  };
51
70
 
@@ -75,9 +94,16 @@ const nodeHandlers = {
75
94
 
76
95
  'ExpressionStatement': (node, context) => evalNode(node.expression, context),
77
96
  'BinaryExpression': (node, context) => binaryOperations[node.operator](evalNode(node.left, context), evalNode(node.right, context)),
97
+ 'AssignmentExpression': (node, context) => {
98
+ const immediateCtx = immediateContext(node.left, context);
99
+ assignmentOperations[node.operator](immediateCtx, node.left.name, evalNode(node.right, context));
100
+ },
78
101
  'LogicalExpression': (node, context) => logicalOperators[node.operator](evalNode(node.left, context), evalNode(node.right, context)),
79
102
  'UnaryExpression': (node, context) => unaryOperators[node.operator](evalNode(node.argument, context)),
80
- 'UpdateExpression': (node, context) => updateOperators(node.operator, node.prefix)(evalNode(node.argument, context)),
103
+ 'UpdateExpression': (node, context) => {
104
+ const immediateCtx = immediateContext(node.argument, context);
105
+ updateOperators(node.operator, node.prefix)(immediateCtx, node.argument.name, evalNode(node.argument, context));
106
+ },
81
107
  'ConditionalExpression': (node, context) => {
82
108
  const test = evalNode(node.test, context);
83
109
  const consequent = evalNode(node.consequent, context);
@@ -144,6 +170,14 @@ const nodeHandlers = {
144
170
  'DirectiveLiteral': (node, context) => node.value,
145
171
  };
146
172
 
173
+ const immediateContext = (node, context) => {
174
+ if (Array.isArray(context)) {
175
+ return context.find(contextObj => node.name in contextObj);
176
+ } else if (typeof context === 'object') {
177
+ return context;
178
+ }
179
+ }
180
+
147
181
  const evalNode = (node, context) => nodeHandlers[node.type](node, context);
148
182
 
149
183
  const evaluate = (ast, context = {}, loc = {}) => {