malinajs 0.7.0-a6 → 0.7.0-a9

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/malina.js +293 -111
  3. package/package.json +1 -1
  4. package/runtime.js +72 -13
package/CHANGELOG.md CHANGED
@@ -1,4 +1,16 @@
1
1
 
2
+ ## 0.7.x
3
+ * refactoring, optimization, fixes
4
+ * export function
5
+ * manual event delegation @click|root
6
+ * able to delay destroying block (for animations)
7
+ * be able to off autosubscribe for import: !no-autosubscribe
8
+ * destructuring array/object for each
9
+ * functions mount, mountStatic
10
+ * each, index variable is not included by default
11
+ * reference to element is removed on destroying
12
+ * config.useGroupReferencing
13
+
2
14
  ## 0.6.x
3
15
 
4
16
  * style's attribute "global"
package/malina.js CHANGED
@@ -179,22 +179,24 @@
179
179
  };
180
180
 
181
181
 
182
- const replaceElementKeyword = (exp, fn) => {
182
+ const replaceKeyword = (exp, fn) => {
183
183
  let changed = false;
184
184
  let r = parseJS(exp).transform((n, pk) => {
185
185
  if(n.type != 'Identifier') return;
186
186
  if(pk == 'property' || pk == 'params') return;
187
- if(n.name != '$element') return;
188
- n.name = fn();
189
- changed = true;
187
+ let name = fn(n.name);
188
+ if(name) {
189
+ n.name = name;
190
+ changed = true;
191
+ }
190
192
  });
191
- return changed ? r.build().trim() : exp;
193
+ return changed ? r.build() : exp;
192
194
  };
193
195
 
194
196
 
195
197
  const parseJS = (exp) => {
196
198
  let self = {};
197
- self.ast = acorn.parse(exp, { sourceType: 'module', ecmaVersion: 12 });
199
+ self.ast = acorn.parseExpressionAt(exp, 0, { ecmaVersion: 12 });
198
200
 
199
201
  self.transform = function(fn) {
200
202
  const rec = (n, pk) => {
@@ -203,11 +205,11 @@
203
205
  self = n;
204
206
  fn?.(n, pk);
205
207
  }
206
-
208
+
207
209
  for(let k in n) {
208
210
  if(k == '_parent') continue;
209
211
  let v = n[k];
210
- if(v == null || typeof(v) != 'object') continue;
212
+ if(v == null || typeof (v) != 'object') continue;
211
213
  if(Array.isArray(v)) {
212
214
  v.forEach(i => {
213
215
  i._parent = self || n._parent;
@@ -225,7 +227,7 @@
225
227
  };
226
228
 
227
229
  self.build = function(data) {
228
- return astring.generate(data || self.ast);
230
+ return astring.generate(data || self.ast, { indent: '', lineEnd: '' });
229
231
  };
230
232
  return self;
231
233
  };
@@ -420,7 +422,7 @@
420
422
  if(_data === false && !_handler) {
421
423
  handler = noop;
422
424
  data = null;
423
- } else if(_handler === false && typeof(_data) == 'object') {
425
+ } else if(_handler === false && typeof (_data) == 'object') {
424
426
  handler = noop;
425
427
  data = _data;
426
428
  } else if(typeof _data == 'function') {
@@ -466,7 +468,7 @@
466
468
  const resolveDependecies = node => {
467
469
  if(node.$wait) {
468
470
  node.$wait = node.$wait.map(n => {
469
- if(typeof(n) == 'string') {
471
+ if(typeof (n) == 'string') {
470
472
  const context = get_context();
471
473
  assert(context.glob[n], `Wrong dependency '${n}'`);
472
474
  n = context.glob[n];
@@ -477,7 +479,7 @@
477
479
 
478
480
  if(node.$hold) {
479
481
  node.$hold.forEach(n => {
480
- if(typeof(n) == 'string') {
482
+ if(typeof (n) == 'string') {
481
483
  const context = get_context();
482
484
  assert(context.glob[n], `Wrong dependency '${n}'`);
483
485
  n = context.glob[n];
@@ -643,7 +645,8 @@
643
645
  cloneNode = false;
644
646
  if(!node.raw) template = htmlEntitiesToText(template);
645
647
  } else {
646
- convert = '$$htmlToFragment';
648
+ if(get_context().config.hideLabel) convert = '$runtime.$$htmlToFragmentClean';
649
+ else convert = '$runtime.$$htmlToFragment';
647
650
  template = template.replace(/<!---->/g, '<>');
648
651
  }
649
652
  if(node.raw) {
@@ -1301,9 +1304,9 @@
1301
1304
  }
1302
1305
  const onComment = (isBlockComment, value, start, end) => {
1303
1306
  if(isBlockComment) return;
1304
- this.script.comments.push({start, end, value});
1307
+ this.script.comments.push({ start, end, value });
1305
1308
  };
1306
- this.script.ast = acorn.parse(source, {sourceType: 'module', ecmaVersion: 12, onComment});
1309
+ this.script.ast = acorn.parse(source, { sourceType: 'module', ecmaVersion: 12, onComment });
1307
1310
 
1308
1311
  if(source.includes('$props')) this.require('$props');
1309
1312
  if(source.includes('$attributes')) this.require('$attributes');
@@ -1556,6 +1559,8 @@
1556
1559
  });
1557
1560
  }
1558
1561
 
1562
+ let exportedFunctions = [];
1563
+
1559
1564
  ast.body.forEach(n => {
1560
1565
  if(n.type == 'ImportDeclaration') {
1561
1566
  imports.push(n);
@@ -1571,8 +1576,14 @@
1571
1576
  });
1572
1577
  return;
1573
1578
  } else if(n.type == 'ExportNamedDeclaration') {
1574
- if(n.declaration.kind != 'const') constantProps = false;
1579
+ if(n.declaration.type == 'FunctionDeclaration') {
1580
+ exportedFunctions.push(n.declaration.id.name);
1581
+ resultBody.push(n.declaration);
1582
+ return;
1583
+ }
1584
+
1575
1585
  assert(n.declaration.type == 'VariableDeclaration', 'Wrong export');
1586
+ if(n.declaration.kind != 'const') constantProps = false;
1576
1587
  n.declaration.declarations.forEach(d => {
1577
1588
  assert(d.type == 'VariableDeclarator', 'Wrong export');
1578
1589
  let p = { name: d.id.name };
@@ -1674,6 +1685,15 @@
1674
1685
  });
1675
1686
  }));
1676
1687
  }
1688
+
1689
+ this.module.code.push(xNode('exported-functions', {
1690
+ $hold: ['$component'],
1691
+ list: exportedFunctions
1692
+ }, (ctx, n) => {
1693
+ if(!n.list.length) return;
1694
+ this.require('$component');
1695
+ for(let name of n.list) ctx.write(true, `$component.${name} = ${name};`);
1696
+ }));
1677
1697
  }
1678
1698
 
1679
1699
 
@@ -1732,7 +1752,7 @@
1732
1752
  }));
1733
1753
 
1734
1754
  this.module.head.push(xNode('$context', {
1735
- $hold: ['componentFn'],
1755
+ $hold: ['componentFn']
1736
1756
  }, (ctx) => {
1737
1757
  if(this.inuse.$context) {
1738
1758
  this.require('componentFn');
@@ -1785,17 +1805,37 @@
1785
1805
  let bb = this.buildBlock(this.DOM, {
1786
1806
  inline: true,
1787
1807
  protectLastTag: true,
1808
+ allowSingleBlock: true,
1788
1809
  template: {
1789
1810
  name: '$parentElement',
1790
1811
  cloneNode: true
1791
1812
  }
1792
1813
  });
1793
- runtime.push(bb.template);
1794
- runtime.push(xNode('root-event', (ctx) => {
1795
- if(!this.inuse.rootEvent) return;
1796
- ctx.write(true, 'const $$addRootEvent = $runtime.makeRootEvent($parentElement);');
1797
- }));
1798
- runtime.push(bb.source);
1814
+ if(bb.singleBlock) {
1815
+ runtime.push(xNode('attach-block', {
1816
+ block: bb.singleBlock,
1817
+ reference: bb.reference
1818
+ }, (ctx, n) => {
1819
+ if(n.reference) {
1820
+ ctx.write(true, `${n.reference} = `);
1821
+ ctx.add(n.block);
1822
+ ctx.write(';');
1823
+ ctx.write(true, `let $parentElement = ${n.reference}.$dom;`);
1824
+ } else {
1825
+ ctx.write(true, `let $parentElement = `);
1826
+ ctx.add(n.block);
1827
+ ctx.write('.$dom;');
1828
+ }
1829
+ }));
1830
+ } else {
1831
+ runtime.push(bb.template);
1832
+ runtime.push(xNode('root-event', (ctx) => {
1833
+ if(!this.inuse.rootEvent) return;
1834
+ ctx.write(true, 'const $$addRootEvent = $runtime.makeRootEvent($parentElement);');
1835
+ }));
1836
+ runtime.push(bb.source);
1837
+ }
1838
+
1799
1839
 
1800
1840
  if(this.script.onMount) runtime.push('$runtime.$onMount(onMount);');
1801
1841
  if(this.script.onDestroy) runtime.push('$runtime.$onDestroy(onDestroy);');
@@ -1857,13 +1897,21 @@
1857
1897
 
1858
1898
  if(option.each?.blockPrefix) binds.push(option.each.blockPrefix);
1859
1899
 
1860
- if(option.allowSingleBlock && data.body.length == 1) {
1861
- let n = data.body[0];
1862
- if(n.type == 'node' && n.name.match(/^[A-Z]/)) {
1863
- let component = this.makeComponent(n);
1864
- return {
1865
- singleBlock: component.bind
1866
- };
1900
+ if(option.allowSingleBlock) {
1901
+ let nodesForSingleBlock = data.body.filter(n => {
1902
+ if(n.type == 'comment' && !this.config.preserveComments) return false;
1903
+ return true;
1904
+ });
1905
+
1906
+ if(nodesForSingleBlock.length == 1) {
1907
+ let n = nodesForSingleBlock[0];
1908
+ if(n.type == 'node' && n.name.match(/^[A-Z]/)) {
1909
+ let component = this.makeComponent(n);
1910
+ return {
1911
+ singleBlock: component.bind,
1912
+ reference: component.reference
1913
+ };
1914
+ }
1867
1915
  }
1868
1916
  }
1869
1917
 
@@ -1969,7 +2017,7 @@
1969
2017
  if(!exp.endsWith(';')) exp += ';';
1970
2018
  binds.push(xNode('block', {
1971
2019
  body: [
1972
- replaceElementKeyword(exp, () => textNode.bindName())
2020
+ replaceKeyword(exp, (name) => name == '$element' ? textNode.bindName() : null)
1973
2021
  ]
1974
2022
  }));
1975
2023
  });
@@ -2006,11 +2054,18 @@
2006
2054
  let component = this.makeComponent(n);
2007
2055
  binds.push(xNode('insert-component', {
2008
2056
  component: component.bind,
2057
+ reference: component.reference,
2009
2058
  el: el.bindName()
2010
2059
  }, (ctx, n) => {
2011
- ctx.write(true, `$runtime.insertAfter(${n.el}, `);
2012
- ctx.add(n.component);
2013
- ctx.write('.$dom);');
2060
+ if(n.reference) {
2061
+ ctx.write(true, `${n.reference} = `);
2062
+ ctx.add(n.component);
2063
+ ctx.write(true, `$runtime.attachBlock(${n.el}, ${n.reference});`);
2064
+ } else {
2065
+ ctx.write(true, `$runtime.attachBlock(${n.el}, `);
2066
+ ctx.add(n.component);
2067
+ ctx.write(');');
2068
+ }
2014
2069
  }));
2015
2070
  }
2016
2071
  } else {
@@ -2185,26 +2240,152 @@
2185
2240
  root: option.parentElement,
2186
2241
  single: rootTemplate.children.length == 1 && !requireFragment
2187
2242
  }, (ctx, n) => {
2188
- const gen = (parent, parentName) => {
2189
- for(let i = 0; i < parent.children.length; i++) {
2190
- let node = parent.children[i];
2191
- let diff = i == 0 ? '[$runtime.firstChild]' : `[$runtime.childNodes][${i}]`;
2243
+ if(this.config.useGroupReferencing) {
2244
+ const mark = (node) => {
2245
+ let binding = false;
2246
+ let next = false;
2247
+
2248
+ if(node._boundName) binding = true;
2192
2249
 
2193
- if(node._boundName) ctx.write(true, `let ${node._boundName} = ${parentName() + diff};`);
2250
+ if(node.children?.length) {
2251
+ let i = node.children.length - 1;
2252
+ for(;i >= 0;i--) {
2253
+ let n = node.children[i];
2254
+
2255
+ if(mark(n)) {
2256
+ if(next) n.bindName();
2257
+ next = true;
2258
+ binding = true;
2259
+ node._innerBinding = true;
2260
+ }
2261
+ }
2262
+ }
2263
+ return binding;
2264
+ };
2265
+ mark(n.tpl);
2266
+
2267
+ const encodeShift = (i) => {
2268
+ if(i <= 42) return String.fromCharCode(48 + i);
2269
+ let b = i % 42;
2270
+ let a = (i - b) / 42;
2271
+ assert(a <= 42, 'Node-shift overflow: ' + i);
2272
+ return '!' + String.fromCharCode(48 + a) + String.fromCharCode(48 + b);
2273
+ };
2274
+
2275
+ const encodeRef = (i) => {
2276
+ if(i <= 26) return String.fromCharCode(97 + i);
2277
+ let b = i % 42;
2278
+ let a = (i - b) / 42;
2279
+ assert(a <= 42, 'Node ref overflow: ' + i);
2280
+ return '#' + String.fromCharCode(48 + a) + String.fromCharCode(48 + b);
2281
+ };
2282
+
2283
+ let result = [];
2284
+ let vars = [];
2285
+ let active = null;
2286
+
2287
+ const walk = (node) => {
2288
+ let shift = 0;
2289
+ let base = null;
2290
+ node.children?.forEach((n, i) => {
2291
+ if(i == 0) {
2292
+ if(n._boundName) {
2293
+ result.push('+');
2294
+ vars.push(n);
2295
+ active = n;
2296
+ walk(n);
2297
+ if(n != active) base = n;
2298
+ } else if(n._innerBinding) {
2299
+ result.push('>');
2300
+ active = n;
2301
+ walk(n);
2302
+ } else if(node._innerBinding) {
2303
+ result.push('>');
2304
+ active = n;
2305
+ walk(n);
2306
+ }
2307
+ } else {
2308
+ if(n._boundName) {
2309
+ if(base) {
2310
+ let x = vars.indexOf(base);
2311
+ result.push(encodeRef(x));
2312
+ base = null;
2313
+ }
2314
+ result.push(encodeShift(shift));
2315
+ result.push('.');
2316
+ shift = 0;
2317
+ active = n;
2318
+ vars.push(n);
2319
+ walk(n);
2320
+ if(n != active) base = n;
2321
+ } else if(n._innerBinding) {
2322
+ if(base) {
2323
+ let x = vars.indexOf(base);
2324
+ result.push(encodeRef(x));
2325
+ base = null;
2326
+ }
2327
+ result.push(encodeShift(shift));
2328
+ active = n;
2329
+ walk(n);
2330
+ }
2331
+ }
2332
+ shift++;
2333
+ });
2334
+ };
2335
+
2336
+ if(n.single) {
2337
+ let node = n.tpl.children[0];
2338
+ if(node._boundName) ctx.write(true, `let ${node._boundName} = ${n.root};`);
2194
2339
  if(node.children) {
2195
- gen(node, () => {
2196
- if(node._boundName) return node._boundName;
2197
- return parentName() + diff;
2198
- });
2340
+ walk(node);
2341
+ if(vars.length) {
2342
+ result = result.join('');
2343
+ vars = vars.map(v => v._boundName).join(', ');
2344
+ ctx.write(true, `let [${vars}] = $runtime.refer(${n.root}, '${result}');`);
2345
+ }
2346
+ }
2347
+ } else {
2348
+ walk(n.tpl);
2349
+ if(vars.length) {
2350
+ result = result.join('');
2351
+ vars = vars.map(v => v._boundName).join(', ');
2352
+ ctx.write(true, `let [${vars}] = $runtime.refer(${n.root}, '${result}');`);
2199
2353
  }
2200
2354
  }
2201
- };
2202
- if(n.single) {
2203
- let node = n.tpl.children[0];
2204
- if(node._boundName) ctx.write(true, `let ${node._boundName} = ${n.root};`);
2205
- if(node.children) gen(node, () => n.root);
2206
2355
  } else {
2207
- gen(n.tpl, () => n.root);
2356
+ const walk = p => {
2357
+ if(p.children?.length) {
2358
+ let col = 0;
2359
+ for(let n of p.children) {
2360
+ col += walk(n);
2361
+ }
2362
+ if(col > 1 && !p._boundName) p.bindName();
2363
+ return col ? 1 : 0;
2364
+ } return p._boundName ? 1 : 0;
2365
+ };
2366
+ walk(n.tpl);
2367
+
2368
+ const gen = (parent, parentName) => {
2369
+ for(let i = 0; i < parent.children.length; i++) {
2370
+ let node = parent.children[i];
2371
+ let diff = i == 0 ? '[$runtime.firstChild]' : `[$runtime.childNodes][${i}]`;
2372
+
2373
+ if(node._boundName) ctx.write(true, `let ${node._boundName} = ${parentName() + diff};`);
2374
+ if(node.children) {
2375
+ gen(node, () => {
2376
+ if(node._boundName) return node._boundName;
2377
+ return parentName() + diff;
2378
+ });
2379
+ }
2380
+ }
2381
+ };
2382
+ if(n.single) {
2383
+ let node = n.tpl.children[0];
2384
+ if(node._boundName) ctx.write(true, `let ${node._boundName} = ${n.root};`);
2385
+ if(node.children) gen(node, () => n.root);
2386
+ } else {
2387
+ gen(n.tpl, () => n.root);
2388
+ }
2208
2389
  }
2209
2390
  }));
2210
2391
  }
@@ -4427,7 +4608,10 @@
4427
4608
  let n = new Node(e.name, { __node: e });
4428
4609
  e.attributes.forEach(a => {
4429
4610
  if(a.name == 'class') {
4430
- if(a.value != null) n.className += ' ' + a.value;
4611
+ if(a.value != null) {
4612
+ if(a.value.includes('{')) n.dynClass = true;
4613
+ else n.className += ' ' + a.value;
4614
+ }
4431
4615
  n.attributes[a.name] = a.value;
4432
4616
  } else if(a.name == 'id') n.attributes.id = n.id = a.value;
4433
4617
  else if(a.name.startsWith('class:')) {
@@ -4507,18 +4691,20 @@
4507
4691
  if(names.length != 1) throw 'Not supported';
4508
4692
  let cls = names[0];
4509
4693
 
4694
+ let rx = RegExp('(^|\\s)' + cls + '(\\s|$)', 'i');
4510
4695
  let result = [];
4511
- this.childNodes.forEach(n => {
4512
- let rx = RegExp('(^|\\s)' + cls + '(\\s|$)', 'i');
4513
- if(rx.test(n.className)) result.push(n);
4514
- result.push.apply(result, n.getElementsByClassName(cls));
4515
- });
4696
+ const walk = (node) => {
4697
+ node.childNodes.forEach(n => {
4698
+ if(n.dynClass) result.push(n);
4699
+ else if(rx.test(n.className)) result.push(n);
4700
+ walk(n);
4701
+ });
4702
+ };
4703
+ walk(this);
4516
4704
  return result;
4517
4705
  };
4518
4706
 
4519
4707
  function makeComponent(node) {
4520
- this.require('apply');
4521
-
4522
4708
  let propList = node.attributes;
4523
4709
 
4524
4710
  this.require('$context');
@@ -4531,7 +4717,7 @@
4531
4717
  let componentName = node.name;
4532
4718
  if(componentName != 'component' && this.config.autoimport) {
4533
4719
  let imported = this.script.autoimport[componentName] || this.script.importedNames.includes(componentName) ||
4534
- this.script.rootVariables[componentName] || this.script.rootFunctions[componentName];
4720
+ this.script.rootVariables[componentName] || this.script.rootFunctions[componentName];
4535
4721
 
4536
4722
  if(!imported) {
4537
4723
  let r = this.config.autoimport(componentName, this.config.path, this);
@@ -4616,7 +4802,7 @@
4616
4802
  if(n.props) ctx.write(', $localProps');
4617
4803
  ctx.write(') => {', true);
4618
4804
  ctx.indent++;
4619
- if(n.props) ctx.write(true, `let {${n.props.join(', ')}} = $localProps;`);
4805
+ if(n.props) ctx.write(true, `let {${n.props.join(', ')}} = $localProps || {};`);
4620
4806
  ctx.add(n.bind);
4621
4807
 
4622
4808
  if(n.props && this.inuse.apply) ctx.write(true, `return ($localProps) => ({${n.props.join(', ')}} = $localProps, $$apply());`);
@@ -4758,14 +4944,12 @@
4758
4944
  staticProps,
4759
4945
  props: propsFn,
4760
4946
  propsSetter,
4761
- reference,
4762
4947
  $class,
4763
4948
  forwardAllEvents,
4764
4949
  events,
4765
4950
  slots: slotBlocks.length ? slotBlocks : null,
4766
4951
  anchors: anchorBlocks.length ? anchorBlocks : null
4767
4952
  }, (ctx, n) => {
4768
- if(n.reference) throw 'not implemented'; // FIXME
4769
4953
  let comma = false;
4770
4954
  ctx.write(`$runtime.callComponent($context, ${n.componentName}, {`);
4771
4955
 
@@ -4779,10 +4963,11 @@
4779
4963
  if(comma) ctx.write(', ');
4780
4964
  comma = true;
4781
4965
  ctx.write('events: $events');
4782
- } else if(n.events && !n.forwardAllEvents) {
4966
+ } else if(n.events) {
4783
4967
  if(comma) ctx.write(',', true);
4784
4968
  comma = true;
4785
- ctx.write('events: {');
4969
+ if(n.forwardAllEvents) ctx.write('events: $runtime.mergeAllEvents($events, {');
4970
+ else ctx.write('events: {');
4786
4971
  ctx.indent++;
4787
4972
  ctx.write(true);
4788
4973
  Object.entries(n.events).forEach(([event, list], index) => {
@@ -4799,9 +4984,8 @@
4799
4984
  }
4800
4985
  });
4801
4986
  ctx.indent--;
4802
- ctx.write(true, '}');
4803
- } else if(n.events && n.forwardAllEvents) {
4804
- throw 'not implemented'; // FIXME
4987
+ if(n.forwardAllEvents) ctx.write(true, '})');
4988
+ else ctx.write(true, '}');
4805
4989
  }
4806
4990
  if(n.slots) {
4807
4991
  if(comma) ctx.write(', ');
@@ -4829,6 +5013,11 @@
4829
5013
  ctx.indent--;
4830
5014
  ctx.write(true, '}');
4831
5015
  }
5016
+ if(n.$class.length && !ctx.inuse.apply) {
5017
+ if(comma) ctx.write(', ');
5018
+ comma = true;
5019
+ ctx.write(`$class: {${n.$class.join(', ')}}`);
5020
+ }
4832
5021
  ctx.write('}');
4833
5022
 
4834
5023
  let other = '';
@@ -4850,7 +5039,7 @@
4850
5039
  ctx.write(',\n', true, `($$_value) => ({${n.propsSetter.join(', ')}} = $$_value)`);
4851
5040
  } else other += ', null';
4852
5041
 
4853
- if(n.$class.length) {
5042
+ if(n.$class.length && ctx.inuse.apply) {
4854
5043
  if(other) ctx.write(other);
4855
5044
  other = '';
4856
5045
  ctx.write(',\n', true, `() => ({${n.$class.join(', ')}})`);
@@ -4860,7 +5049,7 @@
4860
5049
  ctx.write(true, ')');
4861
5050
  });
4862
5051
 
4863
- return { bind: result };
5052
+ return { bind: result, reference };
4864
5053
  }
4865
5054
 
4866
5055
  function makeComponentDyn(node, element) {
@@ -4878,17 +5067,20 @@
4878
5067
  }
4879
5068
 
4880
5069
  assert(dynamicComponent);
5070
+ this.require('apply');
4881
5071
  this.detectDependency(dynamicComponent);
4882
5072
 
4883
- let component = this.makeComponent(node).bind;
5073
+ let { bind: component, reference } = this.makeComponent(node);
4884
5074
 
4885
5075
  component.componentName = '$ComponentConstructor';
4886
5076
  return xNode('dyn-component', {
4887
5077
  el: element.bindName(),
4888
5078
  exp: dynamicComponent,
4889
- component
5079
+ component,
5080
+ reference
4890
5081
  }, (ctx, n) => {
4891
5082
  ctx.write(true, `$runtime.attachDynComponent(${n.el}, () => ${n.exp}, ($ComponentConstructor) => `);
5083
+ if(n.reference) ctx.write(`${n.reference} = `);
4892
5084
  ctx.add(n.component);
4893
5085
  ctx.write(')');
4894
5086
  });
@@ -4905,7 +5097,7 @@
4905
5097
  return {
4906
5098
  bind: xNode('block', {
4907
5099
  body: [
4908
- replaceElementKeyword(exp, () => element.bindName())
5100
+ replaceKeyword(exp, (name) => name == '$element' ? element.bindName() : null)
4909
5101
  ]
4910
5102
  })
4911
5103
  };
@@ -4959,13 +5151,15 @@
4959
5151
  if(name[0] == '#') {
4960
5152
  let target = name.substring(1);
4961
5153
  assert(detectExpressionType(target) == 'identifier', name);
4962
- return { bind: xNode('reference-to-element', {
4963
- target,
4964
- el: element.bindName()
4965
- }, (ctx, n) => {
4966
- ctx.write(true, `${n.target} = ${n.el};`);
4967
- ctx.write(true, `$runtime.$onDestroy(() => ${n.target} = null);`);
4968
- })};
5154
+ return {
5155
+ bind: xNode('reference-to-element', {
5156
+ target,
5157
+ el: element.bindName()
5158
+ }, (ctx, n) => {
5159
+ ctx.write(true, `${n.target} = ${n.el};`);
5160
+ ctx.write(true, `$runtime.$onDestroy(() => ${n.target} = null);`);
5161
+ })
5162
+ };
4969
5163
  } else if(name == 'event') {
4970
5164
  if(prop.name.startsWith('@@')) {
4971
5165
  assert(!prop.value);
@@ -5408,15 +5602,8 @@
5408
5602
  right = right.trim();
5409
5603
 
5410
5604
  const makeKeyFunction = (keyLink) => {
5411
- const e = parseJS(keyName).transform((n, pk) => {
5412
- if(n.type != 'Identifier') return;
5413
- if(pk == 'property') return;
5414
- let r = keyLink[n.name];
5415
- if(r) n.name = r;
5416
- });
5417
- let exp = e.build(e.ast.body[0].expression);
5418
5605
  keyFunction = xNode('key-function', {
5419
- exp
5606
+ exp: replaceKeyword(keyName, n => keyLink[n])
5420
5607
  }, (ctx, n) => {
5421
5608
  ctx.write(`($$item, $index) => ${n.exp}`);
5422
5609
  });
@@ -5428,22 +5615,19 @@
5428
5615
  try {
5429
5616
  let exp = `[${right}]`;
5430
5617
  let e = parseJS(exp);
5431
- assert(e.ast.body.length == 1);
5432
-
5618
+ assert(e.ast.elements.length == 1 || e.ast.elements.length == 2);
5433
5619
  itemName = '$$item';
5434
- let n = e.ast.body[0];
5435
- assert(n.expression.elements.length == 1 || n.expression.elements.length == 2);
5436
- let a = n.expression.elements[0];
5437
- unwrap = exp.substring(a.start, a.end);
5438
-
5439
- if(n.expression.elements.length == 2) {
5440
- let b = n.expression.elements[1];
5620
+
5621
+ unwrap = e.build(e.ast.elements[0]);
5622
+
5623
+ if(e.ast.elements.length == 2) {
5624
+ let b = e.ast.elements[1];
5441
5625
  assert(b.type == 'Identifier');
5442
- indexName = exp.substring(b.start, b.end);
5626
+ indexName = e.build(b);
5443
5627
  }
5444
5628
 
5445
5629
  e = parseJS(`(${unwrap} = $$item)`);
5446
- let l = e.ast.body[0].expression.left;
5630
+ let l = e.ast.left;
5447
5631
  if(l.type == 'ArrayPattern') {
5448
5632
  keywords = l.elements.map(p => p.name);
5449
5633
 
@@ -5486,7 +5670,7 @@
5486
5670
  if(keyName) {
5487
5671
  if(keyName == itemName) keyFunction = 'noop';
5488
5672
  else {
5489
- let keyLink = {[itemName]: '$$item'};
5673
+ let keyLink = { [itemName]: '$$item' };
5490
5674
  if(indexName) keyLink[indexName] = '$index';
5491
5675
  makeKeyFunction(keyLink);
5492
5676
  }
@@ -5531,7 +5715,7 @@
5531
5715
  }, (ctx, n) => {
5532
5716
  ctx.write(`$runtime.makeEachSingleBlock((${n.itemName}`);
5533
5717
  if(n.indexName) ctx.write(`, ${n.indexName}`);
5534
- ctx.write(`) => [`);
5718
+ ctx.write(') => [');
5535
5719
  ctx.indent++;
5536
5720
  ctx.write(true);
5537
5721
  if(n.rebind) ctx.add(n.rebind);
@@ -5675,11 +5859,11 @@
5675
5859
  name: slotName,
5676
5860
  props,
5677
5861
  staticProps,
5678
- placeholder,
5862
+ placeholder
5679
5863
  }, (ctx, n) => {
5680
5864
  let dynamicProps = this.inuse.apply && !n.staticProps;
5681
5865
 
5682
- let missed = '', slotName = n.name == 'default' ? 'null' : n.name;
5866
+ let missed = '', slotName = n.name == 'default' ? 'null' : `'${n.name}'`;
5683
5867
  if(dynamicProps) ctx.write(`$runtime.invokeSlot($component, ${slotName}, $context`);
5684
5868
  else ctx.write(`$runtime.invokeSlotBase($component, ${slotName}, $context`);
5685
5869
 
@@ -6197,7 +6381,9 @@
6197
6381
  if(prop.value) {
6198
6382
  assert$1(!handler);
6199
6383
  exp = unwrapExp(prop.value);
6200
- exp = replaceElementKeyword(exp, requireElement);
6384
+ exp = replaceKeyword(exp, (name) => {
6385
+ if(name == '$element') return requireElement();
6386
+ });
6201
6387
  } else if(!handler) handler = event;
6202
6388
 
6203
6389
  this.detectDependency(exp || handler);
@@ -6296,7 +6482,7 @@
6296
6482
  return { event, fn, rootModifier };
6297
6483
  }
6298
6484
 
6299
- const version = '0.7.0-a6';
6485
+ const version = '0.7.0-a9';
6300
6486
 
6301
6487
 
6302
6488
  async function compile(source, config = {}) {
@@ -6314,7 +6500,8 @@
6314
6500
  debug: true,
6315
6501
  css: true,
6316
6502
  passClass: true,
6317
- immutable: false
6503
+ immutable: false,
6504
+ useGroupReferencing: true
6318
6505
  }, config);
6319
6506
 
6320
6507
  const ctx = {
@@ -6439,11 +6626,6 @@
6439
6626
  const result = ctx.result = xNode('block');
6440
6627
  result.push('import * as $runtime from \'malinajs/runtime.js\';');
6441
6628
  result.push('import { $watch, $tick } from \'malinajs/runtime.js\';');
6442
- if(config.hideLabel) {
6443
- result.push('import { $$htmlToFragmentClean as $$htmlToFragment } from \'malinajs/runtime.js\';');
6444
- } else {
6445
- result.push('import { $$htmlToFragment } from \'malinajs/runtime.js\';');
6446
- }
6447
6629
  result.push(ctx.module.top);
6448
6630
  result.push(xNode('componentFn-wrapper', {
6449
6631
  $compile: [ctx.module.head, ctx.module.code, ctx.module.body, ctx.glob.rootCD],
@@ -6454,7 +6636,7 @@
6454
6636
  else ctx.write(true, `const ${n.name} = `);
6455
6637
  ctx.add(n.componentFn);
6456
6638
  }));
6457
-
6639
+
6458
6640
  ctx.result = xBuild(result);
6459
6641
  });
6460
6642
 
@@ -6507,7 +6689,7 @@
6507
6689
 
6508
6690
  if(localConfig) {
6509
6691
  const confFn = require(localConfig);
6510
- if(typeof(confFn) == 'function') result = confFn(result, filename);
6692
+ if(typeof (confFn) == 'function') result = confFn(result, filename);
6511
6693
  else result = confFn;
6512
6694
  }
6513
6695
  if(!result.path) result.path = filename;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "malinajs",
3
- "version": "0.7.0-a6",
3
+ "version": "0.7.0-a9",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "prepare": "npm run build",
package/runtime.js CHANGED
@@ -432,7 +432,7 @@ const callComponent = (context, component, option = {}, propFn, cmp, setter, cla
432
432
  option.props = parentWatch.fn();
433
433
  $component.push();
434
434
  });
435
- Object.assign(w, {idle: true, cmp, value: parentWatch.value});
435
+ Object.assign(w, { idle: true, cmp, value: parentWatch.value });
436
436
  $component.$cd.watchers.push(w);
437
437
  }
438
438
 
@@ -632,7 +632,7 @@ const makeAnchor = (fn) => {
632
632
  } finally {
633
633
  current_cd = prev;
634
634
  }
635
- }
635
+ };
636
636
  };
637
637
 
638
638
 
@@ -737,7 +737,7 @@ const makeBlockBound = (fr, fn) => {
737
737
  } finally {
738
738
  current_cd = prev;
739
739
  }
740
- }
740
+ };
741
741
  };
742
742
 
743
743
 
@@ -751,6 +751,15 @@ const mergeEvents = (...callbacks) => {
751
751
  return (e) => callbacks.forEach(cb => cb(e));
752
752
  };
753
753
 
754
+ const mergeAllEvents = ($events, local) => {
755
+ let result = Object.assign({}, $events);
756
+ for(let e in local) {
757
+ if(result[e]) result[e] = mergeEvents($events[e], local[e]);
758
+ else result[e] = local[e];
759
+ }
760
+ return result;
761
+ };
762
+
754
763
  const makeRootEvent = (root) => {
755
764
  let events = {}, nodes = [];
756
765
 
@@ -806,6 +815,51 @@ const mount = (label, component, option) => {
806
815
  return app;
807
816
  };
808
817
 
818
+ const mountStatic = (label, component, option) => {
819
+ current_destroyList = [];
820
+ try {
821
+ let app = component(option);
822
+ label.appendChild(app.$dom);
823
+ return app;
824
+ } finally {
825
+ current_destroyList = null;
826
+ }
827
+ };
828
+
829
+ const refer = (active, line) => {
830
+ let result = [], i, v;
831
+ const code = (x, d) => x.charCodeAt() - d;
832
+
833
+ for(i = 0; i < line.length; i++) {
834
+ let a = line[i];
835
+ switch (a) {
836
+ case '>':
837
+ active = active.firstChild;
838
+ break;
839
+ case '+':
840
+ active = active.firstChild;
841
+ case '.':
842
+ result.push(active);
843
+ break;
844
+ case '!':
845
+ v = code(line[++i], 48) * 42 + code(line[++i], 48);
846
+ while(v--) active = active.nextSibling;
847
+ break;
848
+ case '#':
849
+ active = result[code(line[++i], 48) * 26 + code(line[++i], 48)];
850
+ break;
851
+ default:
852
+ v = code(a, 0);
853
+ if(v >= 97) active = result[v - 97];
854
+ else {
855
+ v -= 48;
856
+ while(v--) active = active.nextSibling;
857
+ }
858
+ }
859
+ }
860
+ return result;
861
+ };
862
+
809
863
  let create = (tag, html) => {
810
864
  let fr;
811
865
  if(tag.parentElement instanceof SVGElement) {
@@ -1097,14 +1151,14 @@ function $$eachBlock(label, onlyChild, fn, getKey, bind) {
1097
1151
  d = current_destroyList = [],
1098
1152
  $cd = current_cd = cd_new();
1099
1153
  try {
1100
- ([ $dom, rebind ] = bind(item, i));
1154
+ ([$dom, rebind] = bind(item, i));
1101
1155
  } finally {
1102
1156
  current_destroyList = null;
1103
1157
  current_cd = null;
1104
1158
  }
1105
1159
  if(d.length) p_destroy = 1;
1106
1160
  else d = null;
1107
- ctx = {$cd, d, rebind};
1161
+ ctx = { $cd, d, rebind };
1108
1162
  cd_attach2(eachCD, $cd);
1109
1163
  if($dom.nodeType == 11) {
1110
1164
  ctx.first = $dom[firstChild];
@@ -1123,19 +1177,24 @@ function $$eachBlock(label, onlyChild, fn, getKey, bind) {
1123
1177
 
1124
1178
  const invokeSlotBase = ($component, slotName, $context, props, placeholder) => {
1125
1179
  let $slot = $component.$option.slots?.[slotName || 'default'];
1126
- return $slot ? $slot($component, $context, props)[0] : placeholder?.();
1180
+ return $slot ? $slot($component, $context, props) : placeholder?.();
1127
1181
  };
1128
1182
 
1129
1183
  const invokeSlot = ($component, slotName, $context, propsFn, placeholder, cmp) => {
1130
1184
  let $slot = $component.$option.slots?.[slotName || 'default'];
1131
1185
 
1132
1186
  if($slot) {
1133
- let push, $dom,
1134
- w = new WatchObject(propsFn, value => push(value));
1135
- Object.assign(w, {value: {}, cmp, idle: true});
1187
+ let push, w = new WatchObject(propsFn, value => push(value));
1188
+ Object.assign(w, { value: {}, cmp, idle: true });
1136
1189
  fire(w);
1137
- ([$dom, push] = $slot($component, $context, w.value));
1138
- if(push) current_cd.watchers.push(w);
1190
+ let $dom = $slot($component, $context, w.value);
1191
+ if($dom.$dom) {
1192
+ if($dom.push) {
1193
+ push = $dom.push;
1194
+ current_cd.watchers.push(w);
1195
+ }
1196
+ $dom = $dom.$dom;
1197
+ }
1139
1198
  return $dom;
1140
1199
  } else return placeholder?.();
1141
1200
  };
@@ -1148,11 +1207,11 @@ const makeSlot = (fr, fn) => {
1148
1207
  $onDestroy(() => cd_detach($cd));
1149
1208
  parentCD.component.apply();
1150
1209
  try {
1151
- return [$dom, fn($dom, $context, callerComponent, props)];
1210
+ return { $dom, push: fn($dom, $context, callerComponent, props) };
1152
1211
  } finally {
1153
1212
  current_cd = prev;
1154
1213
  }
1155
1214
  };
1156
1215
  };
1157
1216
 
1158
- export { $$addEventForComponent, $$awaitBlock, $$cloneDeep, $$compareArray, $$compareDeep, $$deepComparator, $$eachBlock, $$htmlBlock, $$htmlBlockStatic, $$htmlToFragment, $$htmlToFragmentClean, $$removeElements, $$removeItem, $context, $digest, $makeEmitter, $onDestroy, $onMount, $tick, $watch, WatchObject, __app_onerror, __bindActionSubscribe, addClass, addEvent, addStyles, attachAnchor, attachBlock, attachDynComponent, autoSubscribe, bindAction, bindAttribute, bindAttributeBase, bindClass, bindClassExp, bindInput, bindStyle, bindText, callComponent, callExportedFragment, cd_attach, cd_attach2, cd_component, cd_detach, cd_new, childNodes, cloneDeep, configure, createTextNode, current_cd, current_component, current_destroyList, destroyResults, eachDefaultKey, exportFragment, fire, firstChild, getFinalLabel, ifBlock, ifBlockReadOnly, insertAfter, invokeSlot, invokeSlotBase, isArray, isFunction, iterNodes, keyComparator, makeAnchor, makeApply, makeBlock, makeBlockBound, makeClassResolver, makeComponent, makeEachBlock, makeEachSingleBlock, makeExternalProperty, makeRootEvent, makeSlot, mergeEvents, mount, noop, prefixPush, removeElementsBetween, setClassToElement, spreadAttributes, svgToFragment, unwrapProps };
1217
+ export { $$addEventForComponent, $$awaitBlock, $$cloneDeep, $$compareArray, $$compareDeep, $$deepComparator, $$eachBlock, $$htmlBlock, $$htmlBlockStatic, $$htmlToFragment, $$htmlToFragmentClean, $$removeElements, $$removeItem, $context, $digest, $makeEmitter, $onDestroy, $onMount, $tick, $watch, WatchObject, __app_onerror, __bindActionSubscribe, addClass, addEvent, addStyles, attachAnchor, attachBlock, attachDynComponent, autoSubscribe, bindAction, bindAttribute, bindAttributeBase, bindClass, bindClassExp, bindInput, bindStyle, bindText, callComponent, callExportedFragment, cd_attach, cd_attach2, cd_component, cd_detach, cd_new, childNodes, cloneDeep, configure, createTextNode, current_cd, current_component, current_destroyList, destroyResults, eachDefaultKey, exportFragment, fire, firstChild, getFinalLabel, ifBlock, ifBlockReadOnly, insertAfter, invokeSlot, invokeSlotBase, isArray, isFunction, iterNodes, keyComparator, makeAnchor, makeApply, makeBlock, makeBlockBound, makeClassResolver, makeComponent, makeEachBlock, makeEachSingleBlock, makeExternalProperty, makeRootEvent, makeSlot, mergeAllEvents, mergeEvents, mount, mountStatic, noop, prefixPush, refer, removeElementsBetween, setClassToElement, spreadAttributes, svgToFragment, unwrapProps };